melchizedek-agents 0.16.2 → 0.18.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.
Files changed (100) hide show
  1. package/.env.example +40 -3
  2. package/AGENT_SETUP.md +4 -2
  3. package/CHANGELOG.md +246 -0
  4. package/DOCUMENTATION.md +311 -15
  5. package/QUICKSTART.md +2 -2
  6. package/README.md +125 -149
  7. package/config/agents/examples/harness.yaml +135 -0
  8. package/config/agents/examples/model_zoo.yaml +23 -9
  9. package/config/agents/examples/pipeline.yaml +120 -0
  10. package/config/agents/examples/specs/open-meteo-forecast.json +77 -0
  11. package/config/agents/examples/specs/open-meteo-geocoding.json +53 -0
  12. package/config/agents/examples/weather.yaml +59 -0
  13. package/config/agents/syndicateSchema.yaml +125 -1
  14. package/db/hardening.sql +4 -3
  15. package/db/migrations/0011_erase_expired.sql +198 -0
  16. package/db/migrations/0012_audit_log.sql +98 -0
  17. package/dist/lib/a2a/app.d.ts +27 -0
  18. package/dist/lib/a2a/app.js +101 -5
  19. package/dist/lib/a2a/executor.d.ts +34 -4
  20. package/dist/lib/a2a/executor.js +109 -9
  21. package/dist/lib/compile.d.ts +41 -3
  22. package/dist/lib/compile.js +162 -17
  23. package/dist/lib/config.d.ts +24 -0
  24. package/dist/lib/config.js +24 -0
  25. package/dist/lib/doctor.js +17 -4
  26. package/dist/lib/index.d.ts +2 -0
  27. package/dist/lib/index.js +1 -0
  28. package/dist/lib/loadEnv.d.ts +5 -0
  29. package/dist/lib/loadEnv.js +7 -0
  30. package/dist/lib/loadSyndicate.d.ts +50 -0
  31. package/dist/lib/loadSyndicate.js +19 -3
  32. package/dist/lib/memory/erase.d.ts +5 -0
  33. package/dist/lib/memory/erase.js +5 -0
  34. package/dist/lib/memory/providers.d.ts +27 -0
  35. package/dist/lib/memory/providers.js +28 -0
  36. package/dist/lib/models/capabilities.js +10 -0
  37. package/dist/lib/models/endpoints.d.ts +1 -1
  38. package/dist/lib/models/endpoints.js +4 -1
  39. package/dist/lib/models/fallback.d.ts +41 -0
  40. package/dist/lib/models/fallback.js +105 -0
  41. package/dist/lib/models/gateway.d.ts +2 -2
  42. package/dist/lib/models/gateway.js +4 -4
  43. package/dist/lib/models/kimiLlm.d.ts +123 -0
  44. package/dist/lib/models/kimiLlm.js +171 -0
  45. package/dist/lib/models/providerMap.d.ts +2 -2
  46. package/dist/lib/models/providerMap.js +4 -1
  47. package/dist/lib/models/registry.d.ts +1 -1
  48. package/dist/lib/models/registry.js +11 -3
  49. package/dist/lib/net/redirects.d.ts +14 -0
  50. package/dist/lib/net/redirects.js +94 -0
  51. package/dist/lib/observability/audit.d.ts +21 -0
  52. package/dist/lib/observability/audit.js +39 -0
  53. package/dist/lib/observability/metrics.d.ts +6 -0
  54. package/dist/lib/observability/tracer.d.ts +14 -1
  55. package/dist/lib/observability/tracer.js +16 -2
  56. package/dist/lib/persistence/supabaseProvider.d.ts +2 -6
  57. package/dist/lib/persistence/supabaseProvider.js +2 -31
  58. package/dist/lib/runtime/questions.d.ts +55 -0
  59. package/dist/lib/runtime/questions.js +107 -0
  60. package/dist/lib/runtime/syndicateTurn.d.ts +45 -5
  61. package/dist/lib/runtime/syndicateTurn.js +165 -17
  62. package/dist/lib/storage/postgres/index.d.ts +9 -0
  63. package/dist/lib/storage/postgres/index.js +13 -0
  64. package/dist/lib/storage/rlsStatus.d.ts +45 -0
  65. package/dist/lib/storage/rlsStatus.js +69 -0
  66. package/dist/lib/syndicateSchema.d.ts +177 -0
  67. package/dist/lib/syndicateSchema.js +343 -3
  68. package/dist/lib/toolRegistry.js +9 -0
  69. package/dist/lib/tools/mcpToolFactory.d.ts +26 -0
  70. package/dist/lib/tools/mcpToolFactory.js +61 -4
  71. package/dist/lib/tools/openapiTools.d.ts +52 -0
  72. package/dist/lib/tools/openapiTools.js +225 -0
  73. package/dist/lib/tools/skillToolset.d.ts +135 -0
  74. package/dist/lib/tools/skillToolset.js +247 -0
  75. package/dist/lib/tools/urlContextTool.d.ts +29 -0
  76. package/dist/lib/tools/urlContextTool.js +39 -0
  77. package/dist/lib/workflow.d.ts +72 -0
  78. package/dist/lib/workflow.js +180 -0
  79. package/dist/lib/workflowConfig.d.ts +95 -0
  80. package/dist/lib/workflowConfig.js +93 -0
  81. package/dist/scripts/a2a_server.js +51 -8
  82. package/dist/scripts/db.d.ts +9 -0
  83. package/dist/scripts/db.js +30 -15
  84. package/dist/scripts/syndicate_chat.js +39 -5
  85. package/package.json +6 -3
  86. package/skills/briefs/_shared.md +1 -1
  87. package/skills/briefs/melchizedek-author.md +8 -4
  88. package/skills/briefs/melchizedek-memory.md +1 -1
  89. package/skills/briefs/melchizedek-models.md +1 -1
  90. package/skills/briefs/melchizedek-scribe.md +4 -4
  91. package/skills/briefs/melchizedek-serve.md +1 -1
  92. package/skills/briefs/melchizedek.md +4 -1
  93. package/skills/melchizedek/SKILL.md +4 -1
  94. package/skills/melchizedek-author/SKILL.md +7 -3
  95. package/skills/melchizedek-memory/SKILL.md +1 -1
  96. package/skills/melchizedek-models/SKILL.md +1 -1
  97. package/skills/melchizedek-scribe/SKILL.md +3 -3
  98. package/skills/melchizedek-serve/SKILL.md +1 -1
  99. /package/skills/melchizedek-author/{templates → assets}/minimal.yaml +0 -0
  100. /package/skills/melchizedek-scribe/{templates → assets}/brief.md +0 -0
