@tangle-network/agent-runtime 0.108.1 → 0.109.1

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.
Files changed (56) hide show
  1. package/README.md +7 -6
  2. package/dist/{activation-DRpnplEm.js → activation-CM-L6TtL.js} +2 -2
  3. package/dist/{activation-DRpnplEm.js.map → activation-CM-L6TtL.js.map} +1 -1
  4. package/dist/agent.d.ts +2 -2
  5. package/dist/agent.js +2 -2
  6. package/dist/analyst-loop.d.ts +1 -1
  7. package/dist/{environment-provider-D0NXc4Qz.d.ts → environment-provider-CMqSAp-O.d.ts} +3 -2
  8. package/dist/environment-provider.d.ts +1 -1
  9. package/dist/{improvement-cycle-CRnDDdX0.js → improvement-cycle-9O4iVFOA.js} +3 -3
  10. package/dist/{improvement-cycle-CRnDDdX0.js.map → improvement-cycle-9O4iVFOA.js.map} +1 -1
  11. package/dist/{index-DQp3BPeC.d.ts → index-Bur6uUF4.d.ts} +6 -152
  12. package/dist/{index-cdkwHruJ.d.ts → index-Cn5TtHUG.d.ts} +3 -3
  13. package/dist/{index-BZbJWqoZ.d.ts → index-bXiMVrUv.d.ts} +8 -7
  14. package/dist/index.d.ts +8 -6
  15. package/dist/index.js +11 -635
  16. package/dist/index.js.map +1 -1
  17. package/dist/intelligence.d.ts +2 -2
  18. package/dist/intelligence.js +2 -2
  19. package/dist/{loops.d.ts → kernel.d.ts} +3 -3
  20. package/dist/{loops.js → kernel.js} +4 -4
  21. package/dist/{knowledge-rdrpPIXs.js → knowledge-1a38bcUT.js} +3 -3
  22. package/dist/{knowledge-rdrpPIXs.js.map → knowledge-1a38bcUT.js.map} +1 -1
  23. package/dist/knowledge.d.ts +1 -1
  24. package/dist/knowledge.js +1 -1
  25. package/dist/{loop-runner-bin-9-JlGqN7.d.ts → loop-runner-bin-CSzrWlgE.d.ts} +3 -3
  26. package/dist/{loop-runner-bin-B1V-XS5A.js → loop-runner-bin-V4EN9aNT.js} +3 -3
  27. package/dist/{loop-runner-bin-B1V-XS5A.js.map → loop-runner-bin-V4EN9aNT.js.map} +1 -1
  28. package/dist/loop-runner-bin.d.ts +1 -1
  29. package/dist/loop-runner-bin.js +1 -1
  30. package/dist/mcp/bin.js +1 -1
  31. package/dist/mcp/index.d.ts +2 -2
  32. package/dist/mcp/index.js +4 -4
  33. package/dist/{openai-tools-BjQq2TTL.js → openai-tools-DT9FH2_t.js} +2 -2
  34. package/dist/{openai-tools-BjQq2TTL.js.map → openai-tools-DT9FH2_t.js.map} +1 -1
  35. package/dist/primeintellect/index.d.ts +1 -1
  36. package/dist/profiles.d.ts +1 -1
  37. package/dist/profiles.js.map +1 -1
  38. package/dist/{runtime-CDekUROm.js → runtime-BVMyqgct.js} +8 -7
  39. package/dist/{runtime-CDekUROm.js.map → runtime-BVMyqgct.js.map} +1 -1
  40. package/dist/runtime-hooks-C7iJOWm3.js +99 -0
  41. package/dist/runtime-hooks-C7iJOWm3.js.map +1 -0
  42. package/dist/runtime-hooks-sbRpjStq.d.ts +88 -0
  43. package/dist/{structural-rollout-BC81Otmc.js → structural-rollout-ASQLr4-v.js} +2 -2
  44. package/dist/{structural-rollout-BC81Otmc.js.map → structural-rollout-ASQLr4-v.js.map} +1 -1
  45. package/dist/{supervise-JfKPwIlO.js → supervise-DyPmmhJ6.js} +6 -5
  46. package/dist/supervise-DyPmmhJ6.js.map +1 -0
  47. package/dist/{supervisor-B2LzaWRb.js → supervisor-sTJC9psT.js} +3 -98
  48. package/dist/supervisor-sTJC9psT.js.map +1 -0
  49. package/dist/testing.js +8 -8
  50. package/dist/tool-loop.d.ts +149 -0
  51. package/dist/tool-loop.js +629 -0
  52. package/dist/tool-loop.js.map +1 -0
  53. package/dist/{types-BevOjfTY.d.ts → types-DnNGJ5Gz.d.ts} +3 -88
  54. package/package.json +21 -10
  55. package/dist/supervise-JfKPwIlO.js.map +0 -1
  56. package/dist/supervisor-B2LzaWRb.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-loop.js","names":[],"sources":["../src/tool-loop.ts"],"sourcesContent":["/**\n * Bounded turn-level tool-dispatch loop.\n *\n * `runAgentTaskStream` runs ONE model turn; `runAgentRounds` orchestrates DELEGATED\n * multi-agent topologies (refine / fanout-vote). Neither is the everyday\n * interactive shape: a chat turn where the model may emit tool calls, each is\n * executed, the results are folded back, and the turn re-runs until the model\n * stops (or a turn cap). Every agent app hand-rolls that loop — this is it,\n * as a reusable primitive.\n *\n * Substrate-neutral by design: the caller supplies `streamTurn` (wrapping\n * whatever backend / `runAgentTaskStream` it uses) and `executeToolCall`\n * (routing to its executors). This module owns the LOOP; the caller owns the\n * model and the executors. `Raw` (streaming variant) is the caller's own\n * event type. The only imported contract is the runtime hook type: hooks are\n * execution-scoped observers, not part of the agent profile.\n */\n\nimport type { RuntimeDecisionEvidenceRef, RuntimeHooks } from './runtime-hooks'\nimport { notifyRuntimeDecisionPoint, notifyRuntimeHookEvent } from './runtime-hooks'\n\nexport interface ToolLoopCall {\n toolCallId?: string\n toolName: string\n args: Record<string, unknown>\n}\n\n/** Outcome of one tool dispatch — structurally compatible with a hub/integration\n * tool-outcome union, so callers can fold either through the loop. */\nexport type ToolCallOutcome =\n | { ok: true; result: unknown }\n | { ok: false; code: string; message: string; status?: number }\n\n/** Runaway-backstop: stops an infinite tool loop where cost is unmetered. Set\n * far above any legitimate workflow — this is a watchdog, not a policy cap.\n * Legitimate per-call budgets come from `maxCostUsd` + `costOf`. */\nconst RUNAWAY_BACKSTOP_TURNS = 200\nconst DEFAULT_DECISION_CONTEXT_CHARS = 12_000\nconst FAILURE_RECOVERY_ACTIONS = ['retry', 'verify', 'continue', 'stop']\n/** Consecutive identical calls (same tool + canonical-JSON args) that trigger\n * stuck-loop detection. The window resets on any different call. */\nconst STUCK_LOOP_THRESHOLD = 3\n\n/** One OpenAI-shaped tool-call entry carried on an assistant message. */\nexport interface ToolLoopAssistantToolCall {\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n}\n\n/**\n * A message in the running conversation the loop sends to `streamTurn`.\n *\n * The base `{ role, content }` covers `system` / `user` / plain `assistant`\n * turns. Two optional fields carry the OpenAI function-calling contract so a\n * strict model (Claude, and any OpenAI-compatible provider that validates tool\n * history) reads its own tool use back instead of re-issuing the same call:\n *\n * - an assistant turn that emitted tool calls carries `tool_calls`, and its\n * `content` is `null` when the turn was tool-only;\n * - each tool result is its own `{ role: 'tool', tool_call_id, content }`\n * message keyed to the call that produced it.\n *\n * Widening is additive: a `streamTurn` that reads only `role` + `content` still\n * works; one that forwards the whole message to an OpenAI-compatible endpoint\n * now sends correct tool history.\n */\nexport type ToolLoopMessage = {\n role: string\n content: string | null\n tool_calls?: ToolLoopAssistantToolCall[]\n tool_call_id?: string\n}\n\n/** A tool-call id is required to key a `role: 'tool'` result back to its call.\n * When the model omitted one, derive a stable id from the tool name so the\n * assistant `tool_calls` entry and its `tool` result still match. */\nfunction toolCallId(call: ToolLoopCall): string {\n return call.toolCallId ?? `call_${call.toolName}`\n}\n\n/** The assistant turn that emitted `pending`, in OpenAI shape: text content\n * (null when the turn was tool-only) plus its `tool_calls` array. */\nfunction assistantToolCallMessage(turnText: string, pending: ToolLoopCall[]): ToolLoopMessage {\n return {\n role: 'assistant',\n content: turnText.trim() || null,\n tool_calls: pending.map((call) => ({\n id: toolCallId(call),\n type: 'function',\n function: { name: call.toolName, arguments: JSON.stringify(call.args) },\n })),\n }\n}\n\n/** One `role: 'tool'` result message keyed to its call by `tool_call_id`. */\nfunction toolResultMessage(call: ToolLoopCall, content: string): ToolLoopMessage {\n return { role: 'tool', tool_call_id: toolCallId(call), content }\n}\n\nfunction defaultRender(label: string, outcome: ToolCallOutcome): string {\n if (outcome.ok) return `- ${label} → ok: ${JSON.stringify(outcome.result)}`\n return `- ${label} → failed (${outcome.code}): ${outcome.message}`\n}\n\n// ── Awaitable variant (drain-only callers, tests) ──────────────────────────\n\nexport type ToolLoopEvent =\n | { type: 'text'; text: string }\n | { type: 'tool_call'; call: ToolLoopCall }\n | { type: 'other'; event: unknown }\n\n/** Why the loop stopped. `completed` = model finished naturally; `stuck-loop` =\n * ≥3 consecutive identical tool calls (same tool + args); `backstop` = hit the\n * runaway-backstop cap (200 by default); `deadline` = wall-clock deadlineMs\n * exceeded; `budget` = maxCostUsd exhausted. Non-`completed` stops are infra /\n * resource outcomes — eval scoring must distinguish them from capability failure. */\nexport type ToolLoopStopReason = 'completed' | 'stuck-loop' | 'backstop' | 'deadline' | 'budget'\n\nexport interface ToolLoopResult {\n finalText: string\n toolResults: Array<{ call: ToolLoopCall; label: string; outcome: ToolCallOutcome }>\n turns: number\n stopReason: ToolLoopStopReason\n /** @deprecated Use `stopReason !== 'completed'` instead. */\n cappedOut: boolean\n}\n\nexport interface RunToolLoopOptions {\n systemPrompt: string\n userMessage: string\n priorMessages?: ToolLoopMessage[]\n streamTurn: (messages: ToolLoopMessage[]) => AsyncIterable<ToolLoopEvent>\n executeToolCall: (call: ToolLoopCall) => Promise<ToolCallOutcome>\n isExecutableTool: (toolName: string) => boolean\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow.\n * For per-workflow limits, use `maxCostUsd` or `deadlineMs` instead. */\n maxToolTurns?: number\n /** Wall-clock deadline in ms since epoch (Date.now()-based). When exceeded the\n * loop stops with stopReason `deadline`. */\n deadlineMs?: number\n /** Maximum total cost in USD. Requires `costOf` to meter each tool call. */\n maxCostUsd?: number\n /** Return the USD cost of one outcome. Required for `maxCostUsd` to work. */\n costOf?: (call: ToolLoopCall, outcome: ToolCallOutcome) => number\n renderResult?: (label: string, outcome: ToolCallOutcome) => string\n labelFor?: (call: ToolLoopCall) => string\n runId?: string\n scenarioId?: string\n hooks?: RuntimeHooks\n}\n\n/** Run the bounded tool loop and return the final text + every executed tool\n * outcome. Awaitable — callers needing to stream events to a UI use\n * {@link streamToolLoop}. */\nexport async function runToolLoop(opts: RunToolLoopOptions): Promise<ToolLoopResult> {\n const backstop = opts.maxToolTurns ?? RUNAWAY_BACKSTOP_TURNS\n const render = opts.renderResult ?? defaultRender\n const labelFor = opts.labelFor ?? ((c: ToolLoopCall) => c.toolName)\n const runId = opts.runId ?? `agent-run-${randomSuffix()}`\n const messages: ToolLoopMessage[] = [\n { role: 'system', content: opts.systemPrompt },\n ...(opts.priorMessages ?? []),\n { role: 'user', content: opts.userMessage },\n ]\n const observer = createToolLoopObserver(opts.hooks, runId, opts.scenarioId)\n const toolResults: ToolLoopResult['toolResults'] = []\n let finalText = ''\n let turns = 0\n let accumulatedCostUsd = 0\n // Stuck-loop detection: track the last canonical-JSON call signature and how\n // many consecutive times we've seen it.\n let lastCallHash: string | null = null\n let consecutiveCount = 0\n\n observer.loopBefore(backstop, messages.length)\n\n for (let toolTurn = 0; ; toolTurn++) {\n turns++\n\n // Wall-clock deadline check — before every new turn.\n if (opts.deadlineMs !== undefined && Date.now() >= opts.deadlineMs) {\n observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: 'deadline' })\n return { finalText, toolResults, turns, stopReason: 'deadline', cappedOut: true }\n }\n\n let turnText = ''\n const pending: ToolLoopCall[] = []\n const turnEventId = observer.turnBefore(toolTurn, messages.length)\n for await (const ev of opts.streamTurn([...messages])) {\n if (ev.type === 'text') {\n turnText += ev.text\n finalText += ev.text\n } else if (ev.type === 'tool_call' && opts.isExecutableTool(ev.call.toolName)) {\n pending.push(ev.call)\n }\n }\n if (pending.length === 0) {\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: 0,\n finalTextChars: finalText.length,\n })\n break\n }\n\n // Runaway backstop — the model keeps emitting calls past the safety ceiling.\n if (toolTurn >= backstop) {\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n stopReason: 'backstop',\n })\n observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: 'backstop' })\n return { finalText, toolResults, turns, stopReason: 'backstop', cappedOut: true }\n }\n\n // The assistant turn that emitted the calls, carrying its tool_calls array,\n // so a strict model reads its own tool use back in OpenAI shape.\n messages.push(assistantToolCallMessage(turnText, pending))\n const outcomes: ExecutedToolCall[] = []\n for (const [callIndex, call] of pending.entries()) {\n // Stuck-loop detection: hash the first pending call each turn (a model\n // stuck in a loop re-issues the same call repeatedly, not alternating calls).\n const callHash = canonicalCallHash(call)\n if (callHash === lastCallHash) {\n consecutiveCount++\n } else {\n lastCallHash = callHash\n consecutiveCount = 1\n }\n if (consecutiveCount >= STUCK_LOOP_THRESHOLD) {\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n stopReason: 'stuck-loop',\n })\n observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: 'stuck-loop' })\n return { finalText, toolResults, turns, stopReason: 'stuck-loop', cappedOut: true }\n }\n\n const callEventId = observer.toolCallBefore(toolTurn, turnEventId, callIndex, call)\n let outcome: ToolCallOutcome\n try {\n outcome = await opts.executeToolCall(call)\n } catch (err) {\n outcome = {\n ok: false,\n code: 'executor_error',\n message: err instanceof Error ? err.message : String(err),\n }\n }\n\n // Budget check after each tool call.\n if (opts.maxCostUsd !== undefined && opts.costOf !== undefined) {\n accumulatedCostUsd += opts.costOf(call, outcome)\n if (accumulatedCostUsd >= opts.maxCostUsd) {\n const label = labelFor(call)\n toolResults.push({ call, label, outcome })\n messages.push(toolResultMessage(call, render(label, outcome)))\n observer.toolCallAfter(toolTurn, callEventId, call, outcome)\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n stopReason: 'budget',\n })\n observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: 'budget' })\n return { finalText, toolResults, turns, stopReason: 'budget', cappedOut: true }\n }\n }\n\n const label = labelFor(call)\n const rendered = render(label, outcome)\n toolResults.push({ call, label, outcome })\n outcomes.push({ call, label, outcome, rendered })\n // One role:'tool' message per result, keyed to its call by tool_call_id.\n messages.push(toolResultMessage(call, rendered))\n observer.toolCallAfter(toolTurn, callEventId, call, outcome)\n }\n observer.failureRecovery({\n toolTurn,\n messages,\n turnText,\n outcomes,\n })\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n toolResults: outcomes.map((item) => ({\n toolName: item.call.toolName,\n toolCallId: item.call.toolCallId,\n ok: item.outcome.ok,\n })),\n failedToolCalls: outcomes.filter((item) => !item.outcome.ok).length,\n })\n }\n observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: 'completed' })\n return { finalText, toolResults, turns, stopReason: 'completed', cappedOut: false }\n}\n\n// ── Streaming variant (SSE chat runtimes + per-event telemetry) ────────────\n\nexport type StreamToolLoopYield<Raw> =\n | { kind: 'event'; event: Raw }\n | {\n kind: 'tool_result'\n toolName: string\n toolCallId?: string\n label: string\n outcome: ToolCallOutcome\n }\n | { kind: 'capped'; pending: number; stopReason: Exclude<ToolLoopStopReason, 'completed'> }\n\nexport interface StreamToolLoopOptions<Raw> {\n systemPrompt: string\n userMessage: string\n priorMessages?: ToolLoopMessage[]\n streamTurn: (messages: ToolLoopMessage[]) => AsyncIterable<Raw>\n extractText: (event: Raw) => string\n extractToolCall: (event: Raw) => ToolLoopCall | null\n isExecutableTool: (toolName: string) => boolean\n executeToolCall: (call: ToolLoopCall) => Promise<ToolCallOutcome>\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow. */\n maxToolTurns?: number\n /** Wall-clock deadline in ms since epoch (Date.now()-based). */\n deadlineMs?: number\n /** Maximum total cost in USD. Requires `costOf` to meter each tool call. */\n maxCostUsd?: number\n /** Return the USD cost of one outcome. Required for `maxCostUsd` to work. */\n costOf?: (call: ToolLoopCall, outcome: ToolCallOutcome) => number\n renderResult?: (label: string, outcome: ToolCallOutcome) => string\n labelFor?: (call: ToolLoopCall) => string\n runId?: string\n scenarioId?: string\n hooks?: RuntimeHooks\n}\n\n/** Streaming bounded tool loop: yields each raw turn event (the caller maps +\n * telemetries + re-emits it) and each executed `tool_result`; emits one\n * `capped` if it stops for any non-completed reason with calls still pending. */\nexport async function* streamToolLoop<Raw>(\n opts: StreamToolLoopOptions<Raw>,\n): AsyncGenerator<StreamToolLoopYield<Raw>, void, unknown> {\n const backstop = opts.maxToolTurns ?? RUNAWAY_BACKSTOP_TURNS\n const render = opts.renderResult ?? defaultRender\n const labelFor = opts.labelFor ?? ((c: ToolLoopCall) => c.toolName)\n const runId = opts.runId ?? `agent-run-${randomSuffix()}`\n const messages: ToolLoopMessage[] = [\n { role: 'system', content: opts.systemPrompt },\n ...(opts.priorMessages ?? []),\n { role: 'user', content: opts.userMessage },\n ]\n const observer = createToolLoopObserver(opts.hooks, runId, opts.scenarioId)\n let accumulatedCostUsd = 0\n let lastCallHash: string | null = null\n let consecutiveCount = 0\n\n observer.loopBefore(backstop, messages.length)\n\n for (let toolTurn = 0; ; toolTurn++) {\n // Wall-clock deadline check before every new turn.\n if (opts.deadlineMs !== undefined && Date.now() >= opts.deadlineMs) {\n observer.loopAfter({ turns: toolTurn + 1, stopReason: 'deadline' })\n yield { kind: 'capped', pending: 0, stopReason: 'deadline' }\n return\n }\n\n let turnText = ''\n const pending: ToolLoopCall[] = []\n const turnEventId = observer.turnBefore(toolTurn, messages.length)\n for await (const event of opts.streamTurn([...messages])) {\n yield { kind: 'event', event }\n turnText += opts.extractText(event)\n const call = opts.extractToolCall(event)\n if (call && opts.isExecutableTool(call.toolName)) pending.push(call)\n }\n if (pending.length === 0) {\n observer.turnAfter(toolTurn, turnEventId, { pendingToolCalls: 0 })\n observer.loopAfter({ turns: toolTurn + 1, stopReason: 'completed' })\n return\n }\n\n // Runaway backstop.\n if (toolTurn >= backstop) {\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n stopReason: 'backstop',\n })\n observer.loopAfter({ turns: toolTurn + 1, stopReason: 'backstop' })\n yield { kind: 'capped', pending: pending.length, stopReason: 'backstop' }\n return\n }\n\n // The assistant turn that emitted the calls, carrying its tool_calls array,\n // so a strict model reads its own tool use back in OpenAI shape.\n messages.push(assistantToolCallMessage(turnText, pending))\n const outcomes: ExecutedToolCall[] = []\n for (const [callIndex, call] of pending.entries()) {\n // Stuck-loop detection.\n const callHash = canonicalCallHash(call)\n if (callHash === lastCallHash) {\n consecutiveCount++\n } else {\n lastCallHash = callHash\n consecutiveCount = 1\n }\n if (consecutiveCount >= STUCK_LOOP_THRESHOLD) {\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n stopReason: 'stuck-loop',\n })\n observer.loopAfter({ turns: toolTurn + 1, stopReason: 'stuck-loop' })\n yield { kind: 'capped', pending: pending.length, stopReason: 'stuck-loop' }\n return\n }\n\n const callEventId = observer.toolCallBefore(toolTurn, turnEventId, callIndex, call)\n let outcome: ToolCallOutcome\n try {\n outcome = await opts.executeToolCall(call)\n } catch (err) {\n outcome = {\n ok: false,\n code: 'executor_error',\n message: err instanceof Error ? err.message : String(err),\n }\n }\n\n // Budget check after each tool call.\n if (opts.maxCostUsd !== undefined && opts.costOf !== undefined) {\n accumulatedCostUsd += opts.costOf(call, outcome)\n if (accumulatedCostUsd >= opts.maxCostUsd) {\n const label = labelFor(call)\n yield {\n kind: 'tool_result',\n toolName: call.toolName,\n toolCallId: call.toolCallId,\n label,\n outcome,\n }\n messages.push(toolResultMessage(call, render(label, outcome)))\n observer.toolCallAfter(toolTurn, callEventId, call, outcome)\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n stopReason: 'budget',\n })\n observer.loopAfter({ turns: toolTurn + 1, stopReason: 'budget' })\n yield { kind: 'capped', pending: pending.length, stopReason: 'budget' }\n return\n }\n }\n\n const label = labelFor(call)\n yield {\n kind: 'tool_result',\n toolName: call.toolName,\n toolCallId: call.toolCallId,\n label,\n outcome,\n }\n const rendered = render(label, outcome)\n outcomes.push({ call, label, outcome, rendered })\n // One role:'tool' message per result, keyed to its call by tool_call_id.\n messages.push(toolResultMessage(call, rendered))\n observer.toolCallAfter(toolTurn, callEventId, call, outcome)\n }\n observer.failureRecovery({\n toolTurn,\n messages,\n turnText,\n outcomes,\n })\n observer.turnAfter(toolTurn, turnEventId, {\n pendingToolCalls: pending.length,\n toolResults: outcomes.map((item) => ({\n toolName: item.call.toolName,\n toolCallId: item.call.toolCallId,\n ok: item.outcome.ok,\n })),\n failedToolCalls: outcomes.filter((item) => !item.outcome.ok).length,\n })\n }\n}\n\ninterface ExecutedToolCall {\n call: ToolLoopCall\n label: string\n outcome: ToolCallOutcome\n rendered: string\n}\n\ninterface NotifyToolFailureRecoveryOptions {\n hooks?: RuntimeHooks\n runId: string\n scenarioId?: string\n stepIndex: number\n messages: ToolLoopMessage[]\n turnText: string\n outcomes: ExecutedToolCall[]\n}\n\ninterface NotifyToolLoopEventOptions {\n hooks?: RuntimeHooks\n runId: string\n scenarioId?: string\n target: 'agent.run' | 'agent.turn' | 'agent.tool_call'\n phase: 'before' | 'after' | 'error' | 'event'\n id?: string\n stepIndex?: number\n parentId?: string\n payload?: Record<string, unknown>\n metadata?: Record<string, unknown>\n}\n\ninterface ToolLoopObserver {\n loopBefore(maxToolTurns: number, messageCount: number): void\n loopAfter(payload: Record<string, unknown>): void\n turnBefore(toolTurn: number, messageCount: number): string\n turnAfter(toolTurn: number, turnEventId: string, payload: Record<string, unknown>): void\n toolCallBefore(\n toolTurn: number,\n turnEventId: string,\n callIndex: number,\n call: ToolLoopCall,\n ): string\n toolCallAfter(\n toolTurn: number,\n callEventId: string,\n call: ToolLoopCall,\n outcome: ToolCallOutcome,\n ): void\n failureRecovery(options: {\n toolTurn: number\n messages: ToolLoopMessage[]\n turnText: string\n outcomes: ExecutedToolCall[]\n }): void\n}\n\nfunction createToolLoopObserver(\n hooks: RuntimeHooks | undefined,\n runId: string,\n scenarioId: string | undefined,\n): ToolLoopObserver {\n const loopEventId = `${runId}:agent.run`\n return {\n loopBefore: (maxToolTurns, messageCount) => {\n notifyToolLoopEvent({\n hooks,\n runId,\n scenarioId,\n target: 'agent.run',\n phase: 'before',\n id: `${loopEventId}:before`,\n payload: { maxToolTurns, messageCount },\n })\n },\n loopAfter: (payload) => {\n notifyToolLoopEvent({\n hooks,\n runId,\n scenarioId,\n target: 'agent.run',\n phase: 'after',\n id: `${loopEventId}:after`,\n payload,\n })\n },\n turnBefore: (toolTurn, messageCount) => {\n const turnEventId = `${loopEventId}:${toolTurn}`\n notifyToolLoopEvent({\n hooks,\n runId,\n scenarioId,\n target: 'agent.turn',\n phase: 'before',\n id: turnEventId,\n stepIndex: toolTurn,\n parentId: loopEventId,\n payload: { messageCount },\n })\n return turnEventId\n },\n turnAfter: (toolTurn, turnEventId, payload) => {\n notifyToolLoopEvent({\n hooks,\n runId,\n scenarioId,\n target: 'agent.turn',\n phase: 'after',\n id: `${turnEventId}:after`,\n stepIndex: toolTurn,\n parentId: turnEventId,\n payload,\n })\n },\n toolCallBefore: (toolTurn, turnEventId, callIndex, call) => {\n const callEventId = `${turnEventId}:tool-call:${callIndex}`\n notifyToolLoopEvent({\n hooks,\n runId,\n scenarioId,\n target: 'agent.tool_call',\n phase: 'before',\n id: callEventId,\n stepIndex: toolTurn,\n parentId: turnEventId,\n payload: toolCallPayload(call),\n })\n return callEventId\n },\n toolCallAfter: (toolTurn, callEventId, call, outcome) => {\n notifyToolLoopEvent({\n hooks,\n runId,\n scenarioId,\n target: 'agent.tool_call',\n phase: 'after',\n id: `${callEventId}:after`,\n stepIndex: toolTurn,\n parentId: callEventId,\n payload: { ...toolCallPayload(call), outcome: outcomePayload(outcome) },\n })\n },\n failureRecovery: (options) => {\n notifyToolFailureRecovery({\n hooks,\n runId,\n scenarioId,\n stepIndex: options.toolTurn,\n messages: options.messages,\n turnText: options.turnText,\n outcomes: options.outcomes,\n })\n },\n }\n}\n\nfunction notifyToolLoopEvent(options: NotifyToolLoopEventOptions): void {\n notifyRuntimeHookEvent(options.hooks, {\n id: options.id ?? `${options.runId}:${options.target}:${options.phase}`,\n runId: options.runId,\n scenarioId: options.scenarioId,\n target: options.target,\n phase: options.phase,\n timestamp: Date.now(),\n stepIndex: options.stepIndex,\n parentId: options.parentId,\n payload: options.payload,\n metadata: { producer: 'tool-loop', ...options.metadata },\n })\n}\n\nfunction notifyToolFailureRecovery(options: NotifyToolFailureRecoveryOptions): void {\n const failed = options.outcomes.filter((item) => !item.outcome.ok)\n if (failed.length === 0) return\n\n const evidence: RuntimeDecisionEvidenceRef[] = []\n for (const item of failed) {\n const id = item.call.toolCallId ?? `${options.stepIndex}:${item.label}`\n evidence.push({\n source: 'tool_call',\n id,\n detail: `${item.call.toolName} ${stringifySafe(item.call.args, 2_000)}`,\n metadata: { toolName: item.call.toolName, label: item.label },\n })\n evidence.push({\n source: 'tool_result',\n id: `${id}:result`,\n detail: item.rendered,\n metadata: failureMetadata(item.outcome),\n })\n }\n\n notifyRuntimeDecisionPoint(options.hooks, {\n id: `${options.runId}:agent.turn:${options.stepIndex}:failure-recovery`,\n runId: options.runId,\n scenarioId: options.scenarioId,\n stepIndex: options.stepIndex,\n kind: 'retry',\n candidateActions: [...FAILURE_RECOVERY_ACTIONS],\n context: renderDecisionContext(options.messages, options.turnText, options.outcomes),\n evidence,\n metadata: {\n target: 'failure-recovery',\n source: 'agent.turn',\n failedToolCount: failed.length,\n toolNames: failed.map((item) => item.call.toolName),\n },\n })\n}\n\nfunction toolCallPayload(call: ToolLoopCall): Record<string, unknown> {\n return {\n toolName: call.toolName,\n toolCallId: call.toolCallId,\n argsPreview: stringifySafe(call.args, 2_000),\n }\n}\n\nfunction outcomePayload(outcome: ToolCallOutcome): Record<string, unknown> {\n if (!outcome.ok) {\n return {\n ok: false,\n code: outcome.code,\n message: trimText(outcome.message, 2_000),\n status: outcome.status,\n }\n }\n return {\n ok: true,\n resultPreview: stringifySafe(outcome.result, 2_000),\n }\n}\n\nfunction failureMetadata(outcome: ToolCallOutcome): Record<string, unknown> | undefined {\n if (outcome.ok) return undefined\n return {\n code: outcome.code,\n message: outcome.message,\n status: outcome.status,\n }\n}\n\nfunction renderDecisionContext(\n messages: ToolLoopMessage[],\n turnText: string,\n outcomes: ExecutedToolCall[],\n): string {\n const recent = messages.slice(-6).map((message) => `[${message.role}]\\n${message.content ?? ''}`)\n const assistant = turnText.trim() ? [`[assistant]\\n${turnText}`] : []\n const toolResults = [`[tool results]\\n${outcomes.map((item) => item.rendered).join('\\n')}`]\n return trimText(\n [...recent, ...assistant, ...toolResults].join('\\n\\n'),\n DEFAULT_DECISION_CONTEXT_CHARS,\n )\n}\n\n/** Canonical identifier for a tool call used by stuck-loop detection.\n * Keys are sorted so `{b:1,a:2}` and `{a:2,b:1}` produce the same hash. */\nfunction canonicalCallHash(call: ToolLoopCall): string {\n const sortedArgs = Object.fromEntries(\n Object.entries(call.args).sort(([a], [b]) => a.localeCompare(b)),\n )\n return `${call.toolName}:${JSON.stringify(sortedArgs)}`\n}\n\nfunction stringifySafe(value: unknown, max: number): string {\n let text: string\n try {\n text = JSON.stringify(value) ?? String(value)\n } catch {\n text = String(value)\n }\n return trimText(text, max)\n}\n\nfunction trimText(text: string, max: number): string {\n if (text.length <= max) return text\n return `${text.slice(0, max)}…`\n}\n\nfunction randomSuffix(len = 8): string {\n return Math.random()\n .toString(36)\n .slice(2, 2 + len)\n}\n"],"mappings":"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"}
