@arnilo/prism 0.0.3 → 0.0.5

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 (131) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +62 -26
  3. package/dist/agent-loops.d.ts +8 -1
  4. package/dist/agent-loops.js +57 -11
  5. package/dist/agents.js +212 -32
  6. package/dist/checkpoints.d.ts +11 -0
  7. package/dist/checkpoints.js +144 -0
  8. package/dist/cli-init.d.ts +41 -0
  9. package/dist/cli-init.js +390 -0
  10. package/dist/cli-runner.d.ts +7 -1
  11. package/dist/cli-runner.js +13 -1
  12. package/dist/compaction.js +9 -1
  13. package/dist/content.d.ts +121 -0
  14. package/dist/content.js +538 -0
  15. package/dist/contracts.d.ts +236 -11
  16. package/dist/contracts.js +8 -0
  17. package/dist/event-multiplexer.d.ts +23 -0
  18. package/dist/event-multiplexer.js +136 -0
  19. package/dist/execution-policy.d.ts +28 -0
  20. package/dist/execution-policy.js +24 -0
  21. package/dist/feedback.d.ts +48 -0
  22. package/dist/feedback.js +230 -0
  23. package/dist/index.d.ts +20 -6
  24. package/dist/index.js +13 -5
  25. package/dist/input.js +11 -1
  26. package/dist/leases.d.ts +8 -0
  27. package/dist/leases.js +111 -0
  28. package/dist/node/agent-definitions.js +3 -5
  29. package/dist/node/config.d.ts +1 -0
  30. package/dist/node/config.js +5 -3
  31. package/dist/node/contribution-discovery.js +5 -8
  32. package/dist/node/session-store-jsonl.js +8 -5
  33. package/dist/node/settings.js +2 -2
  34. package/dist/node/trust.js +2 -4
  35. package/dist/observability.d.ts +3 -0
  36. package/dist/observability.js +18 -0
  37. package/dist/providers/media.d.ts +44 -0
  38. package/dist/providers/media.js +126 -0
  39. package/dist/providers/openai-compatible.js +18 -119
  40. package/dist/providers/openai-primitives.d.ts +9 -0
  41. package/dist/providers/openai-primitives.js +129 -0
  42. package/dist/providers/transport.d.ts +40 -0
  43. package/dist/providers/transport.js +221 -0
  44. package/dist/redaction.js +40 -13
  45. package/dist/resources.d.ts +5 -0
  46. package/dist/resources.js +4 -0
  47. package/dist/structured-output.d.ts +11 -0
  48. package/dist/structured-output.js +59 -0
  49. package/dist/testing/feedback.d.ts +6 -0
  50. package/dist/testing/feedback.js +37 -0
  51. package/dist/testing/persistence-schema.d.ts +102 -0
  52. package/dist/testing/persistence-schema.js +487 -0
  53. package/dist/testing/provider-conformance.js +10 -1
  54. package/dist/testing/run-ledger-conformance.d.ts +33 -0
  55. package/dist/testing/run-ledger-conformance.js +178 -0
  56. package/dist/testing/session-store-conformance.d.ts +16 -0
  57. package/dist/testing/session-store-conformance.js +73 -0
  58. package/dist/tools.d.ts +17 -0
  59. package/dist/tools.js +29 -2
  60. package/docs/a2a.md +73 -0
  61. package/docs/agent-events.md +17 -10
  62. package/docs/agent-loops.md +11 -5
  63. package/docs/agent-session-runtime.md +15 -16
  64. package/docs/cli-rpc.md +36 -5
  65. package/docs/coding-agent-tools.md +43 -9
  66. package/docs/coding-security.md +88 -0
  67. package/docs/compaction-observational-memory.md +2 -0
  68. package/docs/context-and-skills.md +1 -0
  69. package/docs/credential-storage.md +177 -0
  70. package/docs/credentials-and-redaction.md +4 -3
  71. package/docs/database-persistence.md +52 -7
  72. package/docs/evaluations.md +122 -0
  73. package/docs/extensions.md +2 -2
  74. package/docs/host-security.md +33 -2
  75. package/docs/index.md +46 -18
  76. package/docs/input-and-prompt-assembly.md +6 -5
  77. package/docs/mcp-tools.md +184 -0
  78. package/docs/middleware-hooks.md +2 -0
  79. package/docs/migration.md +51 -28
  80. package/docs/model-registry.md +5 -3
  81. package/docs/multimodal-content.md +156 -0
  82. package/docs/observability.md +171 -0
  83. package/docs/performance.md +249 -1
  84. package/docs/persistence-credentials-multimodality-primitives.md +303 -0
  85. package/docs/postgres-persistence.md +143 -0
  86. package/docs/provider-conformance.md +18 -0
  87. package/docs/provider-layer.md +1 -1
  88. package/docs/provider-packages.md +2 -0
  89. package/docs/provider-primitives.md +281 -0
  90. package/docs/providers/ai-sdk.md +113 -0
  91. package/docs/providers/kimi.md +1 -0
  92. package/docs/providers/neuralwatt.md +1 -0
  93. package/docs/providers/openai-compatible.md +2 -1
  94. package/docs/providers/openai.md +8 -1
  95. package/docs/providers/opencode-go.md +1 -0
  96. package/docs/providers/openrouter.md +1 -0
  97. package/docs/providers/zai.md +1 -0
  98. package/docs/public-contracts.md +13 -5
  99. package/docs/rag.md +113 -0
  100. package/docs/release-and-install.md +237 -30
  101. package/docs/resource-loading.md +14 -4
  102. package/docs/review-coverage-2026-07-14.md +260 -0
  103. package/docs/review-coverage-2026-07-15.md +193 -0
  104. package/docs/run-ledger-conformance.md +96 -0
  105. package/docs/runs-and-usage.md +43 -4
  106. package/docs/server.md +139 -0
  107. package/docs/session-store-conformance.md +16 -0
  108. package/docs/session-stores-and-branching.md +1 -0
  109. package/docs/settings-auth-trust-security.md +6 -5
  110. package/docs/sqlite-persistence.md +123 -0
  111. package/docs/structured-output.md +9 -0
  112. package/docs/supervisors.md +71 -0
  113. package/docs/tool-conformance.md +1 -0
  114. package/docs/tool-execution-primitives.md +374 -0
  115. package/docs/tools.md +39 -1
  116. package/docs/workflow-orchestration-primitives.md +581 -0
  117. package/docs/workflow-tui-primitives.md +5 -0
  118. package/docs/workflows.md +293 -0
  119. package/docs/working-and-semantic-memory.md +169 -0
  120. package/package.json +43 -5
  121. package/templates/init/README.md.tmpl +28 -0
  122. package/templates/init/env.example.tmpl +1 -0
  123. package/templates/init/gitignore.tmpl +11 -0
  124. package/templates/init/optional/evals-example.ts.tmpl +17 -0
  125. package/templates/init/optional/workflows-example.ts.tmpl +27 -0
  126. package/templates/init/package.json.tmpl +22 -0
  127. package/templates/init/providers.json +76 -0
  128. package/templates/init/src/agent.ts.tmpl +10 -0
  129. package/templates/init/src/index.ts.tmpl +12 -0
  130. package/templates/init/src/tests/agent.test.ts.tmpl +24 -0
  131. package/templates/init/tsconfig.json.tmpl +15 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.5] - 2026-07-16
