melchizedek-agents 0.9.6 → 0.16.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 (246) hide show
  1. package/.env.example +272 -0
  2. package/AGENT_SETUP.md +39 -23
  3. package/CHANGELOG.md +623 -0
  4. package/DOCUMENTATION.md +429 -109
  5. package/QUICKSTART.md +92 -18
  6. package/README.md +45 -5
  7. package/config/agents/README.md +46 -1
  8. package/config/agents/examples/ares.yaml +117 -40
  9. package/config/agents/examples/assistant.yaml +159 -0
  10. package/config/agents/examples/augustin.yaml +154 -51
  11. package/config/agents/examples/cartographers.yaml +91 -23
  12. package/config/agents/examples/claude.yaml +45 -6
  13. package/config/agents/examples/council.yaml +23 -8
  14. package/config/agents/examples/critic.yaml +57 -21
  15. package/config/agents/examples/delegation.yaml +76 -9
  16. package/config/agents/examples/hierarchical.yaml +55 -8
  17. package/config/agents/examples/image_production.yaml +54 -16
  18. package/config/agents/examples/librarian.yaml +70 -14
  19. package/config/agents/examples/model_zoo.yaml +105 -29
  20. package/config/agents/examples/patient_advocate.yaml +102 -34
  21. package/config/agents/examples/research.yaml +250 -0
  22. package/config/agents/examples/scribe.yaml +242 -0
  23. package/config/agents/examples/scriptorium.yaml +87 -24
  24. package/config/agents/examples/style_council.yaml +31 -9
  25. package/config/agents/examples/syndicate.yaml +29 -10
  26. package/config/agents/examples/tutor.yaml +10 -3
  27. package/config/agents/syndicateSchema.yaml +44 -9
  28. package/config/agents/templates/README.md +58 -0
  29. package/config/agents/templates/account_memory.yaml +153 -0
  30. package/config/agents/templates/case_desk.yaml +188 -0
  31. package/config/agents/templates/conversational.yaml +110 -0
  32. package/config/agents/templates/draft_review.yaml +130 -0
  33. package/config/agents/templates/intake_extractor.yaml +152 -0
  34. package/config/agents/templates/research_brief.yaml +169 -0
  35. package/config/agents/templates/research_desk.yaml +152 -0
  36. package/config/agents/templates/review_panel.yaml +188 -0
  37. package/config/agents/templates/support_triage.yaml +273 -0
  38. package/config/agents/templates/systems_operator.yaml +157 -0
  39. package/db/hardening.sql +202 -0
  40. package/db/memory_v2.sql +77 -0
  41. package/db/migrations/0001_base.sql +167 -0
  42. package/db/migrations/0002_erase_scope.sql +172 -0
  43. package/db/migrations/0003_postgres_storage.sql +58 -0
  44. package/db/migrations/0004_usage.sql +94 -0
  45. package/db/migrations/0005_agent_registry.sql +200 -0
  46. package/db/migrations/0006_task_leases.sql +25 -0
  47. package/db/migrations/0007_memory_commit.sql +134 -0
  48. package/db/migrations/0008_memory_retention.sql +37 -0
  49. package/db/migrations/0009_task_queue.sql +51 -0
  50. package/db/migrations/0010_erase_complete.sql +180 -0
  51. package/db/telemetry.sql +401 -0
  52. package/demo/README.md +77 -0
  53. package/demo/a2a_demo.mjs +95 -0
  54. package/dist/lib/a2a/app.d.ts +253 -0
  55. package/dist/lib/a2a/app.js +751 -0
  56. package/dist/lib/a2a/executor.d.ts +167 -0
  57. package/dist/lib/a2a/executor.js +548 -0
  58. package/dist/lib/a2a/identity.d.ts +122 -0
  59. package/dist/lib/a2a/identity.js +254 -0
  60. package/dist/lib/a2a/policy.d.ts +110 -0
  61. package/dist/lib/a2a/policy.js +178 -0
  62. package/dist/lib/a2a/redisLimits.d.ts +26 -0
  63. package/dist/lib/a2a/redisLimits.js +31 -0
  64. package/dist/lib/a2a/remoteAgent.d.ts +91 -0
  65. package/dist/lib/a2a/remoteAgent.js +268 -0
  66. package/dist/lib/a2a/turnLock.d.ts +28 -0
  67. package/dist/lib/a2a/turnLock.js +76 -0
  68. package/dist/lib/compile.d.ts +11 -1
  69. package/dist/lib/compile.js +109 -8
  70. package/dist/lib/config.d.ts +7 -7
  71. package/dist/lib/config.js +7 -7
  72. package/dist/lib/doctor.d.ts +145 -0
  73. package/dist/lib/doctor.js +517 -0
  74. package/dist/lib/grounding.d.ts +2 -2
  75. package/dist/lib/grounding.js +2 -2
  76. package/dist/lib/guards/index.d.ts +37 -0
  77. package/dist/lib/guards/index.js +66 -0
  78. package/dist/lib/guards/science.d.ts +117 -0
  79. package/dist/lib/guards/science.js +444 -0
  80. package/dist/lib/index.d.ts +37 -2
  81. package/dist/lib/index.js +35 -4
  82. package/dist/lib/loadEnv.d.ts +23 -11
  83. package/dist/lib/loadEnv.js +77 -29
  84. package/dist/lib/loadSyndicate.d.ts +97 -5
  85. package/dist/lib/loadSyndicate.js +176 -76
  86. package/dist/lib/memory/erase.d.ts +24 -0
  87. package/dist/lib/memory/erase.js +47 -0
  88. package/dist/lib/memory/namespace.d.ts +37 -0
  89. package/dist/lib/memory/namespace.js +74 -0
  90. package/dist/lib/memory/providers.d.ts +99 -0
  91. package/dist/lib/memory/providers.js +240 -0
  92. package/dist/lib/memory/store.d.ts +74 -0
  93. package/dist/lib/memory/store.js +108 -0
  94. package/dist/lib/memory/supabaseMemoryService.d.ts +69 -9
  95. package/dist/lib/memory/supabaseMemoryService.js +239 -125
  96. package/dist/lib/models/capabilities.d.ts +103 -0
  97. package/dist/lib/models/capabilities.js +291 -0
  98. package/dist/lib/models/claudeLlm.d.ts +10 -1
  99. package/dist/lib/models/claudeLlm.js +57 -27
  100. package/dist/lib/models/endpoints.d.ts +100 -0
  101. package/dist/lib/models/endpoints.js +282 -0
  102. package/dist/lib/models/gateway.d.ts +107 -0
  103. package/dist/lib/models/gateway.js +157 -0
  104. package/dist/lib/models/gatewayLlm.d.ts +57 -0
  105. package/dist/lib/models/gatewayLlm.js +105 -0
  106. package/dist/lib/models/gptLlm.d.ts +51 -2
  107. package/dist/lib/models/gptLlm.js +169 -26
  108. package/dist/lib/models/grokLlm.d.ts +19 -10
  109. package/dist/lib/models/grokLlm.js +29 -14
  110. package/dist/lib/models/ollamaLlm.d.ts +26 -0
  111. package/dist/lib/models/ollamaLlm.js +43 -1
  112. package/dist/lib/models/openAiCompatibleLlm.d.ts +67 -2
  113. package/dist/lib/models/openAiCompatibleLlm.js +200 -37
  114. package/dist/lib/models/providerMap.js +9 -2
  115. package/dist/lib/models/registry.d.ts +42 -5
  116. package/dist/lib/models/registry.js +140 -29
  117. package/dist/lib/models/retry.d.ts +146 -0
  118. package/dist/lib/models/retry.js +305 -0
  119. package/dist/lib/models/schemaNormalize.d.ts +20 -0
  120. package/dist/lib/models/schemaNormalize.js +43 -0
  121. package/dist/lib/net/addressGuard.d.ts +60 -0
  122. package/dist/lib/net/addressGuard.js +216 -0
  123. package/dist/lib/observability/embeddings.d.ts +11 -2
  124. package/dist/lib/observability/embeddings.js +18 -14
  125. package/dist/lib/observability/metrics.d.ts +36 -0
  126. package/dist/lib/observability/metrics.js +86 -0
  127. package/dist/lib/observability/redact.d.ts +29 -0
  128. package/dist/lib/observability/redact.js +102 -0
  129. package/dist/lib/observability/supabaseSpanExporter.d.ts +2 -0
  130. package/dist/lib/observability/supabaseSpanExporter.js +22 -6
  131. package/dist/lib/observability/tracer.d.ts +10 -0
  132. package/dist/lib/observability/tracer.js +70 -3
  133. package/dist/lib/persistence/supabaseProvider.d.ts +11 -0
  134. package/dist/lib/persistence/supabaseProvider.js +32 -6
  135. package/dist/lib/registry.d.ts +87 -0
  136. package/dist/lib/registry.js +146 -0
  137. package/dist/lib/runtime/approvals.d.ts +44 -0
  138. package/dist/lib/runtime/approvals.js +67 -0
  139. package/dist/lib/runtime/syndicateTurn.d.ts +233 -0
  140. package/dist/lib/runtime/syndicateTurn.js +597 -0
  141. package/dist/lib/runtime/turnControl.d.ts +90 -0
  142. package/dist/lib/runtime/turnControl.js +131 -0
  143. package/dist/lib/session/supabaseSessionService.js +1 -1
  144. package/dist/lib/session/transcript.d.ts +9 -1
  145. package/dist/lib/session/transcript.js +15 -7
  146. package/dist/lib/skills.d.ts +50 -0
  147. package/dist/lib/skills.js +138 -0
  148. package/dist/lib/storage/postgres/index.d.ts +98 -0
  149. package/dist/lib/storage/postgres/index.js +146 -0
  150. package/dist/lib/storage/postgres/memoryStore.d.ts +11 -0
  151. package/dist/lib/storage/postgres/memoryStore.js +82 -0
  152. package/dist/lib/storage/postgres/sessionService.d.ts +51 -0
  153. package/dist/lib/storage/postgres/sessionService.js +225 -0
  154. package/dist/lib/storage/postgres/taskQueue.d.ts +22 -0
  155. package/dist/lib/storage/postgres/taskQueue.js +101 -0
  156. package/dist/lib/storage/postgres/taskStore.d.ts +55 -0
  157. package/dist/lib/storage/postgres/taskStore.js +168 -0
  158. package/dist/lib/storage/schema.d.ts +27 -0
  159. package/dist/lib/storage/schema.js +50 -0
  160. package/dist/lib/storage/schemaVersion.d.ts +25 -0
  161. package/dist/lib/storage/schemaVersion.js +46 -0
  162. package/dist/lib/syndicateSchema.d.ts +275 -0
  163. package/dist/lib/syndicateSchema.js +534 -0
  164. package/dist/lib/toolRegistry.d.ts +15 -0
  165. package/dist/lib/toolRegistry.js +59 -10
  166. package/dist/lib/tools/collectionsSearchTool.js +1 -1
  167. package/dist/lib/tools/mcpServe.d.ts +45 -0
  168. package/dist/lib/tools/mcpServe.js +88 -0
  169. package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
  170. package/dist/lib/tools/mcpToolFactory.js +41 -21
  171. package/dist/lib/tools/science/corrections.d.ts +42 -0
  172. package/dist/lib/tools/science/corrections.js +115 -0
  173. package/dist/lib/tools/science/europepmc.d.ts +51 -0
  174. package/dist/lib/tools/science/europepmc.js +127 -0
  175. package/dist/lib/tools/science/http.d.ts +31 -0
  176. package/dist/lib/tools/science/http.js +104 -0
  177. package/dist/lib/tools/science/openalex.d.ts +30 -0
  178. package/dist/lib/tools/science/openalex.js +81 -0
  179. package/dist/lib/tools/science/trials.d.ts +58 -0
  180. package/dist/lib/tools/science/trials.js +203 -0
  181. package/dist/lib/tools/science/types.d.ts +69 -0
  182. package/dist/lib/tools/science/types.js +48 -0
  183. package/dist/lib/tools/scienceTools.d.ts +100 -0
  184. package/dist/lib/tools/scienceTools.js +277 -0
  185. package/dist/lib/tools/taskTools.d.ts +204 -0
  186. package/dist/lib/tools/taskTools.js +374 -0
  187. package/dist/lib/tools/toolContract.d.ts +17 -4
  188. package/dist/lib/tools/toolContract.js +13 -5
  189. package/dist/lib/tools/webExtractTool.d.ts +13 -10
  190. package/dist/lib/tools/webExtractTool.js +23 -55
  191. package/dist/lib/tools/webSearchTool.d.ts +2 -1
  192. package/dist/lib/tools/webSearchTool.js +4 -3
  193. package/dist/lib/tools/wikiTools.d.ts +2 -2
  194. package/dist/lib/tools/wikiTools.js +12 -12
  195. package/dist/lib/tools/xApiSearchTool.d.ts +194 -0
  196. package/dist/lib/tools/xApiSearchTool.js +583 -0
  197. package/dist/lib/tools/xSearchTool.js +1 -1
  198. package/dist/lib/wiki/agentRun.js +1 -1
  199. package/dist/lib/wiki/builder.js +21 -6
  200. package/dist/lib/wiki/dive.js +2 -2
  201. package/dist/lib/wiki/entities.d.ts +2 -2
  202. package/dist/lib/wiki/entities.js +7 -7
  203. package/dist/lib/wiki/extract.js +5 -1
  204. package/dist/lib/wiki/fill.js +5 -5
  205. package/dist/lib/wiki/format.d.ts +7 -5
  206. package/dist/lib/wiki/format.js +2 -2
  207. package/dist/lib/wiki/graph.js +2 -2
  208. package/dist/lib/wiki/lint.js +2 -2
  209. package/dist/lib/wiki/markdown.d.ts +1 -1
  210. package/dist/lib/wiki/markdown.js +6 -1
  211. package/dist/lib/wiki/pipeline.js +2 -2
  212. package/dist/lib/wiki/vault.js +2 -2
  213. package/dist/scripts/a2a_server.d.ts +17 -11
  214. package/dist/scripts/a2a_server.js +405 -1028
  215. package/dist/scripts/assistant_worker.d.ts +33 -0
  216. package/dist/scripts/assistant_worker.js +193 -0
  217. package/dist/scripts/db.d.ts +26 -0
  218. package/dist/scripts/db.js +179 -0
  219. package/dist/scripts/doctor.d.ts +23 -0
  220. package/dist/scripts/doctor.js +90 -0
  221. package/dist/scripts/init.d.ts +43 -0
  222. package/dist/scripts/init.js +152 -0
  223. package/dist/scripts/registry.d.ts +25 -0
  224. package/dist/scripts/registry.js +182 -0
  225. package/dist/scripts/skills_install.d.ts +7 -0
  226. package/dist/scripts/skills_install.js +137 -0
  227. package/dist/scripts/syndicate_chat.js +159 -276
  228. package/package.json +90 -16
  229. package/skills/README.md +74 -0
  230. package/skills/briefs/README.md +16 -0
  231. package/skills/briefs/_shared.md +16 -0
  232. package/skills/briefs/melchizedek-author.md +60 -0
  233. package/skills/briefs/melchizedek-memory.md +47 -0
  234. package/skills/briefs/melchizedek-models.md +47 -0
  235. package/skills/briefs/melchizedek-scribe.md +37 -0
  236. package/skills/briefs/melchizedek-serve.md +56 -0
  237. package/skills/briefs/melchizedek.md +69 -0
  238. package/skills/briefs/skills-readme.md +33 -0
  239. package/skills/melchizedek/SKILL.md +159 -0
  240. package/skills/melchizedek-author/SKILL.md +99 -0
  241. package/skills/melchizedek-author/templates/minimal.yaml +60 -0
  242. package/skills/melchizedek-memory/SKILL.md +138 -0
  243. package/skills/melchizedek-models/SKILL.md +137 -0
  244. package/skills/melchizedek-scribe/SKILL.md +72 -0
  245. package/skills/melchizedek-scribe/templates/brief.md +27 -0
  246. package/skills/melchizedek-serve/SKILL.md +119 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * lib/storage/postgres/taskStore.ts — the A2A SDK's TaskStore on Postgres.