@@ -1,93 +1,8 @@
1
1
  import { u as AgentTaskSpec, x as RuntimeStreamEvent } from "./types-C9j4qg6l.js";
2
+ import { l as RuntimeHooks } from "./runtime-hooks-sbRpjStq.js";
2
3
  import { DefaultVerdict } from "@tangle-network/agent-eval";
3
4
  import { AgentProfile as AgentProfile$1 } from "@tangle-network/agent-interface";
4
5
  import { CreateSandboxOptions, SandboxEvent, SandboxInstance } from "@tangle-network/sandbox";
5
- //#region src/runtime-hooks.d.ts
6
- /**
7
- *
8
- * Runtime hook contracts. Hooks are execution-scoped observers, not part of an
9
- * `AgentProfile`: profiles stay portable agent recipes; hooks attach to the
10
- * loop or product harness that is running the profile.
11
- *
12
- * @experimental
13
- */
14
- type RuntimeHookPhase = 'before' | 'after' | 'error' | 'event';
15
- type RuntimeHookTarget = 'agent.run' | 'agent.turn' | 'agent.tool_call' | 'agent.spawn' | 'agent.child' | 'agent.plan' | 'agent.decision' | (string & {});
16
- type RuntimeDecisionKind = 'continue' | 'verify' | 'ask' | 'retry' | 'stop' | 'memory-write' | 'memory-read' | 'tool-select' | 'skill-select' | 'workflow-select' | 'surface-promote' | (string & {});
17
- interface RuntimeHookEvent<Payload = unknown> {
18
- id: string;
19
- runId: string;
20
- scenarioId?: string;
21
- target: RuntimeHookTarget;
22
- phase: RuntimeHookPhase;
23
- timestamp: number;
24
- stepIndex?: number;
25
- parentId?: string;
26
- payload?: Payload;
27
- metadata?: Record<string, unknown>;
28
- }
29
- interface RuntimeHookContext {
30
- signal?: AbortSignal;
31
- }
32
- interface RuntimeDecisionEvidenceRef {
33
- source: string;
34
- id: string;
35
- detail?: string;
36
- metadata?: Record<string, unknown>;
37
- }
38
- interface RuntimeDecisionPoint {
39
- id: string;
40
- runId: string;
41
- scenarioId?: string;
42
- stepIndex: number;
43
- kind: RuntimeDecisionKind;
44
- candidateActions: string[];
45
- context?: string;
46
- evidence: RuntimeDecisionEvidenceRef[];
47
- metadata?: Record<string, unknown>;
48
- }
49
- interface RuntimeHookErrorContext {
50
- hook: 'onEvent' | 'onDecisionPoint';
51
- eventId?: string;
52
- target?: RuntimeHookTarget;
53
- phase?: RuntimeHookPhase;
54
- decisionId?: string;
55
- decisionKind?: RuntimeDecisionKind;
56
- }
57
- /**
58
- * The observation seam attached to a running loop (never to the portable genome).
59
- * Implement the optional hooks to receive lifecycle events, semantic decision points,
60
- * and hook errors. Author with {@link defineRuntimeHooks} for inference, and attach N
61
- * observers at once with {@link composeRuntimeHooks} — there is ONE event stream, not a
62
- * callback-prop zoo.
63
- */
64
- interface RuntimeHooks {
65
- /**
66
- * General before/after/event hook. Use this for telemetry, memory capture,
67
- * policy wrapping, child lifecycle observers, or product-specific extension
68
- * points.
69
- */
70
- onEvent?: (event: RuntimeHookEvent, context: RuntimeHookContext) => void | Promise<void>;
71
- /**
72
- * Semantic decision hook. Belief-state evaluation consumes this, but runtime
73
- * code should keep emitting ordinary lifecycle events as the base layer.
74
- */
75
- onDecisionPoint?: (point: RuntimeDecisionPoint, context: RuntimeHookContext) => void | Promise<void>;
76
- onHookError?: (error: Error, context: RuntimeHookErrorContext) => void | Promise<void>;
77
- }
78
- /** Identity helper that types a {@link RuntimeHooks} literal so the fields are inferred. */
79
- declare function defineRuntimeHooks(hooks: RuntimeHooks): RuntimeHooks;
80
- /**
81
- * Merge several {@link RuntimeHooks} into one. Falsy entries are dropped (so you can
82
- * pass `flag && hooks`), and every observer's `onEvent`/`onDecisionPoint` fires for each
83
- * event. Use this to attach N observers to a loop instead of a second event bus.
84
- */
85
- declare function composeRuntimeHooks(...entries: Array<RuntimeHooks | undefined | null | false>): RuntimeHooks;
86
- /** Fire `hooks.onEvent`, swallowing sync throws and surfacing async failures to `onError`. */
87
- declare function notifyRuntimeHookEvent(hooks: RuntimeHooks | undefined, event: RuntimeHookEvent, context?: RuntimeHookContext): void;
88
- /** Fire `hooks.onDecisionPoint`, swallowing sync throws and surfacing async failures to `onError`. */
89
- declare function notifyRuntimeDecisionPoint(hooks: RuntimeHooks | undefined, point: RuntimeDecisionPoint, context?: RuntimeHookContext): void;
90
- //#endregion
91
6
  //#region src/runtime-run.d.ts