11
+
12
+ - `@arnilo/prism-providers` now installs all seven first-party adapters including AI SDK interoperability; `@arnilo/prism-all` now installs every first-party package while activating none automatically.
13
+
14
+ - Added optional `@arnilo/prism-supervisor` with bounded explicit child delegation, derived memory scope IDs, narrowing-only permissions, A2A 1.0 cards/ES256 signatures, authorized JSON-RPC/SSE serving, and an exact-origin remote client.
15
+
16
+ - Added bounded immutable run/trace feedback with exact ownership, evaluation linkage, memory/SQLite/PostgreSQL stores, schema migration 003, and safe OpenTelemetry projection.
17
+
18
+ - Phase 11 extends workflows with explicit durable schedules/background execution, nested composition, bounded validated state, immutable-lineage replay, and optional command/Web bindings over existing checkpoint/lease primitives.
19
+
20
+ - Optional `@arnilo/prism-server` package with authorized bounded Web-standard direct/SSE agent and durable workflow routes; `@arnilo/prism-mcp` now supports explicit authorized Prism tool/command server exposure and bounded Web-standard Streamable HTTP handling.
21
+ - Optional `@arnilo/prism-rag` package: bounded deterministic text/Markdown chunking, Phase 7 vector indexing/retrieval, stable citations, metadata filters, redaction, and explicit ContextProvider injection.
22
+ - Workflows now support durable human `suspend()`/approve/deny, expected-version exact-once resume, validated/redacted resume payloads, and opt-in tool approval with execution-policy recheck.
23
+
24
+ ### Added
25
+
26
+ - Optional `@arnilo/prism-memory` package: schema/template-backed working memory, semantic recall, package-owned `Embedder`/`VectorStore` contracts, in-memory adapters, context provider, opt-in processor, shared conformance, and PostgreSQL/pgvector production path.
27
+
28
+ ## [0.0.4] - 2026-07-14
29
+
30
+ ### Added
31
+
32
+ - Shared bounded provider transport, OpenAI serialization/media helpers, native structured-output contracts, provider/tool timing metadata, and audio/file/document content capability checks.
33
+ - Generic checkpoint, atomic lease, and bounded event-multiplexer contracts plus persistence/run-ledger conformance helpers.
34
+ - Optional packages for JSON Schema tool validation, MCP, coding approval/sandboxing, OpenTelemetry, encrypted/keychain credentials, SQLite/PostgreSQL persistence, and bounded workflow orchestration.
35
+ - Manifest-only `base`, `code`, and `sdk` profiles; `prism-all` now transitively installs every first-party package.
36
+ - Workflow, multimodal, persistence/resume, provider telemetry, cache, and external-adapter examples.
37
+
38
+ ### Changed
39
+
40
+ - Single-shot loops support ordered bounded parallel tools; `ToolDefinition.exclusive` serializes dangerous turns without reducing later concurrency.
41
+ - Provider requests, SSE/error bodies, media, schemas, event queues, checkpoints, and workflow fan-out/output use documented finite limits.
42
+ - Session/ledger writes preserve order and redact before persistence; revision-loop transcript ordering and OAuth abort polling are hardened.
43
+ - All first-party providers use shared bounded transport helpers and expose current structured-output, multimodal, caching, reasoning, telemetry, and retry behavior where supported.
44
+
45
+ ### Security
46
+
47
+ - Added fail-closed schema/prototype-pollution, SSRF/media, SQL/tenant, path/shell approval, MCP result, credential-envelope, OAuth, redaction, and stale-worker fencing coverage.
48
+ - Optional privileged capabilities remain inactive until hosts explicitly register transports/tools, configure roots/credentials/databases, and approve execution.
49
+
10
50
  ## [0.0.3] - 2026-07-08
