@hassangameryt/freeflow 0.7.1 → 0.7.3

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 (147) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/README.md +153 -128
  6. package/capabilities/cognitive-routing/SKILL.md +126 -274
  7. package/command-surface.json +7 -1
  8. package/gemini-extension.json +1 -1
  9. package/package.json +4 -4
  10. package/pi-extension/dist/cognitive-routing-v2/budget.js +42 -0
  11. package/pi-extension/dist/cognitive-routing-v2/config.js +136 -0
  12. package/pi-extension/dist/cognitive-routing-v2/projection.js +330 -0
  13. package/pi-extension/dist/cognitive-routing-v2/render.js +252 -0
  14. package/pi-extension/dist/cognitive-routing-v2/runtime.js +2181 -0
  15. package/pi-extension/dist/cognitive-routing-v2/schemas.js +173 -0
  16. package/pi-extension/dist/cognitive-routing-v2/state.js +828 -0
  17. package/pi-extension/dist/cognitive-routing-v2/tools.js +60 -0
  18. package/pi-extension/dist/cognitive-routing-v2/types.js +48 -0
  19. package/pi-extension/dist/context-virtualization/runtime.js +29 -7
  20. package/pi-extension/dist/freeflow-context/tool.js +12 -6
  21. package/pi-extension/dist/index.js +302 -626
  22. package/pi-extension/dist/provider-support/astra/adapter.js +110 -0
  23. package/pi-extension/dist/provider-support/astra/history.js +125 -0
  24. package/pi-extension/dist/provider-support/astra/index.js +11 -0
  25. package/pi-extension/dist/provider-support/astra/session-state.js +114 -0
  26. package/pi-extension/dist/provider-support/index.js +4 -0
  27. package/pi-extension/dist/runtime/request-history.js +216 -0
  28. package/pi-extension/dist/runtime/runtime-context.js +85 -16
  29. package/pi-extension/dist/session-sources/events.js +217 -0
  30. package/pi-extension/dist/session-sources/provenance.js +32 -0
  31. package/pi-extension/dist/session-sources/read-only-session.js +148 -0
  32. package/pi-extension/dist/session-sources/sources.js +277 -0
  33. package/pi-extension/dist/settings/settings-ui.js +274 -147
  34. package/pi-extension/src/cognitive-routing-v2/budget.ts +42 -0
  35. package/pi-extension/src/cognitive-routing-v2/config.ts +173 -0
  36. package/pi-extension/src/cognitive-routing-v2/projection.ts +375 -0
  37. package/pi-extension/src/cognitive-routing-v2/render.ts +277 -0
  38. package/pi-extension/src/cognitive-routing-v2/runtime.ts +2241 -0
  39. package/pi-extension/src/cognitive-routing-v2/schemas.ts +180 -0
  40. package/pi-extension/src/cognitive-routing-v2/state.ts +839 -0
  41. package/pi-extension/src/cognitive-routing-v2/tools.ts +62 -0
  42. package/pi-extension/src/cognitive-routing-v2/types.ts +254 -0
  43. package/pi-extension/src/context-virtualization/runtime.ts +30 -8
  44. package/pi-extension/src/freeflow-context/tool.ts +12 -6
  45. package/pi-extension/src/index.ts +308 -675
  46. package/pi-extension/src/provider-support/astra/adapter.ts +108 -0
  47. package/pi-extension/src/provider-support/astra/history.ts +145 -0
  48. package/pi-extension/src/provider-support/astra/index.ts +12 -0
  49. package/pi-extension/src/provider-support/astra/session-state.ts +113 -0
  50. package/pi-extension/src/provider-support/index.ts +5 -0
  51. package/pi-extension/src/runtime/request-history.ts +243 -0
  52. package/pi-extension/src/runtime/runtime-context.ts +100 -17
  53. package/pi-extension/src/session-sources/events.ts +230 -0
  54. package/pi-extension/src/session-sources/provenance.ts +39 -0
  55. package/pi-extension/src/session-sources/read-only-session.ts +170 -0
  56. package/pi-extension/src/session-sources/sources.ts +334 -0
  57. package/pi-extension/src/settings/settings-ui.ts +311 -158
  58. package/pi-extension/tests/cache-reuse/history.test.js +117 -0
  59. package/pi-extension/tests/cache-reuse/native.test.js +91 -0
  60. package/pi-extension/tests/cache-reuse/settings.test.js +211 -0
  61. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +577 -0
  62. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +183 -0
  63. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +557 -0
  64. package/pi-extension/tests/cognitive-routing-v2/native.test.js +474 -0
  65. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +87 -0
  66. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +300 -0
  67. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +241 -0
  68. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +1034 -0
  69. package/pi-extension/tests/cognitive-routing-v2/render.test.js +244 -0
  70. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +423 -0
  71. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +521 -0
  72. package/pi-extension/tests/cognitive-routing-v2/state.test.js +586 -0
  73. package/pi-extension/tests/context-virtualization/extension.test.js +33 -7
  74. package/pi-extension/tests/context-virtualization/runtime.test.js +17 -3
  75. package/pi-extension/tests/fixtures/routing-native.js +191 -0
  76. package/pi-extension/tests/integration/mode-free-core.test.js +6 -4
  77. package/pi-extension/tests/integration/pi-extension.test.js +655 -59
  78. package/pi-extension/tests/integration/prompt-architecture.test.js +127 -30
  79. package/pi-extension/tests/integration/unaffected-regressions.test.js +78 -11
  80. package/pi-extension/tests/provider-support/astra-boundaries.test.js +75 -0
  81. package/pi-extension/tests/provider-support/astra.test.js +306 -0
  82. package/pi-extension/tests/provider-support/cli.test.js +92 -0
  83. package/pi-extension/tests/provider-support/native.test.js +183 -0
  84. package/plugin.json +1 -1
  85. package/runtime/prompts/cognitive-routing.md +4 -5
  86. package/runtime/prompts/core.md +51 -45
  87. package/runtime/prompts/interaction-contract.md +5 -5
  88. package/skills/action-selection/SKILL.md +57 -97
  89. package/skills/design-for-depth/SKILL.md +58 -193
  90. package/skills/design-for-depth/references/implementation-decisions.md +76 -0
  91. package/skills/diagnose-failure/SKILL.md +64 -196
  92. package/skills/discuss/SKILL.md +64 -123
  93. package/skills/discuss/references/discussion-continuity.md +7 -3
  94. package/skills/execute-work/SKILL.md +69 -88
  95. package/skills/execute-work/references/execute-work-edges.md +9 -3
  96. package/skills/handoff/SKILL.md +44 -54
  97. package/skills/handoff/references/handoff-templates.md +15 -6
  98. package/skills/review-artifact/SKILL.md +80 -127
  99. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
  100. package/skills/review-work/SKILL.md +65 -115
  101. package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
  102. package/skills/simplify-code/SKILL.md +47 -100
  103. package/skills/track-work/SKILL.md +128 -119
  104. package/skills/track-work/references/working-record-format.md +14 -2
  105. package/skills/verify-work/SKILL.md +58 -62
  106. package/skills/verify-work/references/integration-evidence.md +4 -2
  107. package/skills/verify-work/references/test-design.md +89 -0
  108. package/skills/workflow/SKILL.md +73 -119
  109. package/skills/write-plan/SKILL.md +46 -77
  110. package/skills/write-plan/references/plan-shapes.md +5 -5
  111. package/skills/write-spec/SKILL.md +51 -63
  112. package/skills/write-spec/references/artifact-standards.md +2 -2
  113. package/skills/write-spec/references/decision-records.md +3 -2
  114. package/skills/write-spec/references/spec-shapes.md +15 -0
  115. package/pi-extension/dist/cognitive-routing/commands.js +0 -96
  116. package/pi-extension/dist/cognitive-routing/config.js +0 -232
  117. package/pi-extension/dist/cognitive-routing/controller.js +0 -1292
  118. package/pi-extension/dist/cognitive-routing/history.js +0 -451
  119. package/pi-extension/dist/cognitive-routing/host.js +0 -188
  120. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +0 -414
  121. package/pi-extension/dist/cognitive-routing/pi-session-control.js +0 -320
  122. package/pi-extension/dist/cognitive-routing/runtime.js +0 -43
  123. package/pi-extension/dist/cognitive-routing/tool.js +0 -212
  124. package/pi-extension/dist/cognitive-routing/types.js +0 -6
  125. package/pi-extension/src/cognitive-routing/commands.ts +0 -129
  126. package/pi-extension/src/cognitive-routing/config.ts +0 -303
  127. package/pi-extension/src/cognitive-routing/controller.ts +0 -1628
  128. package/pi-extension/src/cognitive-routing/history.ts +0 -650
  129. package/pi-extension/src/cognitive-routing/host.ts +0 -266
  130. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +0 -538
  131. package/pi-extension/src/cognitive-routing/pi-session-control.ts +0 -444
  132. package/pi-extension/src/cognitive-routing/runtime.ts +0 -57
  133. package/pi-extension/src/cognitive-routing/tool.ts +0 -273
  134. package/pi-extension/src/cognitive-routing/types.ts +0 -89
  135. package/pi-extension/tests/cognitive-routing/commands.test.js +0 -134
  136. package/pi-extension/tests/cognitive-routing/controller.test.js +0 -1128
  137. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +0 -1142
  138. package/pi-extension/tests/cognitive-routing/history.test.js +0 -274
  139. package/pi-extension/tests/cognitive-routing/host-identity.test.js +0 -63
  140. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +0 -434
  141. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +0 -174
  142. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +0 -91
  143. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +0 -279
  144. package/pi-extension/tests/cognitive-routing/tool.test.js +0 -230
  145. package/skills/tdd/SKILL.md +0 -143
  146. package/skills/tdd/references/test-design.md +0 -96
  147. /package/pi-extension/tests/{cognitive-routing/host-fixture.js → fixtures/pi-host.js} +0 -0