92
7
  /** @stable */
93
8
  type RuntimeRunStatus = 'running' | 'completed' | 'failed' | 'cancelled';
@@ -761,5 +676,5 @@ interface ExecCtx {
761
676
  parentSpanId?: string;
762
677
  }
763
678
  //#endregion
764
- export { RuntimeRunCompleteInput as A, RuntimeDecisionPoint as B, MountRecorder as C, SelectionReceipt as D, SandboxClient as E, RuntimeRunRow as F, RuntimeHookTarget as G, RuntimeHookErrorContext as H, RuntimeRunStatus as I, defineRuntimeHooks as J, RuntimeHooks as K, startRuntimeRun as L, RuntimeRunHandle as M, RuntimeRunOptions as N, ValidationCtx as O, RuntimeRunPersistenceAdapter as P, RuntimeDecisionEvidenceRef as R, MountManifestEntry as S, RunProvenance as T, RuntimeHookEvent as U, RuntimeHookContext as V, RuntimeHookPhase as W, notifyRuntimeHookEvent as X, notifyRuntimeDecisionPoint as Y, LoopTeardownFailedPayload as _, Iteration as a, LoopTraceEvent as b, LoopIterationDispatchPayload as c, LoopLineageOptions as d, LoopPlanDescription as f, LoopStartedPayload as g, LoopSandboxPlacement as h, ExecCtx as i, RuntimeRunCost as j, Validator as k, LoopIterationEndedPayload as l, LoopResult as m, DefaultVerdict as n, LoopDecisionPayload as o, LoopPlanPayload as p, composeRuntimeHooks as q, Driver as r, LoopEndedPayload as s, AgentRunSpec as t, LoopIterationStartedPayload as u, LoopTokenUsage as v, OutputAdapter as w, LoopWinner as x, LoopTraceEmitter as y, RuntimeDecisionKind as z };
765
- //# sourceMappingURL=types-BevOjfTY.d.ts.map
679
+ export { RuntimeRunCompleteInput as A, MountRecorder as C, SelectionReceipt as D, SandboxClient as E, RuntimeRunRow as F, RuntimeRunStatus as I, startRuntimeRun as L, RuntimeRunHandle as M, RuntimeRunOptions as N, ValidationCtx as O, RuntimeRunPersistenceAdapter as P, MountManifestEntry as S, RunProvenance as T, LoopTeardownFailedPayload as _, Iteration as a, LoopTraceEvent as b, LoopIterationDispatchPayload as c, LoopLineageOptions as d, LoopPlanDescription as f, LoopStartedPayload as g, LoopSandboxPlacement as h, ExecCtx as i, RuntimeRunCost as j, Validator as k, LoopIterationEndedPayload as l, LoopResult as m, DefaultVerdict as n, LoopDecisionPayload as o, LoopPlanPayload as p, Driver as r, LoopEndedPayload as s, AgentRunSpec as t, LoopIterationStartedPayload as u, LoopTokenUsage as v, OutputAdapter as w, LoopWinner as x, LoopTraceEmitter as y };
680
+ //# sourceMappingURL=types-DnNGJ5Gz.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-runtime",
3
- "version": "0.108.1",
3
+ "version": "0.109.1",
4
4
  "description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
