@kisev/agentomatic 11.0.0-dev.46.gfbe1e4c6992e

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 (91) hide show
  1. package/README.md +95 -0
  2. package/README.ru.md +98 -0
  3. package/dist/agent-profiles.d.ts +125 -0
  4. package/dist/agent-profiles.js +820 -0
  5. package/dist/assets/agents/architect.md +67 -0
  6. package/dist/assets/agents/critic.md +55 -0
  7. package/dist/assets/agents/manager.md +40 -0
  8. package/dist/assets/agents/mapper.md +33 -0
  9. package/dist/assets/agents/review.md +37 -0
  10. package/dist/assets/agents/worker.md +78 -0
  11. package/dist/assets/commands/agents-md.md +10 -0
  12. package/dist/assets/commands/askme.md +10 -0
  13. package/dist/assets/commands/ast-grep.md +10 -0
  14. package/dist/assets/commands/briefing.md +10 -0
  15. package/dist/assets/commands/code-explain.md +10 -0
  16. package/dist/assets/commands/code-review.md +10 -0
  17. package/dist/assets/commands/commit-msg.md +10 -0
  18. package/dist/assets/commands/docs-prepare.md +10 -0
  19. package/dist/assets/commands/docs-review.md +10 -0
  20. package/dist/assets/commands/goal.md +10 -0
  21. package/dist/assets/commands/humanize.md +10 -0
  22. package/dist/assets/commands/mattermost-triage.md +10 -0
  23. package/dist/assets/commands/mattermost.md +10 -0
  24. package/dist/assets/commands/mr-prepare.md +10 -0
  25. package/dist/assets/commands/release-prepare.md +10 -0
  26. package/dist/assets/commands/release-review.md +10 -0
  27. package/dist/assets/commands/rtk-stats.md +13 -0
  28. package/dist/assets/commands/rtk.md +10 -0
  29. package/dist/assets/commands/skill-improve.md +10 -0
  30. package/dist/assets/commands/slides-prompts-prepare.md +10 -0
  31. package/dist/assets/commands/spec-manage.md +13 -0
  32. package/dist/assets/commands/stopit.md +10 -0
  33. package/dist/assets/commands/task-prepare.md +10 -0
  34. package/dist/assets/commands/task-review.md +10 -0
  35. package/dist/assets/commands/task-triage.md +10 -0
  36. package/dist/assets/commands/taskmatic.md +10 -0
  37. package/dist/assets/commands/team-retro.md +10 -0
  38. package/dist/assets/commands/team-roadmap.md +10 -0
  39. package/dist/assets/commands/team-sprint-close.md +10 -0
  40. package/dist/assets/commands/team-sprint-start.md +10 -0
  41. package/dist/assets/lsp-catalog.json +30 -0
  42. package/dist/assets/migration-inventory.json +301 -0
  43. package/dist/assets/plugins/memomatic.js +3 -0
  44. package/dist/assets/plugins/rtk.js +3 -0
  45. package/dist/assets/plugins/rules-injector.js +3 -0
  46. package/dist/assets/plugins/zed-bell.js +3 -0
  47. package/dist/catalog.d.ts +8 -0
  48. package/dist/catalog.js +39 -0
  49. package/dist/cli-output.d.ts +27 -0
  50. package/dist/cli-output.js +296 -0
  51. package/dist/cli.d.ts +2 -0
  52. package/dist/cli.js +1135 -0
  53. package/dist/config-setup.d.ts +71 -0
  54. package/dist/config-setup.js +438 -0
  55. package/dist/contracts.d.ts +52 -0
  56. package/dist/contracts.js +102 -0
  57. package/dist/doctor.d.ts +44 -0
  58. package/dist/doctor.js +638 -0
  59. package/dist/generate-assets.d.ts +1 -0
  60. package/dist/generate-assets.js +54 -0
  61. package/dist/index.d.ts +125 -0
  62. package/dist/index.js +141 -0
  63. package/dist/installer.d.ts +56 -0
  64. package/dist/installer.js +710 -0
  65. package/dist/jsonc.d.ts +56 -0
  66. package/dist/jsonc.js +432 -0
  67. package/dist/lifecycle.d.ts +77 -0
  68. package/dist/lifecycle.js +736 -0
  69. package/dist/package-metadata.d.ts +4 -0
  70. package/dist/package-metadata.js +42 -0
  71. package/dist/plugins/memomatic.d.ts +76 -0
  72. package/dist/plugins/memomatic.js +101 -0
  73. package/dist/plugins/rtk.d.ts +41 -0
  74. package/dist/plugins/rtk.js +177 -0
  75. package/dist/plugins/rules-injector.d.ts +22 -0
  76. package/dist/plugins/rules-injector.js +131 -0
  77. package/dist/plugins/zed-bell.d.ts +13 -0
  78. package/dist/plugins/zed-bell.js +14 -0
  79. package/dist/reconcile.d.ts +47 -0
  80. package/dist/reconcile.js +490 -0
  81. package/dist/registry.d.ts +21 -0
  82. package/dist/registry.js +92 -0
  83. package/dist/routing.d.ts +133 -0
  84. package/dist/routing.js +578 -0
  85. package/dist/runtime/state.d.ts +9 -0
  86. package/dist/runtime/state.js +193 -0
  87. package/dist/runtime/worktree.d.ts +33 -0
  88. package/dist/runtime/worktree.js +220 -0
  89. package/dist/terminal-wizard.d.ts +3 -0
  90. package/dist/terminal-wizard.js +237 -0
  91. package/package.json +72 -0
