melchizedek-agents 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/DOCUMENTATION.md +5 -1
- package/config/agents/syndicate.schema.json +63 -0
- package/config/agents/syndicateSchema.yaml +50 -0
- package/dist/lib/a2a/executor.js +21 -0
- package/dist/lib/compile.d.ts +4 -3
- package/dist/lib/compile.js +8 -3
- package/dist/lib/index.d.ts +8 -0
- package/dist/lib/index.js +5 -0
- package/dist/lib/loadSyndicate.d.ts +13 -0
- package/dist/lib/loadSyndicate.js +11 -1
- package/dist/lib/observability/supabaseSpanExporter.js +2 -1
- package/dist/lib/observability/tracer.d.ts +2 -2
- package/dist/lib/observability/tracer.js +2 -2
- package/dist/lib/runtime/native/checkpoint.d.ts +2 -1
- package/dist/lib/runtime/native/checkpoint.js +6 -2
- package/dist/lib/runtime/native/interrupts.js +9 -2
- package/dist/lib/runtime/nativeTurn.d.ts +2 -1
- package/dist/lib/runtime/nativeTurn.js +5 -1
- package/dist/lib/runtime/syndicateTurn.d.ts +11 -1
- package/dist/lib/runtime/syndicateTurn.js +383 -5
- package/dist/lib/sandbox/sandbox.d.ts +121 -0
- package/dist/lib/sandbox/sandbox.js +276 -0
- package/dist/lib/sandbox/worker.d.ts +29 -0
- package/dist/lib/sandbox/worker.js +280 -0
- package/dist/lib/script/config.d.ts +35 -0
- package/dist/lib/script/config.js +17 -0
- package/dist/lib/script/journal.d.ts +137 -0
- package/dist/lib/script/journal.js +193 -0
- package/dist/lib/script/run.d.ts +138 -0
- package/dist/lib/script/run.js +364 -0
- package/dist/lib/syndicateSchema.d.ts +20 -0
- package/dist/lib/syndicateSchema.js +98 -6
- package/dist/lib/toolRegistry.js +4 -0
- package/dist/lib/tools/runCodeTool.d.ts +50 -0
- package/dist/lib/tools/runCodeTool.js +59 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,89 @@ the starter pack and the templates), not the repo's full history.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 1.7.0 — 2026-10-17
|
|
10
|
+
|
|
11
|
+
A minor release that lets a syndicate hold code (ADR 0137). A `script:`
|
|
12
|
+
block is the fourth orchestration method: one JavaScript module, run in
|
|
13
|
+
a QuickJS sandbox on a worker thread, calls the syndicate's agents, its
|
|
14
|
+
allowed tools and the person as functions, so routing, per-call models
|
|
15
|
+
and human-in-the-loop change with a YAML deploy instead of an engine
|
|
16
|
+
release. Agents on any provider get `run_code`, the same sandbox as a
|
|
17
|
+
registry tool. Both are off until the operator sets
|
|
18
|
+
`MELCHIZEDEK_CODE_SANDBOX=on`; nothing changes for a deployment that
|
|
19
|
+
does not.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`script:` orchestration (ADR 0137 Part B).** `script: { source,
|
|
24
|
+
models?, tools? }` at the root; cannot be combined with `dispatch` or
|
|
25
|
+
`workflow`. The default export is called with `{ message, transcript,
|
|
26
|
+
variables, agent, tool, askUser, parallel, now, random, log }` and its
|
|
27
|
+
return is the answer; guards run on it once. `agent(name, input, {
|
|
28
|
+
model?, reasoning?, temperature?, conversation? })` runs a declared
|
|
29
|
+
agent (parsed JSON when it has an output schema, else text; a failed run
|
|
30
|
+
throws into the script); `conversation: true` answers in the
|
|
31
|
+
conversation as a dispatch route does. `askUser(id, question, {
|
|
32
|
+
payload?, schema? })` pauses the turn `input-required` with the same
|
|
33
|
+
`result.input` an `ask_user` node writes; the next message resumes the
|
|
34
|
+
run by replaying its journal (one content-less event per call, stored in
|
|
35
|
+
the conversation only when the run pauses) on the source the pause pinned, so a redeploy never breaks
|
|
36
|
+
a waiting conversation. A resume whose calls no longer match fails
|
|
37
|
+
`SCRIPT_DIVERGED`; a run past 1,000 calls fails `SCRIPT_CALL_LIMIT`.
|
|
38
|
+
The schema refuses `require_approval`, the `ask_user` tool, local skill
|
|
39
|
+
scripts and authorization_code grants on a script's agents (a pause
|
|
40
|
+
inside a called agent cannot be replayed yet), `context:` and root
|
|
41
|
+
`max_concurrency`; `mode: task` is allowed (a pause reached inside a
|
|
42
|
+
called agent anyway fails `PAUSE_INSIDE_AGENT`). `{{token}}`
|
|
43
|
+
interpolation skips `script.source`; the script reads the bound
|
|
44
|
+
`variables`. `script.require_approval` gates `tool()` calls with the
|
|
45
|
+
standard approval request (`result.approval`). Every `script.models`
|
|
46
|
+
pairing is checked before the run (`SCRIPT_MODEL_INVALID`). Under
|
|
47
|
+
`runDurableTurn` each call's journal event is a checkpoint boundary, so
|
|
48
|
+
a stopped worker resumes mid-script. `agent()` calls report through
|
|
49
|
+
`events.onAgentEvent`; spans `script.invoke` and `script.call`
|
|
50
|
+
(exported to `adk_telemetry`, no arguments or results) trace a run, and
|
|
51
|
+
each agent call's turn span carries `script.run` and `script.call.seq`.
|
|
52
|
+
A `yaml_reference` to a script syndicate runs its whole script as a
|
|
53
|
+
subagent, route or node, its pauses reaching the turn; `agent()` may
|
|
54
|
+
call nested workflow, dispatch and script syndicates.
|
|
55
|
+
`startEngineSpan` takes an optional parent context.
|
|
56
|
+
`TurnStage` gains `'script'`; `SyndicateTurnResult.failedStage` may be
|
|
57
|
+
`'script'`.
|
|
58
|
+
- **`run_code` (ADR 0137 Part A).** A registry tool: `{ code, input? }`,
|
|
59
|
+
an ES module whose default export is called with `input`, returning
|
|
60
|
+
`{ result, logs, error }`, each bounded at 20,000 characters. No
|
|
61
|
+
network, files, host, clock or randomness. An agent's
|
|
62
|
+
`run_code: { attach: true }` puts each run on the reply:
|
|
63
|
+
`SyndicateTurnResult.codeRuns` and, on the A2A server, a `code_runs`
|
|
64
|
+
artifact of `code_run` data parts. `DrainedRun` gains `codeRuns`.
|
|
65
|
+
- **The sandbox.** QuickJS (`quickjs-emscripten-core` and
|
|
66
|
+
`@jitl/quickjs-wasmfile-release-sync`, both pinned at 0.32.0) on a
|
|
67
|
+
worker thread per run: 64 MB memory, 1 MB stack, 5 s of compute (waits
|
|
68
|
+
on the host not counted), a watchdog and the turn's signal terminate
|
|
69
|
+
it; `MELCHIZEDEK_CODE_SANDBOX_WORKERS` (default 16) caps concurrent
|
|
70
|
+
runs. A syndicate with a script or a `run_code` agent is refused with
|
|
71
|
+
`CodeSandboxOffError` when a turn starts while the switch is off.
|
|
72
|
+
- **Package surface.** New subpaths `melchizedek-agents/script`
|
|
73
|
+
(`runScript`, `ScriptFatal`, `SCRIPT_CALL_LIMIT`, and the journal reader
|
|
74
|
+
`openRun` / `scriptPauseIn` with the `SCRIPT_*` marker keys and the
|
|
75
|
+
`JournalEntry`, `OpenRun`, `ScriptCallRecord`, `ScriptPauseRecord`,
|
|
76
|
+
`ScriptPinRecord` types) and
|
|
77
|
+
`melchizedek-agents/sandbox` (`runInSandbox`, `codeSandboxEnabled`,
|
|
78
|
+
`refuseWithoutSandbox`, `CodeSandboxOffError`, `SandboxHalt`,
|
|
79
|
+
`DEFAULT_SANDBOX_LIMITS`). The barrel adds those plus
|
|
80
|
+
`isScriptSyndicate`, `openRun`, `runCodeTool`, `RUN_CODE` and the types
|
|
81
|
+
`ScriptConfig`, `ScriptHost`, `ScriptCallSite`, `JournalEntry`, `OpenRun`,
|
|
82
|
+
`ScriptPauseRecord`, `ScriptPinRecord`, `ScriptRunInput`, `ScriptRunOutcome`,
|
|
83
|
+
`SandboxLimits`, `SandboxResult`, `SandboxRunOptions`, `CodeRun`,
|
|
84
|
+
`RunCodeOutput`.
|
|
85
|
+
|
|
86
|
+
### For melch-app
|
|
87
|
+
|
|
88
|
+
- An Electron build must list `quickjs-emscripten-core`,
|
|
89
|
+
`@jitl/quickjs-wasmfile-release-sync` and `melchizedek-agents/dist/lib/sandbox`
|
|
90
|
+
in `asarUnpack`: the wasm file and the worker script load from disk.
|
|
91
|
+
|
|
9
92
|
## 1.6.0 — 2026-10-15
|
|
10
93
|
|
|
11
94
|
A minor release for applications that show a turn as it runs and account
|
package/DOCUMENTATION.md
CHANGED
|
@@ -35,6 +35,8 @@ lib/tools/mcpToolFactory.ts MCP client: remote tools → the engine's own Tools
|
|
|
35
35
|
scripts/demo_mcp_server.ts demo MCP server (library catalog, SSE)
|
|
36
36
|
lib/runtime/native/… the native loop: steps, delegation, compaction, interrupts
|
|
37
37
|
lib/workflow/… the workflow scheduler for `workflow:` syndicates
|
|
38
|
+
lib/script/… the runner for `script:` syndicates: journal, replay, pause (ADR 0137)
|
|
39
|
+
lib/sandbox/… the QuickJS code sandbox on a worker thread (run_code, script:)
|
|
38
40
|
lib/session/… Supabase-backed session service
|
|
39
41
|
lib/memory/… pgvector long-term memory service
|
|
40
42
|
lib/observability/… OpenTelemetry run tracing
|
|
@@ -63,7 +65,7 @@ agent. The native runtime is the only runtime
|
|
|
63
65
|
([ADR 0107](./wiki/decisions/0107-release-1-0-0-removes-adk.md)): it owns the
|
|
64
66
|
loop, the model contract and its adapters, the tools, the sessions and
|
|
65
67
|
the workflow scheduler, and runs a single-agent, delegating,
|
|
66
|
-
plan-dispatch or `
|
|
68
|
+
plan-dispatch, `workflow:` or `script:` syndicate, `context:` compaction
|
|
67
69
|
([ADR 0078](./wiki/decisions/0078-native-compaction-ports-adk-compactor.md)),
|
|
68
70
|
`mode: task`, and pause and resume included
|
|
69
71
|
([ADR 0095](./wiki/decisions/0095-native-workflow-turn-drains-through-the-adk-reader.md)).
|
|
@@ -130,11 +132,13 @@ Field reference:
|
|
|
130
132
|
| `memory_retention_days` | root | Days a fact in this syndicate's namespace is kept; the server deletes older facts when it loads the syndicate and daily after. Requires `memory_namespace` (never applied to the shared default namespace). |
|
|
131
133
|
| `dispatch` | root | Switches the syndicate from DELEGATE to PLAN-DISPATCH routing (§6). `default_route` (required) names the fail-static subagent; `route_key` / `reason_key` name the router's JSON properties (defaults `route` / `reason`). Honoured by every surface (the CLI, the server, the worker, evals). |
|
|
132
134
|
| `workflow` | root | Switches the syndicate to a WORKFLOW (§6): a graph whose nodes are its agents plus declared `join`, `map`, `tool` and `ask_user` nodes, and whose `edges` say what runs after what and on which route. The orchestrator is a node like any other; nothing delegates. Cannot be combined with `dispatch`. Contract: `lib/workflow.ts`; worked example: `examples/pipeline.yaml`. |
|
|
135
|
+
| `script` | root | Switches the syndicate to a SCRIPT ([ADR 0137](./wiki/decisions/0137-script-orchestration-and-sandboxed-code.md)): `source` is one JavaScript module, run in the engine's QuickJS sandbox, whose default export gets `{ message, transcript, variables, agent, tool, askUser, parallel, now, random, log }` and returns the answer. `models` lists the models `agent(name, input, { model })` may move a call to; `tools` the registry tools `tool(name, args)` may run. `askUser` pauses the turn; the next message replays the run's journal on the source it pinned. Needs `MELCHIZEDEK_CODE_SANDBOX=on`; cannot be combined with `dispatch` or `workflow`; the agents may not pause inside themselves (no `require_approval`, `ask_user` tool, local skill scripts or authorization_code grants). `{{token}}` does not touch `source`. |
|
|
133
136
|
| `retries` | root | Self-correction, on by default ([ADR 0034](./wiki/decisions/0034-self-correction.md)): `model_errors` (default 2) retries a model reply marked malformed (`MALFORMED_FUNCTION_CALL`) instead of failing the turn; `tool_errors` (default 3) answers a tool that threw with structured reflection guidance and caps its retries. `0` turns either off. Every retry is a model call under `max_steps`. |
|
|
134
137
|
| `guards` | root | Optional list of post-answer guard NAMES (`lib/guards/index.ts`). Each runs after the answering turn and before the reply publishes, receiving the final text plus every tool-result text of that turn, and rewrites in place rather than re-asking the model; its notes land in the `[STATUS]` stream. Guards declared by a syndicate reached through `yaml_reference:` count too — the server resolves the union via `collectGuards()`. Resolved by name, never by module path, so adding one is a deliberate act in code; **the published registry ships one, `science`** (citation checks for `research.yaml`); `registerGuard()` adds your own from code, and an unregistered name is warned about and skipped. |
|
|
135
138
|
| `name` / `model` / `instruction` | agent | The agent triple. Any Gemini id, `claude-*`, or `ollama/*` for open-weight local models (see §5). |
|
|
136
139
|
| `description` | subagent | **The delegation API.** The orchestrator reads this when deciding to hand off — write it like a function signature ("Use this subagent to…, pass it…"). |
|
|
137
140
|
| `tools` | agent | Names resolved by the tool registry (§3). Long-term memory agents add `preload_memory` / `load_memory`. |
|
|
141
|
+
| `run_code` | agent | With `run_code` in `tools`, the agent runs JavaScript in the engine's sandbox on any provider (`{ code, input? }` → `{ result, logs, error }`; no network, host, clock or randomness; needs `MELCHIZEDEK_CODE_SANDBOX=on`, [ADR 0137](./wiki/decisions/0137-script-orchestration-and-sandboxed-code.md)). `run_code: { attach: true }` puts each run's code and result on the reply as a `code_run` data part (`result.codeRuns`). |
|
|
138
142
|
| `reasoning` | agent | How hard the agent reasons, on any provider: `none`, `low`, `medium`, `high`, `xhigh`, `max`, or `{ budget_tokens: <int> }`. The compiler sends each provider the field it reads: a thinking level on Gemini 3, a thinking budget on Claude 4.6 and earlier (2,048 / 8,192 / 16,384 tokens for low / medium / high), adaptive thinking with that effort on later Claude models ([ADR 0049](./wiki/decisions/0049-claude-requests-by-model-generation.md)), an effort word on GPT, Grok, Kimi, Ollama and the gateway ([ADR 0047](./wiki/decisions/0047-provider-neutral-reasoning-key.md)). `xhigh` and `max` reach the models that take them (both on Claude from Opus 4.7 and Sonnet 5, `max` on Kimi K3, `xhigh` on GPT-5.2 and later and grok-4.7); elsewhere the model's highest setting is sent and the span carries `llm.reasoning.weakened` (`npm run doctor -- --matrix` lists each path, [ADR 0117](./wiki/decisions/0117-tool-choice-and-effort-above-high-in-v2.md)). Unset, each adapter keeps its own default. |
|
|
139
143
|
| `sampling` | agent | `{ temperature, top_p, max_output_tokens, stop }`, the fields the engine sends to every provider; each adapter sends the ones its API takes ([ADR 0115](./wiki/decisions/0115-yaml-schema-v2-provider-neutral-keys.md)). |
|
|
140
144
|
| `output` | agent | `{ schema, mime }`. `mime` is `application/json` (JSON mode, with or without a schema) or `text/plain` (the default). `schema` is the structured-JSON contract: the agent ends its turn on one JSON object matching it. An orchestrator may hold one beside its subagents: it delegates first, then answers in the schema itself. The schema travels in the provider's own structured-output field beside the tools on Claude from Opus 4.8, Sonnet 5 and Haiku 5.5, on OpenAI, and on Gemini 2+ through Vertex AI, and as a `set_model_response` tool elsewhere (the capability matrix's `structured_output_with_tools`, [ADR 0109](./wiki/decisions/0109-structured-output-beside-tools.md)). Worked example: `examples/structured_critic.yaml`. A leaf that holds the schema (`critic.yaml`) and plan-dispatch remain the choices when the answer should be a specialist's. |
|
|
@@ -943,6 +943,17 @@
|
|
|
943
943
|
"type": "string",
|
|
944
944
|
"const": "gemini"
|
|
945
945
|
},
|
|
946
|
+
"run_code": {
|
|
947
|
+
"description": "How this agent's run_code tool behaves (ADR 0137). Needs run_code in `tools`.",
|
|
948
|
+
"type": "object",
|
|
949
|
+
"properties": {
|
|
950
|
+
"attach": {
|
|
951
|
+
"description": "Put each run's code and result on the reply as a code_run data part. Default false.",
|
|
952
|
+
"type": "boolean"
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
"additionalProperties": false
|
|
956
|
+
},
|
|
946
957
|
"context": {
|
|
947
958
|
"description": "Summarize a long conversation instead of overflowing the window. The orchestrator of a delegate syndicate only.",
|
|
948
959
|
"type": "object",
|
|
@@ -1967,6 +1978,17 @@
|
|
|
1967
1978
|
"type": "string",
|
|
1968
1979
|
"const": "gemini"
|
|
1969
1980
|
},
|
|
1981
|
+
"run_code": {
|
|
1982
|
+
"description": "How this agent's run_code tool behaves (ADR 0137). Needs run_code in `tools`.",
|
|
1983
|
+
"type": "object",
|
|
1984
|
+
"properties": {
|
|
1985
|
+
"attach": {
|
|
1986
|
+
"description": "Put each run's code and result on the reply as a code_run data part. Default false.",
|
|
1987
|
+
"type": "boolean"
|
|
1988
|
+
}
|
|
1989
|
+
},
|
|
1990
|
+
"additionalProperties": false
|
|
1991
|
+
},
|
|
1970
1992
|
"context": {
|
|
1971
1993
|
"description": "Summarize a long conversation instead of overflowing the window. The orchestrator of a delegate syndicate only.",
|
|
1972
1994
|
"type": "object",
|
|
@@ -2302,6 +2324,47 @@
|
|
|
2302
2324
|
"additionalProperties": false,
|
|
2303
2325
|
"description": "Opts into a WORKFLOW: the syndicate as a graph (lib/workflow.ts). Cannot be combined with dispatch."
|
|
2304
2326
|
},
|
|
2327
|
+
"script": {
|
|
2328
|
+
"type": "object",
|
|
2329
|
+
"properties": {
|
|
2330
|
+
"source": {
|
|
2331
|
+
"type": "string",
|
|
2332
|
+
"minLength": 1,
|
|
2333
|
+
"maxLength": 65536,
|
|
2334
|
+
"description": "One JavaScript module whose default export is an async function of { message, variables, agent, tool, askUser, parallel, now, random, log }: the orchestration. Its return value is the answer. {{token}} interpolation does not touch it; it reads `variables`."
|
|
2335
|
+
},
|
|
2336
|
+
"models": {
|
|
2337
|
+
"description": "The models agent(name, input, { model }) may move a call to; each agent is compiled and checked on every one at load.",
|
|
2338
|
+
"maxItems": 16,
|
|
2339
|
+
"type": "array",
|
|
2340
|
+
"items": {
|
|
2341
|
+
"type": "string",
|
|
2342
|
+
"minLength": 1
|
|
2343
|
+
}
|
|
2344
|
+
},
|
|
2345
|
+
"tools": {
|
|
2346
|
+
"description": "The registry tools tool(name, args) may run; the only path from the script to the network or the host.",
|
|
2347
|
+
"type": "array",
|
|
2348
|
+
"items": {
|
|
2349
|
+
"type": "string",
|
|
2350
|
+
"minLength": 1
|
|
2351
|
+
}
|
|
2352
|
+
},
|
|
2353
|
+
"require_approval": {
|
|
2354
|
+
"description": "Tools from script.tools whose every call waits for a person to approve the exact call (ADR 0028): the turn ends input-required; approved, the pinned arguments run, refused, the script gets { error }.",
|
|
2355
|
+
"type": "array",
|
|
2356
|
+
"items": {
|
|
2357
|
+
"type": "string",
|
|
2358
|
+
"minLength": 1
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
},
|
|
2362
|
+
"required": [
|
|
2363
|
+
"source"
|
|
2364
|
+
],
|
|
2365
|
+
"additionalProperties": false,
|
|
2366
|
+
"description": "Opts into SCRIPT orchestration (lib/script/): the syndicate's logic as code run in the engine's sandbox. Needs MELCHIZEDEK_CODE_SANDBOX=on. Cannot be combined with dispatch or workflow."
|
|
2367
|
+
},
|
|
2305
2368
|
"retries": {
|
|
2306
2369
|
"description": "Self-correction on model and tool errors: the engine's reflect-and-retry. On by default.",
|
|
2307
2370
|
"type": "object",
|
|
@@ -190,6 +190,48 @@ dispatch:
|
|
|
190
190
|
# Confirm: { ask_user: "Publish? Reply yes, or say what to change." }
|
|
191
191
|
# max_concurrency: 4
|
|
192
192
|
|
|
193
|
+
# [optional] Switches this syndicate to a SCRIPT (ADR 0137): one
|
|
194
|
+
# JavaScript module, run in the engine's QuickJS sandbox, is the
|
|
195
|
+
# orchestration. It calls the agents below, the tools it lists and the
|
|
196
|
+
# person as functions, so routing, per-call models and pauses live here
|
|
197
|
+
# and change with a deploy. Needs MELCHIZEDEK_CODE_SANDBOX=on. Cannot be
|
|
198
|
+
# combined with `dispatch` or `workflow`. Contract: lib/script/.
|
|
199
|
+
#
|
|
200
|
+
# - The default export is called with { message, transcript, variables,
|
|
201
|
+
# agent, tool, askUser, parallel, now, random, log }; its return (text,
|
|
202
|
+
# or JSON) is the answer, and `guards` run on it once.
|
|
203
|
+
# - agent(name, input, { model?, reasoning?, temperature?,
|
|
204
|
+
# conversation? }) runs a declared agent seeing only `input` (null:
|
|
205
|
+
# the message); it returns parsed JSON when the agent has an output
|
|
206
|
+
# schema, else its text, and throws when the run fails. With
|
|
207
|
+
# `conversation: true` it answers in the conversation, as a dispatch
|
|
208
|
+
# route does. `model` must be in script.models.
|
|
209
|
+
# - tool(name, args) runs a registry tool listed in script.tools; one
|
|
210
|
+
# also in script.require_approval waits for a person to approve the
|
|
211
|
+
# exact call (refused, the script gets { error }).
|
|
212
|
+
# - askUser(id, question, { payload?, schema? }) pauses the turn
|
|
213
|
+
# input-required; the next message resumes it, returning
|
|
214
|
+
# { reply, input }. The run replays its journal from the top, so a
|
|
215
|
+
# script must make the same calls on every run: use now() and
|
|
216
|
+
# random(), never Date or Math.random (they throw), and never
|
|
217
|
+
# Promise.race.
|
|
218
|
+
# - require_approval, the ask_user tool, skills.scripts: local and
|
|
219
|
+
# authorization_code grants are refused on a script's agents in this
|
|
220
|
+
# version (a pause inside a called agent cannot be replayed).
|
|
221
|
+
# - {{token}} interpolation does not touch `source`; read `variables`.
|
|
222
|
+
# - A yaml_reference to a script syndicate runs its whole script as a
|
|
223
|
+
# subagent, a route or a node, its pauses reaching the turn.
|
|
224
|
+
# script:
|
|
225
|
+
# models: ["gemini-3.8-flash", "claude-opus-5-5"]
|
|
226
|
+
# tools: ["web_extract", "publish"]
|
|
227
|
+
# require_approval: ["publish"]
|
|
228
|
+
# source: |
|
|
229
|
+
# export default async ({ message, agent, askUser, log }) => {
|
|
230
|
+
# const triage = await agent("OrchestratorName", message);
|
|
231
|
+
# log(`Routed to ${triage.route}`);
|
|
232
|
+
# return agent(triage.route, null, { conversation: true });
|
|
233
|
+
# };
|
|
234
|
+
|
|
193
235
|
# ── Orchestrator ────────────────────────────────────────────
|
|
194
236
|
# The turn's root agent: the engine's agent loop runs it. Its keys are
|
|
195
237
|
# spelled after ADK's LlmAgentConfig.
|
|
@@ -293,6 +335,14 @@ orchestrator:
|
|
|
293
335
|
# Google's server-side sandbox; nothing runs on this host. Gemini only.
|
|
294
336
|
# code_execution: "gemini"
|
|
295
337
|
|
|
338
|
+
# [optional] With `run_code` in tools, the agent runs JavaScript in the
|
|
339
|
+
# engine's QuickJS sandbox on any provider and rests its answer on the
|
|
340
|
+
# result (no network, no host, no clock; needs MELCHIZEDEK_CODE_SANDBOX=on,
|
|
341
|
+
# ADR 0137). `attach: true` puts each run's code and result on the reply
|
|
342
|
+
# as a code_run data part. Default off.
|
|
343
|
+
# run_code:
|
|
344
|
+
# attach: true
|
|
345
|
+
|
|
296
346
|
# [optional] Summarize a long conversation instead of overflowing the
|
|
297
347
|
# window: past `compact_after_tokens` (the last request's prompt), earlier
|
|
298
348
|
# turns become one summary and the last `keep_recent_events` stay
|
package/dist/lib/a2a/executor.js
CHANGED
|
@@ -375,6 +375,24 @@ export function approvalAnswer(rawParts, pending) {
|
|
|
375
375
|
}
|
|
376
376
|
return undefined;
|
|
377
377
|
}
|
|
378
|
+
/** The `code_runs` artifact: one `code_run` data part per run an attaching agent made (ADR 0137 item 3). */
|
|
379
|
+
function publishCodeRuns(eventBus, taskId, contextId, runs) {
|
|
380
|
+
eventBus.publish(AgentEvent.artifactUpdate({
|
|
381
|
+
taskId,
|
|
382
|
+
contextId,
|
|
383
|
+
artifact: {
|
|
384
|
+
artifactId: 'code_runs',
|
|
385
|
+
name: 'code_runs',
|
|
386
|
+
description: 'The code the answer rests on, and what it returned',
|
|
387
|
+
parts: runs.map((run) => ({ content: { $case: 'data', value: run }, metadata: undefined, filename: '', mediaType: 'application/json' })),
|
|
388
|
+
metadata: undefined,
|
|
389
|
+
extensions: [],
|
|
390
|
+
},
|
|
391
|
+
append: false,
|
|
392
|
+
lastChunk: true,
|
|
393
|
+
metadata: undefined,
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
378
396
|
function publishFinal(eventBus, taskId, contextId, state, text) {
|
|
379
397
|
eventBus.publish(AgentEvent.statusUpdate({
|
|
380
398
|
taskId,
|
|
@@ -687,6 +705,9 @@ export class SyndicateExecutor {
|
|
|
687
705
|
return;
|
|
688
706
|
}
|
|
689
707
|
log(`✓ Task ${short} complete — ${result.text.length.toLocaleString()} chars, ${spent}`);
|
|
708
|
+
// run_code's attach (ADR 0137): the code an answer rests on, and its result, as data parts beside it.
|
|
709
|
+
if (result.codeRuns?.length)
|
|
710
|
+
publishCodeRuns(eventBus, taskId, contextId, result.codeRuns);
|
|
690
711
|
publishFinal(eventBus, taskId, contextId, 'completed', result.text || undefined);
|
|
691
712
|
await report(ctx, 'completed', undefined, u);
|
|
692
713
|
// Long-term memory: there is no "session end" on a server, so ingest
|
package/dist/lib/compile.d.ts
CHANGED
|
@@ -115,9 +115,10 @@ export type SpecTool = {
|
|
|
115
115
|
dispatch: DispatchSpec;
|
|
116
116
|
};
|
|
117
117
|
/**
|
|
118
|
-
* A nested dispatch (plan-dispatch) syndicate (ADR 0120)
|
|
119
|
-
*
|
|
120
|
-
*
|
|
118
|
+
* A nested dispatch (plan-dispatch) syndicate (ADR 0120), or a nested
|
|
119
|
+
* script syndicate (ADR 0137 item 15): it runs as it runs at the top, as a
|
|
120
|
+
* turn of its own on its own conversation (its classifier picks a route and
|
|
121
|
+
* the route answers, or its script runs), so what it compiles to is
|
|
121
122
|
* the file and the options its turn loads its routes with. The turn runner
|
|
122
123
|
* runs it (lib/runtime/syndicateTurn.ts, TurnControl.nestedDispatch), as a
|
|
123
124
|
* delegated call, a dispatch route or a workflow node.
|
package/dist/lib/compile.js
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
import { relative } from 'node:path';
|
|
38
38
|
import { isDispatchSyndicate } from './dispatch.js';
|
|
39
39
|
import { isWorkflowSyndicate, nodeKind } from './workflowConfig.js';
|
|
40
|
+
import { isScriptSyndicate } from './script/config.js';
|
|
40
41
|
import { loadSyndicate, nestedLoader } from './loadSyndicate.js';
|
|
41
42
|
import { REASONING_OLDER_SPELLING } from './syndicateSchema.js';
|
|
42
43
|
import { reasoningConfig } from './models/reasoning.js';
|
|
@@ -134,7 +135,7 @@ function gateTools(tools, names, agentName) {
|
|
|
134
135
|
}
|
|
135
136
|
/** True when any agent of a syndicate may pause for approval: a gated tool, or skill scripts. */
|
|
136
137
|
export function declaresApprovals(config) {
|
|
137
|
-
return agentGates(config.orchestrator) || (config.subagents ?? []).some((s) => agentGates(s));
|
|
138
|
+
return agentGates(config.orchestrator) || (config.subagents ?? []).some((s) => agentGates(s)) || !!config.script?.require_approval?.length;
|
|
138
139
|
}
|
|
139
140
|
/**
|
|
140
141
|
* An agent's `examples:` as their InstructionTool (lib/tools/examples.ts):
|
|
@@ -398,8 +399,9 @@ export async function compileEntrySpec(subCfg, opts = {}, delegated = false) {
|
|
|
398
399
|
if (isWorkflowSyndicate(nested)) {
|
|
399
400
|
return { kind: 'workflow', workflow: await compileWorkflowSpec(nested, nestedOpts, subCfg.name, subCfg.description, subCfg.yaml_reference, delegated) };
|
|
400
401
|
}
|
|
401
|
-
// A nested dispatch syndicate classifies and routes as at the top (ADR 0120), wherever it runs
|
|
402
|
-
|
|
402
|
+
// A nested dispatch syndicate classifies and routes as at the top (ADR 0120), wherever it runs; a nested script
|
|
403
|
+
// syndicate runs its whole script the same way, as a turn of its own (ADR 0137 item 15).
|
|
404
|
+
if (isDispatchSyndicate(nested) || isScriptSyndicate(nested))
|
|
403
405
|
return { kind: 'dispatch', dispatch: compileDispatchEntry(subCfg, nested, nestedOpts) };
|
|
404
406
|
return { kind: 'agent', spec: await compileSpec(nested, nestedOpts, subCfg.name, subCfg.description) };
|
|
405
407
|
}
|
|
@@ -437,6 +439,9 @@ export async function compileSubagentSpec(subCfg, opts = {}) {
|
|
|
437
439
|
if (isDispatchSyndicate(nested)) {
|
|
438
440
|
throw new Error(`${subCfg.yaml_reference}: '${subCfg.name}' is a dispatch syndicate, which runs as its own turn (its classifier, then a route), not as one agent; compile the entry with compileEntrySpec (ADR 0120).`);
|
|
439
441
|
}
|
|
442
|
+
if (isScriptSyndicate(nested)) {
|
|
443
|
+
throw new Error(`${subCfg.yaml_reference}: '${subCfg.name}' is a script syndicate, which runs as its own turn (its script), not as one agent; compile the entry with compileEntrySpec (ADR 0137).`);
|
|
444
|
+
}
|
|
440
445
|
return compileSpec(nested, nestedOpts, subCfg.name, subCfg.description);
|
|
441
446
|
}
|
|
442
447
|
// YAML v2 (ADR 0115): the engine form, for a config built in code (a loaded one already is).
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -28,6 +28,14 @@ export type { AgentBranch, DrainedRun, MessagePart, TurnUsage, RouteDecision, Sy
|
|
|
28
28
|
export type { CompileOptions } from './compile.ts';
|
|
29
29
|
export { isWorkflowSyndicate, describeInput } from './workflow.ts';
|
|
30
30
|
export type { WorkflowConfig, WorkflowNodeYaml, EdgeElement, PendingInput } from './workflow.ts';
|
|
31
|
+
export { isScriptSyndicate } from './script/config.ts';
|
|
32
|
+
export type { ScriptConfig } from './script/config.ts';
|
|
33
|
+
export { openRun, runScript, ScriptFatal, SCRIPT_CALL_LIMIT } from './script/run.ts';
|
|
34
|
+
export type { JournalEntry, OpenRun, ScriptCallSite, ScriptHost, ScriptPauseRecord, ScriptPinRecord, ScriptRunInput, ScriptRunOutcome } from './script/run.ts';
|
|
35
|
+
export { CODE_SANDBOX_ENV, CodeSandboxOffError, DEFAULT_SANDBOX_LIMITS, SandboxHalt, codeSandboxEnabled, runInSandbox } from './sandbox/sandbox.ts';
|
|
36
|
+
export type { SandboxLimits, SandboxResult, SandboxRunOptions } from './sandbox/sandbox.ts';
|
|
37
|
+
export { RUN_CODE, runCodeTool } from './tools/runCodeTool.ts';
|
|
38
|
+
export type { CodeRun, RunCodeOutput } from './tools/runCodeTool.ts';
|
|
31
39
|
export { createA2AApp, compileAgentCard, currentRequestContext } from './a2a/app.ts';
|
|
32
40
|
export type { A2AApp, A2AAppOptions, RequestIdentity } from './a2a/app.ts';
|
|
33
41
|
export type { A2AContext } from './a2a/executor.ts';
|
package/dist/lib/index.js
CHANGED
|
@@ -23,6 +23,11 @@ export { DEFAULT_RUNTIME, describeRuntime, RuntimeRemovedError, UnsupportedOnRun
|
|
|
23
23
|
export { InProcessSessionService } from './runtime/sessions.js';
|
|
24
24
|
export { approvalResponsePart, pendingApproval, APPROVAL_REQUEST } from './runtime/approvals.js';
|
|
25
25
|
export { isWorkflowSyndicate, describeInput } from './workflow.js';
|
|
26
|
+
// SCRIPT orchestration and the code sandbox (ADR 0137).
|
|
27
|
+
export { isScriptSyndicate } from './script/config.js';
|
|
28
|
+
export { openRun, runScript, ScriptFatal, SCRIPT_CALL_LIMIT } from './script/run.js';
|
|
29
|
+
export { CODE_SANDBOX_ENV, CodeSandboxOffError, DEFAULT_SANDBOX_LIMITS, SandboxHalt, codeSandboxEnabled, runInSandbox } from './sandbox/sandbox.js';
|
|
30
|
+
export { RUN_CODE, runCodeTool } from './tools/runCodeTool.js';
|
|
26
31
|
// ── Serve it over A2A, or call a remote A2A agent ───────────────────────────
|
|
27
32
|
export { createA2AApp, compileAgentCard, currentRequestContext } from './a2a/app.js';
|
|
28
33
|
export { callerTokens, firstOf, hashCallerToken, jwtIdentity, parseCallers, scopeSegment, sharedSecret, trustedHeader, } from './a2a/identity.js';
|
|
@@ -11,6 +11,8 @@ export type { SkillsConfig } from './tools/skillToolset.ts';
|
|
|
11
11
|
import type { WorkflowConfig } from './workflowConfig.ts';
|
|
12
12
|
import type { ReasoningSetting } from './models/contract.ts';
|
|
13
13
|
export type { WorkflowConfig } from './workflowConfig.ts';
|
|
14
|
+
export type { ScriptConfig } from './script/config.ts';
|
|
15
|
+
import type { ScriptConfig } from './script/config.ts';
|
|
14
16
|
export interface ThinkingConfig {
|
|
15
17
|
/** Token budget for internal reasoning. 0 = off, -1 = dynamic. */
|
|
16
18
|
thinkingBudget?: number;
|
|
@@ -175,6 +177,10 @@ export interface AgentYamlConfig {
|
|
|
175
177
|
openapi?: OpenApiConfig[];
|
|
176
178
|
/** `gemini`: the model's Python runs in Gemini's server-side sandbox (ADR 0033). Gemini models only. */
|
|
177
179
|
code_execution?: 'gemini';
|
|
180
|
+
/** How this agent's `run_code` tool behaves (ADR 0137): `attach` puts each run's code and result on the reply. Needs run_code in `tools`. */
|
|
181
|
+
run_code?: {
|
|
182
|
+
attach?: boolean;
|
|
183
|
+
};
|
|
178
184
|
/** Compact a long conversation into a summary past a token threshold (ADR 0033). Delegate orchestrator only. */
|
|
179
185
|
context?: ContextConfig;
|
|
180
186
|
/** `task`: the agent works until it calls finish_task; its arguments are the node's output. Workflow nodes only. */
|
|
@@ -239,6 +245,13 @@ export interface SyndicateYamlConfig {
|
|
|
239
245
|
* Contract: lib/workflow.ts.
|
|
240
246
|
*/
|
|
241
247
|
workflow?: WorkflowConfig;
|
|
248
|
+
/**
|
|
249
|
+
* Opts this syndicate into SCRIPT orchestration (ADR 0137): one JavaScript
|
|
250
|
+
* module, run in the engine's sandbox, calls the agents, the allowed tools
|
|
251
|
+
* and a person as functions. Cannot be combined with `dispatch` or
|
|
252
|
+
* `workflow`. Needs MELCHIZEDEK_CODE_SANDBOX=on. Contract: lib/script/.
|
|
253
|
+
*/
|
|
254
|
+
script?: ScriptConfig;
|
|
242
255
|
/** Optional default variable definitions for {{token}} interpolation. */
|
|
243
256
|
variables?: VariableMap;
|
|
244
257
|
/** Defines the persistence and semantic memory layer for the syndicate. */
|
|
@@ -87,7 +87,12 @@ function resolveAndValidate(raw, label, options) {
|
|
|
87
87
|
...yamlVars,
|
|
88
88
|
...bindings,
|
|
89
89
|
};
|
|
90
|
-
|
|
90
|
+
// A script's source is code, not a template: `{{` is JavaScript there, and the script reads `variables` itself (ADR 0137 item 5).
|
|
91
|
+
const scriptSource = isPlainObject(raw) && isPlainObject(raw.script) && typeof raw.script.source === 'string' ? raw.script.source : undefined;
|
|
92
|
+
let interpolated = isPlainObject(raw) ? interpolateDeep(raw, merged) : raw;
|
|
93
|
+
if (scriptSource !== undefined && isPlainObject(interpolated) && isPlainObject(interpolated.script)) {
|
|
94
|
+
interpolated = { ...interpolated, script: { ...interpolated.script, source: scriptSource } };
|
|
95
|
+
}
|
|
91
96
|
warnV1Spellings(interpolated, label, options.onWarning);
|
|
92
97
|
const unresolved = findUnresolvedTokens(interpolated);
|
|
93
98
|
if (unresolved.length > 0) {
|
|
@@ -96,6 +101,11 @@ function resolveAndValidate(raw, label, options) {
|
|
|
96
101
|
const valid = validateSyndicateConfig(bundle !== undefined && isPlainObject(interpolated) ? { ...interpolated, bundled_references: bundle } : interpolated, label);
|
|
97
102
|
const { variables: _stripped, ...clean } = valid;
|
|
98
103
|
let config = clean;
|
|
104
|
+
// The script reads the bound variables (ADR 0137 item 6): the YAML's, then the caller's bindings, as {{token}} resolves them.
|
|
105
|
+
if (config.script) {
|
|
106
|
+
const yamlBound = isPlainObject(interpolated) && isPlainObject(interpolated.variables) ? interpolated.variables : {};
|
|
107
|
+
config = { ...config, script: { ...config.script, variables: { ...defaultBindings(), ...yamlBound, ...bindings } } };
|
|
108
|
+
}
|
|
99
109
|
if (overrides) {
|
|
100
110
|
config = applyOverrides(config, overrides);
|
|
101
111
|
}
|
|
@@ -81,7 +81,8 @@ export function payloadDecision(policy, traceId, facts) {
|
|
|
81
81
|
}
|
|
82
82
|
// ── Span → row mapping (pure; tested offline) ───────────────────────────────
|
|
83
83
|
const ROOT_PREFIX = 'Syndicate Execution: ';
|
|
84
|
-
|
|
84
|
+
// A script's own spans (ADR 0137 item 16) carry its run, its calls' kinds, names, sequence and status, never an argument or a result.
|
|
85
|
+
const EXPORTED_SPAN_NAMES = /^(llm\.request$|Syndicate Execution: |script\.(invoke|call) )/;
|
|
85
86
|
function attrsOf(span) {
|
|
86
87
|
return span.attributes;
|
|
87
88
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Span, SpanContext, SpanOptions } from '@opentelemetry/api';
|
|
1
|
+
import type { Context, Span, SpanContext, SpanOptions } from '@opentelemetry/api';
|
|
2
2
|
import type { ReadableSpan } from '@opentelemetry/sdk-trace-base';
|
|
3
3
|
import type { LlmResponse } from '../models/genaiMapping.ts';
|
|
4
4
|
import type { TurnEvent as Event } from '../runtime/events.ts';
|
|
@@ -67,7 +67,7 @@ export declare function initializeTracing(): void;
|
|
|
67
67
|
* the engine opens inside a turn goes through here or checks turnUntraced
|
|
68
68
|
* itself.
|
|
69
69
|
*/
|
|
70
|
-
export declare function startEngineSpan(tracerName: string, name: string, options?: SpanOptions): Span;
|
|
70
|
+
export declare function startEngineSpan(tracerName: string, name: string, options?: SpanOptions, parent?: Context): Span;
|
|
71
71
|
export interface TraceMetadata {
|
|
72
72
|
syndicateName: string;
|
|
73
73
|
bindings?: Record<string, any>;
|
|
@@ -345,11 +345,11 @@ function nonRecordingSpan() {
|
|
|
345
345
|
* the engine opens inside a turn goes through here or checks turnUntraced
|
|
346
346
|
* itself.
|
|
347
347
|
*/
|
|
348
|
-
export function startEngineSpan(tracerName, name, options) {
|
|
348
|
+
export function startEngineSpan(tracerName, name, options, parent) {
|
|
349
349
|
if (turnUntraced())
|
|
350
350
|
return nonRecordingSpan();
|
|
351
351
|
initializeTracing();
|
|
352
|
-
return trace.getTracer(tracerName).startSpan(name, options);
|
|
352
|
+
return trace.getTracer(tracerName).startSpan(name, options, parent);
|
|
353
353
|
}
|
|
354
354
|
/** Turn identity by trace id, so llm.request children can carry it too. */
|
|
355
355
|
const turnContexts = new Map();
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* runner, unchanged, continues where the snapshot stood. runDurableTurn
|
|
16
16
|
* is runSyndicateTurn with that store and a sink.
|
|
17
17
|
*
|
|
18
|
-
* A STEP BOUNDARY is an append that carries function responses
|
|
18
|
+
* A STEP BOUNDARY is an append that carries function responses, or a
|
|
19
|
+
* script's journal event (ADR 0137 item 8), after which
|
|
19
20
|
* every function call any session of the run holds has its response (calls
|
|
20
21
|
* listed in their event's longRunningToolIds, which wait on a person, aside).
|
|
21
22
|
* So a parent waiting on a delegated subagent is never at a boundary, the
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* runner, unchanged, continues where the snapshot stood. runDurableTurn
|
|
16
16
|
* is runSyndicateTurn with that store and a sink.
|
|
17
17
|
*
|
|
18
|
-
* A STEP BOUNDARY is an append that carries function responses
|
|
18
|
+
* A STEP BOUNDARY is an append that carries function responses, or a
|
|
19
|
+
* script's journal event (ADR 0137 item 8), after which
|
|
19
20
|
* every function call any session of the run holds has its response (calls
|
|
20
21
|
* listed in their event's longRunningToolIds, which wait on a person, aside).
|
|
21
22
|
* So a parent waiting on a delegated subagent is never at a boundary, the
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
*/
|
|
76
77
|
import { createHash, randomUUID } from 'node:crypto';
|
|
77
78
|
import { getFunctionCalls, getFunctionResponses, parseTurnEvents } from '../events.js';
|
|
79
|
+
import { JOURNAL_EVERY_CALL, isJournalEvent } from '../../script/journal.js';
|
|
78
80
|
import { InProcessSessionService } from '../sessions.js';
|
|
79
81
|
import { runSyndicateTurn } from '../syndicateTurn.js';
|
|
80
82
|
// ── Shapes ───────────────────────────────────────────────────────────────────
|
|
@@ -127,6 +129,8 @@ export function usableCheckpoint(cp, runId, parts) {
|
|
|
127
129
|
/** Content compared as stored JSON. */
|
|
128
130
|
const sameJson = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
129
131
|
class CheckpointingStore {
|
|
132
|
+
/** A script run stores each call's journal event as it ends, and each is a step boundary (ADR 0137 item 8). */
|
|
133
|
+
[JOURNAL_EVERY_CALL] = true;
|
|
130
134
|
inner;
|
|
131
135
|
opts;
|
|
132
136
|
openingParts;
|
|
@@ -250,7 +254,7 @@ class CheckpointingStore {
|
|
|
250
254
|
if (stored.author !== 'user' && this.firstAgentSeq === undefined)
|
|
251
255
|
this.firstAgentSeq = order;
|
|
252
256
|
this.track(stored);
|
|
253
|
-
if (getFunctionResponses(stored).length > 0 && this.openCalls.size === 0)
|
|
257
|
+
if ((getFunctionResponses(stored).length > 0 || isJournalEvent(stored)) && this.openCalls.size === 0)
|
|
254
258
|
await this.boundary();
|
|
255
259
|
return stored;
|
|
256
260
|
}
|
|
@@ -163,6 +163,7 @@ import { approvalRequestOf, pendingApproval } from '../approvals.js';
|
|
|
163
163
|
import { consentFrom, pendingConsent } from '../credentials.js';
|
|
164
164
|
import { getFunctionCalls, getFunctionResponses } from '../events.js';
|
|
165
165
|
import { ASK_USER, pendingQuestion } from '../questions.js';
|
|
166
|
+
import { isJournalEvent, scriptPauseIn } from '../../script/journal.js';
|
|
166
167
|
import { childAppName, entryAppName, legacyChild, legacyEntry } from './delegate.js';
|
|
167
168
|
import { REQUEST_CONFIRMATION_CALL, REQUEST_CREDENTIAL_CALL, REQUEST_INPUT_CALL, isSegmentPrefix } from './history.js';
|
|
168
169
|
import { isToolset } from './request.js';
|
|
@@ -516,6 +517,12 @@ async function conversationPause(key, child, name, id, depth, seen = new Set())
|
|
|
516
517
|
...(routed.consent ? { consent: { ...routed.consent, path } } : {}),
|
|
517
518
|
};
|
|
518
519
|
}
|
|
520
|
+
// A nested script syndicate's own pause (ADR 0137 item 15): its askUser or a gated tool() call, authored by the script.
|
|
521
|
+
const scripted = scriptPauseIn(child.events, id);
|
|
522
|
+
if (scripted) {
|
|
523
|
+
const path = scripted.asker === name ? [name] : down([scripted.asker]);
|
|
524
|
+
return { path, ...(scripted.approval ? { approval: { ...scripted.approval, path } } : {}), ...(scripted.question ? { question: { ...scripted.question, path } } : {}) };
|
|
525
|
+
}
|
|
519
526
|
const approval = pendingApproval(child.events);
|
|
520
527
|
if (approval && approval.agent && approval.agent !== 'user' && approval.agent !== name && wanted(approval.id)) {
|
|
521
528
|
const path = down([approval.agent]);
|
|
@@ -543,8 +550,8 @@ async function conversationPause(key, child, name, id, depth, seen = new Set())
|
|
|
543
550
|
...(first.consent ? { consent: { ...first.consent, path } } : {}),
|
|
544
551
|
};
|
|
545
552
|
}
|
|
546
|
-
/** A walk's session: its node events carry a dotted node path (`<workflow>.<node>`). */
|
|
547
|
-
const isWalk = (session) => session.events.some((e) => typeof e.nodeInfo?.path === 'string' && e.nodeInfo.path.includes('.'));
|
|
553
|
+
/** A walk's session: its node events carry a dotted node path (`<workflow>.<node>`); a script's journal events do not count. */
|
|
554
|
+
const isWalk = (session) => session.events.some((e) => typeof e.nodeInfo?.path === 'string' && e.nodeInfo.path.includes('.') && !isJournalEvent(e));
|
|
548
555
|
/**
|
|
549
556
|
* The pause a nested workflow's walk holds in its child session (ADR 0111):
|
|
550
557
|
* the workflow's own pause record, the last event, authored by the
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* feature (UnsupportedOnRuntimeError): a caller's `transformAgent` (it
|
|
19
19
|
* transformed ADK agents, and ADK left in 1.0.0), and an `ask_user` tool on
|
|
20
20
|
* a workflow's agent (the schema refuses it; a config that skipped
|
|
21
|
-
* validation is refused here, ADR 0095)
|
|
21
|
+
* validation is refused here, ADR 0095), and a script or a `run_code`
|
|
22
|
+
* tool while MELCHIZEDEK_CODE_SANDBOX is off (CodeSandboxOffError, ADR 0137). A workflow syndicate runs on the
|
|
22
23
|
* engine's scheduler (lib/workflow/turn.ts, ADR 0095). An answer to an
|
|
23
24
|
* approval resumes the call (lib/runtime/native/interrupts.ts), an answer
|
|
24
25
|
* to a question resumes the agent that asked (lib/runtime/questions.ts, ADR
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* feature (UnsupportedOnRuntimeError): a caller's `transformAgent` (it
|
|
19
19
|
* transformed ADK agents, and ADK left in 1.0.0), and an `ask_user` tool on
|
|
20
20
|
* a workflow's agent (the schema refuses it; a config that skipped
|
|
21
|
-
* validation is refused here, ADR 0095)
|
|
21
|
+
* validation is refused here, ADR 0095), and a script or a `run_code`
|
|
22
|
+
* tool while MELCHIZEDEK_CODE_SANDBOX is off (CodeSandboxOffError, ADR 0137). A workflow syndicate runs on the
|
|
22
23
|
* engine's scheduler (lib/workflow/turn.ts, ADR 0095). An answer to an
|
|
23
24
|
* approval resumes the call (lib/runtime/native/interrupts.ts), an answer
|
|
24
25
|
* to a question resumes the agent that asked (lib/runtime/questions.ts, ADR
|
|
@@ -30,6 +31,7 @@ import { createTurnEvent } from './events.js';
|
|
|
30
31
|
import { runAgentLoop } from './native/agentLoop.js';
|
|
31
32
|
import { ASK_USER } from './questions.js';
|
|
32
33
|
import { unsupportedOnNative } from './runtimeFlag.js';
|
|
34
|
+
import { refuseWithoutSandbox } from '../sandbox/sandbox.js';
|
|
33
35
|
// ── What native refuses before a turn starts ─────────────────────────────────
|
|
34
36
|
/**
|
|
35
37
|
* Throws UnsupportedOnRuntimeError for a syndicate or a call the runtime
|
|
@@ -39,6 +41,8 @@ export function refuseOnNative(config, call) {
|
|
|
39
41
|
const where = config.syndicate_name || config.orchestrator?.name || 'syndicate';
|
|
40
42
|
if (call.transformAgent)
|
|
41
43
|
throw unsupportedOnNative('transformAgent (it transformed ADK agents; the ADK runtime was removed in 1.0.0)', where);
|
|
44
|
+
// A script, or an agent listing run_code, runs code: only once the operator turned the sandbox on (ADR 0137 item 4).
|
|
45
|
+
refuseWithoutSandbox(config);
|
|
42
46
|
if (call.isWorkflow) {
|
|
43
47
|
// A pause inside an agent node cannot be resumed by the native walk (lib/workflow/resume.ts).
|
|
44
48
|
const asking = [config.orchestrator, ...(config.subagents ?? [])].find((agent) => (agent?.tools ?? []).includes(ASK_USER));
|