@@ -0,0 +1,828 @@
1
+ import {
2
+ EFFORTS,
3
+ PROFILES,
4
+ ROUTING_ENTRY,
5
+ canonical,
6
+ eventKey,
7
+ eventValue,
8
+ isObject,
9
+ isWorkerProfile,
10
+ requireCondition as check,
11
+ } from "./types.js";
12
+ const text = (x, max = 32768) => typeof x === "string" && x.trim().length > 0 && x.length <= max;
13
+ const identity = (x) => text(x, 512);
14
+ const nullableId = (x) => x === null || identity(x);
15
+ const shape = (x, keys) => isObject(x) && Object.keys(x).every((k) => keys.includes(k));
16
+ const uniqueStrings = (x) => Array.isArray(x) && x.every(identity) && new Set(x).size === x.length;
17
+ const pair = (x) =>
18
+ shape(x, ["provider", "modelId", "thinking"]) &&
19
+ identity(x.provider) &&
20
+ identity(x.modelId) &&
21
+ EFFORTS.includes(x.thinking);
22
+ const profileOverrides = (x) =>
23
+ isObject(x) &&
24
+ Object.keys(x).length > 0 &&
25
+ Object.keys(x).every((key) => PROFILES.includes(key)) &&
26
+ Object.values(x).every((value) => value === null || pair(value));
27
+ const problemList = (xs) =>
28
+ Array.isArray(xs) &&
29
+ xs.every(
30
+ (x) =>
31
+ shape(x, ["ref", "code", "detail"]) && typeof x.ref === "string" && text(x.code, 256) && text(x.detail, 4096),
32
+ );
33
+ const reservation = (r) =>
34
+ shape(r, [
35
+ "id",
36
+ "selectionRevision",
37
+ "receiver",
38
+ "target",
39
+ "qualification",
40
+ "sources",
41
+ "maximumInputTokens",
42
+ "outputReserve",
43
+ "estimateMethod",
44
+ ]) &&
45
+ identity(r.id) &&
46
+ Number.isSafeInteger(r.selectionRevision) &&
47
+ r.selectionRevision >= 0 &&
48
+ PROFILES.includes(r.receiver) &&
49
+ pair(r.target) &&
50
+ text(r.qualification) &&
51
+ Array.isArray(r.sources) &&
52
+ r.sources.every((s) => shape(s, ["ref", "bodyHash"]) && identity(s.ref) && /^[a-f0-9]{64}$/.test(s.bodyHash)) &&
53
+ new Set(r.sources.map((s) => s.ref)).size === r.sources.length &&
54
+ Number.isFinite(r.maximumInputTokens) &&
55
+ r.maximumInputTokens > 0 &&
56
+ Number.isFinite(r.outputReserve) &&
57
+ r.outputReserve >= 0 &&
58
+ text(r.estimateMethod);
59
+ const recovery = (r) =>
60
+ shape(r, [
61
+ "id",
62
+ "assignmentId",
63
+ "assessmentHandoffId",
64
+ "baseReportRevision",
65
+ "request",
66
+ "requestedPaths",
67
+ "paths",
68
+ "state",
69
+ "requestHandoffId",
70
+ "supplementHandoffId",
71
+ "supplementRevision",
72
+ "cancellationReason",
73
+ ]) &&
74
+ identity(r.id) &&
75
+ identity(r.assignmentId) &&
76
+ identity(r.assessmentHandoffId) &&
77
+ Number.isSafeInteger(r.baseReportRevision) &&
78
+ r.baseReportRevision > 0 &&
79
+ text(r.request) &&
80
+ Array.isArray(r.requestedPaths) &&
81
+ r.requestedPaths.length <= 32 &&
82
+ r.requestedPaths.every((path) => text(path, 4096)) &&
83
+ new Set(r.requestedPaths).size === r.requestedPaths.length &&
84
+ Array.isArray(r.paths) &&
85
+ r.paths.length <= 32 &&
86
+ r.paths.every((path) => text(path, 4096)) &&
87
+ new Set(r.paths).size === r.paths.length &&
88
+ ["requested", "reading", "returning", "completed", "cancelled"].includes(r.state) &&
89
+ identity(r.requestHandoffId) &&
90
+ (r.supplementHandoffId === undefined || identity(r.supplementHandoffId)) &&
91
+ Number.isSafeInteger(r.supplementRevision) &&
92
+ r.supplementRevision >= 0 &&
93
+ (r.cancellationReason === undefined || text(r.cancellationReason, 2048));
94
+ const handoff = (h) =>
95
+ shape(h, [
96
+ "id",
97
+ "kind",
98
+ "assignmentId",
99
+ "text",
100
+ "from",
101
+ "to",
102
+ "executionId",
103
+ "toolCallId",
104
+ "basisUserEntryId",
105
+ "state",
106
+ "reportRevision",
107
+ "outcome",
108
+ "limitations",
109
+ "reason",
110
+ ]) &&
111
+ identity(h.id) &&
112
+ identity(h.assignmentId) &&
113
+ text(h.text) &&
114
+ identity(h.executionId) &&
115
+ identity(h.toolCallId) &&
116
+ nullableId(h.basisUserEntryId) &&
117
+ ["delegate", "return", "recovery-request", "recovery-return"].includes(h.kind) &&
118
+ PROFILES.includes(h.from) &&
119
+ PROFILES.includes(h.to) &&
120
+ h.from !== h.to &&
121
+ ["pending", "configured", "blocked", "superseded"].includes(h.state) &&
122
+ Number.isSafeInteger(h.reportRevision) &&
123
+ h.reportRevision >= 0 &&
124
+ Array.isArray(h.limitations) &&
125
+ h.limitations.length <= 32 &&
126
+ h.limitations.every((x) => text(x, 2048)) &&
127
+ (h.reason === undefined || text(h.reason, 4096)) &&
128
+ (h.outcome === undefined || ["completed", "partial", "blocked"].includes(h.outcome)) &&
129
+ (["delegate", "recovery-request"].includes(h.kind)
130
+ ? h.outcome === undefined && h.reportRevision === 0
131
+ : ["completed", "partial", "blocked"].includes(h.outcome) && h.reportRevision > 0);
132
+ const fields = {
133
+ "execution-interrupted": ["executionId", "reason"],
134
+ "assignment-resumed": ["assignmentId", "basisUserEntryId"],
135
+ control: ["control", "profile", "reason"],
136
+ "profile-overrides": ["overrides", "reason"],
137
+ "execution-opened": ["execution"],
138
+ "execution-bound": ["executionId", "assistantEntryId", "resultEntryIds", "outcome"],
139
+ "delegate-accepted": ["unit", "assignment", "handoff", "replacement"],
140
+ "return-accepted": ["handoff"],
141
+ "recovery-request-accepted": ["recovery", "handoff"],
142
+ "recovery-supplement-accepted": ["recoveryId", "handoff"],
143
+ "recovery-cancelled": ["recoveryId", "reason"],
144
+ "handoff-retry-requested": [
145
+ "handoffId",
146
+ "attemptId",
147
+ "executionId",
148
+ "toolCallId",
149
+ "reportRevision",
150
+ "selectionRevision",
151
+ ],
152
+ "handoff-prepared": ["handoffId", "reservation"],
153
+ "handoff-state": ["handoffId", "state", "reason", "observedPair"],
154
+ "selection-changed": ["assignmentId", "selection"],
155
+ "assessment-suspended": ["handoffId", "reason", "basisUserEntryId", "problems"],
156
+ "assessment-resumed": ["handoffId", "basisUserEntryId", "reservation"],
157
+ "unit-closed": ["unitId", "outcome", "assessment", "supersededAssignmentId"],
158
+ "sources-exposed": ["executionId", "view", "sources"],
159
+ "request-observed": ["executionId", "profile", "boundary", "manifestHash", "handoffId", "selectionRevision"],
160
+ };
161
+ export function parseRoutingEvent(raw) {
162
+ check(
163
+ isObject(raw) &&
164
+ raw.version === 2 &&
165
+ identity(raw.eventId) &&
166
+ identity(raw.operationId) &&
167
+ identity(raw.stepId) &&
168
+ identity(raw.recordedSessionId) &&
169
+ isObject(raw.data),
170
+ "invalid_event",
171
+ );
172
+ check(
173
+ Object.keys(raw).every((k) =>
174
+ ["version", "eventId", "operationId", "stepId", "recordedSessionId", "data"].includes(k),
175
+ ),
176
+ "invalid_event_field",
177
+ );
178
+ const d = raw.data;
179
+ check(
180
+ typeof d.type === "string" &&
181
+ Object.hasOwn(fields, d.type) &&
182
+ Object.keys(d).every((k) => k === "type" || fields[d.type].includes(k)),
183
+ "invalid_event_type",
184
+ );
185
+ check(Buffer.byteLength(JSON.stringify(raw)) <= 512 * 1024, "event_too_large");
186
+ if (d.handoffId !== undefined) check(identity(d.handoffId), "invalid_handoff_id");
187
+ switch (d.type) {
188
+ case "execution-interrupted":
189
+ check(identity(d.executionId) && text(d.reason, 4096), "invalid_interruption");
190
+ break;
191
+ case "assignment-resumed":
192
+ check(identity(d.assignmentId) && nullableId(d.basisUserEntryId), "invalid_resume");
193
+ break;
194
+ case "control":
195
+ check(
196
+ ["automatic", "manual", "inactive"].includes(d.control) &&
197
+ text(d.reason, 4096) &&
198
+ (d.profile === undefined || PROFILES.includes(d.profile)) &&
199
+ (d.control === "inactive" || PROFILES.includes(d.profile)),
200
+ "invalid_control",
201
+ );
202
+ break;
203
+ case "profile-overrides":
204
+ check(profileOverrides(d.overrides) && text(d.reason, 4096), "invalid_profile_overrides");
205
+ break;
206
+ case "execution-opened": {
207
+ const e = d.execution;
208
+ check(
209
+ shape(e, ["id", "profile", "assignmentId", "recoveryId", "basisUserEntryId", "pair", "resultEntryIds"]) &&
210
+ identity(e.id) &&
211
+ ["solo", ...PROFILES].includes(e.profile) &&
212
+ nullableId(e.basisUserEntryId) &&
213
+ pair(e.pair) &&
214
+ uniqueStrings(e.resultEntryIds) &&
215
+ e.resultEntryIds.length === 0 &&
216
+ (e.assignmentId === undefined || identity(e.assignmentId)) &&
217
+ (e.recoveryId === undefined || identity(e.recoveryId)),
218
+ "invalid_execution",
219
+ );
220
+ break;
221
+ }
222
+ case "execution-bound":
223
+ check(
224
+ identity(d.executionId) &&
225
+ identity(d.assistantEntryId) &&
226
+ uniqueStrings(d.resultEntryIds) &&
227
+ ["completed", "failed", "aborted"].includes(d.outcome),
228
+ "invalid_binding",
229
+ );
230
+ break;
231
+ case "delegate-accepted": {
232
+ const u = d.unit,
233
+ a = d.assignment,
234
+ r = d.replacement;
235
+ check(
236
+ shape(u, ["id", "objective", "state", "assignmentIds"]) &&
237
+ identity(u.id) &&
238
+ text(u.objective) &&
239
+ u.state === "open" &&
240
+ uniqueStrings(u.assignmentIds),
241
+ "invalid_unit",
242
+ );
243
+ check(
244
+ shape(a, ["id", "unitId", "contract", "basisUserEntryId", "state", "delegateHandoffId"]) &&
245
+ identity(a.id) &&
246
+ identity(a.unitId) &&
247
+ text(a.contract) &&
248
+ nullableId(a.basisUserEntryId) &&
249
+ a.state === "outstanding" &&
250
+ identity(a.delegateHandoffId) &&
251
+ handoff(d.handoff),
252
+ "invalid_assignment",
253
+ );
254
+ check(
255
+ r === undefined ||
256
+ (shape(r, ["assignmentId", "supersededHandoffId", "reason"]) &&
257
+ identity(r.assignmentId) &&
258
+ text(r.reason, 2048) &&
259
+ (r.supersededHandoffId === undefined || identity(r.supersededHandoffId))),
260
+ "invalid_replacement",
261
+ );
262
+ break;
263
+ }
264
+ case "return-accepted":
265
+ check(handoff(d.handoff) && d.handoff.kind === "return", "invalid_return");
266
+ break;
267
+ case "recovery-request-accepted":
268
+ check(
269
+ recovery(d.recovery) &&
270
+ handoff(d.handoff) &&
271
+ d.handoff.kind === "recovery-request" &&
272
+ d.recovery.requestHandoffId === d.handoff.id &&
273
+ d.recovery.assignmentId === d.handoff.assignmentId &&
274
+ d.recovery.request === d.handoff.text,
275
+ "invalid_recovery_request",
276
+ );
277
+ break;
278
+ case "recovery-supplement-accepted":
279
+ check(
280
+ identity(d.recoveryId) && handoff(d.handoff) && d.handoff.kind === "recovery-return",
281
+ "invalid_recovery_supplement",
282
+ );
283
+ break;
284
+ case "recovery-cancelled":
285
+ check(identity(d.recoveryId) && text(d.reason, 2048), "invalid_recovery_cancellation");
286
+ break;
287
+ case "handoff-retry-requested":
288
+ check(
289
+ identity(d.attemptId) &&
290
+ identity(d.executionId) &&
291
+ identity(d.toolCallId) &&
292
+ Number.isSafeInteger(d.reportRevision) &&
293
+ d.reportRevision > 0 &&
294
+ Number.isSafeInteger(d.selectionRevision) &&
295
+ d.selectionRevision >= 0,
296
+ "invalid_retry",
297
+ );
298
+ break;
299
+ case "handoff-prepared":
300
+ check(reservation(d.reservation), "invalid_reservation");
301
+ break;
302
+ case "handoff-state":
303
+ check(
304
+ ["configured", "blocked", "superseded"].includes(d.state) &&
305
+ (d.reason === undefined || text(d.reason, 4096)) &&
306
+ (d.observedPair === undefined || pair(d.observedPair)),
307
+ "invalid_delivery",
308
+ );
309
+ break;
310
+ case "selection-changed": {
311
+ const s = d.selection;
312
+ check(
313
+ identity(d.assignmentId) &&
314
+ shape(s, ["revision", "selected", "unresolved", "withdrawals"]) &&
315
+ Number.isSafeInteger(s.revision) &&
316
+ s.revision > 0 &&
317
+ uniqueStrings(s.selected) &&
318
+ problemList(s.unresolved) &&
319
+ Array.isArray(s.withdrawals) &&
320
+ s.withdrawals.every((w) => shape(w, ["ref", "reason"]) && identity(w.ref) && text(w.reason, 2048)),
321
+ "invalid_selection",
322
+ );
323
+ break;
324
+ }
325
+ case "assessment-suspended":
326
+ check(
327
+ ["user-attention", "delivery-gap"].includes(d.reason) &&
328
+ nullableId(d.basisUserEntryId) &&
329
+ problemList(d.problems),
330
+ "invalid_suspension",
331
+ );
332
+ break;
333
+ case "assessment-resumed":
334
+ check(
335
+ nullableId(d.basisUserEntryId) && (d.reservation === undefined || reservation(d.reservation)),
336
+ "invalid_resumption",
337
+ );
338
+ break;
339
+ case "unit-closed":
340
+ check(
341
+ identity(d.unitId) &&
342
+ ["accepted", "cancelled", "deferred"].includes(d.outcome) &&
343
+ text(d.assessment) &&
344
+ (d.supersededAssignmentId === undefined || identity(d.supersededAssignmentId)),
345
+ "invalid_closure",
346
+ );
347
+ break;
348
+ case "request-observed":
349
+ check(
350
+ identity(d.executionId) &&
351
+ ["solo", ...PROFILES].includes(d.profile) &&
352
+ ["assembled", "payload-hook"].includes(d.boundary) &&
353
+ /^[a-f0-9]{64}$/.test(d.manifestHash) &&
354
+ (d.selectionRevision === undefined ||
355
+ (Number.isSafeInteger(d.selectionRevision) && d.selectionRevision >= 0)),
356
+ "invalid_request_observation",
357
+ );
358
+ break;
359
+ case "sources-exposed":
360
+ check(
361
+ identity(d.executionId) &&
362
+ ["solo", ...PROFILES].includes(d.view) &&
363
+ Array.isArray(d.sources) &&
364
+ d.sources.every((s) => shape(s, ["ref", "bodyHash"]) && identity(s.ref) && /^[a-f0-9]{64}$/.test(s.bodyHash)),
365
+ "invalid_exposure",
366
+ );
367
+ break;
368
+ }
369
+ return structuredClone(raw);
370
+ }
371
+ export function initialState() {
372
+ return {
373
+ control: "inactive",
374
+ units: new Map(),
375
+ assignments: new Map(),
376
+ handoffs: new Map(),
377
+ recoveries: new Map(),
378
+ executions: new Map(),
379
+ selections: new Map(),
380
+ reservations: new Map(),
381
+ attempts: new Map(),
382
+ exposure: new Map(),
383
+ authors: new Map(),
384
+ resumeBasis: new Map(),
385
+ profileOverrides: new Map(),
386
+ events: new Map(),
387
+ eventIds: new Map(),
388
+ };
389
+ }
390
+ function applyEvent(state, event, owned = false) {
391
+ const e = parseRoutingEvent(event),
392
+ key = eventKey(e),
393
+ value = eventValue(e);
394
+ const previous = state.events.get(key),
395
+ priorId = state.eventIds.get(e.eventId);
396
+ check(!priorId || priorId === value, "event_identity_conflict");
397
+ if (previous) {
398
+ check(eventValue(previous) === value, "operation_conflict");
399
+ return state;
400
+ }
401
+ const s = owned
402
+ ? state
403
+ : {
404
+ ...state,
405
+ units: new Map([...state.units].map(([k, v]) => [k, { ...v }])),
406
+ assignments: new Map([...state.assignments].map(([k, v]) => [k, { ...v }])),
407
+ handoffs: new Map([...state.handoffs].map(([k, v]) => [k, { ...v }])),
408
+ recoveries: new Map([...state.recoveries].map(([k, v]) => [k, { ...v, paths: [...v.paths] }])),
409
+ executions: new Map([...state.executions].map(([k, v]) => [k, { ...v }])),
410
+ selections: new Map(state.selections),
411
+ reservations: new Map(state.reservations),
412
+ attempts: new Map(state.attempts),
413
+ exposure: new Map(state.exposure),
414
+ authors: new Map(state.authors),
415
+ resumeBasis: new Map(state.resumeBasis),
416
+ profileOverrides: new Map(state.profileOverrides),
417
+ events: new Map(state.events),
418
+ eventIds: new Map(state.eventIds),
419
+ assessment: state.assessment ? { ...state.assessment } : undefined,
420
+ },
421
+ d = e.data;
422
+ const assignment = () => (s.assignmentId ? s.assignments.get(s.assignmentId) : undefined);
423
+ const assignmentWorker = () => {
424
+ const a = assignment();
425
+ const h = a ? s.handoffs.get(a.delegateHandoffId) : undefined;
426
+ check(h?.kind === "delegate" && h.assignmentId === a?.id && isWorkerProfile(h.to), "assignment_worker_missing");
427
+ return h.to;
428
+ };
429
+ const pending = () => (s.pendingId ? s.handoffs.get(s.pendingId) : undefined);
430
+ const isPending = () => pending() && ["pending", "blocked"].includes(pending().state);
431
+ const currentReturn = (id) => {
432
+ const h = s.handoffs.get(id);
433
+ check(
434
+ h && ["return", "recovery-return"].includes(h.kind) && h.assignmentId === s.assignmentId,
435
+ "not_current_return",
436
+ );
437
+ return h;
438
+ };
439
+ switch (d.type) {
440
+ case "execution-interrupted": {
441
+ const x = s.executions.get(d.executionId);
442
+ check(x && !x.assistantEntryId, "execution_not_unbound");
443
+ x.interrupted = d.reason;
444
+ break;
445
+ }
446
+ case "assignment-resumed": {
447
+ const a = assignment();
448
+ check(
449
+ s.control === "automatic" && a?.id === d.assignmentId && a.state === "outstanding" && !isPending(),
450
+ "assignment_not_resumable",
451
+ );
452
+ s.resumeBasis.set(a.id, d.basisUserEntryId);
453
+ break;
454
+ }
455
+ case "control":
456
+ s.control = d.control;
457
+ s.profile = d.profile;
458
+ break;
459
+ case "profile-overrides":
460
+ for (const profile of PROFILES) {
461
+ if (!Object.hasOwn(d.overrides, profile)) continue;
462
+ const override = d.overrides[profile];
463
+ if (override === null) s.profileOverrides.delete(profile);
464
+ else s.profileOverrides.set(profile, override);
465
+ }
466
+ break;
467
+ case "execution-opened":
468
+ check(!s.executions.has(d.execution.id), "duplicate_execution");
469
+ s.executions.set(d.execution.id, d.execution);
470
+ break;
471
+ case "execution-bound": {
472
+ const x = s.executions.get(d.executionId);
473
+ check(x, "execution_missing");
474
+ if (x.assistantEntryId)
475
+ check(
476
+ x.assistantEntryId === d.assistantEntryId &&
477
+ canonical(x.resultEntryIds) === canonical(d.resultEntryIds) &&
478
+ x.outcome === d.outcome,
479
+ "binding_conflict",
480
+ );
481
+ x.assistantEntryId = d.assistantEntryId;
482
+ x.resultEntryIds = d.resultEntryIds;
483
+ x.outcome = d.outcome;
484
+ for (const id of [d.assistantEntryId, ...d.resultEntryIds]) {
485
+ const prev = s.authors.get(id);
486
+ check(!prev || prev.executionId === x.id, "source_authorship_conflict");
487
+ s.authors.set(id, { profile: x.profile, executionId: x.id, assignmentId: x.assignmentId });
488
+ }
489
+ break;
490
+ }
491
+ case "delegate-accepted": {
492
+ check(s.control === "automatic" && s.profile === "coordinator", "wrong_control");
493
+ const a = assignment(),
494
+ h = d.handoff,
495
+ u = s.unitId ? s.units.get(s.unitId) : undefined;
496
+ check(!s.assignments.has(d.assignment.id) && !s.handoffs.has(h.id), "reused_work_identity");
497
+ check(
498
+ h.kind === "delegate" &&
499
+ h.from === "coordinator" &&
500
+ isWorkerProfile(h.to) &&
501
+ h.state === "pending" &&
502
+ h.text === d.assignment.contract &&
503
+ h.assignmentId === d.assignment.id &&
504
+ d.assignment.delegateHandoffId === h.id &&
505
+ d.assignment.unitId === d.unit.id,
506
+ "delegate_identity_mismatch",
507
+ );
508
+ if (u) check(u.state === "open" && u.id === d.unit.id && u.objective === d.unit.objective, "unit_conflict");
509
+ else check(!s.units.has(d.unit.id), "closed_unit_reused");
510
+ check(
511
+ canonical(d.unit.assignmentIds) === canonical([...(u?.assignmentIds ?? []), d.assignment.id]),
512
+ "assignment_order",
513
+ );
514
+ if (d.replacement) {
515
+ check(a?.state === "outstanding" && a.id === d.replacement.assignmentId && u, "no_replaceable_assignment");
516
+ check(
517
+ ![...s.executions.values()].some(
518
+ (x) => x.assignmentId === a.id && isWorkerProfile(x.profile) && !x.assistantEntryId && !x.interrupted,
519
+ ),
520
+ "unresolved_worker_execution",
521
+ );
522
+ check(
523
+ !isPending() || (pending().kind === "delegate" && d.replacement.supersededHandoffId === pending().id),
524
+ "handoff_pending",
525
+ );
526
+ a.state = "superseded";
527
+ s.handoffs.get(a.delegateHandoffId).state = "superseded";
528
+ } else check(a?.state !== "outstanding" && !isPending(), "assignment_outstanding");
529
+ s.units.set(d.unit.id, d.unit);
530
+ s.unitId = d.unit.id;
531
+ s.assignments.set(d.assignment.id, d.assignment);
532
+ s.assignmentId = d.assignment.id;
533
+ s.handoffs.set(h.id, h);
534
+ s.pendingId = h.id;
535
+ s.assessment = undefined;
536
+ break;
537
+ }
538
+ case "return-accepted": {
539
+ check(s.control === "automatic" && s.profile === assignmentWorker(), "wrong_control");
540
+ const a = assignment(),
541
+ h = d.handoff;
542
+ check(
543
+ a &&
544
+ h.assignmentId === a.id &&
545
+ h.kind === "return" &&
546
+ h.from === assignmentWorker() &&
547
+ h.to === "coordinator" &&
548
+ h.state === "pending",
549
+ "return_identity_mismatch",
550
+ );
551
+ const previousReturn = a.returnHandoffId ? s.handoffs.get(a.returnHandoffId) : undefined;
552
+ if (a.state === "outstanding")
553
+ check(!isPending() && !s.handoffs.has(h.id) && h.reportRevision === 1, "handoff_pending");
554
+ else
555
+ check(
556
+ a.state === "returned" &&
557
+ previousReturn &&
558
+ previousReturn.id === h.id &&
559
+ ["pending", "blocked"].includes(previousReturn.state) &&
560
+ h.reportRevision === previousReturn.reportRevision + 1,
561
+ "invalid_report_revision",
562
+ );
563
+ a.state = "returned";
564
+ a.returnHandoffId = h.id;
565
+ s.handoffs.set(h.id, h);
566
+ s.pendingId = h.id;
567
+ s.assessment = {
568
+ handoffId: h.id,
569
+ assignmentId: a.id,
570
+ view: "active",
571
+ basisUserEntryId: h.basisUserEntryId,
572
+ problems: [],
573
+ };
574
+ break;
575
+ }
576
+ case "recovery-request-accepted": {
577
+ const a = assignment(),
578
+ base = s.handoffs.get(d.recovery.assessmentHandoffId),
579
+ h = d.handoff;
580
+ check(
581
+ s.control === "automatic" &&
582
+ s.profile === "coordinator" &&
583
+ a?.state === "returned" &&
584
+ d.recovery.assignmentId === a.id &&
585
+ d.recovery.state === "requested" &&
586
+ d.recovery.supplementRevision === 0 &&
587
+ d.recovery.supplementHandoffId === undefined &&
588
+ d.recovery.cancellationReason === undefined &&
589
+ s.assessment?.handoffId === base?.id &&
590
+ base?.kind === "return" &&
591
+ a.returnHandoffId === base.id &&
592
+ base.state === "configured" &&
593
+ base.reportRevision === d.recovery.baseReportRevision &&
594
+ !s.recoveryId &&
595
+ !isPending() &&
596
+ h.kind === "recovery-request" &&
597
+ h.assignmentId === a.id &&
598
+ h.from === "coordinator" &&
599
+ h.to === assignmentWorker() &&
600
+ h.state === "pending" &&
601
+ !s.handoffs.has(h.id) &&
602
+ !s.recoveries.has(d.recovery.id),
603
+ "recovery_not_available",
604
+ );
605
+ s.recoveries.set(d.recovery.id, d.recovery);
606
+ s.recoveryId = d.recovery.id;
607
+ s.handoffs.set(h.id, h);
608
+ s.pendingId = h.id;
609
+ s.assessment.view = "suspended";
610
+ s.assessment.suspensionReason = "recovery";
611
+ s.assessment.basisUserEntryId = h.basisUserEntryId;
612
+ s.assessment.problems = [];
613
+ break;
614
+ }
615
+ case "recovery-supplement-accepted": {
616
+ const a = assignment(),
617
+ r = s.recoveries.get(d.recoveryId),
618
+ h = d.handoff,
619
+ previous = r?.supplementHandoffId ? s.handoffs.get(r.supplementHandoffId) : undefined;
620
+ check(
621
+ s.control === "automatic" &&
622
+ s.profile === assignmentWorker() &&
623
+ a?.state === "returned" &&
624
+ r?.id === s.recoveryId &&
625
+ r.assignmentId === a.id &&
626
+ s.assessment?.handoffId === r.assessmentHandoffId &&
627
+ s.handoffs.get(r.assessmentHandoffId)?.reportRevision === r.baseReportRevision &&
628
+ ["reading", "returning"].includes(r.state) &&
629
+ h.kind === "recovery-return" &&
630
+ h.assignmentId === a.id &&
631
+ h.from === assignmentWorker() &&
632
+ h.to === "coordinator" &&
633
+ h.state === "pending" &&
634
+ (!previous ? !s.handoffs.has(h.id) : ["pending", "blocked"].includes(previous.state) && previous.id === h.id),
635
+ "recovery_not_returnable",
636
+ );
637
+ const expectedRevision = (previous?.reportRevision ?? 0) + 1;
638
+ check(h.reportRevision === expectedRevision, "invalid_report_revision");
639
+ r.state = "returning";
640
+ r.supplementHandoffId = h.id;
641
+ r.supplementRevision = h.reportRevision;
642
+ s.handoffs.set(h.id, h);
643
+ s.pendingId = h.id;
644
+ break;
645
+ }
646
+ case "recovery-cancelled": {
647
+ const r = s.recoveries.get(d.recoveryId);
648
+ check(
649
+ s.control === "automatic" &&
650
+ s.profile === "coordinator" &&
651
+ r?.id === s.recoveryId &&
652
+ ["requested", "reading", "returning"].includes(r.state),
653
+ "recovery_not_cancellable",
654
+ );
655
+ const p = pending();
656
+ if (p) {
657
+ check([r.requestHandoffId, r.supplementHandoffId].includes(p.id), "handoff_pending");
658
+ p.state = "superseded";
659
+ s.pendingId = undefined;
660
+ }
661
+ r.state = "cancelled";
662
+ r.cancellationReason = d.reason;
663
+ s.recoveryId = undefined;
664
+ break;
665
+ }
666
+ case "handoff-retry-requested": {
667
+ const h = currentReturn(d.handoffId);
668
+ check(
669
+ s.control === "automatic" &&
670
+ s.profile === assignmentWorker() &&
671
+ ["blocked", "pending"].includes(h.state) &&
672
+ h.reportRevision === d.reportRevision &&
673
+ (s.selections.get(h.assignmentId)?.revision ?? 0) === d.selectionRevision,
674
+ "invalid_return_retry",
675
+ );
676
+ s.attempts.set(h.id, {
677
+ executionId: d.executionId,
678
+ reportRevision: d.reportRevision,
679
+ selectionRevision: d.selectionRevision,
680
+ });
681
+ h.state = "pending";
682
+ break;
683
+ }
684
+ case "handoff-prepared": {
685
+ const h = currentReturn(d.handoffId);
686
+ check(["pending", "blocked"].includes(h.state), "handoff_terminal");
687
+ check(d.reservation.selectionRevision === (s.selections.get(h.assignmentId)?.revision ?? 0), "stale_selection");
688
+ s.reservations.set(h.id, d.reservation);
689
+ if (s.assessment?.handoffId === h.id) s.assessment.reservation = d.reservation;
690
+ if (h.kind === "recovery-return") {
691
+ const recovery = [...s.recoveries.values()].find((candidate) => candidate.supplementHandoffId === h.id);
692
+ if (recovery && s.assessment?.handoffId === recovery.assessmentHandoffId)
693
+ s.assessment.reservation = d.reservation;
694
+ }
695
+ break;
696
+ }
697
+ case "handoff-state": {
698
+ const h = s.handoffs.get(d.handoffId);
699
+ check(h && s.pendingId === h.id && ["pending", "blocked"].includes(h.state), "handoff_terminal");
700
+ h.state = d.state;
701
+ h.reason = d.reason;
702
+ if (d.state === "configured") {
703
+ check(d.observedPair, "configuration_unobserved");
704
+ s.profile = h.to;
705
+ s.pendingId = undefined;
706
+ const r = s.recoveryId ? s.recoveries.get(s.recoveryId) : undefined;
707
+ if (r && h.id === r.requestHandoffId) r.state = "reading";
708
+ if (r && h.id === r.supplementHandoffId) {
709
+ r.state = "completed";
710
+ s.recoveryId = undefined;
711
+ }
712
+ }
713
+ if (d.state === "superseded") s.pendingId = undefined;
714
+ break;
715
+ }
716
+ case "selection-changed": {
717
+ const a = assignment();
718
+ check(
719
+ a?.id === d.assignmentId &&
720
+ s.control === "automatic" &&
721
+ s.profile === assignmentWorker() &&
722
+ (a.state === "outstanding" ||
723
+ (a.state === "returned" &&
724
+ (isPending() || (s.recoveryId !== undefined && s.recoveries.get(s.recoveryId)?.state === "reading")))),
725
+ "selection_wrong_phase",
726
+ );
727
+ check(d.selection.revision === (s.selections.get(a.id)?.revision ?? 0) + 1, "selection_revision");
728
+ s.selections.set(a.id, d.selection);
729
+ break;
730
+ }
731
+ case "assessment-suspended": {
732
+ check(s.assessment?.handoffId === d.handoffId, "assessment_missing");
733
+ s.assessment.view = "suspended";
734
+ s.assessment.suspensionReason = d.reason;
735
+ s.assessment.basisUserEntryId = d.basisUserEntryId;
736
+ s.assessment.problems = d.problems;
737
+ break;
738
+ }
739
+ case "assessment-resumed": {
740
+ check(
741
+ s.control === "automatic" &&
742
+ s.profile === "coordinator" &&
743
+ !s.recoveryId &&
744
+ s.assessment?.handoffId === d.handoffId &&
745
+ s.assessment.view === "suspended",
746
+ s.recoveryId ? "recovery_outstanding" : "assessment_not_suspended",
747
+ );
748
+ if (d.reservation)
749
+ check(
750
+ d.reservation.selectionRevision === (s.selections.get(s.assessment.assignmentId)?.revision ?? 0),
751
+ "stale_selection",
752
+ );
753
+ s.assessment.view = "active";
754
+ s.assessment.suspensionReason = undefined;
755
+ s.assessment.basisUserEntryId = d.basisUserEntryId;
756
+ if (d.reservation) {
757
+ s.assessment.reservation = d.reservation;
758
+ s.reservations.set(d.handoffId, d.reservation);
759
+ }
760
+ s.assessment.problems = [];
761
+ break;
762
+ }
763
+ case "unit-closed": {
764
+ const u = s.units.get(d.unitId),
765
+ a = assignment();
766
+ check(
767
+ s.control === "automatic" && s.profile === "coordinator" && u?.id === s.unitId && u.state === "open",
768
+ "unit_not_open",
769
+ );
770
+ if (d.outcome === "accepted")
771
+ check(a?.state !== "outstanding" && !isPending() && !s.recoveryId, "unfinished_work");
772
+ const recovery = s.recoveryId ? s.recoveries.get(s.recoveryId) : undefined;
773
+ if (recovery) {
774
+ check(
775
+ d.outcome !== "accepted" && ["requested", "reading", "returning"].includes(recovery.state),
776
+ "unfinished_work",
777
+ );
778
+ const transfer = pending();
779
+ if (transfer) {
780
+ check([recovery.requestHandoffId, recovery.supplementHandoffId].includes(transfer.id), "handoff_pending");
781
+ transfer.state = "superseded";
782
+ s.pendingId = undefined;
783
+ }
784
+ recovery.state = "cancelled";
785
+ recovery.cancellationReason = d.assessment;
786
+ s.recoveryId = undefined;
787
+ }
788
+ if (a?.state === "outstanding") {
789
+ check(d.supersededAssignmentId === a.id && d.outcome !== "accepted", "supersession_required");
790
+ a.state = "superseded";
791
+ }
792
+ if (pending()) pending().state = "superseded";
793
+ u.state = "closed";
794
+ u.disposition = d.outcome;
795
+ u.assessment = d.assessment;
796
+ s.unitId = undefined;
797
+ s.assignmentId = undefined;
798
+ s.pendingId = undefined;
799
+ s.assessment = undefined;
800
+ break;
801
+ }
802
+ case "request-observed": {
803
+ const x = s.executions.get(d.executionId);
804
+ check(x && x.profile === d.profile, "request_execution_missing");
805
+ if (d.handoffId) check(s.handoffs.has(d.handoffId), "request_handoff_missing");
806
+ break;
807
+ }
808
+ case "sources-exposed": {
809
+ const x = s.executions.get(d.executionId);
810
+ check(x && x.profile === d.view, "exposure_execution_missing");
811
+ if (isWorkerProfile(d.view)) for (const source of d.sources) s.exposure.set(source.ref, source.bodyHash);
812
+ break;
813
+ }
814
+ }
815
+ s.events.set(key, e);
816
+ s.eventIds.set(e.eventId, value);
817
+ return s;
818
+ }
819
+ export function reduce(state, event) {
820
+ return applyEvent(state, event);
821
+ }
822
+ export function replay(entries) {
823
+ let state = initialState();
824
+ for (const entry of entries)
825
+ if (entry.type === "custom" && entry.customType === ROUTING_ENTRY)
826
+ state = applyEvent(state, parseRoutingEvent(entry.data), true);
827
+ return state;
828
+ }