3
+ *
4
+ * Tasks outlive the process that started them and are visible to every
5
+ * instance, so a client that polls a different replica, or polls after a
6
+ * restart, still finds its task. Each task is filed under the tenant and
7
+ * owner the SDK's own scoping rule derives from the call context (the
8
+ * default resolver reads the authenticated user name), so one caller cannot
9
+ * load or list another's task. `list` mirrors the SDK's InMemoryTaskStore:
10
+ * same filters, same newest-first order, same page-token format.
11
+ */
12
+ import type { Pool } from 'pg';
13
+ import type { TaskStore } from '@a2a-js/sdk/server';
14
+ type Task = Parameters<TaskStore['save']>[0];
15
+ type Context = Parameters<TaskStore['save']>[1];
16
+ type ListParams = Parameters<TaskStore['list']>[0];
17
+ type ListResult = Awaited<ReturnType<TaskStore['list']>>;
18
+ export interface PostgresTaskStoreOptions {
19
+ /** Owner of a call. Default: the SDK's resolveUserScope (the authenticated user name). */
20
+ ownerResolver?: (context: Context) => string;
21
+ /** Days a task is kept after its last update (expire_at). Default 7. */
22
+ ttlDays?: number;
23
+ /**
24
+ * While a task is running (submitted or working) it is leased to this
25
+ * instance until `ttlMs` from its last save or renewal (migration 0006).
26
+ */
27
+ lease?: TaskLease;
28
+ }
29
+ export interface TaskLease {
30
+ /** This process's id: what a lease names as its owner. */
31
+ instanceId: string;
32
+ ttlMs: number;
33
+ }
34
+ /** Extends every lease this instance holds. Returns how many it renewed. */
35
+ export declare function renewTaskLeases(pool: Pool, lease: TaskLease): Promise<number>;
36
+ /**
37
+ * Marks running tasks whose lease expired (their instance died) as failed,
38
+ * with a message saying so. Any instance may run it: rows are claimed with
39
+ * SKIP LOCKED, so two reapers never fail the same task twice. Returns how
40
+ * many it failed.
41
+ */
42
+ export declare function reapExpiredTasks(pool: Pool, limit?: number): Promise<number>;
43
+ export declare class PostgresTaskStore implements TaskStore {
44
+ private readonly owner;
45
+ private readonly ttlMs;
46
+ private readonly pool;
47
+ private readonly agentId;
48
+ private readonly lease?;
49
+ constructor(pool: Pool, agentId: string, options?: PostgresTaskStoreOptions);
50
+ private scope;
51
+ save(task: Task, context: Context): Promise<void>;
52
+ load(taskId: string, context: Context): Promise<Task | undefined>;
53
+ list(params: ListParams, context: Context): Promise<ListResult>;
54
+ }
55
+ export {};
@@ -0,0 +1,168 @@
1
+ /**
2
+ * lib/storage/postgres/taskStore.ts — the A2A SDK's TaskStore on Postgres.
3
+ *
4
+ * Tasks outlive the process that started them and are visible to every
5
+ * instance, so a client that polls a different replica, or polls after a
6
+ * restart, still finds its task. Each task is filed under the tenant and
7
+ * owner the SDK's own scoping rule derives from the call context (the
8
+ * default resolver reads the authenticated user name), so one caller cannot
9
+ * load or list another's task. `list` mirrors the SDK's InMemoryTaskStore:
10
+ * same filters, same newest-first order, same page-token format.
11
+ */
12
+ import { randomUUID } from 'node:crypto';
13
+ import { Role, TaskState } from '@a2a-js/sdk';
14
+ import { resolveUserScope } from '@a2a-js/sdk/server';
15
+ const DEFAULT_PAGE_SIZE = 50;
16
+ /** States in which a task is being worked on by some instance. */
17
+ const RUNNING = new Set([TaskState.TASK_STATE_SUBMITTED, TaskState.TASK_STATE_WORKING]);
18
+ /** Extends every lease this instance holds. Returns how many it renewed. */
19
+ export async function renewTaskLeases(pool, lease) {
20
+ const r = await pool.query(`UPDATE adk_a2a_tasks SET lease_until = now() + ($2::int * interval '1 millisecond')
21
+ WHERE lease_owner = $1 AND lease_until IS NOT NULL`, [lease.instanceId, lease.ttlMs]);
22
+ return r.rowCount ?? 0;
23
+ }
24
+ /** The status a task gets when the instance running it is gone. */
25
+ const ORPHANED = 'The server running this task stopped before it finished; send the message again.';
26
+ /**
27
+ * Marks running tasks whose lease expired (their instance died) as failed,
28
+ * with a message saying so. Any instance may run it: rows are claimed with
29
+ * SKIP LOCKED, so two reapers never fail the same task twice. Returns how
30
+ * many it failed.
31
+ */
32
+ export async function reapExpiredTasks(pool, limit = 100) {
33
+ const client = await pool.connect();
34
+ try {
35
+ await client.query('BEGIN');
36
+ const r = await client.query(`SELECT tenant, owner, agent_id, id, task FROM adk_a2a_tasks
37
+ WHERE lease_until IS NOT NULL AND lease_until < now()
38
+ ORDER BY lease_until LIMIT $1 FOR UPDATE SKIP LOCKED`, [limit]);
39
+ for (const row of r.rows) {
40
+ const task = row.task;
41
+ const timestamp = new Date().toISOString();
42
+ task.status = {
43
+ state: TaskState.TASK_STATE_FAILED,
44
+ timestamp,
45
+ message: {
46
+ messageId: randomUUID(),
47
+ contextId: task.contextId ?? '',
48
+ taskId: task.id,
49
+ role: Role.ROLE_AGENT,
50
+ parts: [{ content: { $case: 'text', value: ORPHANED }, filename: '', mediaType: 'text/plain' }],
51
+ extensions: [],
52
+ referenceTaskIds: [],
53
+ },
54
+ };
55
+ await client.query(`UPDATE adk_a2a_tasks SET task = $5::jsonb, state = $6, status_ts = $7, updated_at = now(),
56
+ lease_owner = NULL, lease_until = NULL
57
+ WHERE tenant = $1 AND owner = $2 AND agent_id = $3 AND id = $4`, [row.tenant, row.owner, row.agent_id, row.id, JSON.stringify(task), TaskState.TASK_STATE_FAILED, timestamp]);
58
+ }
59
+ await client.query('COMMIT');
60
+ return r.rows.length;
61
+ }
62
+ catch (err) {
63
+ await client.query('ROLLBACK').catch(() => { });
64
+ throw err;
65
+ }
66
+ finally {
67
+ client.release();
68
+ }
69
+ }
70
+ function encodePageToken(timestamp, id) {
71
+ return Buffer.from(`${timestamp}|${id}`).toString('base64');
72
+ }
73
+ function decodePageToken(token) {
74
+ const [timestamp, ...idParts] = Buffer.from(token, 'base64').toString('utf-8').split('|');
75
+ if (idParts.length === 0)
76
+ throw new Error('Invalid page token format.');
77
+ return { timestamp, id: idParts.join('|') };
78
+ }
79
+ export class PostgresTaskStore {
80
+ owner;
81
+ ttlMs;
82
+ pool;
83
+ agentId;
84
+ lease;
85
+ constructor(pool, agentId, options = {}) {
86
+ this.pool = pool;
87
+ this.agentId = agentId;
88
+ this.owner = options.ownerResolver ?? resolveUserScope;
89
+ this.ttlMs = (options.ttlDays ?? 7) * 24 * 60 * 60 * 1000;
90
+ this.lease = options.lease;
91
+ }
92
+ scope(context) {
93
+ return [context.tenant ?? '', this.owner(context)];
94
+ }
95
+ async save(task, context) {
96
+ const [tenant, owner] = this.scope(context);
97
+ const t = task;
98
+ // A running task is leased to this instance; any other state holds none.
99
+ const leased = !!this.lease && RUNNING.has(t.status?.state ?? -1);
100
+ await this.pool.query(`INSERT INTO adk_a2a_tasks (tenant, owner, agent_id, id, context_id, state, status_ts, task, updated_at, expire_at, lease_owner, lease_until)
101
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, now(), $9, $10,
102
+ CASE WHEN $10::text IS NULL THEN NULL ELSE now() + ($11::int * interval '1 millisecond') END)
103
+ ON CONFLICT (tenant, owner, agent_id, id) DO UPDATE
104
+ SET context_id = EXCLUDED.context_id, state = EXCLUDED.state, status_ts = EXCLUDED.status_ts,
105
+ task = EXCLUDED.task, updated_at = now(), expire_at = EXCLUDED.expire_at,
106
+ lease_owner = EXCLUDED.lease_owner, lease_until = EXCLUDED.lease_until`, [
107
+ tenant,
108
+ owner,
109
+ this.agentId,
110
+ t.id,
111
+ t.contextId ?? null,
112
+ t.status?.state ?? null,
113
+ t.status?.timestamp ?? '',
114
+ JSON.stringify(task),
115
+ new Date(Date.now() + this.ttlMs).toISOString(),
116
+ leased ? this.lease.instanceId : null,
117
+ this.lease?.ttlMs ?? 0,
118
+ ]);
119
+ }
120
+ async load(taskId, context) {
121
+ const [tenant, owner] = this.scope(context);
122
+ const r = await this.pool.query('SELECT task FROM adk_a2a_tasks WHERE tenant = $1 AND owner = $2 AND agent_id = $3 AND id = $4', [tenant, owner, this.agentId, taskId]);
123
+ return r.rowCount ? r.rows[0].task : undefined;
124
+ }
125
+ async list(params, context) {
126
+ const [tenant, owner] = this.scope(context);
127
+ const p = params;
128
+ const pageSize = p.pageSize || DEFAULT_PAGE_SIZE;
129
+ const args = [tenant, owner, this.agentId];
130
+ let where = 'tenant = $1 AND owner = $2 AND agent_id = $3';
131
+ if (p.contextId) {
132
+ args.push(p.contextId);
133
+ where += ` AND context_id = $${args.length}`;
134
+ }
135
+ if (p.status !== undefined && p.status !== 0) {
136
+ args.push(p.status);
137
+ where += ` AND state = $${args.length}`;
138
+ }
139
+ if (p.statusTimestampAfter) {
140
+ args.push(new Date(p.statusTimestampAfter).toISOString());
141
+ where += ` AND status_ts <> '' AND status_ts::timestamptz > $${args.length}::timestamptz`;
142
+ }
143
+ const total = await this.pool.query(`SELECT count(*)::int AS n FROM adk_a2a_tasks WHERE ${where}`, args);
144
+ let cursor = '';
145
+ if (p.pageToken) {
146
+ const c = decodePageToken(p.pageToken);
147
+ args.push(c.timestamp, c.id);
148
+ cursor = ` AND (status_ts, id) < ($${args.length - 1}, $${args.length})`;
149
+ }
150
+ args.push(pageSize + 1);
151
+ const rows = await this.pool.query(`SELECT task, status_ts, id FROM adk_a2a_tasks WHERE ${where}${cursor}
152
+ ORDER BY status_ts DESC, id DESC LIMIT $${args.length}`, args);
153
+ const page = rows.rows.slice(0, pageSize);
154
+ const tasks = page.map((row) => {
155
+ const task = row.task;
156
+ if (!p.includeArtifacts)
157
+ task.artifacts = [];
158
+ return task;
159
+ });
160
+ const last = page[page.length - 1];
161
+ return {
162
+ tasks,
163
+ nextPageToken: rows.rows.length > pageSize && last ? encodePageToken(last.status_ts ?? '', last.id) : '',
164
+ pageSize,
165
+ totalSize: total.rows[0].n,
166
+ };
167
+ }
168
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * lib/storage/schema.ts — the database schema the tables live in
3
+ * (ADR 0021 item 2).
4
+ *
5
+ * The default is `public`, where every existing deployment's tables are.
6
+ * A deployment on its own Postgres can keep them in a private schema
7
+ * instead (MELCHIZEDEK_DB_SCHEMA, e.g. `melchizedek`): a REST layer such as
8
+ * Supabase's exposes `public` by default, so a private schema has no anon
9
+ * REST path at all. It is reached over DATABASE_URL (postgresStorage), which
10
+ * sets the connection's search_path; supabase-js storage cannot use it.
11
+ *
12
+ * The SQL in db/ names `public` in three places, which `sqlForSchema`
13
+ * rewrites, and nowhere else (pinned by tests):
14
+ * - `SET search_path = public` on function definitions;
15
+ * - `'public.<table>'` in to_regclass lookups;
16
+ * - `= 'public'` in catalog filters;
17
+ * plus the pg_cron jobs, which run outside any search_path and so call
18
+ * their function by its qualified name. `public` stays on the path after
19
+ * the schema, for the pgvector type and functions.
20
+ */
21
+ export declare const SCHEMA_NAME: RegExp;
22
+ /** The configured schema: MELCHIZEDEK_DB_SCHEMA, else `public`. */
23
+ export declare function dbSchema(env?: NodeJS.ProcessEnv): string;
24
+ /** A db/ file's SQL for `schema` (unchanged for `public`). */
25
+ export declare function sqlForSchema(sql: string, schema: string): string;
26
+ /** The pg connection option that puts `schema` first on the search_path. */
27
+ export declare function searchPathOption(schema: string): string | undefined;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * lib/storage/schema.ts — the database schema the tables live in
3
+ * (ADR 0021 item 2).
4
+ *
5
+ * The default is `public`, where every existing deployment's tables are.
6
+ * A deployment on its own Postgres can keep them in a private schema
7
+ * instead (MELCHIZEDEK_DB_SCHEMA, e.g. `melchizedek`): a REST layer such as
8
+ * Supabase's exposes `public` by default, so a private schema has no anon
9
+ * REST path at all. It is reached over DATABASE_URL (postgresStorage), which
10
+ * sets the connection's search_path; supabase-js storage cannot use it.
11
+ *
12
+ * The SQL in db/ names `public` in three places, which `sqlForSchema`
13
+ * rewrites, and nowhere else (pinned by tests):
14
+ * - `SET search_path = public` on function definitions;
15
+ * - `'public.<table>'` in to_regclass lookups;
16
+ * - `= 'public'` in catalog filters;
17
+ * plus the pg_cron jobs, which run outside any search_path and so call
18
+ * their function by its qualified name. `public` stays on the path after
19
+ * the schema, for the pgvector type and functions.
20
+ */
21
+ export const SCHEMA_NAME = /^[a-z_][a-z0-9_]{0,62}$/;
22
+ /** The configured schema: MELCHIZEDEK_DB_SCHEMA, else `public`. */
23
+ export function dbSchema(env = process.env) {
24
+ const s = env.MELCHIZEDEK_DB_SCHEMA?.trim() || 'public';
25
+ if (!SCHEMA_NAME.test(s)) {
26
+ throw new Error(`MELCHIZEDEK_DB_SCHEMA must be a lower-case identifier (letters, digits, _), got "${s}"`);
27
+ }
28
+ return s;
29
+ }
30
+ /** A db/ file's SQL for `schema` (unchanged for `public`). */
31
+ export function sqlForSchema(sql, schema) {
32
+ if (schema === 'public')
33
+ return sql;
34
+ if (!SCHEMA_NAME.test(schema))
35
+ throw new Error(`invalid schema name "${schema}"`);
36
+ const body = sql
37
+ .replaceAll('SET search_path = public', `SET search_path = ${schema}, public`)
38
+ .replaceAll("'public.", `'${schema}.`)
39
+ .replaceAll("= 'public'", `= '${schema}'`)
40
+ .replace(/'SELECT (melchizedek_[a-z_]+\(\))'/g, `'SELECT ${schema}.$1'`);
41
+ return `CREATE SCHEMA IF NOT EXISTS ${schema};\nSET search_path = ${schema}, public;\n${body}`;
42
+ }
43
+ /** The pg connection option that puts `schema` first on the search_path. */
44
+ export function searchPathOption(schema) {
45
+ if (schema === 'public')
46
+ return undefined;
47
+ if (!SCHEMA_NAME.test(schema))
48
+ throw new Error(`invalid schema name "${schema}"`);
49
+ return `-c search_path=${schema},public`;
50
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * lib/storage/schemaVersion.ts — the database schema this package expects
3
+ * (ADR 0021 item 3).
4
+ *
5
+ * Every numbered migration in db/migrations/ records itself in
6
+ * `melchizedek_schema_version`. The highest number shipped with the package
7
+ * is the schema this code is written against; a database below it is missing
8
+ * tables or functions the server will call, so the server refuses to start on
9
+ * it and names the fix. A database ABOVE it is a newer migration applied
10
+ * before this server was upgraded (a rolling deploy): migrations are additive,
11
+ * so that only warns.
12
+ */
13
+ /** db/ of this package, from the source tree or from dist/. */
14
+ export declare function packageDbDir(): string;
15
+ /** The highest migration number in db/migrations/ (NNNN_name.sql). */
16
+ export declare function shippedSchemaVersion(migrationsDir?: string): number;
17
+ export interface SchemaCheck {
18
+ /** Version recorded in the database; null when it has none. */
19
+ db: number | null;
20
+ shipped: number;
21
+ state: 'match' | 'behind' | 'ahead';
22
+ }
23
+ export declare function compareSchema(db: number | null, shipped: number): SchemaCheck;
24
+ /** The message for a database behind the code: what is missing and the fix. */
25
+ export declare function schemaBehindMessage(check: SchemaCheck): string;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * lib/storage/schemaVersion.ts — the database schema this package expects
3
+ * (ADR 0021 item 3).
4
+ *
5
+ * Every numbered migration in db/migrations/ records itself in
6
+ * `melchizedek_schema_version`. The highest number shipped with the package
7
+ * is the schema this code is written against; a database below it is missing
8
+ * tables or functions the server will call, so the server refuses to start on
9
+ * it and names the fix. A database ABOVE it is a newer migration applied
10
+ * before this server was upgraded (a rolling deploy): migrations are additive,
11
+ * so that only warns.
12
+ */
13
+ import { existsSync, readdirSync } from 'node:fs';
14
+ import { dirname, join } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ /** db/ of this package, from the source tree or from dist/. */
17
+ export function packageDbDir() {
18
+ let dir = dirname(fileURLToPath(import.meta.url));
19
+ for (let i = 0; i < 6; i++) {
20
+ if (existsSync(join(dir, 'db', 'migrations')))
21
+ return join(dir, 'db');
22
+ dir = dirname(dir);
23
+ }
24
+ throw new Error('db/migrations/ not found next to this package');
25
+ }
26
+ /** The highest migration number in db/migrations/ (NNNN_name.sql). */
27
+ export function shippedSchemaVersion(migrationsDir = join(packageDbDir(), 'migrations')) {
28
+ let max = 0;
29
+ for (const f of readdirSync(migrationsDir)) {
30
+ const m = /^(\d{4})_[a-z0-9_]+\.sql$/.exec(f);
31
+ if (m)
32
+ max = Math.max(max, Number(m[1]));
33
+ }
34
+ return max;
35
+ }
36
+ export function compareSchema(db, shipped) {
37
+ const v = db ?? 0;
38
+ return { db, shipped, state: v === shipped ? 'match' : v < shipped ? 'behind' : 'ahead' };
39
+ }
40
+ /** The message for a database behind the code: what is missing and the fix. */
41
+ export function schemaBehindMessage(check) {
42
+ const have = check.db === null ? 'no recorded schema version' : `schema version ${check.db}`;
43
+ return (`The database has ${have}; this server needs version ${check.shipped}. ` +
44
+ 'Run `npx melchizedek-db apply` (or `npm run db -- apply`) against it, ' +
45
+ 'or set ALLOW_SCHEMA_MISMATCH=true to start anyway.');
46
+ }
@@ -0,0 +1,275 @@
1
+ /**
2
+ * lib/syndicateSchema.ts — the syndicate YAML contract, as a validator.
3
+ *
4
+ * WHY this file exists:
5
+ * A syndicate YAML is the framework's primary authoring surface, and the
6
+ * loader used to type-cast it (`parse(...) as SyndicateYamlConfig`). A typo
7
+ * either crashed deep inside the compiler (`orchestator:` → "cannot read
8
+ * 'model' of undefined") or, worse, was accepted and silently changed
9
+ * behaviour (`memory_system: internl-only` disabled memory in the A2A
10
+ * server). Here the shape the interfaces in lib/loadSyndicate.ts declare is
11
+ * written once as a zod schema, checked at load, and every problem is
12
+ * reported with its key path and, for a misspelt key, the key it was meant
13
+ * to be.
14
+ *
15
+ * The same schema emits config/agents/syndicate.schema.json (npm run
16
+ * schema:gen) so an editor can flag the typo before a run does; a test
17
+ * keeps the committed file equal to the generated one.
18
+ *
19
+ * Strictness is deliberate at the syndicate, agent and dispatch levels — the
20
+ * places a misspelt key silently does nothing — and deliberately ABSENT inside
21
+ * `generateContentConfig` and `outputSchema`, which pass through to provider
22
+ * SDKs whose fields this repo does not own.
23
+ */
24
+ import { z } from 'zod';
25
+ import type { SyndicateYamlConfig } from './loadSyndicate.ts';
26
+ export declare const MEMORY_SYSTEMS: readonly ['internal-only', 'session-only', 'long-term'];
27
+ export declare const agentSchema: z.ZodObject<{
28
+ name: z.ZodString;
29
+ description: z.ZodOptional<z.ZodString>;
30
+ model: z.ZodString;
31
+ instruction: z.ZodString;
32
+ globalInstruction: z.ZodOptional<z.ZodString>;
33
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
34
+ require_approval: z.ZodOptional<z.ZodArray<z.ZodString>>;
35
+ includeContents: z.ZodOptional<z.ZodEnum<{
36
+ default: "default";
37
+ none: "none";
38
+ }>>;
39
+ disallowTransferToParent: z.ZodOptional<z.ZodBoolean>;
40
+ disallowTransferToPeers: z.ZodOptional<z.ZodBoolean>;
41
+ outputKey: z.ZodOptional<z.ZodString>;
42
+ generateContentConfig: z.ZodOptional<z.ZodObject<{
43
+ temperature: z.ZodOptional<z.ZodNumber>;
44
+ topP: z.ZodOptional<z.ZodNumber>;
45
+ topK: z.ZodOptional<z.ZodNumber>;
46
+ maxOutputTokens: z.ZodOptional<z.ZodNumber>;
47
+ stopSequences: z.ZodOptional<z.ZodArray<z.ZodString>>;
48
+ candidateCount: z.ZodOptional<z.ZodNumber>;
49
+ presencePenalty: z.ZodOptional<z.ZodNumber>;
50
+ frequencyPenalty: z.ZodOptional<z.ZodNumber>;
51
+ seed: z.ZodOptional<z.ZodNumber>;
52
+ responseMimeType: z.ZodOptional<z.ZodString>;
53
+ safetySettings: z.ZodOptional<z.ZodArray<z.ZodObject<{
54
+ category: z.ZodString;
55
+ threshold: z.ZodString;
56
+ }, z.core.$loose>>>;
57
+ thinkingConfig: z.ZodOptional<z.ZodObject<{
58
+ thinkingBudget: z.ZodOptional<z.ZodNumber>;
59
+ includeThoughts: z.ZodOptional<z.ZodBoolean>;
60
+ }, z.core.$loose>>;
61
+ }, z.core.$loose>>;
62
+ outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
63
+ mcp_server_url: z.ZodOptional<z.ZodString>;
64
+ orchestration: z.ZodOptional<z.ZodObject<{
65
+ role: z.ZodOptional<z.ZodEnum<{
66
+ primary: "primary";
67
+ "sub-agent": "sub-agent";
68
+ }>>;
69
+ delegates: z.ZodOptional<z.ZodArray<z.ZodString>>;
70
+ }, z.core.$strict>>;
71
+ }, z.core.$strict>;
72
+ /**
73
+ * Model and instruction are optional at the type level because a nested
74
+ * (`yaml_reference`) or remote (`a2a_agent_url`) subagent brings its own; the
75
+ * "an ordinary subagent needs an instruction" rule is checked in
76
+ * crossFieldProblems so it can name the alternative keys in its message.
77
+ */
78
+ export declare const subagentSchema: z.ZodObject<{
79
+ name: z.ZodString;
80
+ globalInstruction: z.ZodOptional<z.ZodString>;
81
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
82
+ require_approval: z.ZodOptional<z.ZodArray<z.ZodString>>;
83
+ includeContents: z.ZodOptional<z.ZodEnum<{
84
+ default: "default";
85
+ none: "none";
86
+ }>>;
87
+ disallowTransferToParent: z.ZodOptional<z.ZodBoolean>;
88
+ disallowTransferToPeers: z.ZodOptional<z.ZodBoolean>;
89
+ outputKey: z.ZodOptional<z.ZodString>;
90
+ generateContentConfig: z.ZodOptional<z.ZodObject<{
91
+ temperature: z.ZodOptional<z.ZodNumber>;
92
+ topP: z.ZodOptional<z.ZodNumber>;
93
+ topK: z.ZodOptional<z.ZodNumber>;
94
+ maxOutputTokens: z.ZodOptional<z.ZodNumber>;
95
+ stopSequences: z.ZodOptional<z.ZodArray<z.ZodString>>;
96
+ candidateCount: z.ZodOptional<z.ZodNumber>;
97
+ presencePenalty: z.ZodOptional<z.ZodNumber>;
98
+ frequencyPenalty: z.ZodOptional<z.ZodNumber>;
99
+ seed: z.ZodOptional<z.ZodNumber>;
100
+ responseMimeType: z.ZodOptional<z.ZodString>;
101
+ safetySettings: z.ZodOptional<z.ZodArray<z.ZodObject<{
102
+ category: z.ZodString;
103
+ threshold: z.ZodString;
104
+ }, z.core.$loose>>>;
105
+ thinkingConfig: z.ZodOptional<z.ZodObject<{
106
+ thinkingBudget: z.ZodOptional<z.ZodNumber>;
107
+ includeThoughts: z.ZodOptional<z.ZodBoolean>;
108
+ }, z.core.$loose>>;
109
+ }, z.core.$loose>>;
110
+ outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
111
+ mcp_server_url: z.ZodOptional<z.ZodString>;
112
+ orchestration: z.ZodOptional<z.ZodObject<{
113
+ role: z.ZodOptional<z.ZodEnum<{
114
+ primary: "primary";
115
+ "sub-agent": "sub-agent";
116
+ }>>;
117
+ delegates: z.ZodOptional<z.ZodArray<z.ZodString>>;
118
+ }, z.core.$strict>>;
119
+ description: z.ZodString;
120
+ model: z.ZodOptional<z.ZodString>;
121
+ instruction: z.ZodOptional<z.ZodString>;
122
+ yaml_reference: z.ZodOptional<z.ZodString>;
123
+ a2a_agent_url: z.ZodOptional<z.ZodString>;
124
+ }, z.core.$strict>;
125
+ export declare const syndicateSchema: z.ZodObject<{
126
+ syndicate_name: z.ZodString;
127
+ orchestrator: z.ZodObject<{
128
+ name: z.ZodString;
129
+ description: z.ZodOptional<z.ZodString>;
130
+ model: z.ZodString;
131
+ instruction: z.ZodString;
132
+ globalInstruction: z.ZodOptional<z.ZodString>;
133
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
134
+ require_approval: z.ZodOptional<z.ZodArray<z.ZodString>>;
135
+ includeContents: z.ZodOptional<z.ZodEnum<{
136
+ default: "default";
137
+ none: "none";
138
+ }>>;
139
+ disallowTransferToParent: z.ZodOptional<z.ZodBoolean>;
140
+ disallowTransferToPeers: z.ZodOptional<z.ZodBoolean>;
141
+ outputKey: z.ZodOptional<z.ZodString>;
142
+ generateContentConfig: z.ZodOptional<z.ZodObject<{
143
+ temperature: z.ZodOptional<z.ZodNumber>;
144
+ topP: z.ZodOptional<z.ZodNumber>;
145
+ topK: z.ZodOptional<z.ZodNumber>;
146
+ maxOutputTokens: z.ZodOptional<z.ZodNumber>;
147
+ stopSequences: z.ZodOptional<z.ZodArray<z.ZodString>>;
148
+ candidateCount: z.ZodOptional<z.ZodNumber>;
149
+ presencePenalty: z.ZodOptional<z.ZodNumber>;
150
+ frequencyPenalty: z.ZodOptional<z.ZodNumber>;
151
+ seed: z.ZodOptional<z.ZodNumber>;
152
+ responseMimeType: z.ZodOptional<z.ZodString>;
153
+ safetySettings: z.ZodOptional<z.ZodArray<z.ZodObject<{
154
+ category: z.ZodString;
155
+ threshold: z.ZodString;
156
+ }, z.core.$loose>>>;
157
+ thinkingConfig: z.ZodOptional<z.ZodObject<{
158
+ thinkingBudget: z.ZodOptional<z.ZodNumber>;
159
+ includeThoughts: z.ZodOptional<z.ZodBoolean>;
160
+ }, z.core.$loose>>;
161
+ }, z.core.$loose>>;
162
+ outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
163
+ mcp_server_url: z.ZodOptional<z.ZodString>;
164
+ orchestration: z.ZodOptional<z.ZodObject<{
165
+ role: z.ZodOptional<z.ZodEnum<{
166
+ primary: "primary";
167
+ "sub-agent": "sub-agent";
168
+ }>>;
169
+ delegates: z.ZodOptional<z.ZodArray<z.ZodString>>;
170
+ }, z.core.$strict>>;
171
+ }, z.core.$strict>;
172
+ subagents: z.ZodOptional<z.ZodArray<z.ZodObject<{
173
+ name: z.ZodString;
174
+ globalInstruction: z.ZodOptional<z.ZodString>;
175
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
176
+ require_approval: z.ZodOptional<z.ZodArray<z.ZodString>>;
177
+ includeContents: z.ZodOptional<z.ZodEnum<{
178
+ default: "default";
179
+ none: "none";
180
+ }>>;
181
+ disallowTransferToParent: z.ZodOptional<z.ZodBoolean>;
182
+ disallowTransferToPeers: z.ZodOptional<z.ZodBoolean>;
183
+ outputKey: z.ZodOptional<z.ZodString>;
184
+ generateContentConfig: z.ZodOptional<z.ZodObject<{
185
+ temperature: z.ZodOptional<z.ZodNumber>;
186
+ topP: z.ZodOptional<z.ZodNumber>;
187
+ topK: z.ZodOptional<z.ZodNumber>;
188
+ maxOutputTokens: z.ZodOptional<z.ZodNumber>;
189
+ stopSequences: z.ZodOptional<z.ZodArray<z.ZodString>>;
190
+ candidateCount: z.ZodOptional<z.ZodNumber>;
191
+ presencePenalty: z.ZodOptional<z.ZodNumber>;
192
+ frequencyPenalty: z.ZodOptional<z.ZodNumber>;
193
+ seed: z.ZodOptional<z.ZodNumber>;
194
+ responseMimeType: z.ZodOptional<z.ZodString>;
195
+ safetySettings: z.ZodOptional<z.ZodArray<z.ZodObject<{
196
+ category: z.ZodString;
197
+ threshold: z.ZodString;
198
+ }, z.core.$loose>>>;
199
+ thinkingConfig: z.ZodOptional<z.ZodObject<{
200
+ thinkingBudget: z.ZodOptional<z.ZodNumber>;
201
+ includeThoughts: z.ZodOptional<z.ZodBoolean>;
202
+ }, z.core.$loose>>;
203
+ }, z.core.$loose>>;
204
+ outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
205
+ mcp_server_url: z.ZodOptional<z.ZodString>;
206
+ orchestration: z.ZodOptional<z.ZodObject<{
207
+ role: z.ZodOptional<z.ZodEnum<{
208
+ primary: "primary";
209
+ "sub-agent": "sub-agent";
210
+ }>>;
211
+ delegates: z.ZodOptional<z.ZodArray<z.ZodString>>;
212
+ }, z.core.$strict>>;
213
+ description: z.ZodString;
214
+ model: z.ZodOptional<z.ZodString>;
215
+ instruction: z.ZodOptional<z.ZodString>;
216
+ yaml_reference: z.ZodOptional<z.ZodString>;
217
+ a2a_agent_url: z.ZodOptional<z.ZodString>;
218
+ }, z.core.$strict>>>;
219
+ dispatch: z.ZodOptional<z.ZodObject<{
220
+ default_route: z.ZodString;
221
+ route_key: z.ZodOptional<z.ZodString>;
222
+ reason_key: z.ZodOptional<z.ZodString>;
223
+ route_overrides: z.ZodOptional<z.ZodArray<z.ZodObject<{
224
+ route: z.ZodString;
225
+ pattern: z.ZodString;
226
+ flags: z.ZodOptional<z.ZodString>;
227
+ reason: z.ZodOptional<z.ZodString>;
228
+ }, z.core.$strict>>>;
229
+ }, z.core.$strict>>;
230
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
231
+ memory_system: z.ZodOptional<z.ZodEnum<{
232
+ "internal-only": "internal-only";
233
+ "long-term": "long-term";
234
+ "session-only": "session-only";
235
+ }>>;
236
+ guards: z.ZodOptional<z.ZodArray<z.ZodString>>;
237
+ memory_namespace: z.ZodOptional<z.ZodString>;
238
+ memory_extraction_rules: z.ZodOptional<z.ZodString>;
239
+ memory_extraction_model: z.ZodOptional<z.ZodString>;
240
+ memory_retention_days: z.ZodOptional<z.ZodNumber>;
241
+ max_steps: z.ZodOptional<z.ZodNumber>;
242
+ bundled_references: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
243
+ }, z.core.$strict>;
244
+ /**
245
+ * The closest candidate, if it is close enough to be a typo. Case and
246
+ * snake/camel drift (`output_key` for `outputKey`) count as an exact match.
247
+ */
248
+ export declare function suggest(input: string, candidates: readonly string[]): string | undefined;
249
+ /**
250
+ * Thrown by validateSyndicateConfig. `problems` holds one `<file>: <path> —
251
+ * <what>` line per problem (the message lists them all); `issues` holds the
252
+ * same lines without the file, for callers that already show it.
253
+ */
254
+ export declare class SyndicateValidationError extends Error {
255
+ readonly file: string | undefined;
256
+ readonly issues: string[];
257
+ readonly problems: string[];
258
+ constructor(issues: string[], file?: string);
259
+ }
260
+ /**
261
+ * Validate a parsed syndicate and return it typed, or throw ONE error that
262
+ * lists every problem as `<file>: <key.path> — <what is wrong>`.
263
+ *
264
+ * Runs on the interpolated config: a `{{token}}` inside a string is still a
265
+ * string, and a full-token number (`max_steps: "{{n}}"`) is checked as the
266
+ * value it resolved to.
267
+ */
268
+ export declare function validateSyndicateConfig(raw: unknown, file?: string): SyndicateYamlConfig;
269
+ /**
270
+ * The syndicate contract as JSON Schema (draft-07: the widest editor
271
+ * support), for `# yaml-language-server: $schema=` and other tooling.
272
+ * Cross-field rules zod expresses as code are added back where JSON Schema
273
+ * can say them, so an editor flags a subagent with no instruction too.
274
+ */
275
+ export declare function syndicateJsonSchema(): Record<string, unknown>;