5
5
  "homepage": "https://github.com/tangle-network/agent-runtime#readme",
6
6
  "repository": {
@@ -34,15 +34,20 @@
34
34
  "import": "./dist/durable.js",
35
35
  "default": "./dist/durable.js"
36
36
  },
37
+ "./tool-loop": {
38
+ "types": "./dist/tool-loop.d.ts",
39
+ "import": "./dist/tool-loop.js",
40
+ "default": "./dist/tool-loop.js"
41
+ },
37
42
  "./intelligence": {
38
43
  "types": "./dist/intelligence.d.ts",
39
44
  "import": "./dist/intelligence.js",
40
45
  "default": "./dist/intelligence.js"
41
46
  },
42
- "./loops": {
43
- "types": "./dist/loops.d.ts",
44
- "import": "./dist/loops.js",
45
- "default": "./dist/loops.js"
47
+ "./kernel": {
48
+ "types": "./dist/kernel.d.ts",
49
+ "import": "./dist/kernel.js",
50
+ "default": "./dist/kernel.js"
46
51
  },
47
52
  "./environment-provider": {
48
53
  "types": "./dist/environment-provider.d.ts",
@@ -106,11 +111,12 @@
106
111
  "devDependencies": {
107
112
  "@arethetypeswrong/cli": "0.18.5",
108
113
  "@biomejs/biome": "^2.5.5",
109
- "@tangle-network/agent-eval": "0.134.2",
114
+ "@tangle-network/agent-eval": "0.135.1",
110
115
  "@tangle-network/agent-interface": "0.36.0",
111
116
  "@tangle-network/sandbox": "0.15.2",
112
117
  "@types/node": "26.1.1",
113
118
  "@types/tar-stream": "3.1.4",
119
+ "miniflare": "4.20260722.0",
114
120
  "playwright": "^1.62.0",
115
121
  "publint": "0.3.22",
116
122
  "tsdown": "0.22.14",
@@ -118,14 +124,16 @@
118
124
  "typedoc": "0.28.20",
119
125
  "typedoc-plugin-markdown": "4.12.0",
120
126
  "typescript": "6.0.3",
121
- "vitest": "^4.1.10"
127
+ "vitest": "^4.1.10",
128
+ "wrangler": "4.114.0",
129
+ "yaml": "2.9.0"
122
130
  },
123
131
  "engines": {
124
132
  "node": ">=22.13.0"
125
133
  },
126
134
  "license": "MIT",
127
135
  "peerDependencies": {
128
- "@tangle-network/agent-eval": ">=0.134.2 <0.135.0",
136
+ "@tangle-network/agent-eval": ">=0.135.1 <0.136.0",
129
137
  "@tangle-network/agent-interface": ">=0.36.0 <0.37.0",
130
138
  "@tangle-network/sandbox": ">=0.15.0 <0.16.0",
131
139
  "playwright": "^1.40.0"
@@ -139,7 +147,7 @@
139
147
  }
140
148
  },
141
149
  "dependencies": {
142
- "@tangle-network/agent-knowledge": "6.1.8",
150
+ "@tangle-network/agent-knowledge": "6.1.10",
143
151
  "@tangle-network/agent-profile-materialize": "0.9.2",
144
152
  "tar-stream": "3.2.0"
145
153
  },
@@ -155,11 +163,14 @@
155
163
  "generate:testing-fixture": "tsx scripts/generate-agent-improvement-proposal-fixtures.ts",
156
164
  "check:testing-fixture": "tsx scripts/generate-agent-improvement-proposal-fixtures.ts --check",
157
165
  "check:skills": "node scripts/check-skills.mjs",
166
+ "check:publish-workflow": "node scripts/check-publish-workflow.mjs",
158
167
  "release:prepare": "node scripts/prepare-release.mjs",
168
+ "verify:static-imports": "node scripts/verify-static-imports.mjs",
169
+ "verify:edge-tool-loop": "node scripts/verify-edge-tool-loop.mjs",
159
170
  "verify:bench": "pnpm build && pnpm --filter @tangle-network/agent-bench run typecheck:public && pnpm --filter @tangle-network/agent-bench test && pnpm --filter @tangle-network/agent-bench run verify:package:local-runtime",
160
171
  "verify:bench:published": "pnpm build && pnpm --filter @tangle-network/agent-bench run typecheck:public && pnpm --filter @tangle-network/agent-bench test && pnpm --filter @tangle-network/agent-bench run verify:package",
161
172
  "verify:cohort": "node scripts/verify-packed-cohort.mjs",
162
- "verify:package": "pnpm run build && pnpm run check:testing-fixture && pnpm run check:skills && publint && attw --pack --profile esm-only . && node scripts/verify-package-exports.mjs",
173
+ "verify:package": "pnpm run verify:static-imports && pnpm run build && pnpm run check:testing-fixture && pnpm run check:skills && publint && attw --pack --profile esm-only . && node scripts/verify-package-exports.mjs && pnpm run verify:edge-tool-loop",
163
174
  "verify:official-optimizers": "node scripts/verify-official-optimizers.mjs",
164
175
  "verify:primeintellect": "pnpm build && node scripts/verify-primeintellect.mjs",
165
176
  "verify:primeintellect:live": "pnpm build && node scripts/verify-primeintellect-live.mjs",