@@ -0,0 +1,578 @@
1
+ import { createHash } from "node:crypto";
2
+ import { validateAgentReport } from "./contracts.js";
3
+ export const CATEGORIES = ["exploration", "architecture", "implementation", "review"];
4
+ const MATRIX = {
5
+ schema_version: 1,
6
+ categories: {
7
+ exploration: {
8
+ profiles: ["mapper", "architect"],
9
+ capabilities: ["read", "search"],
10
+ tools: ["read", "glob", "grep"],
11
+ cost: "low",
12
+ latency: "fast",
13
+ },
14
+ architecture: {
15
+ profiles: ["architect", "mapper"],
16
+ capabilities: ["read", "architecture"],
17
+ tools: ["read", "glob", "grep"],
18
+ cost: "medium",
19
+ latency: "standard",
20
+ },
21
+ implementation: {
22
+ profiles: ["worker"],
23
+ capabilities: ["read", "write", "verify"],
24
+ tools: ["read", "edit", "bash"],
25
+ cost: "medium",
26
+ latency: "standard",
27
+ },
28
+ review: {
29
+ profiles: ["review", "critic"],
30
+ capabilities: ["read", "review"],
31
+ tools: ["read", "glob", "grep"],
32
+ cost: "medium",
33
+ latency: "standard",
34
+ },
35
+ },
36
+ };
37
+ function stable(value) {
38
+ if (value === null || typeof value !== "object")
39
+ return JSON.stringify(value);
40
+ if (Array.isArray(value))
41
+ return `[${value.map(stable).join(",")}]`;
42
+ const object = value;
43
+ return `{${Object.keys(object)
44
+ .sort()
45
+ .map((key) => `${JSON.stringify(key)}:${stable(object[key])}`)
46
+ .join(",")}}`;
47
+ }
48
+ function digest(value) {
49
+ return createHash("sha256").update(stable(value), "utf8").digest("hex");
50
+ }
51
+ function validRfc3339(value) {
52
+ if (typeof value !== "string")
53
+ return false;
54
+ const match = /^(\d{4})-(0[1-9]|1[0-2])-(\d{2})T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/i.exec(value);
55
+ if (!match)
56
+ return false;
57
+ const year = Number(match[1]);
58
+ const month = Number(match[2]);
59
+ const day = Number(match[3]);
60
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
61
+ const monthDays = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
62
+ return year > 0 && day >= 1 && day <= monthDays[month - 1];
63
+ }
64
+ export function validateRoutingReceipt(value, context) {
65
+ if (!value || typeof value !== "object" || Array.isArray(value))
66
+ throw new Error("routing receipt is not an object");
67
+ const decision = value;
68
+ if ("destination" in decision) {
69
+ const receipt = value;
70
+ const receiptFields = [
71
+ "schema_version",
72
+ "task_digest",
73
+ "requirements_digest",
74
+ "destination",
75
+ "agent",
76
+ "card_digest",
77
+ "card_revision",
78
+ "host_inventory_revision",
79
+ "expires_at",
80
+ "nonce",
81
+ "receipt_digest",
82
+ ];
83
+ if (Object.keys(receipt).sort().join(",") !== receiptFields.sort().join(",") ||
84
+ receipt.schema_version !== 1 ||
85
+ !CATEGORIES.includes(receipt.destination) ||
86
+ !receipt.agent ||
87
+ !/^[a-f0-9]{64}$/.test(receipt.task_digest) ||
88
+ !/^[a-f0-9]{64}$/.test(receipt.requirements_digest) ||
89
+ (receipt.card_digest !== null && !/^[a-f0-9]{64}$/.test(receipt.card_digest)) ||
90
+ typeof receipt.nonce !== "string" ||
91
+ receipt.nonce.length < 32 ||
92
+ !/^[a-f0-9]{64}$/.test(receipt.host_inventory_revision) ||
93
+ !/^[a-f0-9]{64}$/.test(receipt.receipt_digest) ||
94
+ !validRfc3339(receipt.expires_at) ||
95
+ !Number.isFinite(Date.parse(receipt.expires_at)) ||
96
+ (receipt.card_revision !== null &&
97
+ (!Number.isInteger(receipt.card_revision) || receipt.card_revision < 1)))
98
+ throw new Error("routing receipt is malformed");
99
+ if (Date.parse(receipt.expires_at) < Date.now())
100
+ throw new Error("routing receipt has expired");
101
+ const { receipt_digest: _receiptDigest, ...receiptBase } = receipt;
102
+ if (digest(receiptBase) !== receipt.receipt_digest)
103
+ throw new Error("routing receipt integrity is invalid");
104
+ if (context?.category !== undefined && receipt.destination !== context.category)
105
+ throw new Error("routing receipt destination does not match");
106
+ if (context?.task !== undefined && receipt.task_digest !== digest(context.task))
107
+ throw new Error("routing receipt task does not match");
108
+ if (context?.requirements !== undefined &&
109
+ receipt.requirements_digest !== digest(context.requirements))
110
+ throw new Error("routing receipt requirements do not match");
111
+ if (context?.card !== undefined && receipt.card_digest !== digest(context.card))
112
+ throw new Error("routing receipt card does not match");
113
+ return receipt;
114
+ }
115
+ if (decision.schema_version !== 1 ||
116
+ decision.status !== "selected" ||
117
+ !decision.agent ||
118
+ !CATEGORIES.includes(decision.category))
119
+ throw new Error("routing receipt is not selected");
120
+ if (!decision.matrix_revision ||
121
+ !decision.decision_digest ||
122
+ !/^[a-f0-9]{64}$/.test(decision.decision_digest))
123
+ throw new Error("routing receipt is incomplete");
124
+ const { decision_digest: _digest, ...base } = decision;
125
+ if (digest(base) !== decision.decision_digest)
126
+ throw new Error("routing receipt digest is forged or stale");
127
+ if (decision.matrix_revision !== digest(MATRIX))
128
+ throw new Error("routing receipt matrix is stale");
129
+ if (context?.category !== undefined && decision.category !== context.category)
130
+ throw new Error("routing receipt category does not match");
131
+ if (context?.task !== undefined && decision.task_digest !== digest(context.task))
132
+ throw new Error("routing receipt task does not match");
133
+ return decision;
134
+ }
135
+ function missing(required, actual) {
136
+ const values = new Set(actual ?? []);
137
+ return required.some((item) => !values.has(item));
138
+ }
139
+ function exceeds(value, limit, order) {
140
+ return limit !== undefined && order.indexOf(value) > order.indexOf(limit);
141
+ }
142
+ export function resolveRouting(input) {
143
+ const category = MATRIX.categories[input.category];
144
+ const revision = digest(MATRIX);
145
+ const hostInventoryRevision = input.inventory_revision ?? digest(input.agents);
146
+ const inventory = new Map(input.agents.map((agent) => [agent.agent, agent]));
147
+ const alternatives = [];
148
+ const eligible = [];
149
+ const dynamicCritics = input.category === "review"
150
+ ? input.agents
151
+ .map(({ agent }) => agent)
152
+ .filter((agent) => /^critic-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agent))
153
+ .sort()
154
+ : [];
155
+ const profiles = input.trusted_override && input.override && !category.profiles.includes(input.override)
156
+ ? [...category.profiles, ...dynamicCritics, input.override]
157
+ : [...category.profiles, ...dynamicCritics];
158
+ for (const profile of profiles) {
159
+ const agent = inventory.get(profile);
160
+ const reasons = [];
161
+ if (!agent)
162
+ reasons.push("agent_missing");
163
+ else {
164
+ if (agent.available === false)
165
+ reasons.push("agent_unavailable");
166
+ if (missing([...category.capabilities, ...input.requirements], agent.capabilities))
167
+ reasons.push("capabilities_missing");
168
+ if (missing(category.tools, agent.tools))
169
+ reasons.push("tools_missing");
170
+ if (exceeds(category.cost, input.budget?.cost_class, ["low", "medium", "high"]))
171
+ reasons.push("cost_class_exceeded");
172
+ if (exceeds(category.latency, input.budget?.latency_class, ["fast", "standard", "slow"]))
173
+ reasons.push("latency_class_exceeded");
174
+ }
175
+ if (reasons.length)
176
+ alternatives.push({ agent: profile, excluded_reasons: reasons });
177
+ else if (agent)
178
+ eligible.push(agent);
179
+ }
180
+ if (input.override &&
181
+ !input.trusted_override &&
182
+ !dynamicCritics.includes(input.override) &&
183
+ !category.profiles.includes(input.override))
184
+ throw new Error("Unknown or user-owned profile requires a trusted override");
185
+ const selected = input.override
186
+ ? eligible.find((agent) => agent.agent === input.override)
187
+ : eligible[0];
188
+ const card = input.execution_card === undefined ? undefined : validateExecutionCard(input.execution_card);
189
+ if (card && !card.valid)
190
+ throw new Error(`Invalid execution card: ${card.failedField}`);
191
+ const base = selected
192
+ ? {
193
+ schema_version: 1,
194
+ status: "selected",
195
+ category: input.category,
196
+ agent: selected.agent,
197
+ reason_codes: [
198
+ input.override
199
+ ? "explicit_override"
200
+ : selected.agent === category.profiles[0]
201
+ ? "primary_available"
202
+ : "fallback_selected",
203
+ "capabilities_match",
204
+ ],
205
+ alternatives,
206
+ matrix_revision: revision,
207
+ host_inventory_revision: hostInventoryRevision,
208
+ task_digest: digest(input.task ?? ""),
209
+ requirements_digest: digest(input.requirements),
210
+ ...(card?.valid
211
+ ? {
212
+ execution_card_digest: digest(card.card),
213
+ execution_card_revision: card.card.revision,
214
+ }
215
+ : {}),
216
+ }
217
+ : {
218
+ schema_version: 1,
219
+ status: "escalate",
220
+ category: input.category,
221
+ reason_codes: [input.override ? "override_unavailable" : "no_eligible_profile"],
222
+ alternatives,
223
+ matrix_revision: revision,
224
+ host_inventory_revision: hostInventoryRevision,
225
+ task_digest: digest(input.task ?? ""),
226
+ requirements_digest: digest(input.requirements),
227
+ ...(card?.valid
228
+ ? {
229
+ execution_card_digest: digest(card.card),
230
+ execution_card_revision: card.card.revision,
231
+ }
232
+ : {}),
233
+ };
234
+ return { ...base, decision_digest: digest(base) };
235
+ }
236
+ function isNonEmptyStringArray(value) {
237
+ return (Array.isArray(value) &&
238
+ value.length > 0 &&
239
+ value.every((item) => typeof item === "string" && item.length > 0));
240
+ }
241
+ export function validateExecutionCard(card) {
242
+ if (!card || typeof card !== "object" || Array.isArray(card)) {
243
+ return { valid: false, failedField: "root" };
244
+ }
245
+ const c = card;
246
+ const expectedFields = [
247
+ "schema_version",
248
+ "status",
249
+ "card_id",
250
+ "revision",
251
+ "objective",
252
+ "evidence",
253
+ "changed_behavior",
254
+ "risks",
255
+ "write_set",
256
+ "control_markers",
257
+ "decisions",
258
+ "steps",
259
+ "acceptance_criteria",
260
+ "checks",
261
+ "operations",
262
+ "confirmations",
263
+ "boundaries",
264
+ ];
265
+ if (Object.keys(c).sort().join(",") !== expectedFields.slice().sort().join(","))
266
+ return { valid: false, failedField: "schema" };
267
+ if (c.schema_version !== 1)
268
+ return { valid: false, failedField: "schema_version" };
269
+ if (c.status !== "READY")
270
+ return { valid: false, failedField: "status" };
271
+ if (typeof c.card_id !== "string" || !c.card_id)
272
+ return { valid: false, failedField: "card_id" };
273
+ if (typeof c.revision !== "number" || !Number.isInteger(c.revision) || c.revision < 1)
274
+ return { valid: false, failedField: "revision" };
275
+ if (typeof c.objective !== "string" || !c.objective)
276
+ return { valid: false, failedField: "objective" };
277
+ if (!isNonEmptyStringArray(c.evidence))
278
+ return { valid: false, failedField: "evidence" };
279
+ if (!isNonEmptyStringArray(c.changed_behavior))
280
+ return { valid: false, failedField: "changed_behavior" };
281
+ if (!isNonEmptyStringArray(c.risks))
282
+ return { valid: false, failedField: "risks" };
283
+ if (!isNonEmptyStringArray(c.write_set))
284
+ return { valid: false, failedField: "write_set" };
285
+ if (new Set(c.write_set).size !== c.write_set.length)
286
+ return { valid: false, failedField: "write_set" };
287
+ if (!Array.isArray(c.control_markers) || c.control_markers.length === 0)
288
+ return { valid: false, failedField: "control_markers" };
289
+ for (const marker of c.control_markers) {
290
+ if (!marker || typeof marker !== "object" || Array.isArray(marker))
291
+ return { valid: false, failedField: "control_markers" };
292
+ const m = marker;
293
+ if (typeof m.path !== "string" || !m.path)
294
+ return { valid: false, failedField: "control_markers" };
295
+ const hasExpected = typeof m.expected === "string" && m.expected.length > 0;
296
+ const hasExpectedAbsent = m.expected_absent === true;
297
+ if (hasExpected === hasExpectedAbsent)
298
+ return { valid: false, failedField: "control_markers" };
299
+ if (Object.keys(m).some((k) => k !== "path" && k !== "expected" && k !== "expected_absent"))
300
+ return { valid: false, failedField: "control_markers" };
301
+ }
302
+ if (!isNonEmptyStringArray(c.decisions))
303
+ return { valid: false, failedField: "decisions" };
304
+ if (!Array.isArray(c.steps) || c.steps.length === 0)
305
+ return { valid: false, failedField: "steps" };
306
+ for (const step of c.steps) {
307
+ if (!step || typeof step !== "object" || Array.isArray(step))
308
+ return { valid: false, failedField: "steps" };
309
+ const s = step;
310
+ if (typeof s.path !== "string" || !s.path)
311
+ return { valid: false, failedField: "steps" };
312
+ if (typeof s.operation !== "string" || !s.operation)
313
+ return { valid: false, failedField: "steps" };
314
+ if (Object.keys(s).some((k) => k !== "path" && k !== "operation"))
315
+ return { valid: false, failedField: "steps" };
316
+ }
317
+ if (!isNonEmptyStringArray(c.acceptance_criteria))
318
+ return { valid: false, failedField: "acceptance_criteria" };
319
+ if (!isNonEmptyStringArray(c.checks))
320
+ return { valid: false, failedField: "checks" };
321
+ if (!Array.isArray(c.operations) || c.operations.length === 0)
322
+ return { valid: false, failedField: "operations" };
323
+ const operationKinds = new Set([
324
+ "write",
325
+ "check",
326
+ "commit",
327
+ "rebase",
328
+ "push",
329
+ "merge",
330
+ "tag",
331
+ "release",
332
+ ]);
333
+ for (const operation of c.operations) {
334
+ if (!operation || typeof operation !== "object" || Array.isArray(operation))
335
+ return { valid: false, failedField: "operations" };
336
+ const item = operation;
337
+ if (typeof item.kind !== "string" ||
338
+ !operationKinds.has(item.kind) ||
339
+ typeof item.command !== "string" ||
340
+ !item.command)
341
+ return { valid: false, failedField: "operations" };
342
+ if (item.path !== undefined && (typeof item.path !== "string" || !item.path))
343
+ return { valid: false, failedField: "operations" };
344
+ if (item.confirmation_ref !== undefined &&
345
+ (typeof item.confirmation_ref !== "string" || !item.confirmation_ref))
346
+ return { valid: false, failedField: "operations" };
347
+ if (Object.keys(item).some((key) => !["kind", "command", "path", "confirmation_ref"].includes(key)))
348
+ return { valid: false, failedField: "operations" };
349
+ if ((item.kind === "write" || item.kind === "check") && typeof item.path !== "string")
350
+ return { valid: false, failedField: "operations" };
351
+ }
352
+ const operationSet = new Set(c.operations.map(({ kind }) => kind));
353
+ for (const required of ["commit", "rebase", "push", "merge", "tag", "release"])
354
+ if (!operationSet.has(required))
355
+ return { valid: false, failedField: "operations" };
356
+ if (!c.confirmations || typeof c.confirmations !== "object" || Array.isArray(c.confirmations))
357
+ return { valid: false, failedField: "confirmations" };
358
+ const confirmations = c.confirmations;
359
+ if (typeof confirmations.execution !== "string" || !confirmations.execution)
360
+ return { valid: false, failedField: "confirmations" };
361
+ if (operationSet.has("push") ||
362
+ operationSet.has("merge") ||
363
+ operationSet.has("tag") ||
364
+ operationSet.has("release"))
365
+ if (typeof confirmations.publication !== "string" || !confirmations.publication)
366
+ return { valid: false, failedField: "confirmations" };
367
+ if (operationSet.has("rebase"))
368
+ if (typeof confirmations.history_rewrite !== "string" || !confirmations.history_rewrite)
369
+ return { valid: false, failedField: "confirmations" };
370
+ if (Object.keys(confirmations).some((key) => !["execution", "publication", "history_rewrite"].includes(key)))
371
+ return { valid: false, failedField: "confirmations" };
372
+ if (!c.boundaries || typeof c.boundaries !== "object" || Array.isArray(c.boundaries))
373
+ return { valid: false, failedField: "boundaries" };
374
+ const b = c.boundaries;
375
+ if (!isNonEmptyStringArray(b.forbidden_paths) || typeof b.scope !== "string" || !b.scope)
376
+ return { valid: false, failedField: "boundaries" };
377
+ if (Object.keys(b).some((k) => k !== "forbidden_paths" && k !== "scope"))
378
+ return { valid: false, failedField: "boundaries" };
379
+ const writeSet = c.write_set;
380
+ const forbiddenPaths = b.forbidden_paths;
381
+ const safePath = (path) => !path.startsWith("/") &&
382
+ !path.includes("\\") &&
383
+ !path.split("/").some((part) => !part || part === "." || part === "..");
384
+ if (![...writeSet, ...forbiddenPaths].every(safePath))
385
+ return { valid: false, failedField: "boundaries" };
386
+ const stepPaths = c.steps.map((s) => s.path);
387
+ const markerPaths = c.control_markers.map((m) => m.path);
388
+ const referenced = new Set([...stepPaths, ...markerPaths]);
389
+ if (![...referenced].every((p) => writeSet.includes(p)))
390
+ return { valid: false, failedField: "steps" };
391
+ const overlaps = (left, right) => left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
392
+ if (writeSet.some((p) => forbiddenPaths.some((forbidden) => overlaps(p, forbidden))))
393
+ return { valid: false, failedField: "boundaries" };
394
+ if (!writeSet.every((path) => stepPaths.includes(path)))
395
+ return { valid: false, failedField: "steps" };
396
+ const operations = c.operations;
397
+ if (!writeSet.every((path) => operations.some((operation) => operation.kind === "write" && operation.path === path) &&
398
+ operations.some((operation) => operation.kind === "check" && operation.path === path)))
399
+ return { valid: false, failedField: "operations" };
400
+ for (const operation of operations) {
401
+ if (["commit", "rebase", "push", "merge", "tag", "release"].includes(operation.kind) &&
402
+ !operation.confirmation_ref)
403
+ return { valid: false, failedField: "operations" };
404
+ if (operation.kind === "rebase" && operation.confirmation_ref !== confirmations.history_rewrite)
405
+ return { valid: false, failedField: "confirmations" };
406
+ if (["push", "merge", "tag", "release"].includes(operation.kind) &&
407
+ operation.confirmation_ref !== confirmations.publication)
408
+ return { valid: false, failedField: "confirmations" };
409
+ }
410
+ return { valid: true, card: c };
411
+ }
412
+ export class ExecutionCardLifecycle {
413
+ #status = "READY";
414
+ #cardID;
415
+ #revision;
416
+ constructor(card) {
417
+ const result = validateExecutionCard(card);
418
+ if (!result.valid)
419
+ throw new Error(`Invalid execution card: ${result.failedField}`);
420
+ this.#cardID = result.card.card_id;
421
+ this.#revision = result.card.revision;
422
+ }
423
+ get status() {
424
+ return this.#status;
425
+ }
426
+ get card_id() {
427
+ return this.#cardID;
428
+ }
429
+ get revision() {
430
+ return this.#revision;
431
+ }
432
+ transition(status, identity) {
433
+ if (identity.card_id !== this.#cardID || identity.revision !== this.#revision)
434
+ throw new Error("Execution card identity is stale");
435
+ const allowed = {
436
+ READY: ["RUNNING", "CANCELLED"],
437
+ RUNNING: ["COMPLETED", "BLOCKED", "FAILED", "REJECTED_PLAN", "CANCELLED"],
438
+ COMPLETED: ["APPROVED", "CHANGES_REQUIRED"],
439
+ BLOCKED: [],
440
+ FAILED: [],
441
+ REJECTED_PLAN: [],
442
+ APPROVED: [],
443
+ CHANGES_REQUIRED: [],
444
+ CANCELLED: [],
445
+ };
446
+ if (!allowed[this.#status].includes(status))
447
+ throw new Error(`Invalid execution-card transition: ${this.#status} -> ${status}`);
448
+ this.#status = status;
449
+ return this.#status;
450
+ }
451
+ }
452
+ export class RoutingGate {
453
+ #receipts = new Map();
454
+ #active = new Map();
455
+ #ttlMs = 10 * 60 * 1000;
456
+ preview(input) {
457
+ return resolveRouting(input);
458
+ }
459
+ dispatch(input, provided) {
460
+ const decision = resolveRouting(input);
461
+ if (!provided || typeof provided !== "object" || stable(provided) !== stable(decision))
462
+ throw new Error("Routing decision is stale; request a new preview");
463
+ if (decision.status !== "selected" || !decision.agent)
464
+ throw new Error("Routing escalated; no agent was dispatched");
465
+ return decision;
466
+ }
467
+ grant(sessionID, decision, context) {
468
+ const now = Date.now();
469
+ const taskDigest = context?.task === undefined ? decision.task_digest : digest(context.task);
470
+ const requirementsDigest = context?.requirements === undefined
471
+ ? decision.requirements_digest
472
+ : digest(context.requirements);
473
+ const cardDigest = context?.card === undefined ? decision.execution_card_digest : digest(context.card);
474
+ const receiptBase = {
475
+ schema_version: 1,
476
+ task_digest: taskDigest,
477
+ requirements_digest: requirementsDigest,
478
+ destination: decision.category,
479
+ agent: decision.agent,
480
+ card_digest: cardDigest ?? null,
481
+ card_revision: decision.execution_card_revision ?? null,
482
+ host_inventory_revision: decision.host_inventory_revision,
483
+ expires_at: new Date(now + this.#ttlMs).toISOString(),
484
+ nonce: createHash("sha256")
485
+ .update(`${sessionID}:${now}:${decision.decision_digest}`)
486
+ .digest("hex"),
487
+ };
488
+ const receipt = { ...receiptBase, receipt_digest: digest(receiptBase) };
489
+ this.#receipts.set(sessionID, {
490
+ decision,
491
+ taskDigest,
492
+ requirementsDigest,
493
+ cardDigest,
494
+ expiresAt: Date.parse(receipt.expires_at),
495
+ });
496
+ return receipt;
497
+ }
498
+ cancel(sessionID) {
499
+ this.#receipts.delete(sessionID);
500
+ this.#active.delete(sessionID);
501
+ }
502
+ consume(sessionID, agent, context) {
503
+ const receipt = this.#receipts.get(sessionID);
504
+ if (!receipt)
505
+ throw new Error("Native Task requires an active routing receipt; use the route tool");
506
+ if (receipt.decision.agent !== agent)
507
+ throw new Error("Native Task agent does not match the active routing receipt");
508
+ if (Date.now() > receipt.expiresAt)
509
+ throw new Error("Routing receipt has expired; request a new preview");
510
+ if (context) {
511
+ const taskDigest = digest(context.task ?? "");
512
+ if (taskDigest !== receipt.taskDigest)
513
+ throw new Error("Routing receipt task does not match the active routing receipt");
514
+ const requirementsDigest = digest(context.requirements ?? []);
515
+ if (requirementsDigest !== receipt.requirementsDigest)
516
+ throw new Error("Routing receipt requirements do not match the active routing receipt");
517
+ const cardDigest = context.card !== undefined ? digest(context.card) : undefined;
518
+ if (cardDigest !== receipt.cardDigest)
519
+ throw new Error("Routing receipt execution card does not match the active routing receipt");
520
+ }
521
+ this.#receipts.delete(sessionID);
522
+ const validatedCard = context?.card === undefined ? undefined : validateExecutionCard(context.card);
523
+ const card = validatedCard?.valid ? validatedCard.card : undefined;
524
+ const lifecycle = card ? new ExecutionCardLifecycle(card) : undefined;
525
+ if (lifecycle)
526
+ lifecycle.transition("RUNNING", { card_id: card.card_id, revision: card.revision });
527
+ this.#active.set(sessionID, { agent, card, lifecycle, expiresAt: receipt.expiresAt });
528
+ }
529
+ complete(sessionID, agent, output) {
530
+ const active = this.#active.get(sessionID);
531
+ if (!active || active.agent !== agent)
532
+ throw new Error("Task result has no matching active routing receipt");
533
+ if (Date.now() > active.expiresAt)
534
+ throw new Error("Task result routing receipt has expired");
535
+ const report = output && typeof output === "object" ? output : undefined;
536
+ const role = /^critic-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agent) ? "critic" : agent;
537
+ const value = report?.[role === "architect" ? "execution_card" : `${role}_report`] ??
538
+ (role === "critic" && !active.card ? report?.review_report : undefined) ??
539
+ report?.report;
540
+ if (!value || typeof value !== "object" || Array.isArray(value))
541
+ throw new Error("Task result is missing a structured agent report");
542
+ const result = value;
543
+ validateAgentReport(role, report, active.card);
544
+ if (active.card &&
545
+ (result.card_id !== active.card.card_id || result.revision !== active.card.revision))
546
+ throw new Error("Task result does not match the active execution card");
547
+ const status = result.status;
548
+ const allowed = agent === "worker"
549
+ ? ["COMPLETED", "BLOCKED", "FAILED", "REJECTED_PLAN"]
550
+ : agent === "critic"
551
+ ? ["APPROVED", "CHANGES_REQUIRED"]
552
+ : undefined;
553
+ if (allowed && (typeof status !== "string" || !allowed.includes(status)))
554
+ throw new Error("Task result has an invalid machine transition");
555
+ if (active.lifecycle && status === "COMPLETED")
556
+ active.lifecycle.transition("COMPLETED", {
557
+ card_id: active.card.card_id,
558
+ revision: active.card.revision,
559
+ });
560
+ if (active.lifecycle &&
561
+ (status === "BLOCKED" || status === "FAILED" || status === "REJECTED_PLAN"))
562
+ active.lifecycle.transition(status, {
563
+ card_id: active.card.card_id,
564
+ revision: active.card.revision,
565
+ });
566
+ if (active.lifecycle && (status === "APPROVED" || status === "CHANGES_REQUIRED")) {
567
+ active.lifecycle.transition("COMPLETED", {
568
+ card_id: active.card.card_id,
569
+ revision: active.card.revision,
570
+ });
571
+ active.lifecycle.transition(status, {
572
+ card_id: active.card.card_id,
573
+ revision: active.card.revision,
574
+ });
575
+ }
576
+ this.#active.delete(sessionID);
577
+ }
578
+ }
@@ -0,0 +1,9 @@
1
+ export declare function stateRoot(name: string, homePath?: string): string;
2
+ export declare function configRoot(name: string, homePath?: string): string;
3
+ export declare function readState<T>(path: string, boundary: string): Promise<T | undefined>;
4
+ export declare function listState(boundary: string, suffix?: string): Promise<string[]>;
5
+ export declare function writeState(path: string, boundary: string, value: unknown): Promise<void>;
6
+ export declare function writeVersionedState(path: string, boundary: string, logicalName: string, value: unknown): Promise<void>;
7
+ export declare function appendState(path: string, boundary: string, value: unknown): Promise<void>;
8
+ export declare function withStateLock<T>(boundary: string, callback: () => Promise<T>): Promise<T>;
9
+ export declare function digest(value: unknown): string;