11
51
 
12
52
  ### Added
package/README.md CHANGED
@@ -17,23 +17,23 @@ packages. Prism defines contracts, not apps.
17
17
  OpenAI/OpenRouter use best-effort explicit cache hints, NeuralWatt uses
18
18
  best-effort implicit prefix caching, and other providers have route/model-specific
19
19
  or no cache-control support; see [docs/provider-caching.md](docs/provider-caching.md).
20
- - **First-party packages**: `@arnilo/prism-provider-openai`, `@arnilo/prism-provider-opencode-go`,
21
- `@arnilo/prism-provider-openrouter`, `@arnilo/prism-provider-zai`, `@arnilo/prism-provider-kimi`,
22
- `@arnilo/prism-provider-neuralwatt`, `@arnilo/prism-compaction-llm`, and
23
- `@arnilo/prism-compaction-observational-memory`.
20
+ - **First-party packages**: six provider adapters, two compaction strategies,
21
+ coding tools/security, JSON Schema validation, MCP, workflows, OpenTelemetry,
22
+ encrypted credentials, SQLite/PostgreSQL persistence, and manifest-only install profiles.
24
23
  - **Tools, context, skills**: host-owned tool registry with allow/deny filtering
25
24
  and dispatch, context providers, and a skill registry with progressive
