melchizedek-agents 1.0.3 → 1.1.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 +19 -2
- package/CHANGELOG.md +292 -0
- package/DOCUMENTATION.md +99 -39
- package/README.md +1 -0
- package/config/agents/README.md +2 -2
- package/config/agents/examples/ares.yaml +6 -10
- package/config/agents/examples/assistant.yaml +6 -6
- package/config/agents/examples/augustin.yaml +9 -15
- package/config/agents/examples/cartographers.yaml +6 -6
- package/config/agents/examples/council.yaml +6 -6
- package/config/agents/examples/critic.yaml +16 -16
- package/config/agents/examples/harness.yaml +4 -4
- package/config/agents/examples/image_production.yaml +33 -32
- package/config/agents/examples/librarian.yaml +4 -4
- package/config/agents/examples/patient_advocate.yaml +6 -10
- package/config/agents/examples/pipeline.yaml +20 -19
- package/config/agents/examples/research.yaml +36 -43
- package/config/agents/examples/scribe.yaml +31 -32
- package/config/agents/examples/scriptorium.yaml +6 -6
- package/config/agents/examples/structured_critic.yaml +82 -0
- package/config/agents/examples/style_council.yaml +9 -11
- package/config/agents/examples/syndicate.yaml +3 -5
- package/config/agents/examples/tutor.yaml +2 -2
- package/config/agents/examples/weather.yaml +2 -2
- package/config/agents/syndicate.schema.json +797 -9
- package/config/agents/syndicateSchema.yaml +109 -72
- package/config/agents/templates/README.md +1 -1
- package/config/agents/templates/account_memory.yaml +4 -4
- package/config/agents/templates/case_desk.yaml +6 -6
- package/config/agents/templates/conversational.yaml +2 -2
- package/config/agents/templates/draft_review.yaml +6 -6
- package/config/agents/templates/intake_extractor.yaml +61 -60
- package/config/agents/templates/research_brief.yaml +6 -6
- package/config/agents/templates/research_desk.yaml +6 -6
- package/config/agents/templates/review_panel.yaml +8 -8
- package/config/agents/templates/support_triage.yaml +30 -29
- package/config/agents/templates/systems_operator.yaml +21 -5
- package/db/migrations/0014_durable_runs.sql +31 -0
- package/dist/lib/a2a/app.d.ts +15 -0
- package/dist/lib/a2a/app.js +53 -5
- package/dist/lib/a2a/executor.d.ts +7 -1
- package/dist/lib/a2a/executor.js +34 -4
- package/dist/lib/a2a/oauthSetup.d.ts +90 -0
- package/dist/lib/a2a/oauthSetup.js +205 -0
- package/dist/lib/agentDialect.d.ts +66 -0
- package/dist/lib/agentDialect.js +134 -0
- package/dist/lib/compile.d.ts +10 -5
- package/dist/lib/compile.js +69 -37
- package/dist/lib/compileNative.js +2 -0
- package/dist/lib/dispatch.d.ts +10 -5
- package/dist/lib/dispatch.js +10 -5
- package/dist/lib/doctor.d.ts +34 -1
- package/dist/lib/doctor.js +47 -3
- package/dist/lib/loadSyndicate.d.ts +45 -2
- package/dist/lib/loadSyndicate.js +22 -2
- package/dist/lib/memory/namespace.d.ts +6 -5
- package/dist/lib/memory/namespace.js +6 -5
- package/dist/lib/memory/providers.js +3 -4
- package/dist/lib/models/capabilities.d.ts +16 -1
- package/dist/lib/models/capabilities.js +55 -1
- package/dist/lib/observability/tracer.d.ts +8 -1
- package/dist/lib/observability/tracer.js +33 -5
- package/dist/lib/runtime/approvals.d.ts +6 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +18 -1
- package/dist/lib/runtime/native/agentLoop.js +105 -11
- package/dist/lib/runtime/native/checkpoint.d.ts +164 -0
- package/dist/lib/runtime/native/checkpoint.js +365 -0
- package/dist/lib/runtime/native/delegate.d.ts +141 -18
- package/dist/lib/runtime/native/delegate.js +235 -39
- package/dist/lib/runtime/native/interrupts.d.ts +101 -2
- package/dist/lib/runtime/native/interrupts.js +201 -1
- package/dist/lib/runtime/native/request.d.ts +13 -3
- package/dist/lib/runtime/native/request.js +28 -4
- package/dist/lib/runtime/native/telemetry.js +10 -15
- package/dist/lib/runtime/questions.d.ts +7 -4
- package/dist/lib/runtime/sessions.d.ts +1 -1
- package/dist/lib/runtime/syndicateTurn.d.ts +7 -1
- package/dist/lib/runtime/syndicateTurn.js +74 -22
- package/dist/lib/runtime/turnControl.d.ts +15 -0
- package/dist/lib/runtime/turnControl.js +10 -0
- package/dist/lib/storage/postgres/index.d.ts +6 -1
- package/dist/lib/storage/postgres/index.js +7 -2
- package/dist/lib/storage/postgres/taskQueue.d.ts +14 -2
- package/dist/lib/storage/postgres/taskQueue.js +20 -3
- package/dist/lib/storage/postgres/taskStore.d.ts +26 -2
- package/dist/lib/storage/postgres/taskStore.js +74 -8
- package/dist/lib/syndicateSchema.d.ts +278 -0
- package/dist/lib/syndicateSchema.js +182 -15
- package/dist/lib/tools/auth.d.ts +7 -5
- package/dist/lib/tools/auth.js +12 -4
- package/dist/lib/tools/credentialEnv.d.ts +28 -0
- package/dist/lib/tools/credentialEnv.js +52 -0
- package/dist/lib/tools/credentialStore.js +9 -2
- package/dist/lib/tools/mcpToolFactory.d.ts +25 -2
- package/dist/lib/tools/mcpToolFactory.js +179 -21
- package/dist/lib/tools/oauthHosts.d.ts +71 -0
- package/dist/lib/tools/oauthHosts.js +154 -0
- package/dist/lib/tools/oauthTools.d.ts +233 -0
- package/dist/lib/tools/oauthTools.js +421 -0
- package/dist/lib/tools/openapiTools.d.ts +4 -6
- package/dist/lib/tools/openapiTools.js +40 -36
- package/dist/lib/tools/taskTools.d.ts +30 -5
- package/dist/lib/tools/taskTools.js +74 -10
- package/dist/lib/workflow.d.ts +8 -4
- package/dist/lib/workflow.js +8 -4
- package/dist/lib/workflowConfig.d.ts +6 -0
- package/dist/scripts/a2a_server.d.ts +36 -0
- package/dist/scripts/a2a_server.js +122 -1
- package/dist/scripts/assistant_worker.d.ts +10 -0
- package/dist/scripts/assistant_worker.js +69 -19
- package/dist/scripts/doctor.d.ts +4 -1
- package/dist/scripts/doctor.js +5 -2
- package/dist/scripts/yaml_codemod.d.ts +52 -0
- package/dist/scripts/yaml_codemod.js +570 -0
- package/package.json +5 -2
- package/skills/briefs/melchizedek-author.md +7 -5
- package/skills/briefs/melchizedek-models.md +4 -3
- package/skills/briefs/melchizedek.md +1 -0
- package/skills/melchizedek/SKILL.md +1 -0
- package/skills/melchizedek-author/SKILL.md +7 -4
- package/skills/melchizedek-author/assets/minimal.yaml +13 -13
- package/skills/melchizedek-models/SKILL.md +6 -3
package/.env.example
CHANGED
|
@@ -33,8 +33,11 @@ ALLOW_PRIVATE_MCP=false
|
|
|
33
33
|
# ── Optional: OpenAPI tools (an agent's `openapi:` list) ──────────────────────
|
|
34
34
|
# ALLOW_PRIVATE_OPENAPI: set to "true" to let an OpenAPI tool call a localhost /
|
|
35
35
|
# private / link-local server. Off by default as an SSRF guard. Credentials an
|
|
36
|
-
# `openapi:` entry names (auth.bearer_env, auth.api_key.env
|
|
37
|
-
#
|
|
36
|
+
# `openapi:` entry names (auth.bearer_env, auth.api_key.env, and an OAuth
|
|
37
|
+
# grant's auth.oauth2.client_id_env / client_secret_env, which an MCP server's
|
|
38
|
+
# mcp_auth.oauth2 uses too) are read from this file or the environment, never
|
|
39
|
+
# written in YAML. Give each API its own variable: the framework's own
|
|
40
|
+
# settings below are refused (ADR 0112).
|
|
38
41
|
# ALLOW_PRIVATE_OPENAPI=false
|
|
39
42
|
|
|
40
43
|
# ── Optional: Anthropic / Claude ──────────────────────────────────────────────
|
|
@@ -245,6 +248,20 @@ PUBLIC_URL=
|
|
|
245
248
|
# out of the database and its backups. Unset = no credential store. A row
|
|
246
249
|
# sealed with another key stays unreadable (the user reconnects).
|
|
247
250
|
# MELCHIZEDEK_CREDENTIAL_KEY=
|
|
251
|
+
# The OAuth consent callback (ADR 0085), exactly as registered at every
|
|
252
|
+
# provider a YAML grant names: https, no query. Mounts the callback and lets a
|
|
253
|
+
# run ask a user to connect a provider. Needs MELCHIZEDEK_CREDENTIAL_KEY.
|
|
254
|
+
# OAUTH_REDIRECT_URI=https://agents.example.com/oauth/callback
|
|
255
|
+
# Who may complete a callback (ADR 0114): required (default) — the browser
|
|
256
|
+
# carries the user's identity, which only A2A_AUTH=header behind a gateway
|
|
257
|
+
# gives it; state — the single-use state alone binds the flow to its user.
|
|
258
|
+
# OAUTH_CALLBACK_IDENTITY=required
|
|
259
|
+
# The hosts each provider's tokens may be sent to (ADR 0114), the operator's
|
|
260
|
+
# binding, never the YAML's: provider=host,host;provider=host. A host is a
|
|
261
|
+
# hostname, *.domain (subdomains only), IPv4 or [IPv6]; no scheme or port.
|
|
262
|
+
# Unset: authorization_code grants are refused, client_credentials allowed.
|
|
263
|
+
# Set: every grant's provider is listed and every host it calls is its own.
|
|
264
|
+
# MELCHIZEDEK_OAUTH_HOSTS=tracker=api.tracker.example.com,auth.tracker.example.com
|
|
248
265
|
|
|
249
266
|
# ── Optional: Knowledge Bundle (wiki) ─────────────────────────────────────────
|
|
250
267
|
# WIKI_ROOT: filesystem path of the OKF knowledge bundle the wiki tools serve.
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,298 @@ the starter pack and the templates), not the repo's full history.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 1.1.0 — 2026-10-09
|
|
10
|
+
|
|
11
|
+
Release 1.1.0 closes workstream 6 of the ADK-independence plan (ADRs
|
|
12
|
+
0109–0116): the first features built on the engine's own loop. An
|
|
13
|
+
orchestrator can delegate and answer in its own JSON schema; a pause inside
|
|
14
|
+
a delegated subagent or a nested syndicate reaches the turn; tools can hold
|
|
15
|
+
OAuth grants declared in YAML, sent only to hosts the operator binds;
|
|
16
|
+
background jobs checkpoint and resume, and cancel works across replicas; a
|
|
17
|
+
step's subagent calls run at once; and the YAML gains provider-neutral v2
|
|
18
|
+
keys, with a codemod. Two fixes: `trace: false` now records nothing, and
|
|
19
|
+
MCP settings on an orchestrator are honoured.
|
|
20
|
+
|
|
21
|
+
### Upgrade
|
|
22
|
+
|
|
23
|
+
Read this list before deploying 1.1.0.
|
|
24
|
+
|
|
25
|
+
1. **Apply migration `0014_durable_runs` before you deploy**
|
|
26
|
+
(`db/migrations/0014_durable_runs.sql`, through `npx melchizedek-db
|
|
27
|
+
apply`) on any server or worker that uses `DATABASE_URL`. Every save
|
|
28
|
+
of the Postgres task stores now writes the new columns
|
|
29
|
+
(`adk_a2a_tasks.cancel_requested_at`, `melchizedek_tasks.checkpoint`),
|
|
30
|
+
so without the migration every Postgres task-store save fails. The A2A
|
|
31
|
+
server refuses to start against a database behind 0014 unless
|
|
32
|
+
`ALLOW_SCHEMA_MISMATCH=true` is set, and with that override its saves
|
|
33
|
+
fail. The migration adds nullable columns only and is safe to re-run.
|
|
34
|
+
2. **New OAuth environment variables** for `melchizedek-serve`, needed
|
|
35
|
+
only when a served syndicate declares an `oauth2` grant:
|
|
36
|
+
`MELCHIZEDEK_OAUTH_HOSTS` (which hosts each provider's tokens may go to),
|
|
37
|
+
`OAUTH_REDIRECT_URI` (the consent callback: https, or http on a loopback
|
|
38
|
+
host) and `OAUTH_CALLBACK_IDENTITY` (`required`, the default, or
|
|
39
|
+
`state`). `MELCHIZEDEK_CREDENTIAL_KEY` keys the credential store.
|
|
40
|
+
3. **`authorization_code` grants are refused until an allowlist is set.**
|
|
41
|
+
With no `MELCHIZEDEK_OAUTH_HOSTS` (or `createA2AApp({ oauthHosts })`), a
|
|
42
|
+
syndicate holding an `authorization_code` grant is refused when served;
|
|
43
|
+
`client_credentials` grants are allowed.
|
|
44
|
+
4. **`max_concurrency` now runs a step's subagent calls at once**, at most
|
|
45
|
+
four by default. Set `max_concurrency: 1` at the root of a delegate
|
|
46
|
+
syndicate to restore the old one-after-another order.
|
|
47
|
+
5. **YAML v2 keys.** `sampling:`, `output:` and `model_overrides:` replace
|
|
48
|
+
`generateContentConfig` and `outputSchema`. The old spellings are
|
|
49
|
+
deprecated, not removed: a v1 file loads and behaves as before, with one
|
|
50
|
+
deprecation line per file per process. `npx melchizedek-codemod
|
|
51
|
+
[--check] <file|dir>…` rewrites v1 files to v2.
|
|
52
|
+
6. **`trace: false` now records nothing.** A turn run with
|
|
53
|
+
`runSyndicateTurn({ trace: false })` writes no telemetry row and exports
|
|
54
|
+
no span. If you relied on its agent, model or tool spans, trace the turn.
|
|
55
|
+
7. **Child sessions are keyed by agent path.** A delegated subagent's
|
|
56
|
+
session is filed under `<app>/<caller>/<subagent>`; conversations stored
|
|
57
|
+
under the old key still resume. Code that reads a subagent's session by
|
|
58
|
+
its name alone reads the path instead.
|
|
59
|
+
8. **The capability matrix has a new member, `structured_output_with_tools`.**
|
|
60
|
+
Code that builds a `Record<Capability, …>` must add it.
|
|
61
|
+
9. **An A2A task cancelled while waiting for the turn lock now ends
|
|
62
|
+
`canceled`**, not `rejected`.
|
|
63
|
+
|
|
64
|
+
### Breaking — read before upgrading
|
|
65
|
+
|
|
66
|
+
- **Breaking: migration 0014 is required** on a Postgres deployment
|
|
67
|
+
(Upgrade item 1).
|
|
68
|
+
- **Breaking: `Capability`, `CAPABILITIES` and `CAPABILITY_MATRIX`
|
|
69
|
+
(`melchizedek-agents/models/capabilities`) gain
|
|
70
|
+
`structured_output_with_tools`**, so an exhaustive
|
|
71
|
+
`Record<Capability, …>` no longer type-checks without it.
|
|
72
|
+
- **Breaking: a step's calls to subagents run concurrently by default**
|
|
73
|
+
(ADR 0116). See Changed; `max_concurrency: 1` keeps the old order.
|
|
74
|
+
- **Breaking: a delegated subagent's own session is filed under its agent
|
|
75
|
+
path** (ADR 0111). See Changed; stored conversations still resume.
|
|
76
|
+
- **Breaking: `runSyndicateTurn({ trace: false })` records nothing**: no
|
|
77
|
+
span of any kind, no ledger row, no export. See Fixed.
|
|
78
|
+
- **Breaking: an A2A task cancelled while it waits for its conversation's
|
|
79
|
+
turn lock ends `canceled`**, not `rejected` (ADR 0113).
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
- **An orchestrator that delegates can answer in its own `outputSchema`**
|
|
84
|
+
(ADR 0109). An agent holding an `outputSchema` beside subagents or tools
|
|
85
|
+
calls them first, then ends its turn on one JSON object matching the
|
|
86
|
+
schema; no relay leaf or `dispatch:` block is needed (plan-dispatch stays
|
|
87
|
+
available). On Claude from Opus 4.8, Sonnet 5 and Haiku 5.5, on every
|
|
88
|
+
OpenAI id, and on Gemini 2 and later through Vertex AI, the schema now
|
|
89
|
+
travels in the provider's own structured-output field in the same request
|
|
90
|
+
as the tools (`output_config.format`, `text.format`, `responseJsonSchema`)
|
|
91
|
+
instead of as a `set_model_response` tool, on the agent's `fallback_model`
|
|
92
|
+
too; every other path keeps `set_model_response`.
|
|
93
|
+
- **The capability matrix gains `structured_output_with_tools`**
|
|
94
|
+
(`melchizedek-agents/models/capabilities`): supported on Anthropic and
|
|
95
|
+
OpenAI (and Gemini on Vertex AI), degraded (`set_model_response`) on the
|
|
96
|
+
Gemini API, xAI, Moonshot, Ollama and the gateway. `npm run doctor` names
|
|
97
|
+
it as a gap for an agent with a schema beside tools on a degraded path.
|
|
98
|
+
`outputSchemaBesideTools(model)` says whether a model's path takes both.
|
|
99
|
+
- **New starter-pack example `config/agents/examples/structured_critic.yaml`**
|
|
100
|
+
(`npm run syndicate:structured-critic`): the critic as one agent, an
|
|
101
|
+
orchestrator on `claude-opus-5-5` that asks a Drafter on
|
|
102
|
+
`claude-haiku-5-5` for a draft and grades it in its own `output.schema`.
|
|
103
|
+
It needs only `ANTHROPIC_API_KEY`.
|
|
104
|
+
- **A pause inside a delegated subagent reaches the turn** (WS6-2a,
|
|
105
|
+
[ADR 0110](./wiki/decisions/0110-pauses-inside-delegated-subagents-reach-the-turn.md)).
|
|
106
|
+
`require_approval` and `ask_user` are allowed on a delegated subagent, and
|
|
107
|
+
gates inside a nested delegate syndicate (`yaml_reference`) no longer fail
|
|
108
|
+
to load. The turn ends `input-required` with the request or question; the
|
|
109
|
+
decision or answer goes back down to the subagent, which finishes before
|
|
110
|
+
its caller continues. Before, a gate there was a load error and a pause
|
|
111
|
+
answered the call with an empty text.
|
|
112
|
+
- `PendingApproval.path` and `PendingInput.path` (optional): the agents from
|
|
113
|
+
the turn's own agent down to the one that asked, set only for a pause
|
|
114
|
+
inside a delegated subagent. The A2A `approval_request` and
|
|
115
|
+
`input_request` data parts carry `path` beside their fields when it is set.
|
|
116
|
+
- **Pauses inside nested syndicates reach the turn** (WS6-2b,
|
|
117
|
+
[ADR 0111](./wiki/decisions/0111-pauses-inside-nested-syndicates.md)). An
|
|
118
|
+
approval request or `ask_user` question raised inside a `yaml_reference`
|
|
119
|
+
subagent ends the turn `input-required` with the agent path, over
|
|
120
|
+
`runSyndicateTurn` and A2A, and the answer resumes it. A nested dispatch
|
|
121
|
+
syndicate may gate its classifier (a gate on one of its routes, which never
|
|
122
|
+
run nested, is refused by name). A nested workflow delegated to as a
|
|
123
|
+
subagent may hold `ask_user` nodes and gated agent nodes; the path ends at
|
|
124
|
+
the node. As a dispatch route or a workflow node, a nested workflow's
|
|
125
|
+
pauses stay refused, with a message that says so.
|
|
126
|
+
- **OAuth grants in YAML (`auth: { oauth2 }`, `mcp_auth: { oauth2 }`;
|
|
127
|
+
ADR 0112).** An `openapi:` entry's `auth` takes a third form, `oauth2`,
|
|
128
|
+
and an agent with `mcp_server_url` may declare `mcp_auth: { oauth2 }`.
|
|
129
|
+
The block names a `provider`, a `grant` (`authorization_code` or
|
|
130
|
+
`client_credentials`), `authorization_url`, `token_url`, `client_id` or
|
|
131
|
+
`client_id_env`, `client_secret_env` and `scopes`; secrets are
|
|
132
|
+
environment variable names, never values, under the same rule as
|
|
133
|
+
`bearer_env`. `authorization_code` sends the run's user's own token
|
|
134
|
+
(`ctx.accessToken`, so the consent pause asks a user who has not granted
|
|
135
|
+
it); `client_credentials` sends the server's own token from the token
|
|
136
|
+
endpoint, held in memory until shortly before it expires. A token goes
|
|
137
|
+
only over https (or http to a loopback host). An authorization-code MCP
|
|
138
|
+
server needs `mcp_tools` and runs each user on their own connection.
|
|
139
|
+
`npm run doctor` lists every tool that needs a grant. New module
|
|
140
|
+
`melchizedek-agents/tools/oauthTools` (`oauthClientsFor`, which builds
|
|
141
|
+
`oauthConsent({ providers })` from the YAML, `oauthRefreshProviders`,
|
|
142
|
+
which gives `credentialStore({ providers })` the matching refresh hooks,
|
|
143
|
+
`clientCredentialsGrant`, `oauthTokenSource`, `tokenTransportProblem`)
|
|
144
|
+
and `melchizedek-agents/tools/credentialEnv` (`credentialEnvProblem`,
|
|
145
|
+
still exported from `tools/openapiTools`), both under the existing
|
|
146
|
+
`./tools/*` export. `OAuthTokenSource` takes the call's destination
|
|
147
|
+
(`(ctx, destination)`), and `oauthTokenSource` accepts several servers.
|
|
148
|
+
`ToolCredentialError` gains the codes `unavailable` and `grant_failed`,
|
|
149
|
+
and `createMcpTools` / `loadMcpTools` an optional second argument. The
|
|
150
|
+
systems_operator template carries a commented `mcp_auth` block.
|
|
151
|
+
- **The OAuth host allowlist (`MELCHIZEDEK_OAUTH_HOSTS`,
|
|
152
|
+
`createA2AApp({ oauthHosts })`; ADR 0114).** The operator binds each
|
|
153
|
+
provider to the hosts its tokens and client secret may be sent to
|
|
154
|
+
(`provider=host,host;…`; `*.domain` for subdomains). With none, an
|
|
155
|
+
`authorization_code` grant is refused and `client_credentials` allowed;
|
|
156
|
+
with one, every grant's provider must be listed and its server, token
|
|
157
|
+
endpoint and authorization endpoint must be that provider's hosts. A
|
|
158
|
+
syndicate that breaks it is refused when served, when compiled and at
|
|
159
|
+
each call (`ToolCredentialError` code `host_refused`). New module
|
|
160
|
+
`melchizedek-agents/tools/oauthHosts` (`parseOAuthHosts`,
|
|
161
|
+
`checkOAuthHosts`, `setOAuthHosts`, `oauthHosts`, `hostAllowed`,
|
|
162
|
+
`oauthHostProblem`) under the existing `./tools/*` export;
|
|
163
|
+
`tools/oauthTools` gains `oauthGrantHostProblems`, `oauthCallProblem`,
|
|
164
|
+
`syndicateOAuthGrants` and `syndicateOAuthHostProblems`, and
|
|
165
|
+
`oauthClientsFor` an `allowlist` option.
|
|
166
|
+
- **`melchizedek-serve` wires tool credentials and the consent step**
|
|
167
|
+
from `MELCHIZEDEK_CREDENTIAL_KEY` (rows in Postgres with `DATABASE_URL`,
|
|
168
|
+
else process memory), `OAUTH_REDIRECT_URI`, `OAUTH_CALLBACK_IDENTITY`
|
|
169
|
+
(`required`, the default, or `state`) and `MELCHIZEDEK_OAUTH_HOSTS`, with
|
|
170
|
+
the consent clients and refresh hooks built from the served syndicate
|
|
171
|
+
files. `melchizedek-agents/server` exports `serverOAuth` and
|
|
172
|
+
`servedFileSyndicates`. `npm run doctor` prints an `oauth` line (which
|
|
173
|
+
variables are set, and each problem by name) and each grant's host
|
|
174
|
+
refusals; `--check` fails on an OAuth problem.
|
|
175
|
+
- **Durable long-running runs** (WS6-5, ADR 0113):
|
|
176
|
+
- **The worker resumes a job instead of starting over.**
|
|
177
|
+
`melchizedek-worker` checkpoints each job's sessions beside the job at
|
|
178
|
+
every step boundary (Postgres: the `checkpoint` column; the JSON store:
|
|
179
|
+
`<store>.checkpoints.json`). A job claimed again after its worker
|
|
180
|
+
stopped resumes from its last checkpoint. A dispatch syndicate
|
|
181
|
+
checkpointed inside an agent route starts over, and its classifier runs
|
|
182
|
+
again.
|
|
183
|
+
- **SIGTERM re-queues the job in hand** with its checkpoint, so the next
|
|
184
|
+
worker finishes it. Before, the job was recorded as failed.
|
|
185
|
+
- **A running background job can be cancelled.** `task_update` with
|
|
186
|
+
`status: cancelled` now accepts a running job. The worker stops at its
|
|
187
|
+
next lease renewal or checkpoint save and writes no result.
|
|
188
|
+
- **A2A cancel works across replicas**
|
|
189
|
+
(`melchizedek-agents/storage/postgres`): a `tasks/cancel` that reaches a
|
|
190
|
+
replica not running the task answers `canceled` and records the
|
|
191
|
+
request; the replica holding the task's lease aborts the run on its
|
|
192
|
+
lease heartbeat; the stored task stays `canceled`; the reaper keeps a
|
|
193
|
+
cancel that was already reported. New:
|
|
194
|
+
`PostgresTaskStore.requestCancel(taskId, context)`, the optional
|
|
195
|
+
`leases.cancelRequested` on `postgresStorage()`, and the optional
|
|
196
|
+
`storage.leases.cancelRequested` option of `createA2AApp`.
|
|
197
|
+
- **`TaskBackend` (`melchizedek-agents/tools/taskTools`), additive only:**
|
|
198
|
+
new optional members `saveCheckpoint` and `loadCheckpoint`; `renew` may
|
|
199
|
+
resolve `false` when the claim is gone; new export `taskCheckpointPath`.
|
|
200
|
+
A backend you wrote yourself still type-checks. It just does not
|
|
201
|
+
checkpoint.
|
|
202
|
+
- **YAML schema v2: provider-neutral keys** (WS6-4, ADR 0115).
|
|
203
|
+
`sampling: { temperature, top_p, max_output_tokens, stop }`,
|
|
204
|
+
`output: { schema, mime }` (`application/json` or `text/plain`) and
|
|
205
|
+
`reasoning:` (ADR 0047) replace `generateContentConfig` and
|
|
206
|
+
`outputSchema`. `model_overrides.<provider>` (`gemini`, `anthropic`,
|
|
207
|
+
`openai`, `xai`, `moonshot`, `ollama`) gives one provider its own
|
|
208
|
+
`instruction` or an `instruction_append`, applied for the provider the
|
|
209
|
+
agent's model routes to.
|
|
210
|
+
- **New bin `melchizedek-codemod [--check] <file|dir>…`** rewrites v1 YAML
|
|
211
|
+
into v2, keeping comments and layout; idempotent; `--check` exits
|
|
212
|
+
non-zero when a file would change. A registry row is migrated by running
|
|
213
|
+
it on the file it was published from and republishing.
|
|
214
|
+
- **`LoadSyndicateOptions.onWarning`** receives the v1 deprecation line
|
|
215
|
+
(default `console.warn`). The agent YAML types (`AgentYamlConfig`,
|
|
216
|
+
`SubagentYamlConfig`) gain `sampling`, `output` and `model_overrides`.
|
|
217
|
+
- **`max_concurrency`**, a new optional root key of a delegate syndicate:
|
|
218
|
+
a positive integer up to 32, default 4 (ADR 0116). See Changed.
|
|
219
|
+
- **`turnUntraced()`** (`melchizedek-agents/runtime/turnControl`) says
|
|
220
|
+
whether the running code belongs to a turn that opted out of tracing;
|
|
221
|
+
`createTurnControl` takes `untraced`, and `TurnControl` carries it.
|
|
222
|
+
**`startEngineSpan(tracerName, name, options?)`**
|
|
223
|
+
(`melchizedek-agents/observability/tracer`) starts a span the way the
|
|
224
|
+
engine does: a non-recording one, without starting the tracer, inside an
|
|
225
|
+
untraced turn.
|
|
226
|
+
- Engine internals with no exports map entry: `delegatedPauses`,
|
|
227
|
+
`openCalls`, `resumedDelegations` in `lib/runtime/native/interrupts.ts`;
|
|
228
|
+
`resumeSubagent`, `SubagentPause`, `resumeWorkflowSubagent`,
|
|
229
|
+
`childAppName`, `legacyChild` in `lib/runtime/native/delegate.ts`;
|
|
230
|
+
`runDurableTurn`, `checkpointingSessions` in
|
|
231
|
+
`lib/runtime/native/checkpoint.ts` (the worker's runner).
|
|
232
|
+
|
|
233
|
+
### Changed
|
|
234
|
+
|
|
235
|
+
- **A step's calls to subagents run at once** (ADR 0116). When an
|
|
236
|
+
orchestrator's model calls several subagents in one step (the council's
|
|
237
|
+
Advocate and Skeptic, say), they now run concurrently instead of one after
|
|
238
|
+
another, at most `max_concurrency` at a time (default 4).
|
|
239
|
+
`max_concurrency: 1` keeps the old order. Two calls to the same subagent
|
|
240
|
+
in one step still run one after the other on its one session. The stored
|
|
241
|
+
responses stay in call order whatever order the subagents finish in, so
|
|
242
|
+
stored history does not change; a pause, a cancel, `max_steps` and durable
|
|
243
|
+
checkpoints keep their rules. The key is refused beside `workflow:` (use
|
|
244
|
+
`workflow.max_concurrency`) and `dispatch:`. Other tool calls are
|
|
245
|
+
unchanged.
|
|
246
|
+
- **A delegated subagent's own session is filed under its agent path**
|
|
247
|
+
(WS6-2b, ADR 0111): `<app>/<caller>/<subagent>`, and below a nested
|
|
248
|
+
syndicate `<app>/<caller>/<subagent>/<inner>`, instead of the subagent's
|
|
249
|
+
name alone, so two syndicates with a same-named subagent on one
|
|
250
|
+
conversation no longer share it. A conversation stored before keeps its
|
|
251
|
+
subagents' sessions under the old key: they are still read, continued and
|
|
252
|
+
resumed. Code that read a subagent's session directly by its name reads
|
|
253
|
+
the path instead. The stored events do not change.
|
|
254
|
+
- **`generateContentConfig` and `outputSchema` are deprecated, not
|
|
255
|
+
removed** (ADR 0115). A v1 file loads and behaves exactly as before; each
|
|
256
|
+
load that finds them prints one line, once per file or registry id per
|
|
257
|
+
process, naming the key paths (never their values). A v2 key beside its
|
|
258
|
+
v1 spelling on one agent (`sampling.temperature` with
|
|
259
|
+
`generateContentConfig.temperature`, `output.schema` with
|
|
260
|
+
`outputSchema`, …) is a load error. `toolConfig` and the effort words
|
|
261
|
+
`xhigh` and `max` have no v2 key yet and stay readable there.
|
|
262
|
+
`syndicate.schema.json` marks both keys `deprecated`. The loader (and
|
|
263
|
+
`validateSyndicateConfig`) returns the engine form: `sampling` and
|
|
264
|
+
`output` arrive folded into `generateContentConfig` and `outputSchema`.
|
|
265
|
+
- **Every shipped example, template, the annotated `syndicateSchema.yaml`
|
|
266
|
+
and the author skill's `minimal.yaml` are in the v2 spelling.** Copies of
|
|
267
|
+
them keep working either way. The `ares` example's search agent now says
|
|
268
|
+
`reasoning: none` (`thinkingLevel: MINIMAL` on Gemini 3) where it said
|
|
269
|
+
`thinkingBudget: 0`.
|
|
270
|
+
- **An A2A task cancelled during the turn-lock wait** now ends `canceled`,
|
|
271
|
+
not `rejected` (ADR 0113).
|
|
272
|
+
- **An OpenAPI `auth` that sets two forms** now reads "exactly one of
|
|
273
|
+
bearer_env, api_key or oauth2" (was "exactly one of bearer_env or
|
|
274
|
+
api_key").
|
|
275
|
+
- **The authoring skill (`skills/melchizedek-author`) allows an
|
|
276
|
+
`output: { schema }` on an orchestrator that delegates** when the answer
|
|
277
|
+
is the orchestrator's own structured judgment (ADR 0109), and points at
|
|
278
|
+
`structured_critic.yaml`; a schema on a leaf stays the choice when the
|
|
279
|
+
user should receive a specialist's answer.
|
|
280
|
+
|
|
281
|
+
### Fixed
|
|
282
|
+
|
|
283
|
+
- **`runSyndicateTurn({ trace: false })` records nothing.** It used to drop
|
|
284
|
+
only the turn's root span: the agent, model-step, `llm.request` and tool
|
|
285
|
+
spans still started the global tracer, so with `TELEMETRY_SUPABASE=true`
|
|
286
|
+
an untraced turn still wrote `adk_telemetry` rows (and could write
|
|
287
|
+
`adk_payloads` rows), and with `OTEL_EXPORTER_OTLP_ENDPOINT` set it still
|
|
288
|
+
exported. Now a turn with `trace: false` opens no span of any kind, sends
|
|
289
|
+
nothing to the ledger, the console exporter, `onSpanEnd` listeners or an
|
|
290
|
+
OTLP endpoint, and does not start the tracer. Its step budget and its
|
|
291
|
+
`usage` are unchanged. The default (traced) is unchanged, and the A2A
|
|
292
|
+
server and the chat bin trace as before.
|
|
293
|
+
- **`mcp_server_url` and `mcp_tools` on the orchestrator are honoured.**
|
|
294
|
+
The schema accepted them there, but only subagents read them, so a
|
|
295
|
+
one-agent syndicate with an MCP server on its orchestrator got no MCP
|
|
296
|
+
tools and no warning. The orchestrator now resolves them as a subagent
|
|
297
|
+
does (narrowed by `mcp_tools`, gateable by its `require_approval`). On a
|
|
298
|
+
plan-dispatch syndicate the orchestrator is the classifier, and an MCP
|
|
299
|
+
server declared there is now dialled at compile time.
|
|
300
|
+
|
|
9
301
|
## 1.0.3 — 2026-10-09
|
|
10
302
|
|
|
11
303
|
Dependency updates and one packaging fix. No export, bin, YAML key or
|
package/DOCUMENTATION.md
CHANGED
|
@@ -106,8 +106,8 @@ orchestrator:
|
|
|
106
106
|
tools:
|
|
107
107
|
- "google_search" # names resolved via lib/toolRegistry.ts
|
|
108
108
|
reasoning: medium # none | low | medium | high, or { budget_tokens: 4096 }
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
sampling:
|
|
110
|
+
max_output_tokens: 4096
|
|
111
111
|
|
|
112
112
|
subagents:
|
|
113
113
|
- name: "Researcher"
|
|
@@ -136,19 +136,22 @@ Field reference:
|
|
|
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
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. |
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
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
|
+
| `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
|
+
| `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
|
+
| `generateContentConfig` / `outputSchema` | agent | The deprecated v1 spelling of `sampling`, `output.mime`, `reasoning` (as `thinkingConfig` / `reasoningEffort`) 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` and the effort words `xhigh` / `max` have no v2 form yet; `topK`, `seed`, the penalties, `candidateCount`, `safetySettings` and `includeThoughts` reach no provider. `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)). |
|
|
141
143
|
| `yaml_reference` | subagent | Mount another syndicate file as a nested subagent. |
|
|
142
144
|
| `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. |
|
|
143
145
|
| `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`. |
|
|
144
146
|
| `includeContents` / `outputKey` / `globalInstruction` / `disallowTransferToParent` / `disallowTransferToPeers` | agent | Passed through to the agent (spelled after ADK's `LlmAgent` fields). `includeContents: none` makes an agent see only the current message. |
|
|
145
147
|
| `fallback_model` | any agent | A model, ideally on another provider, that answers when this agent's model fails provider-side (5xx, 429, a connection reset, after its own retries) before producing any output, or while that provider's circuit is open: after `MODEL_BREAKER_THRESHOLD` consecutive provider failures (default 5; 0 disables) the provider is skipped for `MODEL_BREAKER_COOLDOWN_MS` (default 30 s). A 4xx and a canceled turn are never redirected, and a stream that already produced text is never replayed elsewhere ([ADR 0044](./wiki/decisions/0044-fallback-model-and-circuit-breaker.md)). |
|
|
146
148
|
| `mcp_tools` | subagent | The MCP server's tools this agent may use; any other tool the server lists is not exposed. On a dispatch route `require_approval` may name them ([ADR 0041](./wiki/decisions/0041-tool-vendors-get-least-privilege.md)). |
|
|
149
|
+
| `mcp_auth` | subagent | `{ oauth2 }`: the OAuth grant the MCP server takes, the same block as an OpenAPI `auth.oauth2`. `client_credentials` sends the server's own token; `authorization_code` sends each user's own token on their own connection (the consent pause asks for it) and needs `mcp_tools` ([ADR 0112](./wiki/decisions/0112-oauth-grants-declared-beside-the-tool.md)). |
|
|
147
150
|
| `mcp_server_url` | subagent | Discover this subagent's tools from a remote MCP server at load time (§3). SSRF-guarded; `ALLOW_PRIVATE_MCP=true` permits localhost for development. |
|
|
148
151
|
| `openapi` | any agent | HTTP APIs as tools, each from an OpenAPI 3 spec file (§3, OpenAPI tools): `spec`, and optionally `operations` (default: the GET operations only), `auth` (from environment variables), `base_url`, `prefix`. |
|
|
149
152
|
| `code_execution` | any Gemini agent | `"gemini"`: the model writes Python and Gemini runs it in Google's server-side sandbox, returning the output to the model; nothing runs on this host. For arithmetic, data and checks a model gets wrong in its head. Gemini models only ([ADR 0033](./wiki/decisions/0033-context-task-code.md)). |
|
|
150
153
|
| `context` | orchestrator | Compacts a long conversation: when the last request's prompt passed `compact_after_tokens`, earlier turns become one summary (written by `summary_model`, default the agent's own) and the last `keep_recent_events` stay verbatim. The full history stays stored; only what the model reads shrinks. The orchestrator of a delegate syndicate only: a dispatch route already reads a bounded projection, a workflow node sees only its input. |
|
|
151
|
-
| `mode` | workflow node | `"task"`: the agent works with its tools until it calls `finish_task`, whose arguments (matching its `
|
|
154
|
+
| `mode` | workflow node | `"task"`: the agent works with its tools until it calls `finish_task`, whose arguments (matching its `output.schema`) become the node's output. Workflow nodes only. |
|
|
152
155
|
| `examples` | any agent | Few-shot exchanges, `[{ input, output }]` (up to 20), added to every request's instruction as a few-shot block, the one ADK's `ExampleTool` wrote (an Instruction tool, `lib/tools/examples.ts`); the model never calls it. Keeps worked examples out of the prose of `instruction`. |
|
|
153
156
|
| `skills` | any agent | Agent Skills (a directory of SKILL.md folders) the agent holds the way a coding harness does: every skill's name and description is appended to its instruction at compile time; `load_skill` reads one in full with the names of its files, `load_skill_resource` reads one file. `scripts: local` adds `run_skill_script`, which runs a skill's own scripts on this machine, each after a person approves (the `require_approval` pause), with PATH, HOME, the temp directory and the locale but none of the server's keys, plus the variable names listed under `env:` (or `secret_env:` for a secret-shaped name, ADR 0086), and stdout and stderr each cut at 20,000 characters; `tools:` names registry tools a skill's `allowed-tools` may unlock once loaded. Worked example: `examples/harness.yaml`; engine: `lib/tools/skillToolset.ts` over `lib/tools/skills/`. |
|
|
154
157
|
|
|
@@ -181,8 +184,8 @@ function, and every adapter recognises it by marker
|
|
|
181
184
|
| `generate_image` | Contract | Calls the Gemini image model directly, saves the result under `outputs/`, returns the path. A function tool because binary `inlineData` cannot survive delegation, where a subagent returns only its final text. |
|
|
182
185
|
| `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`). |
|
|
183
186
|
| `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. |
|
|
184
|
-
| `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.
|
|
185
|
-
| `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. |
|
|
187
|
+
| `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. |
|
|
186
189
|
|
|
187
190
|
**MCP tools** are the exception to the registry: a subagent with
|
|
188
191
|
`mcp_server_url:` in its YAML gets its tools from a remote MCP server at
|
|
@@ -221,8 +224,17 @@ without `operations`, only GET operations become tools, so anything that
|
|
|
221
224
|
writes is exposed only by naming it, and a named operation can be listed
|
|
222
225
|
under `require_approval` (as written under `operations`) so a person
|
|
223
226
|
approves each call. `auth` names an environment variable, never a value
|
|
224
|
-
(`bearer_env`, or `api_key` with `in: header | query` and `name`)
|
|
225
|
-
|
|
227
|
+
(`bearer_env`, or `api_key` with `in: header | query` and `name`), or
|
|
228
|
+
declares an OAuth grant (`oauth2`: `provider`, `grant: authorization_code |
|
|
229
|
+
client_credentials`, `authorization_url`, `token_url`, `client_id` or
|
|
230
|
+
`client_id_env`, `client_secret_env`, `scopes`), whose token each call
|
|
231
|
+
fetches: the run's user's own through the consent pause, or the server's own
|
|
232
|
+
from the token endpoint ([ADR 0112](./wiki/decisions/0112-oauth-grants-declared-beside-the-tool.md)).
|
|
233
|
+
`oauthClientsFor(configs)` (`melchizedek-agents/tools/oauthTools`) builds the
|
|
234
|
+
consent step's clients from the same YAML, and `npm run doctor` lists every
|
|
235
|
+
tool that needs a grant. The hosts a grant's token goes to must be ones the
|
|
236
|
+
operator binds to its provider (`MELCHIZEDEK_OAUTH_HOSTS`, [ADR 0114](./wiki/decisions/0114-oauth-tokens-go-only-to-hosts-the-operator-binds.md));
|
|
237
|
+
with no allowlist, `authorization_code` is refused. An unset variable fails the compile, and so does one of the framework's own settings
|
|
226
238
|
(the database URL, a provider key, an `A2A_` secret: anything `.env.example`
|
|
227
239
|
documents), since the YAML chooses the host it goes to.
|
|
228
240
|
`OPENAPI_CREDENTIAL_ENVS`, when set, is the exact list of variables an `auth`
|
|
@@ -365,7 +377,7 @@ The xAI adapter carries the deepest capability surface: `grok-4.5`
|
|
|
365
377
|
requests pin `reasoning.effort: "medium"` (`lib/config.ts`), SSE
|
|
366
378
|
streaming works end-to-end (`runConfig: { streamingMode: 'sse' }` —
|
|
367
379
|
partial delta events stream, one aggregated event persists with usage),
|
|
368
|
-
structured outputs ride `
|
|
380
|
+
structured outputs ride `output.schema` → `text.format`, and two
|
|
369
381
|
xAI-only tools — `x_search` and `collections_search` (§3) — turn on
|
|
370
382
|
live X search and hosted-document RAG. All verified live on grok-4.5.
|
|
371
383
|
|
|
@@ -486,7 +498,7 @@ runs as a one-agent syndicate through `runSyndicateTurn`.
|
|
|
486
498
|
|
|
487
499
|
Reasoning/thinking: scratchpads from every provider are surfaced as
|
|
488
500
|
dimmed THINKING output and kept out of session history. On Claude, any
|
|
489
|
-
`reasoning:` other than `none` (or the
|
|
501
|
+
`reasoning:` other than `none` (or the deprecated
|
|
490
502
|
`generateContentConfig.thinkingConfig.thinkingBudget`) enables Anthropic
|
|
491
503
|
extended thinking, tools included: the signed thinking blocks ride on the
|
|
492
504
|
response's parts as `providerState` and are replayed verbatim within the
|
|
@@ -787,12 +799,24 @@ without it. A message that is not an answer gets the same request back,
|
|
|
787
799
|
without a model call. The paused call and its arguments are pinned, so an
|
|
788
800
|
approval cannot run a different call.
|
|
789
801
|
|
|
790
|
-
Gates are allowed on the orchestrator
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
802
|
+
Gates are allowed on the orchestrator, on the subagents of a plan-dispatch
|
|
803
|
+
syndicate, on the agent nodes of a workflow (§6), and on a delegated
|
|
804
|
+
subagent, at any depth of nested delegate syndicates
|
|
805
|
+
([ADR 0110](./wiki/decisions/0110-pauses-inside-delegated-subagents-reach-the-turn.md)).
|
|
806
|
+
A delegated subagent's gate pauses the whole turn: the call that reached it
|
|
807
|
+
stays open, the request carries `path` (the agents from the turn's own down
|
|
808
|
+
to the one that asked, e.g. `["Desk", "Mailer"]`, in the data part and in
|
|
809
|
+
`approval.path`), and the decision goes back down to that subagent, which
|
|
810
|
+
runs or refuses the call and finishes before its caller continues. The same
|
|
811
|
+
holds inside nested syndicates
|
|
812
|
+
([ADR 0111](./wiki/decisions/0111-pauses-inside-nested-syndicates.md)): a
|
|
813
|
+
nested delegate syndicate's gates anywhere in it, a nested dispatch
|
|
814
|
+
syndicate's on its classifier (its routes never run when it is nested, so a
|
|
815
|
+
gate on one is a load error), and a nested workflow's gated agent nodes and
|
|
816
|
+
`ask_user` nodes when the workflow is a delegated subagent; the path then
|
|
817
|
+
ends at the node, e.g. `["Desk", "Writer", "Send"]`. A gate or `ask_user`
|
|
818
|
+
node in a workflow run as a dispatch route or as another workflow's node is
|
|
819
|
+
still a load error, as are skill scripts on a delegated subagent. Only function tools from the registry can be gated, not MCP tools
|
|
796
820
|
or native-search sentinels. In code, `runSyndicateTurn` returns
|
|
797
821
|
`status: 'input-required'` with `approval`, and the next turn's part
|
|
798
822
|
`approvalResponsePart(approval.id, approved)` answers it.
|
|
@@ -819,12 +843,14 @@ call's result, and the agent resumes its own tool loop where it asked. A
|
|
|
819
843
|
workflow's `ask_user` node (§6, Workflows) publishes the same data part and
|
|
820
844
|
is answered the same way, so a client handles both alike.
|
|
821
845
|
|
|
822
|
-
`ask_user` is allowed where an approval gate is: the orchestrator
|
|
846
|
+
`ask_user` is allowed where an approval gate is: the orchestrator, the
|
|
823
847
|
subagents of a plan-dispatch syndicate (a dispatch turn that answers goes
|
|
824
|
-
straight back to the route that asked, without the classifier)
|
|
825
|
-
subagent
|
|
848
|
+
straight back to the route that asked, without the classifier), and a
|
|
849
|
+
delegated subagent, whose question carries `path` and whose answer goes back
|
|
850
|
+
down to it (ADR 0110). A workflow node listing it is a load error. In code,
|
|
826
851
|
`runSyndicateTurn` returns `status: 'input-required'` with `input`
|
|
827
|
-
(`node`, `message`, `payload
|
|
852
|
+
(`node`, `message`, `payload`, and `path` for a delegated subagent's
|
|
853
|
+
question), and the next message's text answers it.
|
|
828
854
|
|
|
829
855
|
#### OAuth consent for tools
|
|
830
856
|
|
|
@@ -877,6 +903,32 @@ one replica, or route the callback to the instance that paused the call. In
|
|
|
877
903
|
code, `runSyndicateTurn({ …, toolCredentials })` returns
|
|
878
904
|
`status: 'input-required'` with `consent`.
|
|
879
905
|
|
|
906
|
+
**From the environment (`melchizedek-serve`).** The server binary builds the
|
|
907
|
+
store and the consent step itself, with the clients taken from the grants the
|
|
908
|
+
served syndicate files declare (`auth.oauth2`, `mcp_auth`; a registry row may
|
|
909
|
+
name a provider they declare, never define one):
|
|
910
|
+
|
|
911
|
+
| Variable | Shape | What it does |
|
|
912
|
+
|---|---|---|
|
|
913
|
+
| `MELCHIZEDEK_CREDENTIAL_KEY` | 32 random bytes, base64 or 64 hex (`openssl rand -base64 32`) | Seals each user's tokens. Unset: no store, and an authorization-code tool answers `unavailable`. Rows live in Postgres with `DATABASE_URL`, else in process memory |
|
|
914
|
+
| `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
|
+
| `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
|
+
| `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]` |
|
|
917
|
+
|
|
918
|
+
**The host allowlist** ([ADR 0114](./wiki/decisions/0114-oauth-tokens-go-only-to-hosts-the-operator-binds.md))
|
|
919
|
+
is the operator's binding, never the YAML's: `MELCHIZEDEK_OAUTH_HOSTS`, or
|
|
920
|
+
`createA2AApp({ oauthHosts: { tracker: ['api.tracker.example.com'] } })`,
|
|
921
|
+
which wins. Unset, an `authorization_code` grant is refused (a user's token
|
|
922
|
+
never goes to a host nobody bound) and a `client_credentials` grant is
|
|
923
|
+
allowed (its secret is the operator's, as `bearer_env`'s is). Set, it is the
|
|
924
|
+
whole list: every grant's provider must be on it, and its API or MCP server,
|
|
925
|
+
token endpoint and authorization endpoint must be that provider's hosts. A
|
|
926
|
+
syndicate that breaks the rule is refused when the server loads it to serve,
|
|
927
|
+
again when its tools compile, and every call checks the host it sends a
|
|
928
|
+
token to. The server refuses to start on a malformed key, allowlist or
|
|
929
|
+
redirect URI, or on a redirect URI without a key; `npm run doctor` reports
|
|
930
|
+
each of these, and a declared grant with no key or redirect URI, by name.
|
|
931
|
+
|
|
880
932
|
#### Limits
|
|
881
933
|
|
|
882
934
|
| Setting | Default |
|
|
@@ -1056,7 +1108,7 @@ user:
|
|
|
1056
1108
|
| | DELEGATE (default) | PLAN-DISPATCH (`dispatch:` present) |
|
|
1057
1109
|
|---|---|---|
|
|
1058
1110
|
| Subagents are | tools on the orchestrator | plain configs the server selects from |
|
|
1059
|
-
| Orchestrator holds | subagent tools, no `
|
|
1111
|
+
| Orchestrator holds | subagent tools, no `output.schema` | an `output.schema`, no subagent tools |
|
|
1060
1112
|
| Routing decision is | implicit in which tool it calls | an explicit value in code |
|
|
1061
1113
|
| The final answer comes from | the orchestrator re-emitting the answer | **the specialist itself** |
|
|
1062
1114
|
| LLM calls per request | classify + specialist + relay | classify + specialist |
|
|
@@ -1070,14 +1122,14 @@ orchestrator:
|
|
|
1070
1122
|
model: "gemini-3.5-flash-lite"
|
|
1071
1123
|
instruction: |
|
|
1072
1124
|
Name exactly ONE specialist for this message. ...
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1125
|
+
output:
|
|
1126
|
+
mime: "application/json"
|
|
1127
|
+
schema: # the router's schema; dispatch gives it no subagent tools
|
|
1128
|
+
type: "OBJECT"
|
|
1129
|
+
properties:
|
|
1130
|
+
route: { type: "STRING", description: "Exact specialist name" }
|
|
1131
|
+
reason: { type: "STRING", description: "≤8 plain words for the waiting user" }
|
|
1132
|
+
required: ["route"]
|
|
1081
1133
|
```
|
|
1082
1134
|
|
|
1083
1135
|
**Why it exists.** In DELEGATE mode the orchestrator receives the
|
|
@@ -1090,13 +1142,18 @@ Plan-dispatch has no relay turn to fail, and the classifier's output
|
|
|
1090
1142
|
shrinks from a whole relayed answer to ~15 tokens of JSON.
|
|
1091
1143
|
|
|
1092
1144
|
**Why the classifier is tool-less.** An agent that holds an
|
|
1093
|
-
`
|
|
1094
|
-
|
|
1095
|
-
whose header also tells how an orchestrator holding both deadlocked on
|
|
1096
|
-
ADK before 1.0.0).
|
|
1097
|
-
That constraint shapes the method: the classifier is a leaf, and the
|
|
1145
|
+
`output.schema` ends its turn on that JSON. The router's JSON names a
|
|
1146
|
+
route for code to run, so the router holds no subagent tools, and the
|
|
1098
1147
|
hand-off happens in code, where it can be logged, traced, and streamed
|
|
1099
|
-
to the user as progress.
|
|
1148
|
+
to the user as progress. This is a choice of method, not a limit of the
|
|
1149
|
+
engine: an orchestrator may hold an `output.schema` beside its subagents,
|
|
1150
|
+
delegate, and answer in the schema itself
|
|
1151
|
+
([ADR 0109](./wiki/decisions/0109-structured-output-beside-tools.md));
|
|
1152
|
+
under ADK, before 1.0.0, an orchestrator holding both deadlocked (see
|
|
1153
|
+
`config/agents/examples/critic.yaml`'s header). Choose plan-dispatch when
|
|
1154
|
+
the answer is the specialist's, and a schema on the orchestrator when the
|
|
1155
|
+
answer is its own structured judgment of what its team returned
|
|
1156
|
+
(`config/agents/examples/structured_critic.yaml`).
|
|
1100
1157
|
|
|
1101
1158
|
**Sessions.** Every *route* runs in the shared `<contextId>` session, so
|
|
1102
1159
|
one transcript accumulates across routes and long-term memory ingests
|
|
@@ -1190,7 +1247,7 @@ name is an agent or a declared node; a list of names fans out (after one
|
|
|
1190
1247
|
node) or fans in (before one node); a map `{ <route>: <node or nodes>,
|
|
1191
1248
|
default: <node> }` ends a chain and routes on the output of the node
|
|
1192
1249
|
before it — the `route_key` property of a JSON output (an agent with an
|
|
1193
|
-
`
|
|
1250
|
+
`output.schema`), else the trimmed text — with `default` catching what no
|
|
1194
1251
|
key matched. Every node receives the previous node's output as its
|
|
1195
1252
|
message; a join hands on `{ <predecessor>: <output>, … }`.
|
|
1196
1253
|
|
|
@@ -1246,8 +1303,11 @@ DELEGATE subagent its last output is the tool's answer; as a plan-dispatch
|
|
|
1246
1303
|
route it is the turn's answer, and the conversation keeps the message and
|
|
1247
1304
|
that answer; as a node of another workflow it is the node's output. The
|
|
1248
1305
|
graph's events are kept in the entry's own session, as for any subagent.
|
|
1249
|
-
|
|
1250
|
-
|
|
1306
|
+
As a DELEGATE subagent, an `ask_user` node or a gated agent node inside it
|
|
1307
|
+
pauses the whole turn, and the answer resumes the graph
|
|
1308
|
+
([ADR 0111](./wiki/decisions/0111-pauses-inside-nested-syndicates.md)). As a
|
|
1309
|
+
route or a node, a pause inside it cannot reach the turn yet, so an
|
|
1310
|
+
`ask_user` node or a gate there is refused by name, as is a `map` over one.
|
|
1251
1311
|
|
|
1252
1312
|
**Not yet.** Remote `a2a_agent_url` subagents are refused inside a
|
|
1253
1313
|
workflow by the schema. An `ask_user` tool on a node agent is refused
|
package/README.md
CHANGED
|
@@ -173,6 +173,7 @@ Configurations live in `config/agents/examples/` and each demonstrates a differe
|
|
|
173
173
|
| `tutor.yaml` | Single local agent (Ollama); the anatomy of an instruction block | [1.03 · Agent Design](https://lyceumagents.com/curriculum/agent-design/) |
|
|
174
174
|
| `council.yaml` | Advocate/skeptic council weighed by an orchestrator (local) | [1.05 · Workflows & Voice](https://lyceumagents.com/curriculum/workflows-and-voice/) |
|
|
175
175
|
| `critic.yaml` | Drafter → Critic loop that re-drafts until a parsed confidence score clears the bar | [1.04 · Testing & Refinement](https://lyceumagents.com/curriculum/testing-and-refinement/) |
|
|
176
|
+
| `structured_critic.yaml` | The critic as one agent: it delegates to a Drafter, then answers in its own JSON schema (Claude) | — |
|
|
176
177
|
| `delegation.yaml` | Intent routing to specialists via agent descriptions | [1.05 · Workflows & Voice](https://lyceumagents.com/curriculum/workflows-and-voice/) |
|
|
177
178
|
| `hierarchical.yaml` | Goal decomposition into sequential research and writing stages, then a checked merge | [1.05 · Workflows & Voice](https://lyceumagents.com/curriculum/workflows-and-voice/) |
|
|
178
179
|
| `style_council.yaml` | Three stylists with identical knowledge and different voices, behind one router | [1.05 · Workflows & Voice](https://lyceumagents.com/curriculum/workflows-and-voice/) |
|
package/config/agents/README.md
CHANGED
|
@@ -17,8 +17,8 @@ and it is loadable by filename everywhere (`npm run chat:syndicate --
|
|
|
17
17
|
|
|
18
18
|
Everything under `examples/` is a **starter pack**: working, tested
|
|
19
19
|
syndicates that demonstrate the engine's patterns (synthesis, delegation,
|
|
20
|
-
critic loops,
|
|
21
|
-
gardeners). They are teaching material, not product — copy them, gut them,
|
|
20
|
+
critic loops, an orchestrator answering in its own JSON schema,
|
|
21
|
+
hierarchies, MCP tools, local open-weight models, the wiki gardeners). They are teaching material, not product — copy them, gut them,
|
|
22
22
|
delete the whole directory; the engine runs fine without them.
|
|
23
23
|
|
|
24
24
|
## `templates/` — the production templates
|