docs(agent): annotate stream invariants
This commit is contained in:
@@ -48,6 +48,8 @@ export class EmbeddedBlockChunker {
|
||||
}
|
||||
|
||||
drain(params: { force: boolean; emit: (chunk: string) => void }) {
|
||||
// KNOWN: We cannot split inside fenced code blocks (Markdown breaks + UI glitches).
|
||||
// When forced (maxChars), we close + reopen the fence to keep Markdown valid.
|
||||
const { force, emit } = params;
|
||||
const minChars = Math.max(1, Math.floor(this.#chunking.minChars));
|
||||
const maxChars = Math.max(minChars, Math.floor(this.#chunking.maxChars));
|
||||
|
||||
@@ -177,6 +177,9 @@ export function subscribeEmbeddedPiSession(params: {
|
||||
const blockChunker = blockChunking
|
||||
? new EmbeddedBlockChunker(blockChunking)
|
||||
: null;
|
||||
// KNOWN: Provider streams are not strictly once-only or perfectly ordered.
|
||||
// `text_end` can repeat full content; late `text_end` can arrive after `message_end`.
|
||||
// Tests: `src/agents/pi-embedded-subscribe.test.ts` (e.g. late text_end cases).
|
||||
const shouldEmitToolResult = () =>
|
||||
typeof params.shouldEmitToolResult === "function"
|
||||
? params.shouldEmitToolResult()
|
||||
@@ -231,6 +234,8 @@ export function subscribeEmbeddedPiSession(params: {
|
||||
if (evt.type === "message_start") {
|
||||
const msg = (evt as AgentEvent & { message: AgentMessage }).message;
|
||||
if (msg?.role === "assistant") {
|
||||
// KNOWN: Resetting at `text_end` is unsafe (late/duplicate end events).
|
||||
// ASSUME: `message_start` is the only reliable boundary for “new assistant message begins”.
|
||||
// Start-of-message is a safer reset point than message_end: some providers
|
||||
// may deliver late text_end updates after message_end, which would
|
||||
// otherwise re-trigger block replies.
|
||||
@@ -387,6 +392,8 @@ export function subscribeEmbeddedPiSession(params: {
|
||||
if (delta) {
|
||||
chunk = delta;
|
||||
} else if (content) {
|
||||
// KNOWN: Some providers resend full content on `text_end`.
|
||||
// We only append a suffix (or nothing) to keep output monotonic.
|
||||
// Providers may resend full content on text_end; append only the suffix.
|
||||
if (content.startsWith(deltaBuffer)) {
|
||||
chunk = content.slice(deltaBuffer.length);
|
||||
|
||||
@@ -24,6 +24,8 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
|
||||
_ctx,
|
||||
signal,
|
||||
): Promise<AgentToolResult<unknown>> => {
|
||||
// KNOWN: pi-coding-agent `ToolDefinition.execute` has a different signature/order
|
||||
// than pi-agent-core `AgentTool.execute`. This adapter keeps our existing tools intact.
|
||||
return tool.execute(toolCallId, params, signal, onUpdate);
|
||||
},
|
||||
} satisfies ToolDefinition;
|
||||
|
||||
@@ -27,6 +27,8 @@ export function splitMediaFromOutput(raw: string): {
|
||||
mediaUrls?: string[];
|
||||
mediaUrl?: string; // legacy first item for backward compatibility
|
||||
} {
|
||||
// KNOWN: Leading whitespace is semantically meaningful in Markdown (lists, indented fences).
|
||||
// We only trim the end; token cleanup below handles removing `MEDIA:` lines.
|
||||
const trimmedRaw = raw.trimEnd();
|
||||
if (!trimmedRaw.trim()) return { text: "" };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user