26
25
  disclosure.
27
26
  - **Input/prompt/context**: default input and prompt builders, system-prompt
28
27
  layering, and provider-input assembly — every stage replaceable.
29
28
  - **Sessions and memory**: in-memory and JSONL session stores, branching/fork/
30
- clone, default and LLM compaction strategies, retry policy, and
31
- observational-memory recall/status/view.
29
+ clone, default and LLM compaction strategies, retry policy,
30
+ observational-memory recall/status/view, optional working/semantic memory
31
+ (`@arnilo/prism-memory`), and bounded text/Markdown RAG (`@arnilo/prism-rag`).
32
32
  - **Extensions and manifests**: extension kernel + event bus, contribution
33
33
  registries, middleware hooks, and data-only package manifests.
34
34
  - **Config, settings, security**: layered config merge, settings providers,
35
35
  credential resolvers, trust/permission policies, and secret redaction.
36
- - **CLI/RPC**: `prism --mode print|json|rpc` over the same `AgentSession` API.
36
+ - **CLI/RPC/server**: `prism --mode print|json|rpc`, `prism init`, optional framework-free authorized Web agent/workflow routes, and explicit MCP server exposure.
37
37
 
38
38
  ## Install
39
39
 
@@ -41,14 +41,18 @@ packages. Prism defines contracts, not apps.
41
41
  npm install @arnilo/prism
42
42
  ```
43
43
 
44
- First-party provider/compaction packages are separate installs and require
45
- `@arnilo/prism` as a non-optional peer. Install a single package, or use an
46
- umbrella to grab a whole family in one line:
44
+ First-party code packages are separate imports and require `@arnilo/prism` as
45
+ a non-optional peer. Install atomic packages directly or choose a manifest-only
46
+ family/profile; profiles install packages but expose no alias exports and activate nothing:
47
47
 
48
48
  ```bash
49
- npm install @arnilo/prism @arnilo/prism-provider-openai # core + one provider
50
- npm install @arnilo/prism @arnilo/prism-providers # core + all providers
51
- npm install @arnilo/prism-all # everything
49
+ npm install @arnilo/prism @arnilo/prism-provider-openai # core + one provider
50
+ npm install @arnilo/prism-base # core + compaction + validation
51
+ npm install @arnilo/prism-code @arnilo/prism-provider-openai # coding-agent profile
52
+ npm install @arnilo/prism-sdk @arnilo/prism-provider-openai # application profile
53
+ npm install @arnilo/prism-all # every first-party package
54
+ npm install @arnilo/prism-server @arnilo/prism-workflows # optional Web API boundary
55
+ npm install @arnilo/prism-supervisor # optional local delegation + A2A 1.0
52
56
  ```
53
57
 
54
58
  See [docs/release-and-install.md](docs/release-and-install.md) for install
@@ -56,6 +60,17 @@ specifiers, tarball contents, and the offline test budget.
56
60
 
57
61
  ## Quick start
58
62
 
63
+ Scaffold a tiny project (offline mock test included):
64
+
65
+ ```bash
66
+ npx --package @arnilo/prism prism init my-agent
67
+ # or, with a real provider package selected:
68
+ npx --package @arnilo/prism prism init my-agent --provider openai
69
+ cd my-agent && npm install && npm test
70
+ ```
71
+
72
+ Or embed Prism directly:
73
+
59
74
  ```ts
60
75
  import { createAgent, createAgentSession, createMockProvider } from "@arnilo/prism";
61
76
 
