From ca7dde9348448662a44b6c0d4448671cad01e27c Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Tue, 28 Jul 2026 20:16:05 -0600 Subject: [PATCH] fix(package): isolate the Worker tool loop --- CHANGELOG.md | 5 + README.md | 1 + docs/api/README.md | 1 + docs/api/index.md | 539 +---------------- docs/api/primitive-catalog.md | 27 +- docs/api/runtime.md | 8 +- docs/api/tool-loop.md | 540 +++++++++++++++++ docs/canonical-api.md | 4 +- docs/glossary.md | 2 +- package.json | 14 +- pnpm-lock.yaml | 560 +++++++++++++++++- pnpm-workspace.yaml | 1 + scripts/gen-primitive-catalog.mjs | 1 + scripts/lib/static-imports.mjs | 38 +- scripts/verify-edge-tool-loop.mjs | 150 +++++ scripts/verify-package-exports.mjs | 81 ++- scripts/verify-static-imports.mjs | 24 +- src/index.ts | 14 - src/runtime/index.ts | 2 +- src/runtime/run-loop.ts | 4 +- .../fixtures/agent-improvement-proposal.json | 10 +- .../agent-profile-improvement-proposal.json | 4 +- tsdown.config.ts | 1 + typedoc.json | 1 + 24 files changed, 1440 insertions(+), 592 deletions(-) create mode 100644 docs/api/tool-loop.md create mode 100644 scripts/verify-edge-tool-loop.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index f4560dfa..ce43c8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.109.1 + +- Move `runToolLoop` and `streamToolLoop` to the Worker-safe `@tangle-network/agent-runtime/tool-loop` entrypoint. +- Keep the tool-loop bundle free of static external imports and exercise the exact packed entrypoint before publishing. + ## 0.109.0 - Rename the public `./loops` entrypoint to `./kernel` and remove the old entrypoint. diff --git a/README.md b/README.md index c3729c6e..a2b995bf 100644 --- a/README.md +++ b/README.md @@ -435,6 +435,7 @@ The general-purpose pieces, by import path. Every export with its one-line summa | Primitive | What it does | Import | |---|---|---| | Chat-turn runtime | Stream and persist one production chat turn (`handleChatTurn`); derive its stable execution and turn identity (`deriveExecutionId`); normalize any backend's stream into one event shape (`streamAgentTurn`) | `/durable` · `/kernel` | +| Tool-call loop | Run one model turn, execute requested tools, feed results back, and stop on completion, repetition, time, or cost limits (`runToolLoop`, `streamToolLoop`) | `/tool-loop` | | Supervision | One agent spawns, budgets, and steers workers toward a goal (`supervise`, `delegate`), on an in-process loop or a sandboxed coding harness | `/kernel` · `/mcp` | | Loop kernel + combinators | Write a driver (`plan`/`decide`) and run it (`runAgentRounds`), or compose fixed shapes: refine (`loopUntil`), best-of-N (`fanout`), chain (`pipeline`), multi-judge (`panel`) | `/kernel` | | Improvement driver | Optimize one part of an agent and ship only if it wins on tasks it never practiced on (`improve`); production proposal/review/activation flow | root · `/intelligence` | diff --git a/docs/api/README.md b/docs/api/README.md index f83f6983..c930d421 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -21,3 +21,4 @@ - [runtime/environment-provider](runtime/environment-provider.md) - [runtime](runtime.md) - [testing](testing.md) +- [tool-loop](tool-loop.md) diff --git a/docs/api/index.md b/docs/api/index.md index 94b0b0b4..ab5bbbdd 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -5953,7 +5953,7 @@ Findings to fall back to when the generation had NO failing cells, so a ##### superviseOptions? -> `optional` **superviseOptions?**: `Partial`\<`Omit`\<[`SuperviseOptions`](runtime.md#superviseoptions), `"backend"` \| `"budget"` \| `"makeWorkerAgent"` \| `"deliverable"` \| `"allowedModels"`\>\> +> `optional` **superviseOptions?**: `Partial`\<`Omit`\<[`SuperviseOptions`](runtime.md#superviseoptions), `"budget"` \| `"backend"` \| `"makeWorkerAgent"` \| `"deliverable"` \| `"allowedModels"`\>\> ##### allowedModels? @@ -6279,7 +6279,7 @@ Findings to fall back to when the generation had NO failing cells, so a ##### superviseOptions? -> `optional` **superviseOptions?**: `Partial`\<`Omit`\<[`SuperviseOptions`](runtime.md#superviseoptions), `"backend"` \| `"budget"` \| `"makeWorkerAgent"` \| `"deliverable"` \| `"allowedModels"`\>\> +> `optional` **superviseOptions?**: `Partial`\<`Omit`\<[`SuperviseOptions`](runtime.md#superviseoptions), `"budget"` \| `"backend"` \| `"makeWorkerAgent"` \| `"deliverable"` \| `"allowedModels"`\>\> ##### allowedModels? @@ -9075,414 +9075,6 @@ Snapshot of a small streaming-flavored summary derived from collected events. *** -### ToolLoopCall - -#### Properties - -##### toolCallId? - -> `optional` **toolCallId?**: `string` - -##### toolName - -> **toolName**: `string` - -##### args - -> **args**: `Record`\<`string`, `unknown`\> - -*** - -### ToolLoopAssistantToolCall - -One OpenAI-shaped tool-call entry carried on an assistant message. - -#### Properties - -##### id - -> **id**: `string` - -##### type - -> **type**: `"function"` - -##### function - -> **function**: `object` - -###### name - -> **name**: `string` - -###### arguments - -> **arguments**: `string` - -*** - -### ToolLoopResult - -#### Properties - -##### finalText - -> **finalText**: `string` - -##### toolResults - -> **toolResults**: `object`[] - -###### call - -> **call**: [`ToolLoopCall`](#toolloopcall) - -###### label - -> **label**: `string` - -###### outcome - -> **outcome**: [`ToolCallOutcome`](#toolcalloutcome) - -##### turns - -> **turns**: `number` - -##### stopReason - -> **stopReason**: [`ToolLoopStopReason`](#toolloopstopreason) - -##### ~~cappedOut~~ - -> **cappedOut**: `boolean` - -###### Deprecated - -Use `stopReason !== 'completed'` instead. - -*** - -### RunToolLoopOptions - -#### Properties - -##### systemPrompt - -> **systemPrompt**: `string` - -##### userMessage - -> **userMessage**: `string` - -##### priorMessages? - -> `optional` **priorMessages?**: [`ToolLoopMessage`](#toolloopmessage)[] - -##### streamTurn - -> **streamTurn**: (`messages`) => `AsyncIterable`\<[`ToolLoopEvent`](#toolloopevent)\> - -###### Parameters - -###### messages - -[`ToolLoopMessage`](#toolloopmessage)[] - -###### Returns - -`AsyncIterable`\<[`ToolLoopEvent`](#toolloopevent)\> - -##### executeToolCall - -> **executeToolCall**: (`call`) => `Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> - -###### Parameters - -###### call - -[`ToolLoopCall`](#toolloopcall) - -###### Returns - -`Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> - -##### isExecutableTool - -> **isExecutableTool**: (`toolName`) => `boolean` - -###### Parameters - -###### toolName - -`string` - -###### Returns - -`boolean` - -##### maxToolTurns? - -> `optional` **maxToolTurns?**: `number` - -Runaway-backstop cap. Default 200 — set far above any legitimate workflow. - For per-workflow limits, use `maxCostUsd` or `deadlineMs` instead. - -##### deadlineMs? - -> `optional` **deadlineMs?**: `number` - -Wall-clock deadline in ms since epoch (Date.now()-based). When exceeded the - loop stops with stopReason `deadline`. - -##### maxCostUsd? - -> `optional` **maxCostUsd?**: `number` - -Maximum total cost in USD. Requires `costOf` to meter each tool call. - -##### costOf? - -> `optional` **costOf?**: (`call`, `outcome`) => `number` - -Return the USD cost of one outcome. Required for `maxCostUsd` to work. - -###### Parameters - -###### call - -[`ToolLoopCall`](#toolloopcall) - -###### outcome - -[`ToolCallOutcome`](#toolcalloutcome) - -###### Returns - -`number` - -##### renderResult? - -> `optional` **renderResult?**: (`label`, `outcome`) => `string` - -###### Parameters - -###### label - -`string` - -###### outcome - -[`ToolCallOutcome`](#toolcalloutcome) - -###### Returns - -`string` - -##### labelFor? - -> `optional` **labelFor?**: (`call`) => `string` - -###### Parameters - -###### call - -[`ToolLoopCall`](#toolloopcall) - -###### Returns - -`string` - -##### runId? - -> `optional` **runId?**: `string` - -##### scenarioId? - -> `optional` **scenarioId?**: `string` - -##### hooks? - -> `optional` **hooks?**: [`RuntimeHooks`](#runtimehooks) - -*** - -### StreamToolLoopOptions - -#### Type Parameters - -##### Raw - -`Raw` - -#### Properties - -##### systemPrompt - -> **systemPrompt**: `string` - -##### userMessage - -> **userMessage**: `string` - -##### priorMessages? - -> `optional` **priorMessages?**: [`ToolLoopMessage`](#toolloopmessage)[] - -##### streamTurn - -> **streamTurn**: (`messages`) => `AsyncIterable`\<`Raw`\> - -###### Parameters - -###### messages - -[`ToolLoopMessage`](#toolloopmessage)[] - -###### Returns - -`AsyncIterable`\<`Raw`\> - -##### extractText - -> **extractText**: (`event`) => `string` - -###### Parameters - -###### event - -`Raw` - -###### Returns - -`string` - -##### extractToolCall - -> **extractToolCall**: (`event`) => [`ToolLoopCall`](#toolloopcall) \| `null` - -###### Parameters - -###### event - -`Raw` - -###### Returns - -[`ToolLoopCall`](#toolloopcall) \| `null` - -##### isExecutableTool - -> **isExecutableTool**: (`toolName`) => `boolean` - -###### Parameters - -###### toolName - -`string` - -###### Returns - -`boolean` - -##### executeToolCall - -> **executeToolCall**: (`call`) => `Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> - -###### Parameters - -###### call - -[`ToolLoopCall`](#toolloopcall) - -###### Returns - -`Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> - -##### maxToolTurns? - -> `optional` **maxToolTurns?**: `number` - -Runaway-backstop cap. Default 200 — set far above any legitimate workflow. - -##### deadlineMs? - -> `optional` **deadlineMs?**: `number` - -Wall-clock deadline in ms since epoch (Date.now()-based). - -##### maxCostUsd? - -> `optional` **maxCostUsd?**: `number` - -Maximum total cost in USD. Requires `costOf` to meter each tool call. - -##### costOf? - -> `optional` **costOf?**: (`call`, `outcome`) => `number` - -Return the USD cost of one outcome. Required for `maxCostUsd` to work. - -###### Parameters - -###### call - -[`ToolLoopCall`](#toolloopcall) - -###### outcome - -[`ToolCallOutcome`](#toolcalloutcome) - -###### Returns - -`number` - -##### renderResult? - -> `optional` **renderResult?**: (`label`, `outcome`) => `string` - -###### Parameters - -###### label - -`string` - -###### outcome - -[`ToolCallOutcome`](#toolcalloutcome) - -###### Returns - -`string` - -##### labelFor? - -> `optional` **labelFor?**: (`call`) => `string` - -###### Parameters - -###### call - -[`ToolLoopCall`](#toolloopcall) - -###### Returns - -`string` - -##### runId? - -> `optional` **runId?**: `string` - -##### scenarioId? - -> `optional` **scenarioId?**: `string` - -##### hooks? - -> `optional` **hooks?**: [`RuntimeHooks`](#runtimehooks) - -*** - ### AgentTaskSpec **`Stable`** @@ -9991,7 +9583,7 @@ keeping the backend transport thin lets domain repos own MCP plumbing. ##### status -> **status**: `"aborted"` \| `"completed"` \| `"failed"` \| `"active"` +> **status**: `"completed"` \| `"aborted"` \| `"failed"` \| `"active"` ##### resumeToken? @@ -11779,85 +11371,6 @@ The conserved spend incurred before the run failed — real cost is paid even wh *** -### ToolCallOutcome - -> **ToolCallOutcome** = \{ `ok`: `true`; `result`: `unknown`; \} \| \{ `ok`: `false`; `code`: `string`; `message`: `string`; `status?`: `number`; \} - -Outcome of one tool dispatch — structurally compatible with a hub/integration - tool-outcome union, so callers can fold either through the loop. - -*** - -### ToolLoopMessage - -> **ToolLoopMessage** = `object` - -A message in the running conversation the loop sends to `streamTurn`. - -The base `{ role, content }` covers `system` / `user` / plain `assistant` -turns. Two optional fields carry the OpenAI function-calling contract so a -strict model (Claude, and any OpenAI-compatible provider that validates tool -history) reads its own tool use back instead of re-issuing the same call: - - - an assistant turn that emitted tool calls carries `tool_calls`, and its - `content` is `null` when the turn was tool-only; - - each tool result is its own `{ role: 'tool', tool_call_id, content }` - message keyed to the call that produced it. - -Widening is additive: a `streamTurn` that reads only `role` + `content` still -works; one that forwards the whole message to an OpenAI-compatible endpoint -now sends correct tool history. - -#### Properties - -##### role - -> **role**: `string` - -##### content - -> **content**: `string` \| `null` - -##### tool\_calls? - -> `optional` **tool\_calls?**: [`ToolLoopAssistantToolCall`](#toolloopassistanttoolcall)[] - -##### tool\_call\_id? - -> `optional` **tool\_call\_id?**: `string` - -*** - -### ToolLoopEvent - -> **ToolLoopEvent** = \{ `type`: `"text"`; `text`: `string`; \} \| \{ `type`: `"tool_call"`; `call`: [`ToolLoopCall`](#toolloopcall); \} \| \{ `type`: `"other"`; `event`: `unknown`; \} - -*** - -### ToolLoopStopReason - -> **ToolLoopStopReason** = `"completed"` \| `"stuck-loop"` \| `"backstop"` \| `"deadline"` \| `"budget"` - -Why the loop stopped. `completed` = model finished naturally; `stuck-loop` = - ≥3 consecutive identical tool calls (same tool + args); `backstop` = hit the - runaway-backstop cap (200 by default); `deadline` = wall-clock deadlineMs - exceeded; `budget` = maxCostUsd exhausted. Non-`completed` stops are infra / - resource outcomes — eval scoring must distinguish them from capability failure. - -*** - -### StreamToolLoopYield - -> **StreamToolLoopYield**\<`Raw`\> = \{ `kind`: `"event"`; `event`: `Raw`; \} \| \{ `kind`: `"tool_result"`; `toolName`: `string`; `toolCallId?`: `string`; `label`: `string`; `outcome`: [`ToolCallOutcome`](#toolcalloutcome); \} \| \{ `kind`: `"capped"`; `pending`: `number`; `stopReason`: `Exclude`\<[`ToolLoopStopReason`](#toolloopstopreason), `"completed"`\>; \} - -#### Type Parameters - -##### Raw - -`Raw` - -*** - ### AgentTaskStatus > **AgentTaskStatus** = `"completed"` \| `"blocked"` \| `"failed"` \| `"aborted"` @@ -14959,49 +14472,3 @@ Serialize a `RuntimeStreamEvent` as a Server-Sent Event string. #### Returns `string` - -*** - -### runToolLoop() - -> **runToolLoop**(`opts`): `Promise`\<[`ToolLoopResult`](#toolloopresult)\> - -Run the bounded tool loop and return the final text + every executed tool - outcome. Awaitable — callers needing to stream events to a UI use - [streamToolLoop](#streamtoolloop). - -#### Parameters - -##### opts - -[`RunToolLoopOptions`](#runtoolloopoptions) - -#### Returns - -`Promise`\<[`ToolLoopResult`](#toolloopresult)\> - -*** - -### streamToolLoop() - -> **streamToolLoop**\<`Raw`\>(`opts`): `AsyncGenerator`\<[`StreamToolLoopYield`](#streamtoolloopyield)\<`Raw`\>, `void`, `unknown`\> - -Streaming bounded tool loop: yields each raw turn event (the caller maps + - telemetries + re-emits it) and each executed `tool_result`; emits one - `capped` if it stops for any non-completed reason with calls still pending. - -#### Type Parameters - -##### Raw - -`Raw` - -#### Parameters - -##### opts - -[`StreamToolLoopOptions`](#streamtoolloopoptions)\<`Raw`\> - -#### Returns - -`AsyncGenerator`\<[`StreamToolLoopYield`](#streamtoolloopyield)\<`Raw`\>, `void`, `unknown`\> diff --git a/docs/api/primitive-catalog.md b/docs/api/primitive-catalog.md index 8f47db56..5c52773a 100644 --- a/docs/api/primitive-catalog.md +++ b/docs/api/primitive-catalog.md @@ -7,7 +7,7 @@ # Primitive catalog — the never-stale anti-reinvention inventory -> **GENERATED** from `@tangle-network/agent-runtime@0.109.0` and `@tangle-network/agent-eval@0.135.1` by `scripts/gen-primitive-catalog.mjs`. Do NOT hand-edit — run `pnpm run docs:api`. This is the mechanical companion to the JUDGMENT in `canonical-api.md` (§2 decision table + §1.5 AgentProfile law): that doc says WHICH primitive to reach for and what NOT to build; this catalog proves WHAT exists. Per-symbol signatures + `file:line` live in the per-module pages under `docs/api/`. +> **GENERATED** from `@tangle-network/agent-runtime@0.109.1` and `@tangle-network/agent-eval@0.135.1` by `scripts/gen-primitive-catalog.mjs`. Do NOT hand-edit — run `pnpm run docs:api`. This is the mechanical companion to the JUDGMENT in `canonical-api.md` (§2 decision table + §1.5 AgentProfile law): that doc says WHICH primitive to reach for and what NOT to build; this catalog proves WHAT exists. Per-symbol signatures + `file:line` live in the per-module pages under `docs/api/`. ## 1. agent-runtime — own public surface @@ -15,7 +15,7 @@ Every subpath this package declares in `package.json` `exports`. Reach for these ### Root — task lifecycle, conversation, RSI verbs, observability -Import from `@tangle-network/agent-runtime` — 413 exports. +Import from `@tangle-network/agent-runtime` — 401 exports. | Symbol | Kind | Summary | |---|---|---| @@ -105,7 +105,6 @@ Import from `@tangle-network/agent-runtime` — 413 exports. | `runPersonaDispatch` | function | Wrap {@link runPersonaConversation} as a `ProfileDispatchFn` for | | `runSupervisedKnowledgeUpdate` | function | Run a runtime supervisor that updates one candidate knowledge base and stops on readiness. | | `runtimeStreamServerSentEvent` | function | Serialize a `RuntimeStreamEvent` as a Server-Sent Event string. | -| `runToolLoop` | function | Run the bounded tool loop and return the final text + every executed tool | | `sanitizeAgentRuntimeEvent` | function | Reduce an `AgentRuntimeEvent` to a PII-safe, serializable plain object for telemetry. | | `sanitizeKnowledgeReadinessReport` | function | Strip PII and large blobs from a `KnowledgeReadinessReport` for safe telemetry emission. | | `sanitizeRuntimeStreamEvent` | function | Reduce a `RuntimeStreamEvent` to a PII-safe, serializable plain object for telemetry. | @@ -114,7 +113,6 @@ Import from `@tangle-network/agent-runtime` — 413 exports. | `sleep` | function | Resolve after `ms` milliseconds — used for retry backoff in conversation call policy. | | `slugifySpeaker` | function | Reduce a speaker name to ASCII alphanumerics + dashes. Preserves enough | | `startRuntimeRun` | function | Construct a runtime-run handle. The returned handle is mutable across its | -| `streamToolLoop` | function | Streaming bounded tool loop: yields each raw turn event (the caller maps + | | `structuralRolloutPolicyFromProfile` | function | Read the persisted policy off the profile. `undefined` when the profile does | | `toolBuildPrompt` | function | Build the starting instruction for a coder agent tasked with implementing a new tool. | | `turnId` | function | Deterministic turn identifier. Stable across retries of the same logical | @@ -208,7 +206,6 @@ Import from `@tangle-network/agent-runtime` — 413 exports. | `Spend` | interface | Conserved spend, reconciled from the normalized `UsageEvent` stream. Tokens and usd | | `SqlAdapter` | interface | Minimal SQL driver shape. Implementations forward to whichever client the | | `Supervisor` | interface | Owns the conserved pool, the spawn log, the abort cascade, the OTP intensity breaker, | -| `ToolLoopAssistantToolCall` | interface | One OpenAI-shaped tool-call entry carried on an assistant message. | | `VerifyResult` | interface | Outcome of verifying a candidate worktree. `feedback` (compiler errors, | | `AgentBackendKind` | type | The transport a chat backend runs on. | | `AgentCandidateBundleInput` | type | Exact candidate wire shape before the runtime computes its canonical digest. | @@ -249,14 +246,11 @@ Import from `@tangle-network/agent-runtime` — 413 exports. | `Settled` | type | A settled child, delivered by `scope.next()`. `seq` is the monotonic cursor order | | `SupervisedResult` | type | Typed terminal result (M2) — a no-winner is NEVER coerced to a best-effort output. | | `SupervisorFinalizer` | type | The finalization seam: ledger in, output (or `undefined` = nothing deliverable) out. | -| `ToolCallOutcome` | type | Outcome of one tool dispatch — structurally compatible with a hub/integration | -| `ToolLoopMessage` | type | A message in the running conversation the loop sends to `streamTurn`. | -| `ToolLoopStopReason` | type | Why the loop stopped. `completed` = model finished naturally; `stuck-loop` = | | `VerifiedAgentCandidateTaskOutcome` | type | Branded task outcome that has survived independent evaluator verification. | | `Verifier` | type | Verifies the edited worktree. Sync or async; throws only on a setup fault | | `WorktreeCheckRunner` | type | The single shell-command-in-worktree runner seam (replaces the per-executor copies). | -**Undocumented supporting types** (add a TSDoc line at the declaration to earn a table row): `AgentAdapter`, `AgentBackendContext`, `AgentBackendInput`, `AgentCandidateContainerPort`, `AgentCandidateExecutionAttemptRef`, `AgentCandidateExecutionPorts`, `AgentCandidateExecutorWorkspaceFile`, `AgentCandidateExecutorWorkspaceInput`, `AgentCandidateMemoryPort`, `AgentCandidateMemoryResetResult`, `AgentCandidateModelPort`, `AgentCandidatePreparationEvidence`, `AgentCandidateProtectedModelActivation`, `AgentCandidateProtectedModelReservation`, `AgentCandidateProtectedModelSettlement`, `AgentCandidateProtectedRunCapture`, `AgentCandidateVerificationPorts`, `AgentCandidateWorkspaceArchiveLimits`, `AgentExecutionBackend`, `AgenticGeneratorOptions`, `AgenticGeneratorShotReceipt`, `AgentKnowledgeProvider`, `AgentKnowledgeReadinessCheckOptions`, `AgentTaskContext`, `AgentTaskRunResult`, `AgentTaskSpec`, `AnalystRegistry`, `BackendCallPolicy`, `CanonicalCandidateDocument`, `CaptureAgentCandidateWorkspaceOptions`, `CapturedAgentCandidateWorkspace`, `ChatModelCandidate`, `ControlBudget`, `ControlEvalResult`, `ControlRunResult`, `ControlStep`, `Conversation`, `ConversationDriveState`, `ConversationJournal`, `ConversationJournalEntry`, `ConversationParticipant`, `ConversationPolicy`, `ConversationResult`, `ConversationTurn`, `CreateAgentCandidateWorkspacePortOptions`, `CreateKnowledgeImprovementActivationExecutorOptions`, `CreateProtectedAgentCandidateModelPortOptions`, `D1StmtLike`, `DataAcquisitionPlan`, `DelegatedLoopResult`, `DisposePreparedAgentCandidateOptions`, `Driver`, `DriverLoopGeneratorOptions`, `EvalRunEvent`, `EvalRunGeneration`, `EvalRunsExportConfig`, `EvalRunsExportResult`, `ExactProcessCandidateExecutorOptions`, `ExecutePreparedAgentCandidateOptions`, `FileAgentCandidateExecutionClaimStoreOptions`, `HaltContext`, `HaltSignal`, `ImproveCodeOptions`, `ImproveCodeResult`, `ImprovementCodeCandidate`, `ImprovementProfileCandidate`, `ImproveMethodContext`, `ImproveMethodResult`, `ImproveSkillsOptions`, `InMemoryAgentCandidateExecutionClaimStoreOptions`, `KnowledgeImprovementActivationExecutor`, `KnowledgeImprovementCandidatePair`, `KnowledgeImprovementExperimentBundles`, `KnowledgeImprovementJobMeasurement`, `KnowledgeImprovementJobResult`, `KnowledgeReadinessCheckInput`, `KnowledgeReadinessDecision`, `KnowledgeReadinessReport`, `KnowledgeRequirement`, `LoopResult`, `LoopRunnerCliArgs`, `LoopRunnerCliResult`, `McpServeSpec`, `OfficialSensitiveCandidateInput`, `OtelAttribute`, `OtelExportConfig`, `OtelExporter`, `OtelSpan`, `PersonaConversationResult`, `PrepareAgentCandidateExecutionOptions`, `PreparedAgentCandidateExecution`, `PreparedAgentCandidateInstruction`, `PreparedAgentCandidateLaunch`, `PreparedAgentCandidateTrace`, `RawTraceDistillerOptions`, `RecoverExpiredAgentCandidateOptions`, `ReflectiveGeneratorOptions`, `ResearchLoopResult`, `ResearchLoopRunnerOptions`, `ResolveAgentBackendOptions`, `ResolvedAgentCandidateContainer`, `ResolvedChatModel`, `RunAgentTaskOptions`, `RunAgentTaskStreamOptions`, `RunConversationOptions`, `RunDelegatedLoopOptions`, `RunKnowledgeImprovementJobOptions`, `RunPersonaConfig`, `RunPersonaConversationOptions`, `RuntimeDecisionEvidenceRef`, `RuntimeDecisionPoint`, `RuntimeEventCollector`, `RuntimeEventOtelOptions`, `RuntimeHookContext`, `RuntimeHookErrorContext`, `RuntimeHookEvent`, `RuntimeRunCompleteInput`, `RuntimeRunCost`, `RuntimeRunHandle`, `RuntimeRunOptions`, `RuntimeRunPersistenceAdapter`, `RuntimeRunRow`, `RuntimeSession`, `RuntimeSessionStore`, `RuntimeStreamEventCollector`, `RuntimeStreamEventSummary`, `RuntimeTelemetryOptions`, `RunToolLoopOptions`, `SanitizedKnowledgeReadinessReport`, `SanitizedKnowledgeRequirement`, `ServerSentEventOptions`, `StreamToolLoopOptions`, `SupervisedKnowledgeUpdateInput`, `SupervisedKnowledgeUpdateOptions`, `SupervisedKnowledgeUpdateResult`, `ToolLoopCall`, `ToolLoopResult`, `VerifiedAgentCandidate`, `VetoedFact`, `WorktreeLoopRunnerOptions`, `AgentCandidateModelGrantActivateInput`, `AgentCandidateModelGrantReserveInput`, `AgentCandidateModelGrantSettleInput`, `AgentCandidateOutputPurpose`, `AgentCandidateRetryRejection`, `AgentCandidateRunFinalization`, `AgentRuntimeEvent`, `AgentRuntimeEventSink`, `AgentTaskStatus`, `AuthSource`, `ChatModelValidation`, `ControlDecision`, `ConversationStreamEvent`, `DeepReadonly`, `DelegatedLoopMode`, `DelegatedLoopRegistry`, `DelegatedLoopRunner`, `ForwardHeaderName`, `HaltPredicate`, `HaltReason`, `ImproveCandidateValidator`, `ImprovementCandidate`, `ImproveMethodSource`, `ImproveOptimizationRunOptions`, `ImproveProfileSurface`, `ImproveResult`, `KnowledgeReadinessCheck`, `KnowledgeReadinessCheckResult`, `RuntimeDecisionKind`, `RuntimeHookTarget`, `RuntimeRunStatus`, `RuntimeStreamEvent`, `RuntimeStreamEventSink`, `StreamToolLoopYield`, `SupervisedKnowledgeUpdater`, `ToolLoopEvent`, `TurnOrder`. +**Undocumented supporting types** (add a TSDoc line at the declaration to earn a table row): `AgentAdapter`, `AgentBackendContext`, `AgentBackendInput`, `AgentCandidateContainerPort`, `AgentCandidateExecutionAttemptRef`, `AgentCandidateExecutionPorts`, `AgentCandidateExecutorWorkspaceFile`, `AgentCandidateExecutorWorkspaceInput`, `AgentCandidateMemoryPort`, `AgentCandidateMemoryResetResult`, `AgentCandidateModelPort`, `AgentCandidatePreparationEvidence`, `AgentCandidateProtectedModelActivation`, `AgentCandidateProtectedModelReservation`, `AgentCandidateProtectedModelSettlement`, `AgentCandidateProtectedRunCapture`, `AgentCandidateVerificationPorts`, `AgentCandidateWorkspaceArchiveLimits`, `AgentExecutionBackend`, `AgenticGeneratorOptions`, `AgenticGeneratorShotReceipt`, `AgentKnowledgeProvider`, `AgentKnowledgeReadinessCheckOptions`, `AgentTaskContext`, `AgentTaskRunResult`, `AgentTaskSpec`, `AnalystRegistry`, `BackendCallPolicy`, `CanonicalCandidateDocument`, `CaptureAgentCandidateWorkspaceOptions`, `CapturedAgentCandidateWorkspace`, `ChatModelCandidate`, `ControlBudget`, `ControlEvalResult`, `ControlRunResult`, `ControlStep`, `Conversation`, `ConversationDriveState`, `ConversationJournal`, `ConversationJournalEntry`, `ConversationParticipant`, `ConversationPolicy`, `ConversationResult`, `ConversationTurn`, `CreateAgentCandidateWorkspacePortOptions`, `CreateKnowledgeImprovementActivationExecutorOptions`, `CreateProtectedAgentCandidateModelPortOptions`, `D1StmtLike`, `DataAcquisitionPlan`, `DelegatedLoopResult`, `DisposePreparedAgentCandidateOptions`, `Driver`, `DriverLoopGeneratorOptions`, `EvalRunEvent`, `EvalRunGeneration`, `EvalRunsExportConfig`, `EvalRunsExportResult`, `ExactProcessCandidateExecutorOptions`, `ExecutePreparedAgentCandidateOptions`, `FileAgentCandidateExecutionClaimStoreOptions`, `HaltContext`, `HaltSignal`, `ImproveCodeOptions`, `ImproveCodeResult`, `ImprovementCodeCandidate`, `ImprovementProfileCandidate`, `ImproveMethodContext`, `ImproveMethodResult`, `ImproveSkillsOptions`, `InMemoryAgentCandidateExecutionClaimStoreOptions`, `KnowledgeImprovementActivationExecutor`, `KnowledgeImprovementCandidatePair`, `KnowledgeImprovementExperimentBundles`, `KnowledgeImprovementJobMeasurement`, `KnowledgeImprovementJobResult`, `KnowledgeReadinessCheckInput`, `KnowledgeReadinessDecision`, `KnowledgeReadinessReport`, `KnowledgeRequirement`, `LoopResult`, `LoopRunnerCliArgs`, `LoopRunnerCliResult`, `McpServeSpec`, `OfficialSensitiveCandidateInput`, `OtelAttribute`, `OtelExportConfig`, `OtelExporter`, `OtelSpan`, `PersonaConversationResult`, `PrepareAgentCandidateExecutionOptions`, `PreparedAgentCandidateExecution`, `PreparedAgentCandidateInstruction`, `PreparedAgentCandidateLaunch`, `PreparedAgentCandidateTrace`, `RawTraceDistillerOptions`, `RecoverExpiredAgentCandidateOptions`, `ReflectiveGeneratorOptions`, `ResearchLoopResult`, `ResearchLoopRunnerOptions`, `ResolveAgentBackendOptions`, `ResolvedAgentCandidateContainer`, `ResolvedChatModel`, `RunAgentTaskOptions`, `RunAgentTaskStreamOptions`, `RunConversationOptions`, `RunDelegatedLoopOptions`, `RunKnowledgeImprovementJobOptions`, `RunPersonaConfig`, `RunPersonaConversationOptions`, `RuntimeDecisionEvidenceRef`, `RuntimeDecisionPoint`, `RuntimeEventCollector`, `RuntimeEventOtelOptions`, `RuntimeHookContext`, `RuntimeHookErrorContext`, `RuntimeHookEvent`, `RuntimeRunCompleteInput`, `RuntimeRunCost`, `RuntimeRunHandle`, `RuntimeRunOptions`, `RuntimeRunPersistenceAdapter`, `RuntimeRunRow`, `RuntimeSession`, `RuntimeSessionStore`, `RuntimeStreamEventCollector`, `RuntimeStreamEventSummary`, `RuntimeTelemetryOptions`, `SanitizedKnowledgeReadinessReport`, `SanitizedKnowledgeRequirement`, `ServerSentEventOptions`, `SupervisedKnowledgeUpdateInput`, `SupervisedKnowledgeUpdateOptions`, `SupervisedKnowledgeUpdateResult`, `VerifiedAgentCandidate`, `VetoedFact`, `WorktreeLoopRunnerOptions`, `AgentCandidateModelGrantActivateInput`, `AgentCandidateModelGrantReserveInput`, `AgentCandidateModelGrantSettleInput`, `AgentCandidateOutputPurpose`, `AgentCandidateRetryRejection`, `AgentCandidateRunFinalization`, `AgentRuntimeEvent`, `AgentRuntimeEventSink`, `AgentTaskStatus`, `AuthSource`, `ChatModelValidation`, `ControlDecision`, `ConversationStreamEvent`, `DeepReadonly`, `DelegatedLoopMode`, `DelegatedLoopRegistry`, `DelegatedLoopRunner`, `ForwardHeaderName`, `HaltPredicate`, `HaltReason`, `ImproveCandidateValidator`, `ImprovementCandidate`, `ImproveMethodSource`, `ImproveOptimizationRunOptions`, `ImproveProfileSurface`, `ImproveResult`, `KnowledgeReadinessCheck`, `KnowledgeReadinessCheckResult`, `RuntimeDecisionKind`, `RuntimeHookTarget`, `RuntimeRunStatus`, `RuntimeStreamEvent`, `RuntimeStreamEventSink`, `SupervisedKnowledgeUpdater`, `TurnOrder`. ### Vertical agent — manifest + surface proposal source @@ -349,6 +343,21 @@ Import from `@tangle-network/agent-runtime/durable` — 8 exports. | `ChatTurnResult` | interface | HTTP response values returned for one chat turn. | | `RunChatTurnInput` | interface | Inputs for one streamed product chat turn. | +### Bounded tool calls for browser and edge runtimes + +Import from `@tangle-network/agent-runtime/tool-loop` — 12 exports. + +| Symbol | Kind | Summary | +|---|---|---| +| `runToolLoop` | function | Run the bounded tool loop and return the final text + every executed tool | +| `streamToolLoop` | function | Streaming bounded tool loop: yields each raw turn event (the caller maps + | +| `ToolLoopAssistantToolCall` | interface | One OpenAI-shaped tool-call entry carried on an assistant message. | +| `ToolCallOutcome` | type | Outcome of one tool dispatch — structurally compatible with a hub/integration | +| `ToolLoopMessage` | type | A message in the running conversation the loop sends to `streamTurn`. | +| `ToolLoopStopReason` | type | Why the loop stopped. `completed` = model finished naturally; `stuck-loop` = | + +**Undocumented supporting types** (add a TSDoc line at the declaration to earn a table row): `RunToolLoopOptions`, `StreamToolLoopOptions`, `ToolLoopCall`, `ToolLoopResult`, `StreamToolLoopYield`, `ToolLoopEvent`. + ### Intelligence SDK — Observe + provable-OFF billing Import from `@tangle-network/agent-runtime/intelligence` — 166 exports. diff --git a/docs/api/runtime.md b/docs/api/runtime.md index a123b1a0..a20caaeb 100644 --- a/docs/api/runtime.md +++ b/docs/api/runtime.md @@ -12553,7 +12553,7 @@ What the journal proves about one keyed assignment at resume time. ##### state -> `readonly` **state**: `"down"` \| `"completed"` \| `"in-doubt"` +> `readonly` **state**: `"completed"` \| `"down"` \| `"in-doubt"` ##### settled? @@ -16686,7 +16686,7 @@ out to N sandboxes (bounded concurrency), parses + validates each output, and fo the round's results through `driver.decide` — fanout → validate → vote/select → refine, repeated until the driver says stop. One call spans many agent sessions. -Not to be confused with `runToolLoop` / `streamToolLoop` (package root entry): those +Not to be confused with `runToolLoop` / `streamToolLoop` (`/tool-loop`): those run ONE chat turn against ONE model, dispatching the tool calls that turn emits and folding the results back in until the model stops calling tools. No sandboxes, no rounds, no winner selection. @@ -16719,7 +16719,7 @@ rounds, no winner selection. Use [runAgentRounds](#runagentrounds). The clearer name says what it is: the multi-agent fanout/vote/refine kernel over sandboxes, NOT the one-turn tool loop -(`runToolLoop` / `streamToolLoop`, package root entry). `runLoop` shipped on `/kernel` +(`runToolLoop` / `streamToolLoop`, `/tool-loop`). `runLoop` shipped on `/kernel` next to `routerToolLoop`, which made the two read as variants of one thing. The alias is removed in the next major. @@ -18553,7 +18553,7 @@ out to N sandboxes (bounded concurrency), parses + validates each output, and fo the round's results through `driver.decide` — fanout → validate → vote/select → refine, repeated until the driver says stop. One call spans many agent sessions. -Not to be confused with `runToolLoop` / `streamToolLoop` (package root entry): those +Not to be confused with `runToolLoop` / `streamToolLoop` (`/tool-loop`): those run ONE chat turn against ONE model, dispatching the tool calls that turn emits and folding the results back in until the model stops calling tools. No sandboxes, no rounds, no winner selection. diff --git a/docs/api/tool-loop.md b/docs/api/tool-loop.md new file mode 100644 index 00000000..9056d00f --- /dev/null +++ b/docs/api/tool-loop.md @@ -0,0 +1,540 @@ +[**@tangle-network/agent-runtime**](README.md) + +*** + +[@tangle-network/agent-runtime](README.md) / tool-loop + +# tool-loop + +## Interfaces + +### ToolLoopCall + +#### Properties + +##### toolCallId? + +> `optional` **toolCallId?**: `string` + +##### toolName + +> **toolName**: `string` + +##### args + +> **args**: `Record`\<`string`, `unknown`\> + +*** + +### ToolLoopAssistantToolCall + +One OpenAI-shaped tool-call entry carried on an assistant message. + +#### Properties + +##### id + +> **id**: `string` + +##### type + +> **type**: `"function"` + +##### function + +> **function**: `object` + +###### name + +> **name**: `string` + +###### arguments + +> **arguments**: `string` + +*** + +### ToolLoopResult + +#### Properties + +##### finalText + +> **finalText**: `string` + +##### toolResults + +> **toolResults**: `object`[] + +###### call + +> **call**: [`ToolLoopCall`](#toolloopcall) + +###### label + +> **label**: `string` + +###### outcome + +> **outcome**: [`ToolCallOutcome`](#toolcalloutcome) + +##### turns + +> **turns**: `number` + +##### stopReason + +> **stopReason**: [`ToolLoopStopReason`](#toolloopstopreason) + +##### ~~cappedOut~~ + +> **cappedOut**: `boolean` + +###### Deprecated + +Use `stopReason !== 'completed'` instead. + +*** + +### RunToolLoopOptions + +#### Properties + +##### systemPrompt + +> **systemPrompt**: `string` + +##### userMessage + +> **userMessage**: `string` + +##### priorMessages? + +> `optional` **priorMessages?**: [`ToolLoopMessage`](#toolloopmessage)[] + +##### streamTurn + +> **streamTurn**: (`messages`) => `AsyncIterable`\<[`ToolLoopEvent`](#toolloopevent)\> + +###### Parameters + +###### messages + +[`ToolLoopMessage`](#toolloopmessage)[] + +###### Returns + +`AsyncIterable`\<[`ToolLoopEvent`](#toolloopevent)\> + +##### executeToolCall + +> **executeToolCall**: (`call`) => `Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> + +###### Parameters + +###### call + +[`ToolLoopCall`](#toolloopcall) + +###### Returns + +`Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> + +##### isExecutableTool + +> **isExecutableTool**: (`toolName`) => `boolean` + +###### Parameters + +###### toolName + +`string` + +###### Returns + +`boolean` + +##### maxToolTurns? + +> `optional` **maxToolTurns?**: `number` + +Runaway-backstop cap. Default 200 — set far above any legitimate workflow. + For per-workflow limits, use `maxCostUsd` or `deadlineMs` instead. + +##### deadlineMs? + +> `optional` **deadlineMs?**: `number` + +Wall-clock deadline in ms since epoch (Date.now()-based). When exceeded the + loop stops with stopReason `deadline`. + +##### maxCostUsd? + +> `optional` **maxCostUsd?**: `number` + +Maximum total cost in USD. Requires `costOf` to meter each tool call. + +##### costOf? + +> `optional` **costOf?**: (`call`, `outcome`) => `number` + +Return the USD cost of one outcome. Required for `maxCostUsd` to work. + +###### Parameters + +###### call + +[`ToolLoopCall`](#toolloopcall) + +###### outcome + +[`ToolCallOutcome`](#toolcalloutcome) + +###### Returns + +`number` + +##### renderResult? + +> `optional` **renderResult?**: (`label`, `outcome`) => `string` + +###### Parameters + +###### label + +`string` + +###### outcome + +[`ToolCallOutcome`](#toolcalloutcome) + +###### Returns + +`string` + +##### labelFor? + +> `optional` **labelFor?**: (`call`) => `string` + +###### Parameters + +###### call + +[`ToolLoopCall`](#toolloopcall) + +###### Returns + +`string` + +##### runId? + +> `optional` **runId?**: `string` + +##### scenarioId? + +> `optional` **scenarioId?**: `string` + +##### hooks? + +> `optional` **hooks?**: [`RuntimeHooks`](index.md#runtimehooks) + +*** + +### StreamToolLoopOptions + +#### Type Parameters + +##### Raw + +`Raw` + +#### Properties + +##### systemPrompt + +> **systemPrompt**: `string` + +##### userMessage + +> **userMessage**: `string` + +##### priorMessages? + +> `optional` **priorMessages?**: [`ToolLoopMessage`](#toolloopmessage)[] + +##### streamTurn + +> **streamTurn**: (`messages`) => `AsyncIterable`\<`Raw`\> + +###### Parameters + +###### messages + +[`ToolLoopMessage`](#toolloopmessage)[] + +###### Returns + +`AsyncIterable`\<`Raw`\> + +##### extractText + +> **extractText**: (`event`) => `string` + +###### Parameters + +###### event + +`Raw` + +###### Returns + +`string` + +##### extractToolCall + +> **extractToolCall**: (`event`) => [`ToolLoopCall`](#toolloopcall) \| `null` + +###### Parameters + +###### event + +`Raw` + +###### Returns + +[`ToolLoopCall`](#toolloopcall) \| `null` + +##### isExecutableTool + +> **isExecutableTool**: (`toolName`) => `boolean` + +###### Parameters + +###### toolName + +`string` + +###### Returns + +`boolean` + +##### executeToolCall + +> **executeToolCall**: (`call`) => `Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> + +###### Parameters + +###### call + +[`ToolLoopCall`](#toolloopcall) + +###### Returns + +`Promise`\<[`ToolCallOutcome`](#toolcalloutcome)\> + +##### maxToolTurns? + +> `optional` **maxToolTurns?**: `number` + +Runaway-backstop cap. Default 200 — set far above any legitimate workflow. + +##### deadlineMs? + +> `optional` **deadlineMs?**: `number` + +Wall-clock deadline in ms since epoch (Date.now()-based). + +##### maxCostUsd? + +> `optional` **maxCostUsd?**: `number` + +Maximum total cost in USD. Requires `costOf` to meter each tool call. + +##### costOf? + +> `optional` **costOf?**: (`call`, `outcome`) => `number` + +Return the USD cost of one outcome. Required for `maxCostUsd` to work. + +###### Parameters + +###### call + +[`ToolLoopCall`](#toolloopcall) + +###### outcome + +[`ToolCallOutcome`](#toolcalloutcome) + +###### Returns + +`number` + +##### renderResult? + +> `optional` **renderResult?**: (`label`, `outcome`) => `string` + +###### Parameters + +###### label + +`string` + +###### outcome + +[`ToolCallOutcome`](#toolcalloutcome) + +###### Returns + +`string` + +##### labelFor? + +> `optional` **labelFor?**: (`call`) => `string` + +###### Parameters + +###### call + +[`ToolLoopCall`](#toolloopcall) + +###### Returns + +`string` + +##### runId? + +> `optional` **runId?**: `string` + +##### scenarioId? + +> `optional` **scenarioId?**: `string` + +##### hooks? + +> `optional` **hooks?**: [`RuntimeHooks`](index.md#runtimehooks) + +## Type Aliases + +### ToolCallOutcome + +> **ToolCallOutcome** = \{ `ok`: `true`; `result`: `unknown`; \} \| \{ `ok`: `false`; `code`: `string`; `message`: `string`; `status?`: `number`; \} + +Outcome of one tool dispatch — structurally compatible with a hub/integration + tool-outcome union, so callers can fold either through the loop. + +*** + +### ToolLoopMessage + +> **ToolLoopMessage** = `object` + +A message in the running conversation the loop sends to `streamTurn`. + +The base `{ role, content }` covers `system` / `user` / plain `assistant` +turns. Two optional fields carry the OpenAI function-calling contract so a +strict model (Claude, and any OpenAI-compatible provider that validates tool +history) reads its own tool use back instead of re-issuing the same call: + + - an assistant turn that emitted tool calls carries `tool_calls`, and its + `content` is `null` when the turn was tool-only; + - each tool result is its own `{ role: 'tool', tool_call_id, content }` + message keyed to the call that produced it. + +Widening is additive: a `streamTurn` that reads only `role` + `content` still +works; one that forwards the whole message to an OpenAI-compatible endpoint +now sends correct tool history. + +#### Properties + +##### role + +> **role**: `string` + +##### content + +> **content**: `string` \| `null` + +##### tool\_calls? + +> `optional` **tool\_calls?**: [`ToolLoopAssistantToolCall`](#toolloopassistanttoolcall)[] + +##### tool\_call\_id? + +> `optional` **tool\_call\_id?**: `string` + +*** + +### ToolLoopEvent + +> **ToolLoopEvent** = \{ `type`: `"text"`; `text`: `string`; \} \| \{ `type`: `"tool_call"`; `call`: [`ToolLoopCall`](#toolloopcall); \} \| \{ `type`: `"other"`; `event`: `unknown`; \} + +*** + +### ToolLoopStopReason + +> **ToolLoopStopReason** = `"completed"` \| `"stuck-loop"` \| `"backstop"` \| `"deadline"` \| `"budget"` + +Why the loop stopped. `completed` = model finished naturally; `stuck-loop` = + ≥3 consecutive identical tool calls (same tool + args); `backstop` = hit the + runaway-backstop cap (200 by default); `deadline` = wall-clock deadlineMs + exceeded; `budget` = maxCostUsd exhausted. Non-`completed` stops are infra / + resource outcomes — eval scoring must distinguish them from capability failure. + +*** + +### StreamToolLoopYield + +> **StreamToolLoopYield**\<`Raw`\> = \{ `kind`: `"event"`; `event`: `Raw`; \} \| \{ `kind`: `"tool_result"`; `toolName`: `string`; `toolCallId?`: `string`; `label`: `string`; `outcome`: [`ToolCallOutcome`](#toolcalloutcome); \} \| \{ `kind`: `"capped"`; `pending`: `number`; `stopReason`: `Exclude`\<[`ToolLoopStopReason`](#toolloopstopreason), `"completed"`\>; \} + +#### Type Parameters + +##### Raw + +`Raw` + +## Functions + +### runToolLoop() + +> **runToolLoop**(`opts`): `Promise`\<[`ToolLoopResult`](#toolloopresult)\> + +Run the bounded tool loop and return the final text + every executed tool + outcome. Awaitable — callers needing to stream events to a UI use + [streamToolLoop](#streamtoolloop). + +#### Parameters + +##### opts + +[`RunToolLoopOptions`](#runtoolloopoptions) + +#### Returns + +`Promise`\<[`ToolLoopResult`](#toolloopresult)\> + +*** + +### streamToolLoop() + +> **streamToolLoop**\<`Raw`\>(`opts`): `AsyncGenerator`\<[`StreamToolLoopYield`](#streamtoolloopyield)\<`Raw`\>, `void`, `unknown`\> + +Streaming bounded tool loop: yields each raw turn event (the caller maps + + telemetries + re-emits it) and each executed `tool_result`; emits one + `capped` if it stops for any non-completed reason with calls still pending. + +#### Type Parameters + +##### Raw + +`Raw` + +#### Parameters + +##### opts + +[`StreamToolLoopOptions`](#streamtoolloopoptions)\<`Raw`\> + +#### Returns + +`AsyncGenerator`\<[`StreamToolLoopYield`](#streamtoolloopyield)\<`Raw`\>, `void`, `unknown`\> diff --git a/docs/canonical-api.md b/docs/canonical-api.md index b429dfaf..b9cf8943 100644 --- a/docs/canonical-api.md +++ b/docs/canonical-api.md @@ -4,7 +4,7 @@ Generated signatures and the complete export list live in docs/api/. Run pnpm docs:freshness after editing this file. --> -> **Version 0.109.0.** +> **Version 0.109.1.** > [`docs/api/primitive-catalog.md`](./api/primitive-catalog.md) lists every export and import path. > `agent-eval` must satisfy `>=0.135.1 <0.136.0`. > `sandbox` must satisfy `>=0.15.0 <0.16.0`. @@ -38,7 +38,7 @@ Two substrates implement the same recursive-atom over the one `Executor` port an The reactive `Supervisor`/`Scope` plus personify combinators drive dynamic agent trees; the round-synchronous `runAgentRounds` kernel is one leaf backend. `inlineSandboxClient` adapts any non-box `Executor` into a `SandboxClient` for `runAgentRounds`, and `settledToIteration` bridges reactive `Settled` results into the kernel's `Iteration`. `runAgentRounds` was named `runLoop`, which remains a deprecated `/kernel` alias. -It is separate from root `runToolLoop` and `streamToolLoop`, which run one chat turn and fold tool calls back into it. +It is separate from `runToolLoop` and `streamToolLoop` in `/tool-loop`, which run one chat turn and fold tool calls back into it. ## 1.5 The AgentProfile rule: author the profile, the substrate materializes it diff --git a/docs/glossary.md b/docs/glossary.md index 713a402e..0eca5625 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -5,7 +5,7 @@ **Track:** reference · **Role:** canonical (terms). One definition per concept, grounded to `file:line`. When a term has drifted into synonyms, the **canonical** word is marked and the synonyms are listed as "avoid / say X instead". If code and this file disagree, the code wins — fix this file the same turn (the anti-staleness law, `CLAUDE.md`). Two substrates run the same "recursive agent decision" atom — the round-synchronous `runAgentRounds` and the reactive `Scope`/`Supervisor`. Terms below note which substrate they belong to; several are shared. -`runAgentRounds` was named `runLoop`; that name is still exported from `/kernel` as a deprecated alias. Neither is `runToolLoop`/`streamToolLoop` (package root), which run ONE chat turn and fold its tool calls back in. +`runAgentRounds` was named `runLoop`; that name is still exported from `/kernel` as a deprecated alias. Neither is `runToolLoop`/`streamToolLoop` from `/tool-loop`, which run ONE chat turn and fold its tool calls back in. ## The execution units (most-confused — read first) diff --git a/package.json b/package.json index 31de8a73..73c3eabc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tangle-network/agent-runtime", - "version": "0.109.0", + "version": "0.109.1", "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.", "homepage": "https://github.com/tangle-network/agent-runtime#readme", "repository": { @@ -34,6 +34,11 @@ "import": "./dist/durable.js", "default": "./dist/durable.js" }, + "./tool-loop": { + "types": "./dist/tool-loop.d.ts", + "import": "./dist/tool-loop.js", + "default": "./dist/tool-loop.js" + }, "./intelligence": { "types": "./dist/intelligence.d.ts", "import": "./dist/intelligence.js", @@ -118,10 +123,11 @@ "check:skills": "node scripts/check-skills.mjs", "release:prepare": "node scripts/prepare-release.mjs", "verify:static-imports": "node scripts/verify-static-imports.mjs", + "verify:edge-tool-loop": "node scripts/verify-edge-tool-loop.mjs", "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", "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", "verify:cohort": "node scripts/verify-packed-cohort.mjs", - "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", + "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", "verify:official-optimizers": "node scripts/verify-official-optimizers.mjs", "verify:primeintellect": "pnpm build && node scripts/verify-primeintellect.mjs", "verify:primeintellect:live": "pnpm build && node scripts/verify-primeintellect-live.mjs", @@ -137,6 +143,7 @@ "@tangle-network/sandbox": "catalog:", "@types/node": "catalog:", "@types/tar-stream": "3.1.4", + "miniflare": "4.20260722.0", "playwright": "^1.62.0", "publint": "catalog:", "tsdown": "catalog:", @@ -144,7 +151,8 @@ "typedoc": "0.28.20", "typedoc-plugin-markdown": "4.12.0", "typescript": "catalog:", - "vitest": "^4.1.10" + "vitest": "^4.1.10", + "wrangler": "4.114.0" }, "engines": { "node": ">=22.13.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f31a72f..dc294ef2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,9 @@ importers: '@types/tar-stream': specifier: 3.1.4 version: 3.1.4 + miniflare: + specifier: 4.20260722.0 + version: 4.20260722.0 playwright: specifier: ^1.62.0 version: 1.62.0 @@ -99,6 +102,9 @@ importers: vitest: specifier: ^4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(vite@7.3.2(@types/node@26.1.1)(tsx@4.23.1)(yaml@2.9.0)) + wrangler: + specifier: 4.114.0 + version: 4.114.0 bench: dependencies: @@ -228,10 +234,57 @@ packages: '@braidai/lang@1.1.2': resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} + + '@cloudflare/unenv-preset@2.16.1': + resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} + peerDependencies: + unenv: 2.0.0-rc.24 + workerd: '>1.20260305.0 <2.0.0-0' + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/workerd-darwin-64@1.20260722.1': + resolution: {integrity: sha512-vZOP8vIS3NwnuaO+gz0FZ7kIGeiO3bZmxV35Ph9zOXKSREhDFlH7wQ7mkCdhW3O4jnXsew+XT7b+DNEI2CcJGQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20260722.1': + resolution: {integrity: sha512-EmIQymihDq6WNdER4+LF8Qn80yqayBUpJ+tkOO7wmY8pmgfyXjIUFNXotl21AHovTeu2seR7HdVUgeN/BilCWw==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20260722.1': + resolution: {integrity: sha512-jvZ3k9fxcnEn04s80CgIYxQfpOyAiz/8qC42DP8EBa9tR27qWyg9wmm31zIobVlrgBZn/+8NfdP73avRGcQOjQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20260722.1': + resolution: {integrity: sha512-BOSB55SMNdy+DA5uj2WirgiNanpHGis5PVvXH1wSfvjRKr4JGgWK+EZzxz0RFUo6QjjQQC/NimEzNZ7va7jmKg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20260722.1': + resolution: {integrity: sha512-sYM8YgUpKnRz2xjvdJLX1Ojzoi4MlA4gk8WTTExhGydjYB2UTs5NIbv0ZmpKgMoK9io3ixgmiW56ZnTbcWOdiA==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@emnapi/core@1.11.2': resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} @@ -562,9 +615,178 @@ packages: peerDependencies: hono: ^4 + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.2': + resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.2': + resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.2': + resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.1': + resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.1': + resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.1': + resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.1': + resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.1': + resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.1': + resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.1': + resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.1': + resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.2': + resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.2': + resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.2': + resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.2': + resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.2': + resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.2': + resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.2': + resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.2': + resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.2': + resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.2': + resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.2': + resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.2': + resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.2': + resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@loaderkit/resolve@1.0.6': resolution: {integrity: sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==} @@ -593,6 +815,15 @@ packages: '@oxc-project/types@0.140.0': resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + '@poppinss/colors@4.1.6': + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + + '@poppinss/dumper@0.6.5': + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + + '@poppinss/exception@1.2.3': + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@publint/pack@0.1.6': resolution: {integrity: sha512-3uVNyGcVplhPZSLVyeIpL7+cIRn1YCSNHLG/rUIlBQMVH8YuN9++YF+5+UDIIO9RW98dujiUoTltO7RDB5bFJA==} engines: {node: '>=18'} @@ -864,6 +1095,13 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + + '@speed-highlight/core@1.2.17': + resolution: {integrity: sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -1176,6 +1414,9 @@ packages: bare-url@2.4.5: resolution: {integrity: sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==} + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} @@ -1229,9 +1470,17 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + dts-resolver@3.0.0: resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} engines: {node: ^22.18.0 || >=24.0.0} @@ -1259,6 +1508,9 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + es-module-lexer@2.3.1: resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} @@ -1352,6 +1604,10 @@ packages: peerDependencies: ws: '*' + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} @@ -1383,6 +1639,11 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + miniflare@4.20260722.0: + resolution: {integrity: sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw==} + engines: {node: '>=22.0.0'} + hasBin: true + minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -1434,6 +1695,9 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1526,6 +1790,10 @@ packages: engines: {node: '>=10'} hasBin: true + sharp@0.35.2: + resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} + engines: {node: '>=20.9.0'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -1557,6 +1825,10 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1678,6 +1950,13 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + unicode-emoji-modifier-base@1.0.0: resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} engines: {node: '>=4'} @@ -1784,6 +2063,21 @@ packages: engines: {node: '>=8'} hasBin: true + workerd@1.20260722.1: + resolution: {integrity: sha512-NycKuc1x2onvsRfGGpM093vRlLFU2zHDAM0+APpccfg4+gZxDGCH27RmdDvkeBuoZyYqgLo3oAfF6re4mvC3vQ==} + engines: {node: '>=16'} + hasBin: true + + wrangler@4.114.0: + resolution: {integrity: sha512-M65P25t5UHA1TIJfgZXDcj+YzVobgKdRguM2QPz0xnxLFuOcuE3ErgllDht0iaho7MS4o0g/Bb4YK2+GT+bibg==} + engines: {node: '>=22.0.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^5.20260722.1 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1817,6 +2111,12 @@ packages: resolution: {integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==} engines: {node: '>=10'} + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.0-beta.10: + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + yuku-ast@0.8.0: resolution: {integrity: sha512-trBzFsSa6k32vzNUCH6pFhAoTzWD/NifSYOIQ/6v14vXKh7TRhd2vDNIwRguGdXvcyfBNEEGcsfxFHrnJbS+FQ==} @@ -1905,9 +2205,36 @@ snapshots: '@braidai/lang@1.1.2': {} + '@cloudflare/kv-asset-handler@0.5.0': {} + + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1)': + dependencies: + unenv: 2.0.0-rc.24 + optionalDependencies: + workerd: 1.20260722.1 + + '@cloudflare/workerd-darwin-64@1.20260722.1': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20260722.1': + optional: true + + '@cloudflare/workerd-linux-64@1.20260722.1': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20260722.1': + optional: true + + '@cloudflare/workerd-windows-64@1.20260722.1': + optional: true + '@colors/colors@1.5.0': optional: true + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@emnapi/core@1.11.2': dependencies: '@emnapi/wasi-threads': 1.2.2 @@ -2092,8 +2419,121 @@ snapshots: dependencies: hono: 4.12.32 + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.1 + optional: true + + '@img/sharp-darwin-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.1 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.1': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.1': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.1': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + optional: true + + '@img/sharp-linux-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.1 + optional: true + + '@img/sharp-linux-arm@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.1 + optional: true + + '@img/sharp-linux-ppc64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.1 + optional: true + + '@img/sharp-linux-riscv64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.1 + optional: true + + '@img/sharp-linux-s390x@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.1 + optional: true + + '@img/sharp-linux-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.1 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + optional: true + + '@img/sharp-wasm32@0.35.2': + dependencies: + '@emnapi/runtime': 1.11.2 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-win32-arm64@0.35.2': + optional: true + + '@img/sharp-win32-ia32@0.35.2': + optional: true + + '@img/sharp-win32-x64@0.35.2': + optional: true + + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@loaderkit/resolve@1.0.6': dependencies: '@braidai/lang': 1.1.2 @@ -2119,6 +2559,18 @@ snapshots: '@oxc-project/types@0.140.0': {} + '@poppinss/colors@4.1.6': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.5': + dependencies: + '@poppinss/colors': 4.1.6 + '@sindresorhus/is': 7.2.0 + supports-color: 10.2.2 + + '@poppinss/exception@1.2.3': {} + '@publint/pack@0.1.6': dependencies: tinyexec: 1.2.4 @@ -2291,6 +2743,10 @@ snapshots: '@sindresorhus/is@4.6.0': {} + '@sindresorhus/is@7.2.0': {} + + '@speed-highlight/core@1.2.17': {} + '@standard-schema/spec@1.1.0': {} '@tangle-network/agent-core@0.4.25': @@ -2528,6 +2984,8 @@ snapshots: dependencies: bare-path: 3.1.1 + blake3-wasm@2.1.5: {} + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -2578,8 +3036,12 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.1.1: {} + defu@6.1.7: {} + detect-libc@2.1.2: {} + dts-resolver@3.0.0: {} emoji-regex@8.0.0: {} @@ -2592,6 +3054,8 @@ snapshots: environment@1.1.0: {} + error-stack-parser-es@1.0.5: {} + es-module-lexer@2.3.1: {} esbuild@0.27.7: @@ -2712,6 +3176,8 @@ snapshots: ws: 8.21.0 optional: true + kleur@4.1.5: {} + linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 @@ -2748,6 +3214,18 @@ snapshots: mdurl@2.0.0: {} + miniflare@4.20260722.0: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + sharp: 0.35.2 + undici: 7.28.0 + workerd: 1.20260722.1 + ws: 8.21.0 + youch: 4.1.0-beta.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + minimatch@10.2.5: dependencies: brace-expansion: 5.0.6 @@ -2803,6 +3281,8 @@ snapshots: parse5@6.0.1: {} + path-to-regexp@6.3.0: {} + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -2920,6 +3400,38 @@ snapshots: semver@7.8.5: {} + sharp@0.35.2: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.2 + '@img/sharp-darwin-x64': 0.35.2 + '@img/sharp-freebsd-wasm32': 0.35.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-linux-arm': 0.35.2 + '@img/sharp-linux-arm64': 0.35.2 + '@img/sharp-linux-ppc64': 0.35.2 + '@img/sharp-linux-riscv64': 0.35.2 + '@img/sharp-linux-s390x': 0.35.2 + '@img/sharp-linux-x64': 0.35.2 + '@img/sharp-linuxmusl-arm64': 0.35.2 + '@img/sharp-linuxmusl-x64': 0.35.2 + '@img/sharp-webcontainers-wasm32': 0.35.2 + '@img/sharp-win32-arm64': 0.35.2 + '@img/sharp-win32-ia32': 0.35.2 + '@img/sharp-win32-x64': 0.35.2 + siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -2953,6 +3465,8 @@ snapshots: dependencies: ansi-regex: 5.0.1 + supports-color@10.2.2: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3075,6 +3589,12 @@ snapshots: undici-types@8.3.0: {} + undici@7.28.0: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + unicode-emoji-modifier-base@1.0.0: {} validate-npm-package-name@5.0.1: {} @@ -3146,14 +3666,37 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + workerd@1.20260722.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20260722.1 + '@cloudflare/workerd-darwin-arm64': 1.20260722.1 + '@cloudflare/workerd-linux-64': 1.20260722.1 + '@cloudflare/workerd-linux-arm64': 1.20260722.1 + '@cloudflare/workerd-windows-64': 1.20260722.1 + + wrangler@4.114.0: + dependencies: + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) + blake3-wasm: 2.1.5 + esbuild: 0.28.1 + miniflare: 4.20260722.0 + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.24 + workerd: 1.20260722.1 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - ws@8.21.0: - optional: true + ws@8.21.0: {} y18n@5.0.8: {} @@ -3171,6 +3714,19 @@ snapshots: y18n: 5.0.8 yargs-parser: 20.2.9 + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.3 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.10: + dependencies: + '@poppinss/colors': 4.1.6 + '@poppinss/dumper': 0.6.5 + '@speed-highlight/core': 1.2.17 + cookie: 1.1.1 + youch-core: 0.3.3 + yuku-ast@0.8.0: dependencies: '@yuku-toolchain/types': 0.8.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c6f01681..a5ecb70c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,6 +13,7 @@ minimumReleaseAgeIgnoreMissingTime: false allowBuilds: '@ax-llm/ax': false esbuild: true + workerd: true catalog: '@arethetypeswrong/cli': 0.18.5 diff --git a/scripts/gen-primitive-catalog.mjs b/scripts/gen-primitive-catalog.mjs index a12298c7..5693687f 100644 --- a/scripts/gen-primitive-catalog.mjs +++ b/scripts/gen-primitive-catalog.mjs @@ -65,6 +65,7 @@ const ownSurfaceLabels = { './agent': 'Vertical agent — manifest + surface proposal source', './conversation': 'Multi-turn conversations', './durable': 'Product chat turns — edge-safe streaming, persistence, and stable execution IDs', + './tool-loop': 'Bounded tool calls for browser and edge runtimes', './intelligence': 'Intelligence SDK — Observe + provable-OFF billing', './kernel': 'Execution kernel — recursive atom, supervision, executors, round-synchronous loop', './environment-provider': 'Environment provider adapters — generic sandbox/compute bridge', diff --git a/scripts/lib/static-imports.mjs b/scripts/lib/static-imports.mjs index 30aff21a..5305db0e 100644 --- a/scripts/lib/static-imports.mjs +++ b/scripts/lib/static-imports.mjs @@ -5,6 +5,24 @@ export function findStaticPackageImports( packageNames, sourceFileName = 'shipped.js', ) { + const importedSpecifiers = findStaticModuleSpecifiers(source, sourceFileName) + + return packageNames.filter((packageName) => + importedSpecifiers.some( + (specifier) => specifier === packageName || specifier.startsWith(`${packageName}/`), + ), + ) +} + +export function findStaticModuleSpecifiers(source, sourceFileName = 'shipped.js') { + return findModuleSpecifiers(source, sourceFileName, false) +} + +export function findLiteralModuleSpecifiers(source, sourceFileName = 'shipped.js') { + return findModuleSpecifiers(source, sourceFileName, true) +} + +function findModuleSpecifiers(source, sourceFileName, includeDynamicImports) { const sourceFile = ts.createSourceFile( sourceFileName, source, @@ -14,20 +32,16 @@ export function findStaticPackageImports( const importedSpecifiers = new Set() const visit = (node) => { - const specifier = staticModuleSpecifier(node) + const specifier = moduleSpecifier(node, includeDynamicImports) if (specifier) importedSpecifiers.add(specifier) ts.forEachChild(node, visit) } visit(sourceFile) - return packageNames.filter((packageName) => - [...importedSpecifiers].some( - (specifier) => specifier === packageName || specifier.startsWith(`${packageName}/`), - ), - ) + return [...importedSpecifiers] } -function staticModuleSpecifier(node) { +function moduleSpecifier(node, includeDynamicImports) { if ( (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && @@ -45,6 +59,16 @@ function staticModuleSpecifier(node) { return node.arguments[0].text } + if ( + includeDynamicImports && + ts.isCallExpression(node) && + node.expression.kind === ts.SyntaxKind.ImportKeyword && + node.arguments[0] && + ts.isStringLiteralLike(node.arguments[0]) + ) { + return node.arguments[0].text + } + return undefined } diff --git a/scripts/verify-edge-tool-loop.mjs b/scripts/verify-edge-tool-loop.mjs new file mode 100644 index 00000000..6de570c0 --- /dev/null +++ b/scripts/verify-edge-tool-loop.mjs @@ -0,0 +1,150 @@ +import { execFileSync } from 'node:child_process' +import { + mkdirSync, + mkdtempSync, + readdirSync, + rmSync, + writeFileSync, +} from 'node:fs' +import { tmpdir } from 'node:os' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { Miniflare } from 'miniflare' + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const tempRoot = mkdtempSync(join(tmpdir(), 'agent-runtime-edge-tool-loop-')) +const packDir = join(tempRoot, 'pack') +const appDir = join(tempRoot, 'app') +const outputDir = join(appDir, 'worker-dist') + +try { + mkdirSync(packDir, { recursive: true }) + mkdirSync(join(appDir, 'src'), { recursive: true }) + + const pack = JSON.parse( + run('pnpm', ['pack', '--json', '--pack-destination', packDir], repoRoot), + ) + if (!pack || Array.isArray(pack) || typeof pack.filename !== 'string') { + throw new Error('expected one packed Runtime archive') + } + const archive = resolve(pack.filename) + + writeFileSync( + join(appDir, 'package.json'), + `${JSON.stringify( + { + private: true, + type: 'module', + dependencies: { + '@tangle-network/agent-runtime': `file:${archive}`, + }, + }, + null, + 2, + )}\n`, + ) + writeFileSync( + join(appDir, 'wrangler.jsonc'), + `${JSON.stringify( + { + name: 'agent-runtime-edge-tool-loop-check', + main: 'src/worker.js', + compatibility_date: '2026-07-28', + }, + null, + 2, + )}\n`, + ) + writeFileSync( + join(appDir, 'src', 'worker.js'), + ` + import { runToolLoop } from '@tangle-network/agent-runtime/tool-loop' + + export default { + async fetch() { + async function* streamTurn() { + yield { type: 'tool_call', call: { toolName: 'echo', args: { value: 'edge' } } } + yield { type: 'text', text: 'done' } + } + let turn = 0 + const result = await runToolLoop({ + systemPrompt: 'Run one tool.', + userMessage: 'Echo edge.', + streamTurn: async function* () { + turn += 1 + if (turn === 1) yield* streamTurn() + else yield { type: 'text', text: 'complete' } + }, + executeToolCall: async (call) => ({ ok: true, result: call.args }), + isExecutableTool: (name) => name === 'echo', + }) + return Response.json({ + finalText: result.finalText, + stopReason: result.stopReason, + toolCalls: result.toolResults.length, + turns: result.turns, + }) + }, + } + `, + ) + + run( + 'npm', + ['install', '--ignore-scripts', '--legacy-peer-deps', '--no-audit', '--no-fund'], + appDir, + ) + run( + resolve(repoRoot, 'node_modules', 'wrangler', 'bin', 'wrangler.js'), + ['deploy', '--dry-run', '--outdir', outputDir], + appDir, + ) + + const bundle = onlyJavaScriptFile(outputDir) + const miniflare = new Miniflare({ + compatibilityDate: '2026-07-28', + modules: true, + modulesRoot: outputDir, + scriptPath: bundle, + }) + try { + const response = await miniflare.dispatchFetch('http://agent-runtime.test') + const body = await response.json() + if ( + response.status !== 200 || + body.finalText !== 'donecomplete' || + body.stopReason !== 'completed' || + body.toolCalls !== 1 || + body.turns !== 2 + ) { + throw new Error(`unexpected Worker response: ${JSON.stringify(body)}`) + } + } finally { + await miniflare.dispose() + } + + process.stdout.write('Edge tool loop: packed Worker bundled and executed without Node compatibility.\n') +} finally { + rmSync(tempRoot, { recursive: true, force: true }) +} + +function onlyJavaScriptFile(directory) { + const files = readdirSync(directory, { recursive: true }) + .filter((file) => typeof file === 'string' && file.endsWith('.js')) + .map((file) => join(directory, file)) + if (files.length !== 1) { + throw new Error(`expected one Worker JavaScript bundle, found ${files.length}`) + } + return files[0] +} + +function run(command, args, cwd) { + const result = execFileSync(command, args, { + cwd, + encoding: 'utf8', + env: { ...process.env, WRANGLER_SEND_METRICS: 'false' }, + stdio: ['ignore', 'pipe', 'pipe'], + timeout: 300_000, + }) + return result +} diff --git a/scripts/verify-package-exports.mjs b/scripts/verify-package-exports.mjs index 160a7c39..d7465598 100644 --- a/scripts/verify-package-exports.mjs +++ b/scripts/verify-package-exports.mjs @@ -9,14 +9,17 @@ import { writeFileSync, } from 'node:fs' import { tmpdir } from 'node:os' -import { dirname, join, resolve } from 'node:path' +import { dirname, isAbsolute, join, relative, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { assertPublishableDependencySpecs, createStrictNodeConsumerTsconfig, requiredPackedDevelopmentDependency, } from './lib/packed-package-test.mjs' -import { findStaticPackageImports } from './lib/static-imports.mjs' +import { + findLiteralModuleSpecifiers, + findStaticPackageImports, +} from './lib/static-imports.mjs' // Static imports of these packages mutate Node builtins at module load and can // make edge runtimes reject a Worker before startup. Scan installed first-party @@ -71,6 +74,7 @@ try { './agent', './conversation', './durable', + './tool-loop', './intelligence', './kernel', './environment-provider', @@ -97,6 +101,13 @@ try { run('test', ['-f', join(packageDir, relativeTarget)], repoRoot) } } + const toolLoopPath = join(packageDir, packageExports['./tool-loop'].import) + const toolLoopExternalImports = staticExternalImportClosure(toolLoopPath, packageDir) + if (toolLoopExternalImports.length > 0) { + throw new Error( + `tool-loop entry has static external imports: ${toolLoopExternalImports.join(', ')}`, + ) + } const knowledgePackageDir = join( repoRoot, @@ -344,6 +355,40 @@ try { appDir, ) + run( + process.execPath, + [ + '--input-type=module', + '--eval', + ` + const root = await import('@tangle-network/agent-runtime') + const toolLoop = await import('@tangle-network/agent-runtime/tool-loop') + for (const name of ['runToolLoop', 'streamToolLoop']) { + if (typeof toolLoop[name] !== 'function') throw new Error('missing tool-loop export ' + name) + if (name in root) throw new Error('tool-loop export leaked through broad package root: ' + name) + } + async function* streamTurn() { + yield { type: 'text', text: 'done' } + } + const result = await toolLoop.runToolLoop({ + systemPrompt: 'Finish the turn.', + userMessage: 'Return done.', + streamTurn, + executeToolCall: async () => ({ ok: true, result: null }), + isExecutableTool: () => false, + }) + if ( + result.finalText !== 'done' || + result.stopReason !== 'completed' || + result.turns !== 1 + ) { + throw new Error('packed tool-loop smoke failed: ' + JSON.stringify(result)) + } + `, + ], + appDir, + ) + run( process.execPath, [ @@ -668,6 +713,38 @@ function assertNoEdgeUnsafeStaticImports(scopeDir) { ) } +function staticExternalImportClosure(entryPath, packageDir) { + const queue = [resolve(entryPath)] + const visited = new Set() + const externalImports = new Set() + + while (queue.length > 0) { + const file = queue.pop() + if (visited.has(file)) continue + visited.add(file) + + const source = readFileSync(file, 'utf8') + for (const specifier of findLiteralModuleSpecifiers(source, file)) { + if (!specifier.startsWith('.')) { + externalImports.add(specifier) + continue + } + + const importedFile = resolve(dirname(file), specifier) + const packageRelativePath = relative(packageDir, importedFile) + if (packageRelativePath.startsWith('..') || isAbsolute(packageRelativePath)) { + throw new Error(`tool-loop import escapes the packed package: ${specifier}`) + } + if (!existsSync(importedFile)) { + throw new Error(`tool-loop import is missing from the packed package: ${specifier}`) + } + queue.push(importedFile) + } + } + + return [...externalImports].sort() +} + /** Every installed `@tangle-network/*` directory, including nested copies. */ function firstPartyPackageDirs(scopeDir) { const dirs = [] diff --git a/scripts/verify-static-imports.mjs b/scripts/verify-static-imports.mjs index 4a8682b9..8767556e 100644 --- a/scripts/verify-static-imports.mjs +++ b/scripts/verify-static-imports.mjs @@ -1,6 +1,10 @@ import assert from 'node:assert/strict' -import { findStaticPackageImports } from './lib/static-imports.mjs' +import { + findLiteralModuleSpecifiers, + findStaticModuleSpecifiers, + findStaticPackageImports, +} from './lib/static-imports.mjs' const specifier = 'proper-lockfile' const cases = [ @@ -39,4 +43,20 @@ assert.deepEqual( 'returns only statically imported blocked packages', ) -process.stdout.write(`Static import parser: ${cases.length + 1} synthetic cases passed.\n`) +assert.deepEqual( + findStaticModuleSpecifiers( + `import './chunk.js'; export * from 'edge-safe-package'; await import('lazy-package')`, + ), + ['./chunk.js', 'edge-safe-package'], + 'returns every static module specifier and excludes dynamic imports', +) + +assert.deepEqual( + findLiteralModuleSpecifiers( + `import './chunk.js'; await import('lazy-package'); import(variableName)`, + ), + ['./chunk.js', 'lazy-package'], + 'returns literal dynamic imports but excludes computed imports', +) + +process.stdout.write(`Static import parser: ${cases.length + 3} synthetic cases passed.\n`) diff --git a/src/index.ts b/src/index.ts index 8ec394c6..0094a549 100644 --- a/src/index.ts +++ b/src/index.ts @@ -270,20 +270,6 @@ export { runtimeStreamServerSentEvent, type ServerSentEventOptions, } from './sse' -export { - type RunToolLoopOptions, - runToolLoop, - type StreamToolLoopOptions, - type StreamToolLoopYield, - streamToolLoop, - type ToolCallOutcome, - type ToolLoopAssistantToolCall, - type ToolLoopCall, - type ToolLoopEvent, - type ToolLoopMessage, - type ToolLoopResult, - type ToolLoopStopReason, -} from './tool-loop' // ── Core types ─────────────────────────────────────────────────────── export type { AgentAdapter, diff --git a/src/runtime/index.ts b/src/runtime/index.ts index cdbe4b03..519c8c55 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -337,7 +337,7 @@ export { runBenchmark, } from './run-benchmark' // `runAgentRounds` is the multi-agent fanout/vote/refine kernel (many sandbox sessions per -// call). It is NOT `runToolLoop`/`streamToolLoop` (package root: one chat turn, tool calls +// call). It is NOT `runToolLoop`/`streamToolLoop` (`/tool-loop`: one chat turn, tool calls // folded back in) and NOT `routerToolLoop` (also on this subpath — router chat + tools). // `runLoop`/`RunLoopOptions` are the pre-rename names, kept as deprecated aliases. export { diff --git a/src/runtime/run-loop.ts b/src/runtime/run-loop.ts index 799cca37..1c0fe0c8 100644 --- a/src/runtime/run-loop.ts +++ b/src/runtime/run-loop.ts @@ -141,7 +141,7 @@ export interface RunAgentRoundsOptions { * the round's results through `driver.decide` — fanout → validate → vote/select → * refine, repeated until the driver says stop. One call spans many agent sessions. * - * Not to be confused with `runToolLoop` / `streamToolLoop` (package root entry): those + * Not to be confused with `runToolLoop` / `streamToolLoop` (`/tool-loop`): those * run ONE chat turn against ONE model, dispatching the tool calls that turn emits and * folding the results back in until the model stops calling tools. No sandboxes, no * rounds, no winner selection. @@ -401,7 +401,7 @@ export async function runAgentRounds( * * @deprecated Use {@link runAgentRounds}. The clearer name says what it is: the * multi-agent fanout/vote/refine kernel over sandboxes, NOT the one-turn tool loop - * (`runToolLoop` / `streamToolLoop`, package root entry). `runLoop` shipped on `/kernel` + * (`runToolLoop` / `streamToolLoop`, `/tool-loop`). `runLoop` shipped on `/kernel` * next to `routerToolLoop`, which made the two read as variants of one thing. The alias * is removed in the next major. */ diff --git a/src/testing/fixtures/agent-improvement-proposal.json b/src/testing/fixtures/agent-improvement-proposal.json index 0a359cfc..516bf670 100644 --- a/src/testing/fixtures/agent-improvement-proposal.json +++ b/src/testing/fixtures/agent-improvement-proposal.json @@ -1,6 +1,6 @@ { "changedSurfaces": ["prompt"], - "digest": "sha256:0111aecf13f77e5967b34600f49978a2d0fc6036e2f3285e3bf29a892c8390ae", + "digest": "sha256:218d8ea27510e757b998843e63900712a5b12027484d4a481f29e44f016c8307", "evaluation": { "decision": { "contributingChecks": [ @@ -4810,7 +4810,7 @@ ], "metadata": { "fixture": "agent-improvement-proposal", - "runtimeVersion": "0.109.0" + "runtimeVersion": "0.109.1" }, "objectives": [ { @@ -4921,8 +4921,8 @@ "baselineContentHash": "sha256:5c21ee53e513fc604cb09754e21c392b24a424da0ef37dbf8f1ee4a8a0b08f09", "candidateContentHash": "sha256:60fcbb1c728194bd51d7d19cb732d1c3f1881dce7e0a6266b41c8b98cfd65693", "kind": "agent-eval-loop", - "recordDigest": "sha256:0a21cbc2e244683e58623c1b00c672cf34312bb5acb17d522cd3e4a747f08418", - "runId": "agent-runtime-0.109.0-proposal-fixture", + "recordDigest": "sha256:8ca3b2249f293c547e35cfe3cc79e041dff9cf29a3be5a1e2850fd157e719a51", + "runId": "agent-runtime-0.109.1-proposal-fixture", "schema": "agent-candidate-experiment" } }, @@ -4949,5 +4949,5 @@ ], "kind": "agent-improvement-proposal", "proposedAt": "2026-07-10T01:00:00.000Z", - "runId": "agent-runtime-0.109.0-proposal-fixture" + "runId": "agent-runtime-0.109.1-proposal-fixture" } diff --git a/src/testing/fixtures/agent-profile-improvement-proposal.json b/src/testing/fixtures/agent-profile-improvement-proposal.json index 7d9e9d95..0ad78f00 100644 --- a/src/testing/fixtures/agent-profile-improvement-proposal.json +++ b/src/testing/fixtures/agent-profile-improvement-proposal.json @@ -1,6 +1,6 @@ { "changedSurfaces": ["prompt", "skills"], - "digest": "sha256:d91073bcdef9663a219cecd71d230f1855d286ea762a1557ffefff99830a60fd", + "digest": "sha256:34e61cc608be4952ec34dce30fcf94fbec0d39018373913c72a7fdbbf20aaec4", "evaluation": { "decision": { "contributingChecks": [ @@ -1695,7 +1695,7 @@ ], "metadata": { "fixture": "agent-profile-improvement-proposal", - "runtimeVersion": "0.109.0" + "runtimeVersion": "0.109.1" }, "objectives": [ { diff --git a/tsdown.config.ts b/tsdown.config.ts index adbbc2d8..cbd7ab79 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ agent: 'src/agent/index.ts', conversation: 'src/conversation/index.ts', durable: 'src/durable/index.ts', + 'tool-loop': 'src/tool-loop.ts', intelligence: 'src/intelligence/index.ts', kernel: 'src/runtime/index.ts', 'environment-provider': 'src/runtime/environment-provider.ts', diff --git a/typedoc.json b/typedoc.json index 0f3b0165..5d82c958 100644 --- a/typedoc.json +++ b/typedoc.json @@ -5,6 +5,7 @@ "src/agent/index.ts", "src/conversation/index.ts", "src/durable/index.ts", + "src/tool-loop.ts", "src/intelligence/index.ts", "src/runtime/index.ts", "src/runtime/environment-provider.ts",