package/.env.example CHANGED
@@ -25,6 +25,13 @@ GOOGLE_GENAI_API_KEY=
25
25
  # turn it on for local development — e.g. the librarian demo (npm run mcp:demo).
26
26
  ALLOW_PRIVATE_MCP=false
27
27
 
28
+ # ── Optional: OpenAPI tools (an agent's `openapi:` list) ──────────────────────
29
+ # ALLOW_PRIVATE_OPENAPI: set to "true" to let an OpenAPI tool call a localhost /
30
+ # private / link-local server. Off by default as an SSRF guard. Credentials an
31
+ # `openapi:` entry names (auth.bearer_env, auth.api_key.env) are read from this
32
+ # file or the environment, never written in YAML.
33
+ # ALLOW_PRIVATE_OPENAPI=false
34
+
28
35
  # ── Optional: Anthropic / Claude ──────────────────────────────────────────────
29
36
  # Required only when using claude-* model identifiers in syndicate YAML configs.
30
37
  # Get your key at: https://console.anthropic.com
@@ -54,6 +61,16 @@ OPENAI_API_KEY=
54
61
  # Get your key at: https://console.x.ai
55
62
  XAI_API_KEY=
56
63
 
64
+ # ── Optional: Moonshot AI / Kimi ─────────────────────────────────────────────
65
+ # Required only when using kimi-* model identifiers in YAML (kimi-k3 is the
66
+ # flagship; kimi-k2.6 the cheaper tier; kimi-k2.7-code[-highspeed] for code).
67
+ # Get your key at: https://platform.moonshot.ai (the .cn console serves
68
+ # mainland China). Without it, MODEL_GATEWAY serves kimi-* ids as
69
+ # moonshotai/kimi-k3 and friends. web_search is never native on Kimi
70
+ # (Moonshot's built-in search retires 2026-10-20); use web_extract.
71
+ MOONSHOT_API_KEY=
72
+ # MOONSHOT_BASE_URL= # a proxy speaking the same chat-completions dialect
73
+
57
74
  # XAI_COLLECTION_IDS: comma-separated xAI Collection ids searched by the