@@ -67,9 +82,18 @@ const agent = createAgent({
67
82
 
68
83
  const session = createAgentSession({ agent });
69
84
 
70
- // Consume the event stream concurrently with the run. `subscribe()` only
71
- // emits while a run is in progress, so the loop and `run()` must run together;
72
- // awaiting the loop before calling `run()` would deadlock.
85
+ // Direct result: run/prompt return AgentRunResult (text, usage, status, ids).
86
+ const result = await session.run("Hi");
87
+ console.log(result.text, result.usage?.totalTokens);
88
+
89
+ // Integrated streaming: subscribe-before-run for one owned run.
90
+ for await (const event of session.stream("Hi again")) {
91
+ // AgentEvent: agent_started, message_delta, turn_finished, ...
92
+ }
93
+
94
+ // Long-lived subscribe() still works when you need a subscriber across runs.
95
+ // `subscribe()` only emits while a run is in progress, so the loop and `run()`
96
+ // must run together; awaiting the loop before calling `run()` would deadlock.
73
97
  (async () => {
74
98
  const consumer = (async () => {
75
99
  for await (const event of session.subscribe()) {
@@ -111,8 +135,8 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
111
135
 
112
136
  - [docs/index.md](docs/index.md) — navigational map of every public surface.
113
137
  - The `examples/` directory holds compile-checked typed examples and runnable
114
- mock demos (provider registration, auth, tools, stores/branching, compaction,
115
- observational-memory recall, CLI, RPC).
138
+ offline demos covering providers, auth, tools, stores, compaction, structured
139
+ output, multimodality, workflows, CLI, and RPC.
116
140
 
117
141
  ## Packages
118
142
 
@@ -127,9 +151,22 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
127
151
  | `@arnilo/prism-provider-neuralwatt` | NeuralWatt provider with implicit vLLM prefix caching |
128
152
  | `@arnilo/prism-compaction-llm` | provider-backed compaction strategy |
129
153
  | `@arnilo/prism-compaction-observational-memory` | source-backed memory + recall tool |
130
- | `@arnilo/prism-providers` | umbrella: all 6 provider adapters |
131
- | `@arnilo/prism-compaction` | umbrella: both compaction strategies |
132
- | `@arnilo/prism-all` | umbrella: core + providers + compaction |
154
+ | `@arnilo/prism-coding-agent` | bounded shell/read/write/edit tools |
155
+ | `@arnilo/prism-coding-security` | coding approval, containment, and sandbox adapters |
156
+ | `@arnilo/prism-tool-validator-json-schema` | bounded JSON Schema tool validation |
157
+ | `@arnilo/prism-mcp` | MCP client/tool bridge |
158
+ | `@arnilo/prism-workflows` | bounded DAG workflows, durable suspend/resume, schedules/background runs, composition/state/replay, and multi-process coordination |
159
+ | `@arnilo/prism-supervisor` | bounded local child delegation and A2A 1.0 interoperability |
160
+ | `@arnilo/prism-observability-opentelemetry` | optional OpenTelemetry adapter |
161
+ | `@arnilo/prism-credentials-node` | encrypted-file and keychain credentials |
162
+ | `@arnilo/prism-session-store-sqlite` | SQLite persistence/checkpoints/leases/owned run feedback |
163
+ | `@arnilo/prism-session-store-postgres` | PostgreSQL persistence/checkpoints/leases/owned run feedback |
164
+ | `@arnilo/prism-providers` | family: all 7 provider adapters, including AI SDK interoperability |
165
+ | `@arnilo/prism-compaction` | family: both compaction strategies |
166
+ | `@arnilo/prism-base` | profile: core + compaction + JSON Schema validation |
167
+ | `@arnilo/prism-code` | profile: base + coding tools/security + MCP |
168
+ | `@arnilo/prism-sdk` | profile: base + workflows + MCP + credentials + OpenTelemetry |
169
+ | `@arnilo/prism-all` | every first-party package, including both persistence adapters |
133
170
 
134
171
  ## Scripts
135
172
 
@@ -142,8 +179,7 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
142
179
 
143
180
  ## Non-goals (v1)
144
181
 
145
- - Built-in shell/filesystem/browser tools — ship as separate packages only.
146
- - MCP bridge — external extension package after extension APIs settle.
147
- - TUI or interactive terminal — CLI/RPC only in core.
148
- - Workflow/graph orchestration — bounded agent loops first.
149
- - Encrypted/keychain credential storage — host-owned until a real app needs it.
182
+ - Privileged tools, MCP servers, telemetry, credentials, or databases activated by install — hosts explicitly configure and register every capability.
183
+ - Browser automation or interactive terminal UI — CLI/RPC and workflow control APIs only.
184
+ - Provider, credential, extension, or package auto-discovery.
185
+ - Core-owned database drivers, secret persistence, sandbox, or application policy — optional packages implement adapters over host-owned boundaries.
@@ -1,4 +1,4 @@
1
- import type { AgentLoopOptions, AgentLoopStrategy, ArtifactParser, ArtifactRepairer, ArtifactValidator } from "./contracts.js";
1
+ import type { AgentLoopOptions, AgentLoopStrategy, ArtifactParser, ArtifactRepairer, ArtifactValidator, LoopContext, ToolCallContent } from "./contracts.js";
2
2
  export declare const singleShotLoop: AgentLoopStrategy;
3
3
  export declare function isAgentLoopOptions(value: AgentLoopStrategy | AgentLoopOptions | undefined): value is AgentLoopOptions;
4
4
  export declare function generateValidateReviseLoop(opts: {
@@ -7,6 +7,13 @@ export declare function generateValidateReviseLoop(opts: {
7
7
  readonly repairer?: ArtifactRepairer<unknown>;
8
8
  readonly maxRevisions?: number;
9
9
  }): AgentLoopStrategy;
10
+ export declare function resolveToolConcurrency(options: {
11
+ loop?: AgentLoopStrategy | AgentLoopOptions;
12
+ }, config: {
13
+ loop?: AgentLoopStrategy | AgentLoopOptions;
14
+ }): number;
15
+ /** Dispatch tool calls with bounded concurrency; append transcript rows in call order. */
16
+ export declare function dispatchToolCallsInOrder(calls: readonly ToolCallContent[], ctx: LoopContext): Promise<void>;
10
17
  export declare function resolveLoop(options: {
11
18
  loop?: AgentLoopStrategy | AgentLoopOptions;
12
19
  }, config: {
@@ -18,13 +18,12 @@ export const singleShotLoop = {
18
18
  name: "single-shot",
19
19
  async run(ctx) {
20
20
  let usage;
21
- const toolResults = [];
22
21
  let toolRounds = 0;
23
22
  let nextInput = ctx.input;
24
23
  for (let turn = 1;; turn += 1) {
25
24
  throwIfAborted(ctx.signal);
26
25
  ctx.emit({ type: "turn_started", sessionId: ctx.sessionId, runId: ctx.runId, turn });
27
- const request = await ctx.assemble(nextInput, toolResults, turn);
26
+ const request = await ctx.assemble(nextInput, undefined, turn);
28
27
  throwIfAborted(ctx.signal);
29
28
  const { content, calls, messageId, started, usage: turnUsage } = await ctx.generate(request);
30
29
  usage = turnUsage ?? usage;
@@ -40,12 +39,7 @@ export const singleShotLoop = {
40
39
  if (calls.length === 0 || toolRounds >= ctx.maxToolRounds)
41
40
  break;
42
41
  toolRounds += 1;
43
- for (const call of calls) {
44
- const result = await ctx.dispatchToolCall(call);
45
- toolResults.push(result);
46
- await ctx.appendMessage(toolResultMessage(result));
47
- throwIfAborted(ctx.signal);
48
- }
42
+ await dispatchToolCallsInOrder(calls, ctx);
49
43
  nextInput = [];
50
44
  }
51
45
  return usage;
@@ -73,6 +67,7 @@ export function generateValidateReviseLoop(opts) {
73
67
  async run(ctx) {
74
68
  let usage;
75
69
  let nextInput = ctx.input;
70
+ let pendingHistory = [];
76
71
  for (let turn = 1; turn <= max + 1; turn += 1) {
77
72
  throwIfAborted(ctx.signal);
78
73
  ctx.emit({ type: "turn_started", sessionId: ctx.sessionId, runId: ctx.runId, turn });
@@ -80,6 +75,10 @@ export function generateValidateReviseLoop(opts) {
80
75
  throwIfAborted(ctx.signal);
81
76
  const { content, messageId, started, usage: turnUsage } = await ctx.generate(request);
82
77
  usage = turnUsage ?? usage;
78
+ if (pendingHistory.length > 0) {
79
+ ctx.history.push(...pendingHistory);
80
+ pendingHistory = [];
81
+ }
83
82
  if (turn === 1)
84
83
  ctx.history.push(...ctx.inputMessages);
85
84
  if (started) {
@@ -121,10 +120,9 @@ export function generateValidateReviseLoop(opts) {
121
120
  ctx.emit({ type: "artifact_revision_started", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, failure: result });
122
121
  const repair = await repairer(parsed.value, result, artifactCtx);
123
122
  const repairMessages = inputMessages(repair).map((m) => ({ ...m, id: randomId("msg") }));
124
- for (const message of repairMessages) {
125
- ctx.history.push(message);
123
+ for (const message of repairMessages)
126
124
  await ctx.appendMessage(message);
127
- }
125
+ pendingHistory = repairMessages;
128
126
  nextInput = repairMessages;
129
127
  continue;
130
128
  }
@@ -135,6 +133,54 @@ export function generateValidateReviseLoop(opts) {
135
133
  // ponytail: resolveLoop returns the loop to run. RunOptions.loop wins over
136
134
  // AgentConfig.loop; default is singleShotLoop. generate-validate-revise maps
137
135
  // its options to the factory; unknown strategy throws.
136
+ export function resolveToolConcurrency(options, config) {
137
+ const loop = options.loop ?? config.loop;
138
+ if (!loop || !isAgentLoopOptions(loop) || loop.strategy !== "single-shot")
139
+ return 1;
140
+ const value = loop.toolConcurrency;
141
+ if (value === undefined)
142
+ return 1;
143
+ if (!Number.isFinite(value) || value < 1)
144
+ return 1;
145
+ return Math.floor(value);
146
+ }
147
+ /** Dispatch tool calls with bounded concurrency; append transcript rows in call order. */
148
+ export async function dispatchToolCallsInOrder(calls, ctx) {
149
+ if (calls.length === 0)
150
+ return;
151
+ const concurrency = calls.some((call) => ctx.isToolCallExclusive?.(call))
152
+ ? 1
153
+ : Math.max(1, Math.min(ctx.toolConcurrency, calls.length));
154
+ if (concurrency === 1) {
155
+ for (const call of calls) {
156
+ const result = await ctx.dispatchToolCall(call);
157
+ await appendToolResultMessage(result, ctx);
158
+ }
159
+ return;
160
+ }
161
+ const results = new Array(calls.length);
162
+ let nextIndex = 0;
163
+ const workers = Array.from({ length: concurrency }, async () => {
164
+ for (;;) {
165
+ throwIfAborted(ctx.signal);
166
+ const index = nextIndex;
167
+ nextIndex += 1;
168
+ if (index >= calls.length)
169
+ return;
170
+ results[index] = await ctx.dispatchToolCall(calls[index]);
171
+ }
172
+ });
173
+ await Promise.all(workers);
174
+ for (const result of results) {
175
+ throwIfAborted(ctx.signal);
176
+ await appendToolResultMessage(result, ctx);
177
+ }
178
+ }
179
+ async function appendToolResultMessage(result, ctx) {
180
+ const message = toolResultMessage(result);
181
+ ctx.history.push(message);
182
+ await ctx.appendMessage(message);
183
+ }
138
184
  export function resolveLoop(options, config) {
139
185
  const loop = options.loop ?? config.loop;
140
186
  if (!loop)