melchizedek-agents 1.1.0 → 1.2.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/.env.example +10 -0
- package/AGENT_SETUP.md +3 -1
- package/CHANGELOG.md +232 -0
- package/DOCUMENTATION.md +91 -16
- package/ONBOARDING.md +339 -0
- package/QUICKSTART.md +15 -2
- package/README.md +5 -2
- package/config/agents/examples/council.yaml +1 -1
- package/config/agents/examples/critic.yaml +8 -8
- package/config/agents/examples/image_production.yaml +4 -3
- package/config/agents/syndicate.schema.json +65 -7
- package/config/agents/syndicateSchema.yaml +18 -6
- package/config/agents/templates/intake_extractor.yaml +3 -2
- package/config/agents/templates/systems_operator.yaml +5 -2
- package/dist/lib/a2a/app.d.ts +12 -0
- package/dist/lib/a2a/app.js +22 -11
- package/dist/lib/a2a/executor.js +14 -6
- package/dist/lib/a2a/oauthSetup.d.ts +16 -0
- package/dist/lib/a2a/oauthSetup.js +57 -3
- package/dist/lib/agentDialect.d.ts +18 -8
- package/dist/lib/agentDialect.js +36 -19
- package/dist/lib/compile.d.ts +38 -7
- package/dist/lib/compile.js +57 -37
- package/dist/lib/compileNative.d.ts +11 -3
- package/dist/lib/compileNative.js +18 -1
- package/dist/lib/doctor.d.ts +76 -0
- package/dist/lib/doctor.js +159 -7
- package/dist/lib/loadSyndicate.d.ts +9 -0
- package/dist/lib/models/capabilities.d.ts +9 -0
- package/dist/lib/models/capabilities.js +24 -0
- package/dist/lib/models/chatCompletionsAdapter.d.ts +11 -3
- package/dist/lib/models/chatCompletionsAdapter.js +19 -5
- package/dist/lib/models/claudeAdapter.js +4 -0
- package/dist/lib/models/contract.d.ts +5 -2
- package/dist/lib/models/gateway.d.ts +2 -1
- package/dist/lib/models/gateway.js +4 -2
- package/dist/lib/models/gatewayAdapter.d.ts +7 -1
- package/dist/lib/models/gatewayAdapter.js +8 -0
- package/dist/lib/models/geminiAdapter.js +8 -2
- package/dist/lib/models/genaiMapping.d.ts +8 -4
- package/dist/lib/models/genaiMapping.js +23 -9
- package/dist/lib/models/gptAdapter.d.ts +4 -2
- package/dist/lib/models/gptAdapter.js +11 -4
- package/dist/lib/models/grokAdapter.d.ts +3 -1
- package/dist/lib/models/grokAdapter.js +11 -3
- package/dist/lib/models/kimiAdapter.d.ts +2 -1
- package/dist/lib/models/kimiAdapter.js +3 -3
- package/dist/lib/models/reasoning.d.ts +30 -2
- package/dist/lib/models/reasoning.js +56 -5
- package/dist/lib/onboarding.d.ts +134 -0
- package/dist/lib/onboarding.js +539 -0
- package/dist/lib/runtime/approvals.d.ts +2 -0
- package/dist/lib/runtime/approvals.js +4 -3
- package/dist/lib/runtime/credentials.d.ts +6 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +5 -2
- package/dist/lib/runtime/native/agentLoop.js +2 -0
- package/dist/lib/runtime/native/checkpoint.d.ts +12 -5
- package/dist/lib/runtime/native/checkpoint.js +13 -19
- package/dist/lib/runtime/native/delegate.d.ts +82 -1
- package/dist/lib/runtime/native/delegate.js +157 -2
- package/dist/lib/runtime/native/interrupts.d.ts +88 -5
- package/dist/lib/runtime/native/interrupts.js +220 -30
- package/dist/lib/runtime/syndicateTurn.d.ts +4 -2
- package/dist/lib/runtime/syndicateTurn.js +295 -62
- package/dist/lib/runtime/turnControl.d.ts +41 -0
- package/dist/lib/session/transcript.d.ts +9 -0
- package/dist/lib/session/transcript.js +20 -1
- package/dist/lib/skills.d.ts +18 -0
- package/dist/lib/skills.js +73 -1
- package/dist/lib/storage/postgres/index.d.ts +6 -1
- package/dist/lib/storage/postgres/index.js +1 -1
- package/dist/lib/storage/postgres/taskQueue.d.ts +7 -3
- package/dist/lib/storage/postgres/taskQueue.js +10 -3
- package/dist/lib/syndicateSchema.d.ts +38 -1
- package/dist/lib/syndicateSchema.js +25 -18
- package/dist/lib/tools/auth.d.ts +13 -1
- package/dist/lib/tools/auth.js +10 -2
- package/dist/lib/tools/credentialHosts.d.ts +88 -0
- package/dist/lib/tools/credentialHosts.js +165 -0
- package/dist/lib/tools/credentialUses.d.ts +92 -0
- package/dist/lib/tools/credentialUses.js +112 -0
- package/dist/lib/tools/mcpToolFactory.js +1 -1
- package/dist/lib/tools/oauthConsent.js +8 -1
- package/dist/lib/tools/oauthHosts.d.ts +2 -0
- package/dist/lib/tools/oauthHosts.js +2 -2
- package/dist/lib/tools/oauthTools.d.ts +10 -1
- package/dist/lib/tools/oauthTools.js +29 -3
- package/dist/lib/tools/openapiTools.d.ts +7 -0
- package/dist/lib/tools/openapiTools.js +49 -3
- package/dist/lib/tools/taskTools.d.ts +32 -2
- package/dist/lib/tools/taskTools.js +93 -33
- package/dist/lib/workflow/turn.d.ts +22 -3
- package/dist/lib/workflow/turn.js +151 -31
- package/dist/lib/workflow.d.ts +10 -7
- package/dist/lib/workflow.js +10 -7
- package/dist/scripts/a2a_server.d.ts +2 -2
- package/dist/scripts/a2a_server.js +11 -2
- package/dist/scripts/assistant_worker.d.ts +2 -0
- package/dist/scripts/assistant_worker.js +13 -4
- package/dist/scripts/doctor.d.ts +4 -2
- package/dist/scripts/doctor.js +6 -4
- package/dist/scripts/setup.d.ts +23 -0
- package/dist/scripts/setup.js +154 -0
- package/dist/scripts/skills_install.d.ts +3 -1
- package/dist/scripts/skills_install.js +17 -2
- package/dist/scripts/yaml_codemod.d.ts +12 -2
- package/dist/scripts/yaml_codemod.js +115 -33
- package/package.json +4 -1
- package/skills/README.md +11 -2
- package/skills/briefs/_shared.md +1 -1
- package/skills/briefs/melchizedek-models.md +4 -2
- package/skills/briefs/skills-readme.md +3 -1
- package/skills/melchizedek-models/SKILL.md +6 -2
- package/skills/melchizedek-onboard/SKILL.md +49 -0
- package/skills/melchizedek-onboard-cloud/SKILL.md +38 -0
- package/skills/melchizedek-onboard-keys/SKILL.md +35 -0
- package/skills/melchizedek-onboard-local/SKILL.md +41 -0
- package/skills/melchizedek-onboard-serve/SKILL.md +43 -0
package/.env.example
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Not sure which of these you need? `npx melchizedek-setup` (in a clone: `npm run setup`)
|
|
2
|
+
# asks what you have and prints the names to set for it; --auto detects it.
|
|
3
|
+
# Only names and shapes live here: type the values yourself, never commit .env.
|
|
4
|
+
|
|
1
5
|
# ── Required for cloud models ─────────────────────────────────────────────────
|
|
2
6
|
# Google AI Studio API key — powers all Gemini model inference.
|
|
3
7
|
# Also required for the gemini-embedding-001 embedding model used by long-term memory.
|
|
@@ -262,6 +266,12 @@ PUBLIC_URL=
|
|
|
262
266
|
# Unset: authorization_code grants are refused, client_credentials allowed.
|
|
263
267
|
# Set: every grant's provider is listed and every host it calls is its own.
|
|
264
268
|
# MELCHIZEDEK_OAUTH_HOSTS=tracker=api.tracker.example.com,auth.tracker.example.com
|
|
269
|
+
# The hosts each static credential variable a YAML sends may be sent to
|
|
270
|
+
# (ADR 0122): bearer_env, api_key.env and client_secret_env, each entry the
|
|
271
|
+
# variable's name, '=', then its hosts (as above). Unset: each goes to the
|
|
272
|
+
# host the YAML names, and the server and the doctor name it as unbound.
|
|
273
|
+
# Set: it is the whole list; an unlisted variable is sent nowhere.
|
|
274
|
+
# MELCHIZEDEK_CREDENTIAL_HOSTS=TRACKER_TOKEN=api.tracker.example.com;WEATHER_API_KEY=api.weather.example.com
|
|
265
275
|
|
|
266
276
|
# ── Optional: Knowledge Bundle (wiki) ─────────────────────────────────────────
|
|
267
277
|
# WIKI_ROOT: filesystem path of the OKF knowledge bundle the wiki tools serve.
|
package/AGENT_SETUP.md
CHANGED
|
@@ -26,7 +26,9 @@ Set up the melchizedek-agents framework on this machine.
|
|
|
26
26
|
it. Never ask me to paste API keys into this chat, never read keys
|
|
27
27
|
from elsewhere on my machine, and never commit .env.
|
|
28
28
|
4. Run the offline test suite (`npm test`) and confirm every shipped
|
|
29
|
-
syndicate compiles.
|
|
29
|
+
syndicate compiles. Then run `npm run setup -- --auto`: it reports
|
|
30
|
+
which authentication level my environment is at, by variable name
|
|
31
|
+
only. Follow the melchizedek-onboard skill in skills/ to pick my level.
|
|
30
32
|
5. Read README.md and QUICKSTART.md. Then report back with:
|
|
31
33
|
- what you did and the test results,
|
|
32
34
|
- the list of available syndicates (one line each, from the README
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,238 @@ the starter pack and the templates), not the repo's full history.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 1.2.0 — 2026-10-09
|
|
10
|
+
|
|
11
|
+
Release 1.2.0 carries pauses and consent further down a turn and gives the
|
|
12
|
+
YAML its last v2 keys (ADRs 0117–0123). A delegated subagent may run skill
|
|
13
|
+
scripts and ask for OAuth consent; a nested workflow pauses the turn as a
|
|
14
|
+
dispatch route or a workflow node; a nested dispatch syndicate routes as it
|
|
15
|
+
does at the top; static credentials go only to hosts the operator binds;
|
|
16
|
+
`tool_choice:` and `reasoning: xhigh | max` complete the provider-neutral
|
|
17
|
+
keys; a background job's checkpoint has a cap, and a durable run resumes
|
|
18
|
+
inside a dispatch route. `melchizedek-setup` is the new onboarding command.
|
|
19
|
+
|
|
20
|
+
### Upgrade first
|
|
21
|
+
|
|
22
|
+
Read this list before deploying 1.2.0 over 1.1.0.
|
|
23
|
+
|
|
24
|
+
- **The older spelling's `reasoningEffort: xhigh` and `max` now reach the
|
|
25
|
+
provider** (ADR 0117). Since 1.0.0 they were read by nothing and sent
|
|
26
|
+
nowhere; an agent that still carries one now asks its model for that
|
|
27
|
+
effort (or the model's highest, see Added), and beside a `thinkingConfig`
|
|
28
|
+
that says `high` the word wins. Such an agent thinks harder and costs
|
|
29
|
+
more. Remove the word to keep the old behaviour.
|
|
30
|
+
- **`ReasoningLevel` (`melchizedek-agents/models/contract`) gains `xhigh`
|
|
31
|
+
and `max`**, and `REASONING_BUDGETS` gains both keys (16,384 each,
|
|
32
|
+
`high`'s budget). An exhaustive `Record<ReasoningLevel, …>` or a `switch`
|
|
33
|
+
over the levels no longer type-checks without them (ADR 0117).
|
|
34
|
+
- **A nested dispatch syndicate answers with its route's text, not its
|
|
35
|
+
classifier's JSON** (ADR 0120). A `yaml_reference` to a plan-dispatch
|
|
36
|
+
syndicate used to compile to its classifier alone, so its caller got the
|
|
37
|
+
routing verdict and its routes never ran; a prompt or parser that relied
|
|
38
|
+
on that verdict sees the specialist's answer now. A gate or skill scripts
|
|
39
|
+
on one of its routes is no longer a load error. `compileSubagentSpec`
|
|
40
|
+
refuses a reference to a dispatch syndicate by name: compile it with
|
|
41
|
+
`compileEntrySpec`.
|
|
42
|
+
- **Child session keys for routes and nodes carry their kind and the agent
|
|
43
|
+
path** (ADR 0119, ADR 0120). A nested syndicate or nested workflow run as
|
|
44
|
+
a dispatch route is filed under `<app>/route:<route>`, and as a workflow
|
|
45
|
+
node under `<walk's app>/node:<node>` (`app/route:Writer/node:Inner` two
|
|
46
|
+
levels down), no longer under the entry's name alone, so it no longer
|
|
47
|
+
shares a row with a same-named entry of another syndicate, and neither
|
|
48
|
+
can share a key with a delegated subagent's (`<app>/<caller>/<subagent>`,
|
|
49
|
+
unchanged). A session 1.1.0 stored under the entry's name, or under
|
|
50
|
+
`<app>/<entry>`, is still continued when the entry ran in that
|
|
51
|
+
conversation before; nothing stored is rewritten. Code that read the
|
|
52
|
+
walk's events at `{ appName: '<entry>' }` or `<app>/<entry>` reads them
|
|
53
|
+
at the kind key now.
|
|
54
|
+
- **`melchizedek-doctor --check` exits 1 on a grant whose hosts the OAuth
|
|
55
|
+
host allowlist refuses.** The doctor already printed each such grant
|
|
56
|
+
(`✗ … is not one of them`, `MELCHIZEDEK_OAUTH_HOSTS`, ADR 0114) and still
|
|
57
|
+
exited 0; a CI step that runs `--check` against a served syndicate now
|
|
58
|
+
fails on it. A blocked syndicate, a runtime problem and the OAuth and
|
|
59
|
+
credential setup's problems fail it as before.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- **Skill scripts on a delegated subagent.** `skills.scripts: local` is no
|
|
64
|
+
longer a load error on a delegated subagent (or a nested delegate
|
|
65
|
+
syndicate's): a `run_skill_script` call there pauses the turn
|
|
66
|
+
`input-required` with `approval.path`, and the decision runs or refuses the
|
|
67
|
+
script in the subagent before its caller continues. An agent a map node
|
|
68
|
+
runs still may not carry scripts (ADR 0118).
|
|
69
|
+
- **OAuth consent inside a delegated subagent.** A subagent's
|
|
70
|
+
`authorization_code` tool (an `mcp_auth` or OpenAPI `auth.oauth2` grant)
|
|
71
|
+
now asks for a missing grant instead of answering `not_connected`: the turn
|
|
72
|
+
ends `input-required` with `consent`, whose new optional `path` names the
|
|
73
|
+
agents from the turn's own down to the one that asked (`PendingConsent.path`;
|
|
74
|
+
the A2A `consent_request` data part carries `path` too). The callback stores
|
|
75
|
+
the grant under the conversation's app, and the next message resumes the
|
|
76
|
+
subagent's call. The host allowlist's checks are unchanged. The
|
|
77
|
+
systems_operator template's comment says authorization_code works on its
|
|
78
|
+
Systems subagent (ADR 0118).
|
|
79
|
+
- **Nested dispatch syndicates route as at the top**
|
|
80
|
+
([ADR 0120](./wiki/decisions/0120-nested-dispatch-syndicates-route-as-at-the-top.md)).
|
|
81
|
+
A `yaml_reference` to a plan-dispatch syndicate runs as a turn of its own
|
|
82
|
+
on its own conversation, wherever it appears (a delegated subagent, a
|
|
83
|
+
dispatch route, a workflow node): its classifier picks one of its routes
|
|
84
|
+
and the route answers, under the turn's `max_steps`, deadline and cancel.
|
|
85
|
+
Approvals, `ask_user` questions and OAuth consent on its routes, and
|
|
86
|
+
inside a route's own delegation, pause the turn `input-required` with
|
|
87
|
+
the path (`["Boss", "Team", "Ops"]` delegated, `["Team", "Ops"]` as a
|
|
88
|
+
route), over `runSyndicateTurn` and A2A; the answer resumes that route
|
|
89
|
+
without classifying again. Its grants are stored under the root's app.
|
|
90
|
+
As a workflow node it fails on a consent pause. A route carrying the
|
|
91
|
+
entry's own name, a `map` node over one, and a reference cycle below it
|
|
92
|
+
are refused by name before any model call. Surface:
|
|
93
|
+
`melchizedek-agents/compile` gains the `DispatchSpec` type, a
|
|
94
|
+
`{ kind: 'dispatch'; dispatch: DispatchSpec }` variant on `EntrySpec` and
|
|
95
|
+
`SpecTool`, and an optional `WorkflowSpec.dispatches`; `melchizedek-agents/runtime/turnControl`
|
|
96
|
+
gains the optional `TurnControl.nestedDispatch` hook and the
|
|
97
|
+
`NestedDispatchRun` and `NestedDispatchEnd` types. The generated
|
|
98
|
+
`syndicate.schema.json` describes `yaml_reference` accordingly.
|
|
99
|
+
- **Static credentials go only to hosts the operator binds** (ADR 0122).
|
|
100
|
+
`MELCHIZEDEK_CREDENTIAL_HOSTS="TRACKER_TOKEN=api.tracker.example.com;…"`,
|
|
101
|
+
or `createA2AApp({ credentialHosts })`, binds each credential variable a
|
|
102
|
+
YAML sends (`bearer_env`, `api_key.env`, `client_secret_env`) to its hosts.
|
|
103
|
+
Unset, nothing changes: `melchizedek-serve` warns at boot and
|
|
104
|
+
`melchizedek-doctor` shows a `credentials` warning naming each unbound
|
|
105
|
+
variable. Set, it is the whole list, checked when a syndicate loads, when
|
|
106
|
+
it compiles and before each send; a refused call sends nothing. New
|
|
107
|
+
modules `melchizedek-agents/tools/credentialHosts` and
|
|
108
|
+
`melchizedek-agents/tools/credentialUses` (through the existing
|
|
109
|
+
`./tools/*` export); `openApiServers` in `tools/openapiTools`;
|
|
110
|
+
`DoctorResult.credentials`; the server's banner gains a `creds` line,
|
|
111
|
+
and `oauthServerSetup` returns `credentialSummary`.
|
|
112
|
+
- **`tool_choice:`**, a v2 agent key (ADR 0117): `auto` (the default),
|
|
113
|
+
`none`, `required` (some tool) or `{ name: <tool> }` (that tool). It
|
|
114
|
+
replaces `generateContentConfig.toolConfig.functionCallingConfig`, may not
|
|
115
|
+
sit beside it, and is refused on a `yaml_reference` or `a2a_agent_url`
|
|
116
|
+
subagent. Each provider sends it as before, weakening a forced choice
|
|
117
|
+
where it must (`llm.tool_choice.weakened`).
|
|
118
|
+
- **`reasoning: xhigh` and `reasoning: max`** (ADR 0117). They reach the
|
|
119
|
+
models that take them: both as `output_config.effort` on Claude's adaptive
|
|
120
|
+
generations, `max` on Kimi K3 (and `xhigh` rounds up to it, so K3 can be
|
|
121
|
+
sent `max` again), `xhigh` on GPT-5.2 and later and on grok-4.7. Every
|
|
122
|
+
other path sends the model's highest setting (`high`, Gemini's `HIGH`,
|
|
123
|
+
or `high`'s 16,384-token budget on Claude 4.6 and earlier and Gemini 2.x;
|
|
124
|
+
`high` on a gateway for any id) and marks the `llm.request` span
|
|
125
|
+
`llm.reasoning.weakened` with the level asked for. The capability
|
|
126
|
+
matrix states each path: `REASONING_ABOVE_HIGH`
|
|
127
|
+
(`melchizedek-agents/models/capabilities`), rendered under
|
|
128
|
+
`npm run doctor -- --matrix`.
|
|
129
|
+
- `melchizedek-agents/models/reasoning` exports `effortCeiling(model)`,
|
|
130
|
+
`effortWord(model, setting, ceiling?)`, `isAboveHigh` and
|
|
131
|
+
`REASONING_ORDER`. `ChatCompletionsAdapter` gains the protected hooks
|
|
132
|
+
`reasoningCeiling(model)` and `effortFor(model, setting)`.
|
|
133
|
+
- **A cap on a background job's checkpoint**
|
|
134
|
+
([ADR 0121](./wiki/decisions/0121-checkpoint-cap-and-dispatch-route-resume.md)).
|
|
135
|
+
A checkpoint above `MELCHIZEDEK_CHECKPOINT_MAX_BYTES` (the worker's
|
|
136
|
+
variable; a positive whole number of bytes of JSON, default 5 MiB) is
|
|
137
|
+
not stored: the job keeps its previous checkpoint, the save still reports
|
|
138
|
+
whether the claim holds (a cancel still stops the run), and one log line
|
|
139
|
+
names the job id and the sizes. New in `melchizedek-agents/tools/taskTools`:
|
|
140
|
+
`TaskBackendOptions` (`checkpointMaxBytes`, `log`), `fileTaskBackendWith`,
|
|
141
|
+
`checkpointJson`, `checkpointMaxBytesSetting`, `DEFAULT_CHECKPOINT_MAX_BYTES`
|
|
142
|
+
and `CHECKPOINT_MAX_BYTES_ENV`; `PostgresStorageOptions.taskQueue` passes
|
|
143
|
+
the options to the Postgres queue. The file store's checkpoint sidecar is
|
|
144
|
+
written compact; one written by 1.1.0 still reads.
|
|
145
|
+
- **`melchizedek-setup`, the onboarding command** (new bin; `npm run setup`
|
|
146
|
+
in a clone). A menu of nine authentication levels (no key / local
|
|
147
|
+
Ollama, one provider's key, several, a gateway key, Vertex AI / Bedrock /
|
|
148
|
+
Azure credentials, per-caller keys when serving, caller identities
|
|
149
|
+
through `A2A_AUTH`, OAuth tool grants, and subscription sign-ins), each
|
|
150
|
+
with a startup guide: the variables to set (names and shapes only), the
|
|
151
|
+
doctor check, the shipped templates and examples that run there, and the
|
|
152
|
+
first commands. `--auto` reads the doctor's result and prints the guide
|
|
153
|
+
for the highest level it detects, never a value; `--level <id|n>` prints
|
|
154
|
+
one guide; `--write-env` creates `.env` from `.env.example` with the
|
|
155
|
+
level's names blank, never over an existing file and only when git
|
|
156
|
+
ignores `.env` (ADR 0123).
|
|
157
|
+
- **`ONBOARDING.md`** ships in the package: the same guides, generated from
|
|
158
|
+
the same code (`npm run setup -- --markdown`).
|
|
159
|
+
- **Five onboarding skills** in `skills/`: `melchizedek-onboard` (triage:
|
|
160
|
+
"what do you have?") and `melchizedek-onboard-local`, `-keys`, `-cloud`
|
|
161
|
+
and `-serve`. `npx melchizedek-skills install` puts them in
|
|
162
|
+
`.claude/skills/` and `.agents/skills/` with the rest of the suite.
|
|
163
|
+
- **`melchizedek-skills install --agents-md`** also writes a pointer to the
|
|
164
|
+
skills into the project's `AGENTS.md` (created when absent; only the
|
|
165
|
+
block between its markers is ever rewritten).
|
|
166
|
+
- **The doctor's `serving` line.** When any A2A identity or billing variable
|
|
167
|
+
is set, `npm run doctor` prints the `A2A_AUTH` mode, `A2A_KEY_MODE`,
|
|
168
|
+
which serving variables are set and what the server would refuse to start
|
|
169
|
+
on, by name. `--json` gains `providers` (the providers line as data) and
|
|
170
|
+
`serving`.
|
|
171
|
+
- **`ToolCredentialError.allowlist`** (`melchizedek-agents/tools/auth`,
|
|
172
|
+
with the `HostAllowlist` type): on a `host_refused` error, `'oauth'` when
|
|
173
|
+
the OAuth host allowlist refused a token's host (ADR 0114) and
|
|
174
|
+
`'credential'` when the credential host allowlist refused a client
|
|
175
|
+
secret's (ADR 0122). The constructor takes it as an optional third
|
|
176
|
+
argument.
|
|
177
|
+
|
|
178
|
+
### Changed
|
|
179
|
+
|
|
180
|
+
- **A nested workflow run as a dispatch route or a workflow node pauses the
|
|
181
|
+
turn** ([ADR 0119](./wiki/decisions/0119-workflow-routes-and-nodes-pause-the-turn.md)).
|
|
182
|
+
Its `ask_user` nodes and gated agent nodes (`require_approval`, skill
|
|
183
|
+
scripts) are no longer a load error there: the turn ends `input-required`
|
|
184
|
+
with `result.input` or `result.approval`, whose `path` runs from the route
|
|
185
|
+
or node down to the node that asked (`["Writer", "Send"]`, and further
|
|
186
|
+
down through a node that is itself a nested workflow), over
|
|
187
|
+
`runSyndicateTurn` and A2A. The next message answers it: a decision, or a
|
|
188
|
+
plain-text reply to the question, resumes a paused route without running
|
|
189
|
+
the classifier (`route.decidedBy` `approval` or `answer`); any other
|
|
190
|
+
message while an approval waits repeats the request and runs nothing. A
|
|
191
|
+
workflow node resumes inside the caller's walk. The conversation of a
|
|
192
|
+
paused route ends on the route's pause record (no content, the walk's
|
|
193
|
+
open interrupt ids in `longRunningToolIds`) in place of its answer.
|
|
194
|
+
- `compileEntrySpec` and `compileWorkflowSpec` still accept their
|
|
195
|
+
`delegated` argument; it no longer changes what compiles.
|
|
196
|
+
- **`melchizedek-codemod` converts the rest of `generateContentConfig`**
|
|
197
|
+
(ADR 0117): `toolConfig` becomes `tool_choice:` (mode `ANY` with several
|
|
198
|
+
allowed names becomes `required`, which is what the engine already sent,
|
|
199
|
+
with a note), `reasoningEffort: xhigh | max` becomes `reasoning:`, and
|
|
200
|
+
`thinkingConfig.includeThoughts` is dropped whatever its value: nothing
|
|
201
|
+
reads it, and the Gemini adapter asks for the thought trace whenever
|
|
202
|
+
reasoning is not `none` (a `true` gets a note). Only `toolConfig` mode
|
|
203
|
+
`VALIDATED` and `thinkingBudget: -1` stay, with a note.
|
|
204
|
+
- **The council example's Moderator consults both subagents in one step**
|
|
205
|
+
(`config/agents/examples/council.yaml`). Its instruction asks for the
|
|
206
|
+
Advocate and the Skeptic together, both function calls in one response,
|
|
207
|
+
each given the user's full claim verbatim, so the two run at once under
|
|
208
|
+
`max_concurrency` (ADR 0116). Its role, its three-part verdict and its
|
|
209
|
+
guardrails are unchanged; the YAML keeps its v2 keys. A project that
|
|
210
|
+
copied the example keeps the old prompt until it copies it again.
|
|
211
|
+
- The header comments of `examples/critic.yaml`, `examples/image_production.yaml`
|
|
212
|
+
and `templates/intake_extractor.yaml` say that a schema on a leaf is
|
|
213
|
+
their choice, not a rule: an orchestrator that delegates may hold one
|
|
214
|
+
(ADR 0109). Comments only; the agents are unchanged.
|
|
215
|
+
|
|
216
|
+
### Fixed
|
|
217
|
+
|
|
218
|
+
- **A durable dispatch run resumes inside its agent route** (ADR 0121). A
|
|
219
|
+
background job killed mid-step inside a plan-dispatch route used to start
|
|
220
|
+
over (its checkpoint was set aside); it now resumes from its checkpoint,
|
|
221
|
+
classifying again and storing what an uninterrupted run stores. A nested
|
|
222
|
+
dispatch syndicate's route resumes the same way, and checkpoints 1.1.0
|
|
223
|
+
saved inside a route restore.
|
|
224
|
+
- **An unrecognised `MODEL_GATEWAY` is never echoed.** The doctor (its
|
|
225
|
+
`gateway` line and `--json`), `gatewayProblem()` and the boot log said
|
|
226
|
+
the value back, which may be a key pasted into the wrong variable. They
|
|
227
|
+
now say `MODEL_GATEWAY is set to an unrecognised value (not shown)` and
|
|
228
|
+
name the accepted values (`vercel`, `openrouter`).
|
|
229
|
+
- **A `host_refused` refusal names the allowlist that refused.** A client
|
|
230
|
+
secret refused by the credential host allowlist was reported as the OAuth
|
|
231
|
+
host allowlist's refusal. The message now names
|
|
232
|
+
`MELCHIZEDEK_OAUTH_HOSTS` for a token's host and
|
|
233
|
+
`MELCHIZEDEK_CREDENTIAL_HOSTS` for a client secret's.
|
|
234
|
+
|
|
235
|
+
### Not supported, on purpose
|
|
236
|
+
|
|
237
|
+
- ChatGPT / Codex, Claude.ai and Gemini CLI subscription sign-ins are not a
|
|
238
|
+
way to fund the engine, and nothing reads their tokens. The setup menu's
|
|
239
|
+
level 9 says so and routes to each vendor's API key or cloud platform.
|
|
240
|
+
|
|
9
241
|
## 1.1.0 — 2026-10-09
|
|
10
242
|
|
|
11
243
|
Release 1.1.0 closes workstream 6 of the ADK-independence plan (ADRs
|
package/DOCUMENTATION.md
CHANGED
|
@@ -135,11 +135,12 @@ Field reference:
|
|
|
135
135
|
| `name` / `model` / `instruction` | agent | The agent triple. Any Gemini id, `claude-*`, or `ollama/*` for open-weight local models (see §5). |
|
|
136
136
|
| `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
137
|
| `tools` | agent | Names resolved by the tool registry (§3). Long-term memory agents add `preload_memory` / `load_memory`. |
|
|
138
|
-
| `reasoning` | agent | How hard the agent reasons, on any provider: `none`, `low`, `medium`, `high`, 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)). Unset, each adapter keeps its own default. |
|
|
138
|
+
| `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
139
|
| `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
140
|
| `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. |
|
|
141
141
|
| `model_overrides` | agent | A prompt nuance for one provider: keyed by `gemini`, `anthropic`, `openai`, `xai`, `moonshot` or `ollama`, each entry holds `instruction` (replaces the agent's instruction on that provider) or `instruction_append` (added after a blank line). The entry for the provider of the agent's `model` applies, before the skills index is appended; a `fallback_model` gets the primary's instruction. |
|
|
142
|
-
| `
|
|
142
|
+
| `tool_choice` | agent | Which tools the model may call: `auto` (the default), `none`, `required` (some tool), or `{ name: <tool> }` (that tool). A provider that rejects forcing sends a weaker choice and marks the span `llm.tool_choice.weakened` ([ADR 0117](./wiki/decisions/0117-tool-choice-and-effort-above-high-in-v2.md)). |
|
|
143
|
+
| `generateContentConfig` / `outputSchema` | agent | The deprecated v1 spelling of `sampling`, `output.mime`, `reasoning` (as `thinkingConfig` / `reasoningEffort`), `tool_choice` (as `toolConfig.functionCallingConfig`) and `output.schema`. Both still load and behave the same; a load that finds either prints one deprecation line per file per process (`LoadSyndicateOptions.onWarning`, default `console.warn`), and a v2 key beside its v1 spelling on one agent is a load error. Only `toolConfig.functionCallingConfig.mode: VALIDATED` and Gemini's dynamic budget (`thinkingBudget: -1`) have no v2 form; `topK`, `seed`, the penalties, `candidateCount`, `safetySettings` and `includeThoughts` reach no provider, and the codemod drops `includeThoughts`. `npx melchizedek-codemod [--check] <file|dir>` rewrites a v1 file to v2, comments kept ([ADR 0115](./wiki/decisions/0115-yaml-schema-v2-provider-neutral-keys.md)). |
|
|
143
144
|
| `yaml_reference` | subagent | Mount another syndicate file as a nested subagent. |
|
|
144
145
|
| `a2a_agent_url` | subagent | A REMOTE agent over A2A (§6): the orchestrator delegates to it with one `request` argument; in plan-dispatch it can be a route. No `model`/`instruction` — the remote agent has its own. Credentials come from `A2A_AGENT_TOKENS`, never YAML. |
|
|
145
146
|
| `max_steps` | root | Cap on model calls per turn, counted across every agent the turn reaches (orchestrator, subagents, nested syndicates). Exceeding it fails the turn with `STEP_LIMIT`. |
|
|
@@ -185,7 +186,7 @@ function, and every adapter recognises it by marker
|
|
|
185
186
|
| `inspect_image` | Contract | **Blind visual inventory** of a file under `outputs/`: subjects with exact counts, composition, light, palette, medium cues, artifacts — zero quality judgments. Its signature accepts *only* a file path, so an orchestrator cannot leak expectations into the observation (see `image_production.yaml`). |
|
|
186
187
|
| `task_add` / `task_list` / `task_get` / `task_update` | Contract | A to-do list and job queue. Default: a single-user JSON file (`MELCHIZEDEK_TASKS_FILE`, default `outputs/tasks.json`), so every caller of a shared endpoint shares one list. With `DATABASE_URL` (migration 0009) each caller has its own list, scoped by the caller's scope key, and any number of workers take jobs safely. |
|
|
187
188
|
| `ask_user` | Contract, long-running | Asks the person one question (optionally with `options`) and ends the turn `input-required`; the next message on the conversation is the call's result. Any agent but a workflow node's; inside a delegated subagent the question reaches the person with the agent path (§6, Questions). |
|
|
188
|
-
| `task_queue` | Contract | Queues a background job (a self-contained instruction). The tool only writes the queue; `npm run assistant:worker` (`melchizedek-worker`) claims each job, runs it through one agent compiled from YAML (default: the Assistant's Worker), and writes the result back for `task_get`. `--once` drains and exits, for cron. A run is durable (ADR 0113): it is checkpointed beside the job at every step boundary (migration 0014 on Postgres), a job claimed again resumes from its last checkpoint, SIGTERM re-queues the job in hand, and `task_update` can cancel a running job. |
|
|
189
|
+
| `task_queue` | Contract | Queues a background job (a self-contained instruction). The tool only writes the queue; `npm run assistant:worker` (`melchizedek-worker`) claims each job, runs it through one agent compiled from YAML (default: the Assistant's Worker), and writes the result back for `task_get`. `--once` drains and exits, for cron. A run is durable (ADR 0113): it is checkpointed beside the job at every step boundary (migration 0014 on Postgres), a job claimed again resumes from its last checkpoint (inside a dispatch route too, ADR 0121), a checkpoint above `MELCHIZEDEK_CHECKPOINT_MAX_BYTES` (default 5 MiB of JSON; `TaskBackendOptions.checkpointMaxBytes`, `postgresStorage({ taskQueue })`) is skipped and the previous one kept, SIGTERM re-queues the job in hand, and `task_update` can cancel a running job. |
|
|
189
190
|
|
|
190
191
|
**MCP tools** are the exception to the registry: a subagent with
|
|
191
192
|
`mcp_server_url:` in its YAML gets its tools from a remote MCP server at
|
|
@@ -238,7 +239,12 @@ with no allowlist, `authorization_code` is refused. An unset variable fails the
|
|
|
238
239
|
(the database URL, a provider key, an `A2A_` secret: anything `.env.example`
|
|
239
240
|
documents), since the YAML chooses the host it goes to.
|
|
240
241
|
`OPENAPI_CREDENTIAL_ENVS`, when set, is the exact list of variables an `auth`
|
|
241
|
-
may name.
|
|
242
|
+
may name. `MELCHIZEDEK_CREDENTIAL_HOSTS` binds each such variable to the hosts
|
|
243
|
+
its value may reach (`TRACKER_TOKEN=api.tracker.example.com;…`, or
|
|
244
|
+
`createA2AApp({ credentialHosts })`, [ADR 0122](./wiki/decisions/0122-static-credentials-go-only-to-hosts-the-operator-binds.md)):
|
|
245
|
+
unset, a credential goes where the YAML says and the server and the doctor
|
|
246
|
+
name it as unbound; set, it is the whole list, checked when a syndicate
|
|
247
|
+
loads, when it compiles and before each call. A refused or unset variable fails the compile, and a static token is applied to the request,
|
|
242
248
|
never stored in session state. Every server must be http(s) and pass the
|
|
243
249
|
SSRF guard: its literal rules when the agent compiles, the full check with
|
|
244
250
|
DNS before each call; `ALLOW_PRIVATE_OPENAPI=true` permits private hosts for
|
|
@@ -426,11 +432,26 @@ reads every syndicate YAML, resolves each agent's model under your `.env`,
|
|
|
426
432
|
and prints one table — agent, model, provider, which declared server-side
|
|
427
433
|
tools that path runs natively (✓) or drops (✗), and whether the path is
|
|
428
434
|
funded — with one verdict per syndicate and the variables that would
|
|
429
|
-
unlock the most. Read-only; no key value is ever printed
|
|
435
|
+
unlock the most. Read-only; no key value is ever printed, and a
|
|
436
|
+
`MODEL_GATEWAY` it does not recognise is named as such, never repeated.
|
|
437
|
+
`--check` exits 1 on a blocked syndicate, a runtime problem, a problem
|
|
438
|
+
with the server's OAuth or credential settings, or a tool's OAuth grant
|
|
439
|
+
whose hosts `MELCHIZEDEK_OAUTH_HOSTS` refuses. Every
|
|
430
440
|
starter-pack file opens with a `# tier:` header (`keyless`, one provider
|
|
431
441
|
such as `gemini`, or `multi-provider`) the doctor checks against the
|
|
432
442
|
models. `GOOGLE_GENAI_API_KEY` alone runs fourteen of the nineteen examples.
|
|
433
443
|
|
|
444
|
+
**Not sure where to start? `npx melchizedek-setup`** (`npm run setup` in a
|
|
445
|
+
clone) opens a menu of authentication levels: no key (Ollama), one
|
|
446
|
+
provider's key, several, a gateway key, Vertex AI / Bedrock / Azure,
|
|
447
|
+
per-caller keys (BYOK), caller identities (`A2A_AUTH`), OAuth tool grants,
|
|
448
|
+
and an honest entry for ChatGPT / Codex, Claude.ai and Gemini CLI sign-ins,
|
|
449
|
+
which the engine does not use. Each prints a startup guide: variable names
|
|
450
|
+
and shapes, the doctor check, the shipped files that run there, and the
|
|
451
|
+
first commands. `--auto` reads the doctor's result and prints the guide
|
|
452
|
+
for the highest level it detects, names only. The same guides, generated
|
|
453
|
+
from the same code, are [`ONBOARDING.md`](./ONBOARDING.md).
|
|
454
|
+
|
|
434
455
|
**One key instead of several — the gateway fallback.** Direct adapters
|
|
435
456
|
are canonical: the native features above exist only on a provider's own
|
|
436
457
|
endpoint. But with `MODEL_GATEWAY=vercel` (or `openrouter`) and
|
|
@@ -811,12 +832,19 @@ runs or refuses the call and finishes before its caller continues. The same
|
|
|
811
832
|
holds inside nested syndicates
|
|
812
833
|
([ADR 0111](./wiki/decisions/0111-pauses-inside-nested-syndicates.md)): a
|
|
813
834
|
nested delegate syndicate's gates anywhere in it, a nested dispatch
|
|
814
|
-
syndicate's on its
|
|
815
|
-
|
|
835
|
+
syndicate's on its routes, which it classifies and runs as at the top
|
|
836
|
+
(`["Boss", "Team", "Ops"]`,
|
|
837
|
+
[ADR 0120](./wiki/decisions/0120-nested-dispatch-syndicates-route-as-at-the-top.md)),
|
|
838
|
+
and a nested workflow's gated agent nodes and
|
|
816
839
|
`ask_user` nodes when the workflow is a delegated subagent; the path then
|
|
817
|
-
ends at the node, e.g. `["Desk", "Writer", "Send"]`. A
|
|
818
|
-
|
|
819
|
-
|
|
840
|
+
ends at the node, e.g. `["Desk", "Writer", "Send"]`. A workflow run as a
|
|
841
|
+
dispatch route or as another workflow's node pauses the turn the same way
|
|
842
|
+
([ADR 0119](./wiki/decisions/0119-workflow-routes-and-nodes-pause-the-turn.md)),
|
|
843
|
+
the path running from the route or node down, e.g. `["Writer", "Send"]`.
|
|
844
|
+
Skill scripts (`skills.scripts: local`) on a delegated
|
|
845
|
+
subagent pause the same way, with `run_skill_script` as the tool
|
|
846
|
+
([ADR 0118](./wiki/decisions/0118-skill-scripts-and-oauth-consent-inside-delegated-subagents.md));
|
|
847
|
+
an agent a map node runs still may not carry them. Only function tools from the registry can be gated, not MCP tools
|
|
820
848
|
or native-search sentinels. In code, `runSyndicateTurn` returns
|
|
821
849
|
`status: 'input-required'` with `approval`, and the next turn's part
|
|
822
850
|
`approvalResponsePart(approval.id, approved)` answers it.
|
|
@@ -891,6 +919,13 @@ await createA2AApp({ /* … */ toolCredentials: { store, consent } });
|
|
|
891
919
|
call again. A message sent before the grant gets the same request back,
|
|
892
920
|
without a model call.
|
|
893
921
|
|
|
922
|
+
The call may sit in a delegated subagent, at any depth (the systems_operator
|
|
923
|
+
template's Systems, say): the request then carries `path`, the agents from
|
|
924
|
+
the turn's own down to the one that asked (in the data part and in
|
|
925
|
+
`consent.path`), the grant is stored under the conversation's app, and the
|
|
926
|
+
next message resumes that subagent's call before its caller continues
|
|
927
|
+
([ADR 0118](./wiki/decisions/0118-skill-scripts-and-oauth-consent-inside-delegated-subagents.md)).
|
|
928
|
+
|
|
894
929
|
The callback refuses these, and stores nothing:
|
|
895
930
|
|
|
896
931
|
- a state that is replayed, tampered with or older than ten minutes;
|
|
@@ -914,6 +949,7 @@ name a provider they declare, never define one):
|
|
|
914
949
|
| `OAUTH_REDIRECT_URI` | `https://agents.example.com/oauth/callback` (no query) | The callback, as registered at every provider. Mounts it and lets a run ask a user to connect. Needs the key |
|
|
915
950
|
| `OAUTH_CALLBACK_IDENTITY` | `required` (default) or `state` | `required`: the browser that completes a grant carries the flow's user's identity, which only `A2A_AUTH=header` behind a gateway gives it. `state`: the single-use state alone binds the flow (a forwarded link can connect the wrong account) |
|
|
916
951
|
| `MELCHIZEDEK_OAUTH_HOSTS` | `provider=host,host;provider=host` | Which hosts each provider's tokens and client secret may be sent to. A host is a hostname, `*.domain` (subdomains only), IPv4 or `[IPv6]` |
|
|
952
|
+
| `MELCHIZEDEK_CREDENTIAL_HOSTS` | `VARIABLE=host,host;VARIABLE=host` | Which hosts each static credential variable a YAML sends (`bearer_env`, `api_key.env`, `client_secret_env`) may be sent to. Unset: as the YAML says, each named as unbound. Set: the whole list ([ADR 0122](./wiki/decisions/0122-static-credentials-go-only-to-hosts-the-operator-binds.md)) |
|
|
917
953
|
|
|
918
954
|
**The host allowlist** ([ADR 0114](./wiki/decisions/0114-oauth-tokens-go-only-to-hosts-the-operator-binds.md))
|
|
919
955
|
is the operator's binding, never the YAML's: `MELCHIZEDEK_OAUTH_HOSTS`, or
|
|
@@ -929,6 +965,21 @@ token to. The server refuses to start on a malformed key, allowlist or
|
|
|
929
965
|
redirect URI, or on a redirect URI without a key; `npm run doctor` reports
|
|
930
966
|
each of these, and a declared grant with no key or redirect URI, by name.
|
|
931
967
|
|
|
968
|
+
**The credential host allowlist** ([ADR 0122](./wiki/decisions/0122-static-credentials-go-only-to-hosts-the-operator-binds.md))
|
|
969
|
+
does the same for the static credentials a YAML sends: `bearer_env` and
|
|
970
|
+
`api_key.env` to an OpenAPI server, and `client_secret_env` to a token
|
|
971
|
+
endpoint. `MELCHIZEDEK_CREDENTIAL_HOSTS`, or
|
|
972
|
+
`createA2AApp({ credentialHosts: { TRACKER_TOKEN: ['api.tracker.example.com'] } })`,
|
|
973
|
+
which wins. Unset, nothing changes: each credential goes to the host the
|
|
974
|
+
YAML names, and the server's boot and `npm run doctor` name every variable a
|
|
975
|
+
served YAML sends that no binding holds (names only). Set, it is the whole
|
|
976
|
+
list: a variable not on it is sent nowhere, and one on it only to its own
|
|
977
|
+
hosts. A syndicate that breaks the rule is refused when it loads, when it
|
|
978
|
+
compiles, and each call (an OpenAPI call, a client-credentials token
|
|
979
|
+
request, a consent code exchange, a refresh) checks again and sends nothing
|
|
980
|
+
when refused. `MCP_BEARER_TOKENS` is not covered: it already maps each token
|
|
981
|
+
to its host.
|
|
982
|
+
|
|
932
983
|
#### Limits
|
|
933
984
|
|
|
934
985
|
| Setting | Default |
|
|
@@ -1222,6 +1273,27 @@ Implemented in `runSyndicateTurn`, so every surface honours it: the A2A
|
|
|
1222
1273
|
server, the CLI runner (`scripts/syndicate_chat.ts`), the worker and the
|
|
1223
1274
|
evals.
|
|
1224
1275
|
|
|
1276
|
+
**Nested.** A `yaml_reference` to a plan-dispatch syndicate runs as it
|
|
1277
|
+
runs at the top, as a turn of its own on its own conversation, wherever
|
|
1278
|
+
it appears
|
|
1279
|
+
([ADR 0120](./wiki/decisions/0120-nested-dispatch-syndicates-route-as-at-the-top.md)):
|
|
1280
|
+
its classifier picks one of its routes, and the route's final text is
|
|
1281
|
+
what its caller gets: the tool's answer as a DELEGATE subagent, the turn's
|
|
1282
|
+
answer as a plan-dispatch route, the node's output as a workflow node. The
|
|
1283
|
+
classifier's JSON never reaches its caller. It runs under the turn's
|
|
1284
|
+
`max_steps`, deadline and cancel; its guards run once, on the answer at the
|
|
1285
|
+
top; its OAuth grants are stored under the root's app. An approval, an
|
|
1286
|
+
`ask_user` question or an OAuth consent on one of its routes, or inside a
|
|
1287
|
+
route's own delegation, pauses the whole turn with the path (e.g.
|
|
1288
|
+
`["Boss", "Team", "Ops"]` delegated, `["Team", "Ops"]` as a route), and
|
|
1289
|
+
the answer resumes that route without classifying again. As a workflow
|
|
1290
|
+
node it cannot pause on a consent request: the node fails. Its
|
|
1291
|
+
conversation is filed at `<app>/<caller>/<entry>` when delegated, at
|
|
1292
|
+
`<app>/route:<route>` as a route and at `<app>/node:<node>` as a node. A
|
|
1293
|
+
route that carries the entry's own name, a `map` over one, and a reference
|
|
1294
|
+
chain below it that reaches itself are refused by name before any model
|
|
1295
|
+
call.
|
|
1296
|
+
|
|
1225
1297
|
### Workflows (`workflow:`) — the third orchestration method
|
|
1226
1298
|
|
|
1227
1299
|
A syndicate that declares a `workflow:` block is a **graph**. Its agents
|
|
@@ -1302,12 +1374,15 @@ graph, under the entry's name and description, wherever it appears: as a
|
|
|
1302
1374
|
DELEGATE subagent its last output is the tool's answer; as a plan-dispatch
|
|
1303
1375
|
route it is the turn's answer, and the conversation keeps the message and
|
|
1304
1376
|
that answer; as a node of another workflow it is the node's output. The
|
|
1305
|
-
graph's events are kept in the entry's own session,
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1377
|
+
graph's events are kept in the entry's own session, filed under the agent
|
|
1378
|
+
path and its kind (`<app>/route:<route>`, `<app>/node:<node>`; a session
|
|
1379
|
+
an earlier release filed under `<app>/<entry>` or the entry's name alone is
|
|
1380
|
+
still continued). Wherever it
|
|
1381
|
+
runs, an `ask_user` node or a gated agent node inside it pauses the whole
|
|
1382
|
+
turn with the path down to that node, and the answer resumes the graph
|
|
1383
|
+
([ADR 0111](./wiki/decisions/0111-pauses-inside-nested-syndicates.md),
|
|
1384
|
+
[ADR 0119](./wiki/decisions/0119-workflow-routes-and-nodes-pause-the-turn.md)).
|
|
1385
|
+
A `map` over one is refused by name.
|
|
1311
1386
|
|
|
1312
1387
|
**Not yet.** Remote `a2a_agent_url` subagents are refused inside a
|
|
1313
1388
|
workflow by the schema. An `ask_user` tool on a node agent is refused
|