58
75
  # `collections_search` tool (grok-* agents only). Create collections and
59
76
  # upload documents at console.x.ai. Declared with no ids set, the tool is
@@ -112,6 +129,8 @@ A2A_SERVER_SECRET=
112
129
  # Used when compiling the AgentCard that advertises this agent to A2A clients.
113
130
  # Defaults to http://localhost:4000 if not set.
114
131
  # Only needed for cloud deployments where the server has a real public URL.
132
+ # With it set, the server also refuses to start until A2A_AUTH,
133
+ # A2A_SERVED_AGENTS and A2A_TRUST_PROXY are set explicitly (ADR 0039).
115
134
  PUBLIC_URL=
116
135
 
117
136
  # ── A2A server: identity and billing (all optional; ADR 0017, 0025) ─────────
@@ -124,6 +143,7 @@ PUBLIC_URL=
124
143
  # jwt a JWT from your identity provider; the scope comes from its claims.
125
144
  # header an authenticating gateway in front sets the user header; requires
126
145
  # A2A_SERVER_SECRET, which only the gateway holds.
146
+ # Required with PUBLIC_URL; prefer callers or jwt there.
127
147
  # A2A_AUTH=secret
128
148
  # callers: `name:sha256[:scope]` entries separated by ';'. The config holds only
129
149
  # each token's SHA-256. Mint one: npx melchizedek-serve --new-caller <name>
@@ -154,8 +174,8 @@ PUBLIC_URL=
154
174
  # The server refuses a database behind the migrations it ships (run
155
175
  # `npx melchizedek-db apply`); this starts it anyway.
156
176
  # ALLOW_SCHEMA_MISMATCH=false
157
- # Only these agent ids are served at /<agentId>/... (comma list). Unset = any
158
- # syndicate the loader can find.
177
+ # Only these agent ids are served at /<agentId>/... (comma list). Unset, or *,
178
+ # = any syndicate the loader can find. Required with PUBLIC_URL (* allowed).
159
179
  # A2A_SERVED_AGENTS=support_triage,research_brief
160
180
  # Per-task wall-clock budget (ms; 0 = none). Default 15 minutes.
161
181
  # A2A_TASK_TIMEOUT_MS=900000
@@ -174,11 +194,23 @@ PUBLIC_URL=
174
194
  # A2A_RATE_LIMIT_MAX=60
175
195
  # A2A_RATE_LIMIT_WINDOW_MS=900000
176
196
  # A2A_AUTH_FAILURE_MAX=30
177
- # Express trust-proxy: proxy hop count, true/false, or a subnet list. Default 1.
197
+ # Express trust-proxy: proxy hop count, true/false, or a subnet list. Default
198
+ # false (no proxy is trusted). Required with PUBLIC_URL: 1 behind one load
199
+ # balancer, false when clients connect directly.
178
200
  # A2A_TRUST_PROXY=1
201
+ # Concurrent tasks for one end user (scope), and for one caller. 0 = unlimited.
202
+ # A2A_MAX_CONCURRENT_PER_SCOPE=4
203
+ # A2A_MAX_CONCURRENT_PER_CALLER=0
204
+
205
+ # OpenAPI tools: the exact environment variables an `auth:` may name. Unset, any
206
+ # variable but the framework's own settings (this file's) is allowed.
207
+ # OPENAPI_CREDENTIAL_ENVS=WEATHER_API_KEY,CRM_TOKEN
179
208
  # A2A_BODY_LIMIT=1mb
180
209
  # How long SIGTERM waits for running tasks before canceling them.
181
210
  # A2A_SHUTDOWN_GRACE_MS=25000
