@agent-native/core 0.84.46 → 0.84.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +23 -9
- package/corpus/core/src/agent/tool-call-journal.ts +35 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +20 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/tool-call-journal.d.ts.map +1 -1
- package/dist/agent/tool-call-journal.js +33 -0
- package/dist/agent/tool-call-journal.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call-journal.d.ts","sourceRoot":"","sources":["../../src/agent/tool-call-journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC;;;OAGG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;
|
|
1
|
+
{"version":3,"file":"tool-call-journal.d.ts","sourceRoot":"","sources":["../../src/agent/tool-call-journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC;;;OAGG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AA+DD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,cAAc,EAAE,GAChC,eAAe,CA4DjB;AAyCD,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,oBAAoB,GAAG,SAAS,CAUlC;AA6CD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,GACvB,MAAM,GAAG,IAAI,CAqCf"}
|
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
const INPUT_SIGNATURE_MAX_CHARS = 120;
|
|
38
38
|
/** Max length of a per-tool-call result summary surfaced in the resume note. */
|
|
39
39
|
const RESULT_SUMMARY_MAX_CHARS = 400;
|
|
40
|
+
/** Max length of explicit next-step guidance surfaced outside result summaries. */
|
|
41
|
+
const NEXT_REQUIRED_ACTION_MAX_CHARS = 900;
|
|
40
42
|
function inputSignature(input) {
|
|
41
43
|
if (input == null)
|
|
42
44
|
return "";
|
|
@@ -224,6 +226,33 @@ function summarizeResult(result) {
|
|
|
224
226
|
? oneLine.slice(0, RESULT_SUMMARY_MAX_CHARS) + "…"
|
|
225
227
|
: oneLine;
|
|
226
228
|
}
|
|
229
|
+
function parseResultObject(result) {
|
|
230
|
+
if (!result)
|
|
231
|
+
return {};
|
|
232
|
+
const trimmed = result.trim();
|
|
233
|
+
if (!trimmed.startsWith("{"))
|
|
234
|
+
return {};
|
|
235
|
+
try {
|
|
236
|
+
const parsed = JSON.parse(trimmed);
|
|
237
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
238
|
+
? parsed
|
|
239
|
+
: {};
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return {};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function summarizeNextRequiredAction(result) {
|
|
246
|
+
const action = parseResultObject(result).nextRequiredAction;
|
|
247
|
+
if (typeof action !== "string")
|
|
248
|
+
return null;
|
|
249
|
+
const oneLine = action.replace(/\s+/g, " ").trim();
|
|
250
|
+
if (!oneLine)
|
|
251
|
+
return null;
|
|
252
|
+
return oneLine.length > NEXT_REQUIRED_ACTION_MAX_CHARS
|
|
253
|
+
? oneLine.slice(0, NEXT_REQUIRED_ACTION_MAX_CHARS) + "..."
|
|
254
|
+
: oneLine;
|
|
255
|
+
}
|
|
227
256
|
function describeInput(input) {
|
|
228
257
|
if (!input)
|
|
229
258
|
return "";
|
|
@@ -249,7 +278,11 @@ export function buildResumeJournalNote(journal) {
|
|
|
249
278
|
lines.push("");
|
|
250
279
|
lines.push("Already completed (do NOT re-run these — their side effects already happened; reuse the results below):");
|
|
251
280
|
for (const entry of journal.completed) {
|
|
281
|
+
const nextRequiredAction = summarizeNextRequiredAction(entry.result);
|
|
252
282
|
lines.push(`- ${entry.tool}${describeInput(entry.input)} → ${summarizeResult(entry.result)}`);
|
|
283
|
+
if (nextRequiredAction) {
|
|
284
|
+
lines.push(` Next required action from result: ${nextRequiredAction}`);
|
|
285
|
+
}
|
|
253
286
|
}
|
|
254
287
|
}
|
|
255
288
|
if (journal.interrupted.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call-journal.js","sourceRoot":"","sources":["../../src/agent/tool-call-journal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-turn tool-call journal — derived (not separately recorded) from the\n * existing run-event ledger. Borrowed from Flue's durable-execution journal:\n * when an agent run resumes after an interruption (gateway/transport drop, cold\n * start, soft-timeout auto-continue), we classify the tool calls already in the\n * ledger so the resumed model does NOT re-execute side effects it already\n * completed (re-sending an email, re-creating a ticket) and so it is explicitly\n * told about any tool call that started but whose outcome was never recorded.\n *\n * IMPORTANT — this is a pure read-over-the-ledger view. There is no new\n * recording hook anywhere in the hot path. Classification uses input-aware\n * `tool_start` → `tool_done` matching when a `tool_done` event carries its\n * input, while preserving the legacy positional fallback that older ledger\n * events and `thread-data-builder.ts` rely on:\n *\n * - `tool_start` events carry `{ tool, input }` (no tool-call id at this\n * layer; the ledger event stream omits it).\n * - `tool_done` events carry `{ tool, input?, result }`.\n * - A `tool_done` with input is matched to the open start with the same tool\n * name and deep-canonicalized input.\n * - A legacy `tool_done` without input is matched to the OLDEST still-open\n * `tool_start` for the same tool name (FIFO per tool).\n *\n * A `tool_start` with no matching `tool_done` is the dangerous case: the call\n * began, its side effect may or may not have landed, and the interruption ate\n * the result. We surface those as \"interrupted / unknown outcome\" so the model\n * decides rather than blindly re-running.\n *\n * Two layers of protection are built on this: (1) a prompt-level note on resume\n * (see `run-loop-with-resume.ts`) telling the model what already completed, and\n * (2) tool-layer enforcement in `production-agent.ts`/`runToolCall`, which uses\n * `findCompletedJournalEntry(...)` to refuse re-executing a journaled-complete\n * write tool — returning the journaled result instead of running the side\n * effect again. Layer 2 is the stronger guarantee.\n */\n\nimport type { AgentChatEvent, AgentToolInput } from \"./types.js\";\n\n/** A single recorded tool-call ledger entry, classified by outcome. */\nexport interface ToolCallJournalEntry {\n /**\n * Stable-ish identity for this tool call within the turn. The ledger event\n * stream has no tool-call id, so we key by tool name + a short input\n * signature + positional order, exactly enough to disambiguate repeats of the\n * same tool within one turn for human/model-readable reporting.\n */\n key: string;\n /** Tool / action name (from the `tool_start` event). */\n tool: string;\n /** Tool input captured at `tool_start`, if any. */\n input?: AgentToolInput;\n /** 0-based position of the `tool_start` among all tool calls in the turn. */\n order: number;\n /** Result text from the matched `tool_done`, when the call completed. */\n result?: string;\n}\n\n/** Result of classifying one turn's ledger events. */\nexport interface ToolCallJournal {\n /** Tool calls with a matching `tool_done` — already ran, do NOT re-run. */\n completed: ToolCallJournalEntry[];\n /**\n * Tool calls with a `tool_start` but no matching `tool_done` — interrupted,\n * outcome unknown. The model must decide whether re-running is safe.\n */\n interrupted: ToolCallJournalEntry[];\n}\n\n/** Max length of an input signature included in a journal key (debug-readable). */\nconst INPUT_SIGNATURE_MAX_CHARS = 120;\n\n/** Max length of a per-tool-call result summary surfaced in the resume note. */\nconst RESULT_SUMMARY_MAX_CHARS = 400;\n\nfunction inputSignature(input: unknown): string {\n if (input == null) return \"\";\n try {\n return JSON.stringify(canonicalizeForSignature(input));\n } catch {\n return String(input);\n }\n}\n\nfunction canonicalizeForSignature(\n input: unknown,\n seen = new WeakSet(),\n): unknown {\n if (input == null) return input;\n if (typeof input === \"bigint\") return input.toString();\n if (typeof input === \"function\" || typeof input === \"symbol\") {\n return String(input);\n }\n if (typeof input !== \"object\") return input;\n\n if (seen.has(input)) return \"[Circular]\";\n seen.add(input);\n if (Array.isArray(input)) {\n const output = input.map((value) =>\n value === undefined\n ? \"[Undefined]\"\n : canonicalizeForSignature(value, seen),\n );\n seen.delete(input);\n return output;\n }\n\n const object = input as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const key of Object.keys(object).sort()) {\n const value = object[key];\n output[key] =\n value === undefined\n ? \"[Undefined]\"\n : canonicalizeForSignature(value, seen);\n }\n seen.delete(input);\n return output;\n}\n\nfunction displayInputSignature(input: unknown): string {\n const sig = inputSignature(input);\n return sig.length > INPUT_SIGNATURE_MAX_CHARS\n ? sig.slice(0, INPUT_SIGNATURE_MAX_CHARS)\n : sig;\n}\n\n/**\n * Classify a single turn's recorded events into completed vs interrupted tool\n * calls. Pure and side-effect free — given the same events it always returns\n * the same journal. A `clear` event resets the per-turn tally exactly as the\n * thread rebuild does, so partial streamed output that was discarded on resume\n * doesn't leave phantom open tool calls.\n */\nexport function classifyToolCallJournal(\n events: readonly AgentChatEvent[],\n): ToolCallJournal {\n // Open tool_start entries awaiting a matching tool_done. We index by tool\n // name first, then prefer matching input signatures when the done event\n // carries input. Older ledger entries without done input fall back to FIFO.\n const openByTool = new Map<string, ToolCallJournalEntry[]>();\n const completed: ToolCallJournalEntry[] = [];\n let order = 0;\n\n for (const event of events) {\n if (event.type === \"clear\") {\n // Discarded partial output: drop any not-yet-completed starts so they\n // aren't reported as interrupted. Already-completed entries stay.\n openByTool.clear();\n continue;\n }\n\n if (event.type === \"tool_start\") {\n const tool = event.tool ?? \"unknown\";\n const input = event.input ?? undefined;\n const entry: ToolCallJournalEntry = {\n key: `${tool}#${order}:${displayInputSignature(input)}`,\n tool,\n ...(input ? { input } : {}),\n order,\n };\n order += 1;\n const queue = openByTool.get(tool);\n if (queue) queue.push(entry);\n else openByTool.set(tool, [entry]);\n continue;\n }\n\n if (event.type === \"tool_done\") {\n const tool = event.tool ?? \"unknown\";\n const queue = openByTool.get(tool);\n const entry = takeMatchingOpenEntry(queue, event);\n if (entry) {\n if (isNonCompletedToolDone(event)) {\n // Failed/blocked calls reached a terminal tool_done, but their side\n // effect did not complete. Drop the matching start instead of\n // classifying it as completed or interrupted.\n continue;\n }\n entry.result = event.result ?? \"\";\n completed.push(entry);\n }\n // A tool_done with no open start is ignored — it can't be re-associated\n // and re-reporting a duplicate done would be noise.\n continue;\n }\n }\n\n // Anything still open never received a tool_done → interrupted / unknown.\n const interrupted: ToolCallJournalEntry[] = [];\n for (const queue of openByTool.values()) {\n for (const entry of queue) interrupted.push(entry);\n }\n interrupted.sort((a, b) => a.order - b.order);\n\n return { completed, interrupted };\n}\n\nfunction takeMatchingOpenEntry(\n queue: ToolCallJournalEntry[] | undefined,\n event: Extract<AgentChatEvent, { type: \"tool_done\" }>,\n): ToolCallJournalEntry | undefined {\n if (!queue || queue.length === 0) return undefined;\n if (event.input !== undefined) {\n const doneSig = inputSignature(event.input);\n const index = queue.findIndex(\n (entry) => inputSignature(entry.input) === doneSig,\n );\n if (index >= 0) return queue.splice(index, 1)[0];\n }\n return queue.shift();\n}\n\nfunction isNonCompletedToolDone(\n event: Extract<AgentChatEvent, { type: \"tool_done\" }>,\n): boolean {\n if (event.completedSideEffect === false) return true;\n if (event.isError === true) return true;\n\n const result = (event.result ?? \"\").trim();\n if (!result) return false;\n\n // Legacy run events did not persist isError. Keep known non-execution results\n // from becoming durable \"completed\" write calls after deploy.\n return (\n result.startsWith(\"Error: Unknown tool\") ||\n result.startsWith(\"Error running \") ||\n result.startsWith(\"Invalid action parameters for \") ||\n result.startsWith(\"Plan mode blocked \") ||\n result.startsWith(\"Skipped \") ||\n result.startsWith(\"Stopped after \") ||\n result.startsWith(\"The tool was not executed\") ||\n result.startsWith(\"Awaiting human approval\") ||\n result.includes(\" did NOT execute\")\n );\n}\n\n/** True when the journal has nothing worth telling a resuming model about. */\nexport function isJournalEmpty(journal: ToolCallJournal): boolean {\n return journal.completed.length === 0 && journal.interrupted.length === 0;\n}\n\n/**\n * Find a COMPLETED journal entry that matches a tool call about to be\n * dispatched, by tool name + input signature (position-independent — a resumed\n * call may sit at a different order than the original). Used by the tool-layer\n * hard-block in production-agent.ts/runToolCall to skip re-executing a side\n * effect that already completed in a prior interrupted chunk: when this returns\n * an entry, the loop returns `entry.result` instead of running the action.\n *\n * Returns the FIRST unmatched completed entry for that (name + input); the\n * caller is expected to claim it (mark it consumed) so two identical fresh\n * calls in the same turn don't both short-circuit on one journaled completion.\n * Returns undefined when there is no completed entry for this exact call —\n * including every fresh call, which must execute normally.\n */\nexport function findCompletedJournalEntry(\n journal: ToolCallJournal,\n toolName: string,\n input: unknown,\n consumedKeys?: Set<string>,\n): ToolCallJournalEntry | undefined {\n const wantSig = inputSignature(input);\n for (const entry of journal.completed) {\n if (entry.tool !== toolName) continue;\n if (inputSignature(entry.input) !== wantSig) continue;\n if (consumedKeys?.has(entry.key)) continue;\n consumedKeys?.add(entry.key);\n return entry;\n }\n return undefined;\n}\n\nfunction summarizeResult(result: string | undefined): string {\n if (!result) return \"(no result recorded)\";\n const oneLine = result.replace(/\\s+/g, \" \").trim();\n if (oneLine.length === 0) return \"(empty result)\";\n return oneLine.length > RESULT_SUMMARY_MAX_CHARS\n ? oneLine.slice(0, RESULT_SUMMARY_MAX_CHARS) + \"…\"\n : oneLine;\n}\n\nfunction describeInput(input: unknown): string {\n if (!input) return \"\";\n const sig = displayInputSignature(input);\n return sig && sig !== \"{}\" ? ` input: ${sig}` : \"\";\n}\n\n/**\n * Build the structured resume note injected on auto-continue. Returns `null`\n * when there are no completed or interrupted tool calls to report, so the\n * caller can preserve the exact pre-existing \"continue from where you left off\"\n * behavior on normal resumes (no regression for turns with no tool activity).\n *\n * The note is intentionally a flat, model-readable block: a list of already-run\n * tool calls (with short results) the model must NOT re-run, and a separate list\n * of interrupted calls whose outcome is unknown for the model to decide on.\n */\nexport function buildResumeJournalNote(\n journal: ToolCallJournal,\n): string | null {\n if (isJournalEmpty(journal)) return null;\n\n const lines: string[] = [];\n lines.push(\n \"Tool-call journal from the interrupted attempt (derived from the durable run ledger):\",\n );\n\n if (journal.completed.length > 0) {\n lines.push(\"\");\n lines.push(\n \"Already completed (do NOT re-run these — their side effects already happened; reuse the results below):\",\n );\n for (const entry of journal.completed) {\n lines.push(\n `- ${entry.tool}${describeInput(entry.input)} → ${summarizeResult(entry.result)}`,\n );\n }\n }\n\n if (journal.interrupted.length > 0) {\n lines.push(\"\");\n lines.push(\n \"Interrupted / unknown outcome (these started but no result was recorded before the cut-off — do not assume they succeeded OR failed; if re-running could duplicate a side effect, verify state first):\",\n );\n for (const entry of journal.interrupted) {\n lines.push(\n `- ${entry.tool}${describeInput(entry.input)} → (no result recorded)`,\n );\n }\n }\n\n return lines.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool-call-journal.js","sourceRoot":"","sources":["../../src/agent/tool-call-journal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-turn tool-call journal — derived (not separately recorded) from the\n * existing run-event ledger. Borrowed from Flue's durable-execution journal:\n * when an agent run resumes after an interruption (gateway/transport drop, cold\n * start, soft-timeout auto-continue), we classify the tool calls already in the\n * ledger so the resumed model does NOT re-execute side effects it already\n * completed (re-sending an email, re-creating a ticket) and so it is explicitly\n * told about any tool call that started but whose outcome was never recorded.\n *\n * IMPORTANT — this is a pure read-over-the-ledger view. There is no new\n * recording hook anywhere in the hot path. Classification uses input-aware\n * `tool_start` → `tool_done` matching when a `tool_done` event carries its\n * input, while preserving the legacy positional fallback that older ledger\n * events and `thread-data-builder.ts` rely on:\n *\n * - `tool_start` events carry `{ tool, input }` (no tool-call id at this\n * layer; the ledger event stream omits it).\n * - `tool_done` events carry `{ tool, input?, result }`.\n * - A `tool_done` with input is matched to the open start with the same tool\n * name and deep-canonicalized input.\n * - A legacy `tool_done` without input is matched to the OLDEST still-open\n * `tool_start` for the same tool name (FIFO per tool).\n *\n * A `tool_start` with no matching `tool_done` is the dangerous case: the call\n * began, its side effect may or may not have landed, and the interruption ate\n * the result. We surface those as \"interrupted / unknown outcome\" so the model\n * decides rather than blindly re-running.\n *\n * Two layers of protection are built on this: (1) a prompt-level note on resume\n * (see `run-loop-with-resume.ts`) telling the model what already completed, and\n * (2) tool-layer enforcement in `production-agent.ts`/`runToolCall`, which uses\n * `findCompletedJournalEntry(...)` to refuse re-executing a journaled-complete\n * write tool — returning the journaled result instead of running the side\n * effect again. Layer 2 is the stronger guarantee.\n */\n\nimport type { AgentChatEvent, AgentToolInput } from \"./types.js\";\n\n/** A single recorded tool-call ledger entry, classified by outcome. */\nexport interface ToolCallJournalEntry {\n /**\n * Stable-ish identity for this tool call within the turn. The ledger event\n * stream has no tool-call id, so we key by tool name + a short input\n * signature + positional order, exactly enough to disambiguate repeats of the\n * same tool within one turn for human/model-readable reporting.\n */\n key: string;\n /** Tool / action name (from the `tool_start` event). */\n tool: string;\n /** Tool input captured at `tool_start`, if any. */\n input?: AgentToolInput;\n /** 0-based position of the `tool_start` among all tool calls in the turn. */\n order: number;\n /** Result text from the matched `tool_done`, when the call completed. */\n result?: string;\n}\n\n/** Result of classifying one turn's ledger events. */\nexport interface ToolCallJournal {\n /** Tool calls with a matching `tool_done` — already ran, do NOT re-run. */\n completed: ToolCallJournalEntry[];\n /**\n * Tool calls with a `tool_start` but no matching `tool_done` — interrupted,\n * outcome unknown. The model must decide whether re-running is safe.\n */\n interrupted: ToolCallJournalEntry[];\n}\n\n/** Max length of an input signature included in a journal key (debug-readable). */\nconst INPUT_SIGNATURE_MAX_CHARS = 120;\n\n/** Max length of a per-tool-call result summary surfaced in the resume note. */\nconst RESULT_SUMMARY_MAX_CHARS = 400;\n\n/** Max length of explicit next-step guidance surfaced outside result summaries. */\nconst NEXT_REQUIRED_ACTION_MAX_CHARS = 900;\n\nfunction inputSignature(input: unknown): string {\n if (input == null) return \"\";\n try {\n return JSON.stringify(canonicalizeForSignature(input));\n } catch {\n return String(input);\n }\n}\n\nfunction canonicalizeForSignature(\n input: unknown,\n seen = new WeakSet(),\n): unknown {\n if (input == null) return input;\n if (typeof input === \"bigint\") return input.toString();\n if (typeof input === \"function\" || typeof input === \"symbol\") {\n return String(input);\n }\n if (typeof input !== \"object\") return input;\n\n if (seen.has(input)) return \"[Circular]\";\n seen.add(input);\n if (Array.isArray(input)) {\n const output = input.map((value) =>\n value === undefined\n ? \"[Undefined]\"\n : canonicalizeForSignature(value, seen),\n );\n seen.delete(input);\n return output;\n }\n\n const object = input as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const key of Object.keys(object).sort()) {\n const value = object[key];\n output[key] =\n value === undefined\n ? \"[Undefined]\"\n : canonicalizeForSignature(value, seen);\n }\n seen.delete(input);\n return output;\n}\n\nfunction displayInputSignature(input: unknown): string {\n const sig = inputSignature(input);\n return sig.length > INPUT_SIGNATURE_MAX_CHARS\n ? sig.slice(0, INPUT_SIGNATURE_MAX_CHARS)\n : sig;\n}\n\n/**\n * Classify a single turn's recorded events into completed vs interrupted tool\n * calls. Pure and side-effect free — given the same events it always returns\n * the same journal. A `clear` event resets the per-turn tally exactly as the\n * thread rebuild does, so partial streamed output that was discarded on resume\n * doesn't leave phantom open tool calls.\n */\nexport function classifyToolCallJournal(\n events: readonly AgentChatEvent[],\n): ToolCallJournal {\n // Open tool_start entries awaiting a matching tool_done. We index by tool\n // name first, then prefer matching input signatures when the done event\n // carries input. Older ledger entries without done input fall back to FIFO.\n const openByTool = new Map<string, ToolCallJournalEntry[]>();\n const completed: ToolCallJournalEntry[] = [];\n let order = 0;\n\n for (const event of events) {\n if (event.type === \"clear\") {\n // Discarded partial output: drop any not-yet-completed starts so they\n // aren't reported as interrupted. Already-completed entries stay.\n openByTool.clear();\n continue;\n }\n\n if (event.type === \"tool_start\") {\n const tool = event.tool ?? \"unknown\";\n const input = event.input ?? undefined;\n const entry: ToolCallJournalEntry = {\n key: `${tool}#${order}:${displayInputSignature(input)}`,\n tool,\n ...(input ? { input } : {}),\n order,\n };\n order += 1;\n const queue = openByTool.get(tool);\n if (queue) queue.push(entry);\n else openByTool.set(tool, [entry]);\n continue;\n }\n\n if (event.type === \"tool_done\") {\n const tool = event.tool ?? \"unknown\";\n const queue = openByTool.get(tool);\n const entry = takeMatchingOpenEntry(queue, event);\n if (entry) {\n if (isNonCompletedToolDone(event)) {\n // Failed/blocked calls reached a terminal tool_done, but their side\n // effect did not complete. Drop the matching start instead of\n // classifying it as completed or interrupted.\n continue;\n }\n entry.result = event.result ?? \"\";\n completed.push(entry);\n }\n // A tool_done with no open start is ignored — it can't be re-associated\n // and re-reporting a duplicate done would be noise.\n continue;\n }\n }\n\n // Anything still open never received a tool_done → interrupted / unknown.\n const interrupted: ToolCallJournalEntry[] = [];\n for (const queue of openByTool.values()) {\n for (const entry of queue) interrupted.push(entry);\n }\n interrupted.sort((a, b) => a.order - b.order);\n\n return { completed, interrupted };\n}\n\nfunction takeMatchingOpenEntry(\n queue: ToolCallJournalEntry[] | undefined,\n event: Extract<AgentChatEvent, { type: \"tool_done\" }>,\n): ToolCallJournalEntry | undefined {\n if (!queue || queue.length === 0) return undefined;\n if (event.input !== undefined) {\n const doneSig = inputSignature(event.input);\n const index = queue.findIndex(\n (entry) => inputSignature(entry.input) === doneSig,\n );\n if (index >= 0) return queue.splice(index, 1)[0];\n }\n return queue.shift();\n}\n\nfunction isNonCompletedToolDone(\n event: Extract<AgentChatEvent, { type: \"tool_done\" }>,\n): boolean {\n if (event.completedSideEffect === false) return true;\n if (event.isError === true) return true;\n\n const result = (event.result ?? \"\").trim();\n if (!result) return false;\n\n // Legacy run events did not persist isError. Keep known non-execution results\n // from becoming durable \"completed\" write calls after deploy.\n return (\n result.startsWith(\"Error: Unknown tool\") ||\n result.startsWith(\"Error running \") ||\n result.startsWith(\"Invalid action parameters for \") ||\n result.startsWith(\"Plan mode blocked \") ||\n result.startsWith(\"Skipped \") ||\n result.startsWith(\"Stopped after \") ||\n result.startsWith(\"The tool was not executed\") ||\n result.startsWith(\"Awaiting human approval\") ||\n result.includes(\" did NOT execute\")\n );\n}\n\n/** True when the journal has nothing worth telling a resuming model about. */\nexport function isJournalEmpty(journal: ToolCallJournal): boolean {\n return journal.completed.length === 0 && journal.interrupted.length === 0;\n}\n\n/**\n * Find a COMPLETED journal entry that matches a tool call about to be\n * dispatched, by tool name + input signature (position-independent — a resumed\n * call may sit at a different order than the original). Used by the tool-layer\n * hard-block in production-agent.ts/runToolCall to skip re-executing a side\n * effect that already completed in a prior interrupted chunk: when this returns\n * an entry, the loop returns `entry.result` instead of running the action.\n *\n * Returns the FIRST unmatched completed entry for that (name + input); the\n * caller is expected to claim it (mark it consumed) so two identical fresh\n * calls in the same turn don't both short-circuit on one journaled completion.\n * Returns undefined when there is no completed entry for this exact call —\n * including every fresh call, which must execute normally.\n */\nexport function findCompletedJournalEntry(\n journal: ToolCallJournal,\n toolName: string,\n input: unknown,\n consumedKeys?: Set<string>,\n): ToolCallJournalEntry | undefined {\n const wantSig = inputSignature(input);\n for (const entry of journal.completed) {\n if (entry.tool !== toolName) continue;\n if (inputSignature(entry.input) !== wantSig) continue;\n if (consumedKeys?.has(entry.key)) continue;\n consumedKeys?.add(entry.key);\n return entry;\n }\n return undefined;\n}\n\nfunction summarizeResult(result: string | undefined): string {\n if (!result) return \"(no result recorded)\";\n const oneLine = result.replace(/\\s+/g, \" \").trim();\n if (oneLine.length === 0) return \"(empty result)\";\n return oneLine.length > RESULT_SUMMARY_MAX_CHARS\n ? oneLine.slice(0, RESULT_SUMMARY_MAX_CHARS) + \"…\"\n : oneLine;\n}\n\nfunction parseResultObject(\n result: string | undefined,\n): Record<string, unknown> {\n if (!result) return {};\n const trimmed = result.trim();\n if (!trimmed.startsWith(\"{\")) return {};\n try {\n const parsed = JSON.parse(trimmed);\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction summarizeNextRequiredAction(\n result: string | undefined,\n): string | null {\n const action = parseResultObject(result).nextRequiredAction;\n if (typeof action !== \"string\") return null;\n const oneLine = action.replace(/\\s+/g, \" \").trim();\n if (!oneLine) return null;\n return oneLine.length > NEXT_REQUIRED_ACTION_MAX_CHARS\n ? oneLine.slice(0, NEXT_REQUIRED_ACTION_MAX_CHARS) + \"...\"\n : oneLine;\n}\n\nfunction describeInput(input: unknown): string {\n if (!input) return \"\";\n const sig = displayInputSignature(input);\n return sig && sig !== \"{}\" ? ` input: ${sig}` : \"\";\n}\n\n/**\n * Build the structured resume note injected on auto-continue. Returns `null`\n * when there are no completed or interrupted tool calls to report, so the\n * caller can preserve the exact pre-existing \"continue from where you left off\"\n * behavior on normal resumes (no regression for turns with no tool activity).\n *\n * The note is intentionally a flat, model-readable block: a list of already-run\n * tool calls (with short results) the model must NOT re-run, and a separate list\n * of interrupted calls whose outcome is unknown for the model to decide on.\n */\nexport function buildResumeJournalNote(\n journal: ToolCallJournal,\n): string | null {\n if (isJournalEmpty(journal)) return null;\n\n const lines: string[] = [];\n lines.push(\n \"Tool-call journal from the interrupted attempt (derived from the durable run ledger):\",\n );\n\n if (journal.completed.length > 0) {\n lines.push(\"\");\n lines.push(\n \"Already completed (do NOT re-run these — their side effects already happened; reuse the results below):\",\n );\n for (const entry of journal.completed) {\n const nextRequiredAction = summarizeNextRequiredAction(entry.result);\n lines.push(\n `- ${entry.tool}${describeInput(entry.input)} → ${summarizeResult(entry.result)}`,\n );\n if (nextRequiredAction) {\n lines.push(` Next required action from result: ${nextRequiredAction}`);\n }\n }\n }\n\n if (journal.interrupted.length > 0) {\n lines.push(\"\");\n lines.push(\n \"Interrupted / unknown outcome (these started but no result was recorded before the cut-off — do not assume they succeeded OR failed; if re-running could duplicate a side effect, verify state first):\",\n );\n for (const entry of journal.interrupted) {\n lines.push(\n `- ${entry.tool}${describeInput(entry.input)} → (no result recorded)`,\n );\n }\n }\n\n return lines.join(\"\\n\");\n}\n"]}
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -41,9 +41,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
41
41
|
* Body: { text: string, fieldName?: string, requestSource?: string }
|
|
42
42
|
*/
|
|
43
43
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
44
|
+
text?: undefined;
|
|
44
45
|
ok?: undefined;
|
|
45
46
|
error: string;
|
|
46
|
-
text?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
error?: undefined;
|
|
49
49
|
ok: boolean;
|
|
@@ -41,22 +41,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
summary: import("./types.js").TraceSummary;
|
|
45
46
|
spans: import("./types.js").TraceSpan[];
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
error?: undefined;
|
|
48
48
|
id?: undefined;
|
|
49
49
|
} | {
|
|
50
|
+
ok?: undefined;
|
|
50
51
|
summary?: undefined;
|
|
51
52
|
spans?: undefined;
|
|
52
53
|
id: string;
|
|
53
|
-
ok?: undefined;
|
|
54
54
|
error?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
error: any;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
id?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
summary?: undefined;
|
|
@@ -52,8 +52,8 @@ export declare function handleDeleteResource(event: any): Promise<{
|
|
|
52
52
|
error: string;
|
|
53
53
|
ok?: undefined;
|
|
54
54
|
} | {
|
|
55
|
-
ok: boolean;
|
|
56
55
|
error?: undefined;
|
|
56
|
+
ok: boolean;
|
|
57
57
|
}>;
|
|
58
58
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
59
59
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,9 +73,9 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
+
error?: undefined;
|
|
76
77
|
url: string;
|
|
77
78
|
provider: string;
|
|
78
|
-
error?: undefined;
|
|
79
79
|
}>;
|
|
80
80
|
export {};
|
|
81
81
|
//# sourceMappingURL=handlers.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.48",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|