211
+ # Of that, how long /readyz fails while requests are still served, so a load
212
+ # balancer deregisters this instance before the listener closes.
213
+ # A2A_SHUTDOWN_DELAY_MS=5000
182
214
  # Daily budgets per UTC day (ADR 0026), per caller and per scope (one end
183
215
  # user), on tasks, model calls and tokens. A task over budget ends `rejected`
184
216
  # with the reason. Counted in Postgres/Supabase (db/migrations/0004) when
@@ -262,6 +294,11 @@ WIKI_AGENT_MODEL=
262
294
  # retried with jittered backoff, honouring Retry-After up to 60 s, before any
263
295
  # output is produced. Total attempts per model call; 1 turns retries off.
264
296
  # MODEL_RETRY_MAX_ATTEMPTS=3
297
+ # For agents with a fallback_model: after this many consecutive provider-side
298
+ # failures a provider's circuit opens and those agents answer from their
299
+ # fallback for the cooldown. 0 disables the breaker.
300
+ # MODEL_BREAKER_THRESHOLD=5
301
+ # MODEL_BREAKER_COOLDOWN_MS=30000
265
302
  # Per-attempt timeout for xAI (Grok) calls, ms; floor 120000.
266
303
  # XAI_TIMEOUT_MS=600000
267
304
 
package/AGENT_SETUP.md CHANGED
@@ -70,7 +70,8 @@ After the agent reports back:
70
70
  of them — set only what you'll declare in YAML:
71
71
  `ANTHROPIC_API_KEY` for `claude-*` (https://console.anthropic.com),
72
72
  `OPENAI_API_KEY` for `gpt-*` (https://platform.openai.com/api-keys),
73
- `XAI_API_KEY` for `grok-*` (https://console.x.ai). Not sure which
73
+ `XAI_API_KEY` for `grok-*` (https://console.x.ai),
74
+ `MOONSHOT_API_KEY` for `kimi-*` (https://platform.moonshot.ai). Not sure which
74
75
  you need? `npm run doctor` reads every syndicate and prints what is
75
76
  ready, what is blocked, and which variable unlocks what — read-only,
76
77
  no key value shown. Verify the whole multi-provider surface live with
@@ -192,7 +193,8 @@ user id.
192
193
 
193
194
  Deploying the server itself (Heroku, Fly, a VPS) needs `PUBLIC_URL` set
194
195
  — at which point the server refuses to start without the bearer secret,
195
- by design. Run `db/hardening.sql` before real user data arrives, and
196
+ and until `A2A_AUTH`, `A2A_SERVED_AGENTS` and `A2A_TRUST_PROXY` are set
197
+ explicitly, by design. Run `db/hardening.sql` before real user data arrives, and
196
198
  read the deployment notes in `lib/memory/README.md` end to end. The
197
199
  patient in module 2.02's specimen would expect no less of you.
198
200
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,252 @@ Consumers of the package read this file; it records changes to the
4
4
  **published API surface** (the exports map in `package.json`, the bins,
5
5
  the starter pack and the templates), not the repo's full history.
6
6
 
7
+ ## 0.18.0 — 2026-10-06
8
+
9
+ ### Breaking — read before upgrading
10
+
11
+ - **A public server states its posture (ADR 0039).** With `PUBLIC_URL` set,
12
+ the server refuses to start until all three are set explicitly; the boot
13
+ message names each one missing:
14
+ - `A2A_AUTH`: `callers` or `jwt` (recommended), `header`, or `secret`
15
+ (one shared secret: any holder can act as any user via `X-User-Id`).
16
+ - `A2A_SERVED_AGENTS`: the agent ids to serve, or `*` for every agent.
17
+ - `A2A_TRUST_PROXY`: proxies in front (`1` behind one load balancer), or
18
+ `false` when clients connect directly.
19
+ - **`A2A_TRUST_PROXY` defaults to `false`** (was 1): a server reached
20
+ directly no longer takes the client's `X-Forwarded-For` as its address.
21
+ - **A turn stops at 50 model calls** when the YAML sets no `max_steps`
22
+ (`DEFAULT_MAX_STEPS`). A syndicate that needs more sets `max_steps`.
23
+ - **One end user runs at most 4 tasks at once** (`A2A_MAX_CONCURRENT_PER_SCOPE`,
24
+ `maxConcurrentPerScope`; 0 = unlimited). `A2A_MAX_CONCURRENT_PER_CALLER`
25
+ adds a per-caller cap, off by default. The refusal names the cap.
26
+
27
+ - **The `@google/adk` peer range is `~2.2.0`** (was `^2.2.0`): the tested
28
+ minor. A new ADK minor can bring a second `@google/genai` copy, whose
29
+ response shapes the framework pins; the range widens when a release is
30
+ tested against it. ADK 2.2.0 is the latest published, so no install
31
+ changes today.
32
+
33
+ - **An OpenAPI `auth` may not name one of the framework's own settings**
34
+ (the database URL, a provider key, an `A2A_` secret: anything under the
35
+ framework's prefixes). The YAML chooses the host too, so this was a way
36
+ to send a secret anywhere. `OPENAPI_CREDENTIAL_ENVS` makes the rule an
37
+ exact allowlist. ADR 0041.
38
+ - **MCP tool descriptions are cut at 1,000 characters and results at
39
+ 20,000**, each marked where it was cut.
40
+
41
+ ### Changes
42
+
43
+ - **An append-only audit trail** (`melchizedek_audit`, migration
44
+ `0012_audit_log`; ADR 0042). Every failed authentication, task outcome
45
+ and erasure is recorded with the caller, the source address, the agent
46
+ and task ids and a scope hash, and no content. A trigger refuses UPDATE
47
+ and DELETE; `melchizedek_prune_audit(days)` is the retention path.
48
+ `postgresStorage` supplies the sink (`audit`), and `createA2AApp` takes
49
+ an `audit` option for any other. Run `npx melchizedek-db apply`.
50
+ - **The server says where memory sends transcripts.** Long-term memory's
51
+ extraction and embedding providers default to Gemini whatever the agents
52
+ run on; a syndicate whose agents use other providers now gets one boot
53
+ warning naming the providers memory reaches and the variables that move
54
+ it (`memoryDestinations`, `memoryCrossesProviders`). DOCUMENTATION gains
55
+ "Where your data goes": every destination, its retention, and the setting
56
+ that changes it.
57
+ - **A request can be followed across systems.** Every task record
58
+ (`onTaskEnd`, and the JSON log line) carries `taskId` and `traceId`; the
59
+ trace id is the one the spans and the ledger row carry. A request with a
60
+ W3C `traceparent` header is linked from the turn's root span
61
+ (`caller.trace_id`, and `callerTraceId` on the record); the turn keeps
62
+ its own trace id. `validTraceparent` and `callerSpanContext` are exported
63
+ from `melchizedek-agents/observability/tracer`. ADR 0043.
64
+ - **`fallback_model:`, an agent key, and a per-provider circuit breaker.**
65
+ When an agent's model fails provider-side (5xx, 429, a reset, after its
66
+ retries) before producing output, its `fallback_model` answers; after
67
+ `MODEL_BREAKER_THRESHOLD` consecutive failures (default 5) the provider
68
+ is skipped for `MODEL_BREAKER_COOLDOWN_MS` (30 s) by agents that have a
69
+ fallback. A 4xx, a canceled turn and a half-finished stream are never
70
+ redirected. ADR 0044.
71
+ - **`mcp_tools:`, an agent key**: the MCP server's tools the agent may use;
72
+ any other tool the server lists is not exposed. On a dispatch route,
73
+ `require_approval` may name a listed MCP tool, so a write waits for a
74
+ person. `closeMcpConnections()` closes every open MCP connection.
75
+ - **`melchizedek-db apply` is one transaction under an advisory lock.** The
76
+ whole install (migrations, hardening, telemetry) runs as one psql
77
+ `--single-transaction` script that takes `pg_advisory_xact_lock` first: a
78
+ failure anywhere rolls everything back, and two applies started together
79
+ run one after the other. The output still names each file.
80
+ - `TaskLimiter` takes per-scope and per-caller limits and says which one
81
+ refused (`refusal()`); `createA2AApp` takes `maxConcurrentPerScope` and
82
+ `maxConcurrentPerCaller`; `DEFAULT_MAX_CONCURRENT_PER_SCOPE` is exported
83
+ from `melchizedek-agents/a2a` and `DEFAULT_MAX_STEPS` from
84
+ `melchizedek-agents/config`.
85
+
86
+ ## 0.17.1 — 2026-10-06
87
+
88
+ - **Fix: a rolling deploy no longer refuses requests.** On SIGTERM the
89
+ server closed its listener at once, while the load balancer was still
90
+ sending it traffic. It now fails `/readyz` first and keeps serving for
91
+ `A2A_SHUTDOWN_DELAY_MS` (default 5 s), then closes and drains. The delay
92
+ counts inside `A2A_SHUTDOWN_GRACE_MS`, so a stop takes no longer than
93
+ before. `A2AApp.markUnready()` does the same for an embedding app.
94
+ ADR 0038.
95
+ - **`/readyz` fails while durable storage does not answer** (503,
96
+ `reason: 'storage'`), so an instance that lost its database leaves the
97
+ rotation instead of failing turns. The cause is logged once per outage;
98
+ the answer is cached for 2 s and concurrent probes share one read, so the
99
+ unauthenticated route cannot be used to flood the database.
100
+ - **Fix: the MCP client no longer follows redirects past the SSRF guard.**
101
+ The SSE transport used the default fetch, which follows redirects; it now
102
+ uses `mcpFetch`, under the rule 0.17.0 gave OpenAPI tools: same-origin hops
103
+ get the server's check, cross-origin hops the full guard and no
104
+ credential header.
105
+ - **Fix: a failed MCP connect closes its transport.** The SSE stream's
106
+ reconnect timer kept running after `createMcpTools` gave up, for the life
107
+ of the process.
108
+ - **`MELCHIZEDEK_DOTENV=off`** makes every bin ignore `.env` files and run
109
+ on its environment alone.
110
+
111
+ ## 0.17.0 — 2026-10-06
112
+
113
+ ### Read before upgrading
114
+
115
+ - **Run `npx melchizedek-db apply`.** Migration `0011_erase_expired` ships
116
+ with this version, and the server refuses to start against a database
117
+ behind the shipped migrations.
118
+ - **A public Supabase deployment on `DATABASE_URL` must be hardened.** The
119
+ boot-time RLS check now runs on that path; `db/hardening.sql` (which
120
+ `melchizedek-db apply` runs) satisfies it, `ALLOW_UNHARDENED_DB=true`
121
+ opts out.
122
+ - **Self-correction is on by default.** Retries count against `max_steps`
123
+ and can raise spend; `retries: { model_errors: 0, tool_errors: 0 }`
124
+ restores the previous behaviour.
125
+
126
+ ### Changes
127
+
128
+ - **Behaviour change: the hardening check runs on the `DATABASE_URL` path.**
129
+ The boot-time RLS check ran only on the deprecated supabase-js path, so a
130
+ deployment that followed the recommended setup was never checked, and
131
+ "fatal on public deployments without it" did not hold. `postgresStorage`
132
+ now supplies `rlsHardening()` and the server applies the same rules
133
+ (`lib/storage/rlsStatus.ts`): with `PUBLIC_URL` set, a Supabase database
134
+ whose `public` tables lack RLS stops the server (set
135
+ `ALLOW_UNHARDENED_DB=true` to accept the risk). A database with no `anon`
136
+ or `authenticated` role, or with the tables in a private schema, passes.
137
+ `createA2AApp`'s `storage` option accepts `rlsHardening` for custom storage.
138
+ - **Fix: OpenAPI tools no longer follow redirects past the SSRF guard.**
139
+ ADK's REST tool calls `fetch`, which follows redirects, and the guard
140
+ checked only the configured server: an allowed API with an open redirect
141
+ could send a call to the cloud metadata service, and an `api_key` header
142
+ went with it. A call now follows redirects one hop at a time
143
+ (`lib/net/redirects.ts`): same-origin hops get the server's own check,
144
+ cross-origin hops the full guard (even with `ALLOW_PRIVATE_OPENAPI`) and
145
+ only content-negotiation headers. A refused hop returns an error to the
146
+ model. Nothing changes for a fetch made outside an OpenAPI tool call.
147
+ - **Fix: `DELETE /memory` erases expired conversations.** A namespace erase
148
+ (the default) found conversations through their live session rows, so once
149
+ a session expired after seven idle days its ledger turns, spans, payloads
150
+ and A2A tasks survived the erase while the response reported success.
151
+ Migration `0011_erase_expired` replaces `melchizedek_erase_scope`: a
152
+ namespace erase now keeps a turn or task only when its conversation is
153
+ still live in another namespace. Run `npx melchizedek-db apply`; the server
154
+ refuses to start against a database behind the shipped migrations.
155
+ - **Moonshot AI (Kimi) is a provider.** `kimi-*` model ids route to a new
156
+ direct adapter (`lib/models/kimiLlm.ts`, chat completions against
157
+ `api.moonshot.ai`), funded by `MOONSHOT_API_KEY` (`MOONSHOT_BASE_URL` for a
158
+ proxy). Tool calling, delegation, strict structured output, streaming,
159
+ images and `reasoning_content` work; native `web_search` is dropped with a
160
+ warning (Moonshot's model-side search retires 2026-10-20), and thinking is
161
+ not carried across a tool loop (K3 re-reasons each step; both stated in
162
+ the capability matrix). `kimi-k3` takes `reasoning_effort` (pinned `high`,
163
+ `DEFAULT_KIMI_REASONING_EFFORT`); K2.x ids take a thinking switch. With no
164
+ direct key the gateway serves the ids as `moonshotai/…`. The doctor,
165
+ `.env.example`, the schema comments and the docs know the provider;
166
+ `model_zoo.yaml` gains a sixth agent, `kimi` on `kimi-k3` at low effort,
167
+ and the Zookeeper names six providers. Cost note: `kimi-k3` is priced
168
+ like Claude Sonnet 4.6 ($3 / $15, thinking billed as output), so
169
+ `kimi-k2.6` is the budget tier and the gateway the cheap route to K3.
170
+ One plain turn verified live on 2026-10-03; tool loops were not.
171
+ - **The doctor's providers line names each provider's own key.** It probed
172
+ every provider past the fourth with a `grok-` id, so Moonshot was reported
173
+ as missing `XAI_API_KEY`.
174
+ - **Behaviour change: self-correction is on by default.** Every turn now runs
175
+ ADK's reflect-and-retry plugins. A model reply ADK marks malformed
176
+ (`MALFORMED_FUNCTION_CALL`) is retried up to twice with guidance instead of
177
+ failing the turn; a tool that throws is answered with structured reflection
178
+ guidance and retried at most three times. Every retry counts against
179
+ `max_steps`. Set `retries: { model_errors: 0, tool_errors: 0 }` at the root
180
+ of a syndicate to restore the previous behaviour. ADR 0034.
181
+ - **`url_context` and `examples:`.** `url_context` lets a Gemini agent read
182
+ the pages at URLs in the conversation, server-side; on other providers it is
183
+ a no-op the doctor reports as dropped. An agent's `examples: [{ input,
184
+ output }]` adds few-shot exchanges to every request (ADK's `ExampleTool`).
185
+ - **Three agent keys from ADK: `code_execution`, `context`, `mode`.**
186
+ `code_execution: gemini` lets a Gemini agent write and run Python in
187
+ Gemini's server-side sandbox (nothing runs on the host). `context:
188
+ { compact_after_tokens, keep_recent_events?, summary_model? }` on a delegate
189
+ orchestrator summarizes earlier turns once a prompt passes the threshold,
190
+ keeping the recent ones verbatim and the full history stored. `mode: task`
191
+ on a workflow node makes its output the arguments of its `finish_task`
192
+ call. The schema places each where it means something. ADR 0033.
193
+ - **`openapi:`: any HTTP API as an agent's tools, from its spec.** An agent
194
+ lists OpenAPI 3 spec files (relative to the syndicate file); every operation
195
+ becomes a tool named from its `operationId`, its parameters the arguments,
196
+ its summary the description. Read-only by default (GET operations only,
197
+ unless `operations` names others); a named operation can be listed under
198
+ `require_approval`. Credentials come from environment variables
199
+ (`auth.bearer_env`, `auth.api_key`), never YAML, and are never stored in
200
+ session state. Every server passes the SSRF guard (literal rules at
201
+ compile, DNS before each call; `ALLOW_PRIVATE_OPENAPI=true` for local
202
+ development); results are capped at 20,000 characters. On ADK's
203
+ `OpenAPIToolset`. Worked example: `config/agents/examples/weather.yaml`
204
+ (`npm run syndicate:weather`), with two keyless Open-Meteo specs. ADR 0032.
205
+ - **`ask_user`: an agent asks the person and waits.** A registry tool,
206
+ `ask_user(question, options?)`, on the orchestrator or a plan-dispatch
207
+ route. Called, it ends the turn `input-required` with `result.input`
208
+ (`node`, `message`, `payload.options`); the next plain-text message on the
209
+ conversation becomes the call's result and the agent resumes its own tool
210
+ loop (a dispatch turn goes straight back to the route that asked). Over A2A
211
+ the task carries an `input_request` data part — the same one a workflow's
212
+ `ask_user` node publishes. A delegated subagent or a workflow node listing
213
+ it is a load error. ADR 0031.
214
+ - **Workflows: the `workflow:` block, the third orchestration method.** A
215
+ syndicate may be a graph: its agents are the nodes, `edges` says what runs
216
+ after what and on which route (a map after a node routes on the `route_key`
217
+ of its JSON output, else its text, with `default`), and `nodes` declares
218
+ what is not an agent — `join` (fan-in), `map` (one run per list item,
219
+ concurrently), `tool` (a registry tool as a node), `ask_user` (a pause: the
220
+ turn ends `input-required` with the question, the next message answers,
221
+ and the node outputs `{ reply, input }`). Any node may carry `retry` and
222
+ `timeout`. Every node receives the previous node's output as its message
223
+ and sees nothing else of the conversation unless its YAML says so. Runs on
224
+ ADK's `Workflow`; `runSyndicateTurn` gains `status: 'input-required'` with
225
+ `input` (the A2A server publishes an `input_request` data part;
226
+ `melchizedek-chat` prints the question and takes the next line);
227
+ `compileWorkflow` and `isWorkflowSyndicate` are exported. Not yet inside a
228
+ workflow: approval gates, skill scripts, remote subagents. Worked example:
229
+ `config/agents/examples/pipeline.yaml` (`npm run syndicate:pipeline`);
230
+ ADR 0030.
231
+ - **Agent Skills in a syndicate: the `skills:` agent key and the Harness.**
232
+ An agent may declare `skills: { dir, scripts?, tools? }`. Every skill's
233
+ frontmatter (name, description) is appended to the agent's instruction at
234
+ compile time, so no turn is spent discovering skills; `load_skill` reads one
235
+ SKILL.md in full with the names of the files it ships, and
236
+ `load_skill_resource` reads one file (`references/`, `assets/`, `scripts/`,
237
+ the open standard's layout). A skill's `allowed-tools` frontmatter unlocks
238
+ the registry tools the YAML lists under `skills.tools`, once that skill is
239
+ loaded. With `scripts: local`, `run_skill_script` runs a skill's own scripts
240
+ on the host, each run only after a person approves it through the same
241
+ pause as `require_approval` (A2A `input-required`; `melchizedek-chat` now
242
+ asks `[y/N]`); the model's own code never executes. Built on ADK's
243
+ `SkillToolset` (`lib/tools/skillToolset.ts`). `config/agents/examples/harness.yaml`
244
+ is the specimen: a generic agent that works from whatever shelf of skills
245
+ `--bind skills_dir=…` points it at (default: this package's own suite).
246
+ - **The suite's files move to the standard `assets/` directory.**
247
+ `melchizedek-author` ships `assets/minimal.yaml` (was `templates/minimal.yaml`)
248
+ and `melchizedek-scribe` ships `assets/brief.md` (was `templates/brief.md`),
249
+ so a harness, this one or any other, can read them as skill resources. A
250
+ skill installed by an earlier release keeps its old path until it is
251
+ reinstalled with `--force`.
252
+
7
253
  ## 0.16.2 — 2026-10-02
8
254
 
9
255
  - **`OTEL_EXPORT_CONTENT=off` also drops library content attributes.** In