omk-agent-core 0.98.2 → 0.98.4

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 (113) hide show
  1. package/CHANGELOG.md +658 -0
  2. package/dist/agent-loop.d.ts +1 -31
  3. package/dist/agent-loop.d.ts.map +1 -1
  4. package/dist/agent-loop.js +15 -70
  5. package/dist/agent-loop.js.map +1 -1
  6. package/dist/agent.d.ts +7 -0
  7. package/dist/agent.d.ts.map +1 -1
  8. package/dist/agent.js +13 -34
  9. package/dist/agent.js.map +1 -1
  10. package/dist/effects/effect-journal.d.ts +43 -0
  11. package/dist/effects/effect-journal.d.ts.map +1 -0
  12. package/dist/effects/effect-journal.js +186 -0
  13. package/dist/effects/effect-journal.js.map +1 -0
  14. package/dist/effects/effect-recovery.d.ts +70 -0
  15. package/dist/effects/effect-recovery.d.ts.map +1 -0
  16. package/dist/effects/effect-recovery.js +120 -0
  17. package/dist/effects/effect-recovery.js.map +1 -0
  18. package/dist/effects/effect-transitions.d.ts +34 -0
  19. package/dist/effects/effect-transitions.d.ts.map +1 -0
  20. package/dist/effects/effect-transitions.js +148 -0
  21. package/dist/effects/effect-transitions.js.map +1 -0
  22. package/dist/effects/effect-types.d.ts +135 -0
  23. package/dist/effects/effect-types.d.ts.map +1 -0
  24. package/dist/effects/effect-types.js +32 -0
  25. package/dist/effects/effect-types.js.map +1 -0
  26. package/dist/harness/abort-delivery.d.ts +26 -0
  27. package/dist/harness/abort-delivery.d.ts.map +1 -0
  28. package/dist/harness/abort-delivery.js +36 -0
  29. package/dist/harness/abort-delivery.js.map +1 -0
  30. package/dist/harness/agent-harness.d.ts +18 -0
  31. package/dist/harness/agent-harness.d.ts.map +1 -1
  32. package/dist/harness/agent-harness.js +37 -28
  33. package/dist/harness/agent-harness.js.map +1 -1
  34. package/dist/harness/canonical-digest.d.ts +32 -0
  35. package/dist/harness/canonical-digest.d.ts.map +1 -0
  36. package/dist/harness/canonical-digest.js +164 -0
  37. package/dist/harness/canonical-digest.js.map +1 -0
  38. package/dist/harness/deferred-commands.d.ts +53 -0
  39. package/dist/harness/deferred-commands.d.ts.map +1 -0
  40. package/dist/harness/deferred-commands.js +96 -0
  41. package/dist/harness/deferred-commands.js.map +1 -0
  42. package/dist/harness/operation-outcome.d.ts +18 -10
  43. package/dist/harness/operation-outcome.d.ts.map +1 -1
  44. package/dist/harness/operation-outcome.js +71 -35
  45. package/dist/harness/operation-outcome.js.map +1 -1
  46. package/dist/harness/operation-trace-divergence.d.ts +60 -0
  47. package/dist/harness/operation-trace-divergence.d.ts.map +1 -0
  48. package/dist/harness/operation-trace-divergence.js +199 -0
  49. package/dist/harness/operation-trace-divergence.js.map +1 -0
  50. package/dist/harness/operation-trace.d.ts +134 -0
  51. package/dist/harness/operation-trace.d.ts.map +1 -0
  52. package/dist/harness/operation-trace.js +161 -0
  53. package/dist/harness/operation-trace.js.map +1 -0
  54. package/dist/harness/reverse-skill-text.d.ts +3 -0
  55. package/dist/harness/reverse-skill-text.d.ts.map +1 -0
  56. package/dist/harness/reverse-skill-text.js +20 -0
  57. package/dist/harness/reverse-skill-text.js.map +1 -0
  58. package/dist/harness/reverse-skill-tool-aliases.d.ts +3 -0
  59. package/dist/harness/reverse-skill-tool-aliases.d.ts.map +1 -0
  60. package/dist/harness/reverse-skill-tool-aliases.js +47 -0
  61. package/dist/harness/reverse-skill-tool-aliases.js.map +1 -0
  62. package/dist/harness/reverse-skill-types.d.ts +76 -0
  63. package/dist/harness/reverse-skill-types.d.ts.map +1 -0
  64. package/dist/harness/reverse-skill-types.js +2 -0
  65. package/dist/harness/reverse-skill-types.js.map +1 -0
  66. package/dist/harness/reverse-skill.d.ts +4 -78
  67. package/dist/harness/reverse-skill.d.ts.map +1 -1
  68. package/dist/harness/reverse-skill.js +113 -61
  69. package/dist/harness/reverse-skill.js.map +1 -1
  70. package/dist/harness/subscriber-fanout.d.ts +14 -1
  71. package/dist/harness/subscriber-fanout.d.ts.map +1 -1
  72. package/dist/harness/subscriber-fanout.js +25 -6
  73. package/dist/harness/subscriber-fanout.js.map +1 -1
  74. package/dist/index.d.ts +5 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +5 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/listener-delivery.d.ts +22 -0
  79. package/dist/listener-delivery.d.ts.map +1 -0
  80. package/dist/listener-delivery.js +36 -0
  81. package/dist/listener-delivery.js.map +1 -0
  82. package/dist/pending-message-queue.d.ts +11 -0
  83. package/dist/pending-message-queue.d.ts.map +1 -0
  84. package/dist/pending-message-queue.js +29 -0
  85. package/dist/pending-message-queue.js.map +1 -0
  86. package/dist/provider-input.d.ts +8 -0
  87. package/dist/provider-input.d.ts.map +1 -0
  88. package/dist/provider-input.js +28 -0
  89. package/dist/provider-input.js.map +1 -0
  90. package/dist/provider-payload-contract.d.ts +6 -0
  91. package/dist/provider-payload-contract.d.ts.map +1 -0
  92. package/dist/provider-payload-contract.js +43 -0
  93. package/dist/provider-payload-contract.js.map +1 -0
  94. package/dist/provider-request-types.d.ts +46 -0
  95. package/dist/provider-request-types.d.ts.map +1 -0
  96. package/dist/provider-request-types.js +2 -0
  97. package/dist/provider-request-types.js.map +1 -0
  98. package/dist/provider-request.d.ts +17 -0
  99. package/dist/provider-request.d.ts.map +1 -0
  100. package/dist/provider-request.js +110 -0
  101. package/dist/provider-request.js.map +1 -0
  102. package/dist/run-model-contract.d.ts +11 -0
  103. package/dist/run-model-contract.d.ts.map +1 -0
  104. package/dist/run-model-contract.js +121 -0
  105. package/dist/run-model-contract.js.map +1 -0
  106. package/dist/types.d.ts +4 -1
  107. package/dist/types.d.ts.map +1 -1
  108. package/dist/types.js.map +1 -1
  109. package/dist/vision-route.d.ts +19 -0
  110. package/dist/vision-route.d.ts.map +1 -0
  111. package/dist/vision-route.js +19 -0
  112. package/dist/vision-route.js.map +1 -0
  113. package/package.json +4 -3
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Trace comparison for shadow-mode runtime convergence (FND-003, Stage C).
3
+ *
4
+ * While `AgentHarness` runs as a shadow reducer beside the authoritative
5
+ * `AgentSession`, every scenario yields two traces. This module reduces each
6
+ * trace to a per-operation summary and classifies every difference as a
7
+ * `blocker` (the two runtimes disagree about lifecycle semantics) or a
8
+ * `non_blocker` (they took a different path to the same committed state).
9
+ *
10
+ * Blocker dimensions follow the plan verbatim: operation count and identity,
11
+ * the attempt set and how each attempt closed, terminal status and public
12
+ * error code, accepted deferred-command IDs in order, session-write order,
13
+ * and effect uncertainty left open at settlement. Stage paths and abort
14
+ * signalling without an outcome difference are informational only.
15
+ */
16
+ import { computeTraceDigest } from "./operation-trace.js";
17
+ const BLOCKER_DIMENSIONS = new Set([
18
+ "operation_count",
19
+ "operation_identity",
20
+ "attempt_set",
21
+ "terminal_status",
22
+ "public_error_code",
23
+ "accepted_commands",
24
+ "session_write_order",
25
+ "effect_uncertainty",
26
+ ]);
27
+ export function summarizeTrace(events) {
28
+ const operations = [];
29
+ const byId = new Map();
30
+ const acceptedCommandIds = [];
31
+ const sessionWrites = [];
32
+ for (const event of events) {
33
+ switch (event.type) {
34
+ case "operation_started": {
35
+ const operation = {
36
+ operationId: event.operationId,
37
+ kind: event.kind,
38
+ sequence: event.sequence,
39
+ stages: [],
40
+ attempts: [],
41
+ abortRequested: false,
42
+ unresolved: new Set(),
43
+ };
44
+ operations.push(operation);
45
+ byId.set(event.operationId, operation);
46
+ break;
47
+ }
48
+ case "stage_changed":
49
+ byId.get(event.operationId)?.stages.push(event.stage);
50
+ break;
51
+ case "attempt_started":
52
+ byId
53
+ .get(event.operationId)
54
+ ?.attempts.push({ attemptId: event.attemptId, index: event.index, reason: event.reason });
55
+ break;
56
+ case "attempt_finished": {
57
+ const operation = byId.get(event.operationId);
58
+ if (operation === undefined)
59
+ break;
60
+ operation.attempts = operation.attempts.map((attempt) => attempt.attemptId === event.attemptId ? { ...attempt, outcome: event.outcome } : attempt);
61
+ break;
62
+ }
63
+ case "abort_requested": {
64
+ const operation = byId.get(event.operationId);
65
+ if (operation !== undefined)
66
+ operation.abortRequested = true;
67
+ break;
68
+ }
69
+ case "effect_uncertain":
70
+ byId.get(event.operationId)?.unresolved.add(event.effectId);
71
+ break;
72
+ case "effect_reconciled":
73
+ byId.get(event.operationId)?.unresolved.delete(event.effectId);
74
+ break;
75
+ case "effect_prepared":
76
+ break;
77
+ case "deferred_command_accepted":
78
+ acceptedCommandIds.push(event.commandId);
79
+ break;
80
+ case "session_write_accepted":
81
+ sessionWrites.push(`${event.writeSequence}:${event.writeType}`);
82
+ break;
83
+ case "settlement_started": {
84
+ const operation = byId.get(event.operationId);
85
+ if (operation !== undefined)
86
+ operation.settlement = event.outcome;
87
+ break;
88
+ }
89
+ case "operation_settled": {
90
+ const operation = byId.get(event.operationId);
91
+ if (operation !== undefined)
92
+ operation.settled = event.outcome;
93
+ break;
94
+ }
95
+ default: {
96
+ const unknownEvent = event;
97
+ throw new TypeError(`Unknown trace event ${String(unknownEvent.type)}`);
98
+ }
99
+ }
100
+ }
101
+ return {
102
+ operations: operations.map((operation) => ({
103
+ operationId: operation.operationId,
104
+ kind: operation.kind,
105
+ sequence: operation.sequence,
106
+ stages: operation.stages,
107
+ attempts: operation.attempts,
108
+ abortRequested: operation.abortRequested,
109
+ ...(operation.settlement === undefined ? {} : { settlement: operation.settlement }),
110
+ ...(operation.settled === undefined ? {} : { settled: operation.settled }),
111
+ unresolvedEffectIds: [...operation.unresolved].sort(compareCodeUnits),
112
+ })),
113
+ acceptedCommandIds,
114
+ sessionWrites,
115
+ };
116
+ }
117
+ /** Code-unit order, so the report does not depend on the host locale. */
118
+ function compareCodeUnits(left, right) {
119
+ if (left === right)
120
+ return 0;
121
+ return left < right ? -1 : 1;
122
+ }
123
+ function describeOutcome(outcome) {
124
+ if (outcome === undefined)
125
+ return "<none>";
126
+ return outcome.status === "failed" ? `failed:${outcome.code}` : outcome.status;
127
+ }
128
+ function describeAttempts(attempts) {
129
+ return attempts.map((attempt) => `${attempt.attemptId}/${attempt.reason}=${attempt.outcome ?? "<open>"}`).join(",");
130
+ }
131
+ function compareOperation(index, left, right) {
132
+ const out = [];
133
+ const push = (dimension, leftValue, rightValue) => {
134
+ if (leftValue === rightValue)
135
+ return;
136
+ const divergenceClass = BLOCKER_DIMENSIONS.has(dimension) ? "blocker" : "non_blocker";
137
+ out.push({ dimension, class: divergenceClass, operationIndex: index, left: leftValue, right: rightValue });
138
+ };
139
+ push("operation_identity", `${left.kind}#${left.sequence}`, `${right.kind}#${right.sequence}`);
140
+ push("attempt_set", describeAttempts(left.attempts), describeAttempts(right.attempts));
141
+ const leftSettled = left.settled ?? left.settlement;
142
+ const rightSettled = right.settled ?? right.settlement;
143
+ if ((leftSettled?.status ?? "<none>") !== (rightSettled?.status ?? "<none>")) {
144
+ out.push({
145
+ dimension: "terminal_status",
146
+ class: "blocker",
147
+ operationIndex: index,
148
+ left: describeOutcome(leftSettled),
149
+ right: describeOutcome(rightSettled),
150
+ });
151
+ }
152
+ else if (leftSettled?.status === "failed" && rightSettled?.status === "failed") {
153
+ push("public_error_code", leftSettled.code, rightSettled.code);
154
+ }
155
+ push("effect_uncertainty", left.unresolvedEffectIds.join(","), right.unresolvedEffectIds.join(","));
156
+ push("stage_path", left.stages.join(">"), right.stages.join(">"));
157
+ push("abort_signal", String(left.abortRequested), String(right.abortRequested));
158
+ return out;
159
+ }
160
+ /** Compare two traces of the same scenario. Equal digests short-circuit to an empty report. */
161
+ export function compareOperationTraces(left, right) {
162
+ const leftDigest = computeTraceDigest(left);
163
+ const rightDigest = computeTraceDigest(right);
164
+ if (leftDigest === rightDigest)
165
+ return { equal: true, leftDigest, rightDigest, blockers: [], nonBlockers: [] };
166
+ const leftSummary = summarizeTrace(left);
167
+ const rightSummary = summarizeTrace(right);
168
+ const divergences = [];
169
+ if (leftSummary.operations.length !== rightSummary.operations.length) {
170
+ divergences.push({
171
+ dimension: "operation_count",
172
+ class: "blocker",
173
+ left: String(leftSummary.operations.length),
174
+ right: String(rightSummary.operations.length),
175
+ });
176
+ }
177
+ const shared = Math.min(leftSummary.operations.length, rightSummary.operations.length);
178
+ for (let index = 0; index < shared; index++) {
179
+ divergences.push(...compareOperation(index, leftSummary.operations[index], rightSummary.operations[index]));
180
+ }
181
+ const leftCommands = leftSummary.acceptedCommandIds.join(",");
182
+ const rightCommands = rightSummary.acceptedCommandIds.join(",");
183
+ if (leftCommands !== rightCommands) {
184
+ divergences.push({ dimension: "accepted_commands", class: "blocker", left: leftCommands, right: rightCommands });
185
+ }
186
+ const leftWrites = leftSummary.sessionWrites.join(",");
187
+ const rightWrites = rightSummary.sessionWrites.join(",");
188
+ if (leftWrites !== rightWrites) {
189
+ divergences.push({ dimension: "session_write_order", class: "blocker", left: leftWrites, right: rightWrites });
190
+ }
191
+ return {
192
+ equal: false,
193
+ leftDigest,
194
+ rightDigest,
195
+ blockers: divergences.filter((item) => item.class === "blocker"),
196
+ nonBlockers: divergences.filter((item) => item.class === "non_blocker"),
197
+ };
198
+ }
199
+ //# sourceMappingURL=operation-trace-divergence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-trace-divergence.js","sourceRoot":"","sources":["../../src/harness/operation-trace-divergence.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Trace comparison for shadow-mode runtime convergence (FND-003, Stage C).\n *\n * While `AgentHarness` runs as a shadow reducer beside the authoritative\n * `AgentSession`, every scenario yields two traces. This module reduces each\n * trace to a per-operation summary and classifies every difference as a\n * `blocker` (the two runtimes disagree about lifecycle semantics) or a\n * `non_blocker` (they took a different path to the same committed state).\n *\n * Blocker dimensions follow the plan verbatim: operation count and identity,\n * the attempt set and how each attempt closed, terminal status and public\n * error code, accepted deferred-command IDs in order, session-write order,\n * and effect uncertainty left open at settlement. Stage paths and abort\n * signalling without an outcome difference are informational only.\n */\n\nimport { computeTraceDigest, type OperationTraceEvent, type TraceOutcome } from \"./operation-trace.ts\";\n\nexport type TraceDivergenceClass = \"blocker\" | \"non_blocker\";\n\nexport type TraceDivergenceDimension =\n\t| \"operation_count\"\n\t| \"operation_identity\"\n\t| \"attempt_set\"\n\t| \"terminal_status\"\n\t| \"public_error_code\"\n\t| \"accepted_commands\"\n\t| \"session_write_order\"\n\t| \"effect_uncertainty\"\n\t| \"stage_path\"\n\t| \"abort_signal\";\n\nconst BLOCKER_DIMENSIONS: ReadonlySet<TraceDivergenceDimension> = new Set([\n\t\"operation_count\",\n\t\"operation_identity\",\n\t\"attempt_set\",\n\t\"terminal_status\",\n\t\"public_error_code\",\n\t\"accepted_commands\",\n\t\"session_write_order\",\n\t\"effect_uncertainty\",\n]);\n\nexport interface TraceDivergence {\n\treadonly dimension: TraceDivergenceDimension;\n\treadonly class: TraceDivergenceClass;\n\treadonly operationIndex?: number;\n\treadonly left: string;\n\treadonly right: string;\n}\n\nexport interface TraceComparison {\n\treadonly equal: boolean;\n\treadonly leftDigest: string;\n\treadonly rightDigest: string;\n\treadonly blockers: readonly TraceDivergence[];\n\treadonly nonBlockers: readonly TraceDivergence[];\n}\n\nexport interface TraceAttemptSummary {\n\treadonly attemptId: string;\n\treadonly index: number;\n\treadonly reason: string;\n\treadonly outcome?: string;\n}\n\n/** Everything the comparison reads about one operation, in source order. */\nexport interface OperationTraceSummary {\n\treadonly operationId: string;\n\treadonly kind: string;\n\treadonly sequence: number;\n\treadonly stages: readonly string[];\n\treadonly attempts: readonly TraceAttemptSummary[];\n\treadonly abortRequested: boolean;\n\treadonly settlement?: TraceOutcome;\n\treadonly settled?: TraceOutcome;\n\t/** Effects marked uncertain and not reconciled before the operation settled. */\n\treadonly unresolvedEffectIds: readonly string[];\n}\n\nexport interface TraceSummary {\n\treadonly operations: readonly OperationTraceSummary[];\n\treadonly acceptedCommandIds: readonly string[];\n\treadonly sessionWrites: readonly string[];\n}\n\ninterface MutableOperation {\n\toperationId: string;\n\tkind: string;\n\tsequence: number;\n\tstages: string[];\n\tattempts: TraceAttemptSummary[];\n\tabortRequested: boolean;\n\tsettlement?: TraceOutcome;\n\tsettled?: TraceOutcome;\n\tunresolved: Set<string>;\n}\n\nexport function summarizeTrace(events: readonly OperationTraceEvent[]): TraceSummary {\n\tconst operations: MutableOperation[] = [];\n\tconst byId = new Map<string, MutableOperation>();\n\tconst acceptedCommandIds: string[] = [];\n\tconst sessionWrites: string[] = [];\n\tfor (const event of events) {\n\t\tswitch (event.type) {\n\t\t\tcase \"operation_started\": {\n\t\t\t\tconst operation: MutableOperation = {\n\t\t\t\t\toperationId: event.operationId,\n\t\t\t\t\tkind: event.kind,\n\t\t\t\t\tsequence: event.sequence,\n\t\t\t\t\tstages: [],\n\t\t\t\t\tattempts: [],\n\t\t\t\t\tabortRequested: false,\n\t\t\t\t\tunresolved: new Set(),\n\t\t\t\t};\n\t\t\t\toperations.push(operation);\n\t\t\t\tbyId.set(event.operationId, operation);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"stage_changed\":\n\t\t\t\tbyId.get(event.operationId)?.stages.push(event.stage);\n\t\t\t\tbreak;\n\t\t\tcase \"attempt_started\":\n\t\t\t\tbyId\n\t\t\t\t\t.get(event.operationId)\n\t\t\t\t\t?.attempts.push({ attemptId: event.attemptId, index: event.index, reason: event.reason });\n\t\t\t\tbreak;\n\t\t\tcase \"attempt_finished\": {\n\t\t\t\tconst operation = byId.get(event.operationId);\n\t\t\t\tif (operation === undefined) break;\n\t\t\t\toperation.attempts = operation.attempts.map((attempt) =>\n\t\t\t\t\tattempt.attemptId === event.attemptId ? { ...attempt, outcome: event.outcome } : attempt,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"abort_requested\": {\n\t\t\t\tconst operation = byId.get(event.operationId);\n\t\t\t\tif (operation !== undefined) operation.abortRequested = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"effect_uncertain\":\n\t\t\t\tbyId.get(event.operationId)?.unresolved.add(event.effectId);\n\t\t\t\tbreak;\n\t\t\tcase \"effect_reconciled\":\n\t\t\t\tbyId.get(event.operationId)?.unresolved.delete(event.effectId);\n\t\t\t\tbreak;\n\t\t\tcase \"effect_prepared\":\n\t\t\t\tbreak;\n\t\t\tcase \"deferred_command_accepted\":\n\t\t\t\tacceptedCommandIds.push(event.commandId);\n\t\t\t\tbreak;\n\t\t\tcase \"session_write_accepted\":\n\t\t\t\tsessionWrites.push(`${event.writeSequence}:${event.writeType}`);\n\t\t\t\tbreak;\n\t\t\tcase \"settlement_started\": {\n\t\t\t\tconst operation = byId.get(event.operationId);\n\t\t\t\tif (operation !== undefined) operation.settlement = event.outcome;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"operation_settled\": {\n\t\t\t\tconst operation = byId.get(event.operationId);\n\t\t\t\tif (operation !== undefined) operation.settled = event.outcome;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconst unknownEvent: never = event;\n\t\t\t\tthrow new TypeError(`Unknown trace event ${String((unknownEvent as { type?: unknown }).type)}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn {\n\t\toperations: operations.map((operation) => ({\n\t\t\toperationId: operation.operationId,\n\t\t\tkind: operation.kind,\n\t\t\tsequence: operation.sequence,\n\t\t\tstages: operation.stages,\n\t\t\tattempts: operation.attempts,\n\t\t\tabortRequested: operation.abortRequested,\n\t\t\t...(operation.settlement === undefined ? {} : { settlement: operation.settlement }),\n\t\t\t...(operation.settled === undefined ? {} : { settled: operation.settled }),\n\t\t\tunresolvedEffectIds: [...operation.unresolved].sort(compareCodeUnits),\n\t\t})),\n\t\tacceptedCommandIds,\n\t\tsessionWrites,\n\t};\n}\n\n/** Code-unit order, so the report does not depend on the host locale. */\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction describeOutcome(outcome: TraceOutcome | undefined): string {\n\tif (outcome === undefined) return \"<none>\";\n\treturn outcome.status === \"failed\" ? `failed:${outcome.code}` : outcome.status;\n}\n\nfunction describeAttempts(attempts: readonly TraceAttemptSummary[]): string {\n\treturn attempts.map((attempt) => `${attempt.attemptId}/${attempt.reason}=${attempt.outcome ?? \"<open>\"}`).join(\",\");\n}\n\nfunction compareOperation(index: number, left: OperationTraceSummary, right: OperationTraceSummary): TraceDivergence[] {\n\tconst out: TraceDivergence[] = [];\n\tconst push = (dimension: TraceDivergenceDimension, leftValue: string, rightValue: string): void => {\n\t\tif (leftValue === rightValue) return;\n\t\tconst divergenceClass: TraceDivergenceClass = BLOCKER_DIMENSIONS.has(dimension) ? \"blocker\" : \"non_blocker\";\n\t\tout.push({ dimension, class: divergenceClass, operationIndex: index, left: leftValue, right: rightValue });\n\t};\n\tpush(\"operation_identity\", `${left.kind}#${left.sequence}`, `${right.kind}#${right.sequence}`);\n\tpush(\"attempt_set\", describeAttempts(left.attempts), describeAttempts(right.attempts));\n\tconst leftSettled = left.settled ?? left.settlement;\n\tconst rightSettled = right.settled ?? right.settlement;\n\tif ((leftSettled?.status ?? \"<none>\") !== (rightSettled?.status ?? \"<none>\")) {\n\t\tout.push({\n\t\t\tdimension: \"terminal_status\",\n\t\t\tclass: \"blocker\",\n\t\t\toperationIndex: index,\n\t\t\tleft: describeOutcome(leftSettled),\n\t\t\tright: describeOutcome(rightSettled),\n\t\t});\n\t} else if (leftSettled?.status === \"failed\" && rightSettled?.status === \"failed\") {\n\t\tpush(\"public_error_code\", leftSettled.code, rightSettled.code);\n\t}\n\tpush(\"effect_uncertainty\", left.unresolvedEffectIds.join(\",\"), right.unresolvedEffectIds.join(\",\"));\n\tpush(\"stage_path\", left.stages.join(\">\"), right.stages.join(\">\"));\n\tpush(\"abort_signal\", String(left.abortRequested), String(right.abortRequested));\n\treturn out;\n}\n\n/** Compare two traces of the same scenario. Equal digests short-circuit to an empty report. */\nexport function compareOperationTraces(\n\tleft: readonly OperationTraceEvent[],\n\tright: readonly OperationTraceEvent[],\n): TraceComparison {\n\tconst leftDigest = computeTraceDigest(left);\n\tconst rightDigest = computeTraceDigest(right);\n\tif (leftDigest === rightDigest) return { equal: true, leftDigest, rightDigest, blockers: [], nonBlockers: [] };\n\tconst leftSummary = summarizeTrace(left);\n\tconst rightSummary = summarizeTrace(right);\n\tconst divergences: TraceDivergence[] = [];\n\tif (leftSummary.operations.length !== rightSummary.operations.length) {\n\t\tdivergences.push({\n\t\t\tdimension: \"operation_count\",\n\t\t\tclass: \"blocker\",\n\t\t\tleft: String(leftSummary.operations.length),\n\t\t\tright: String(rightSummary.operations.length),\n\t\t});\n\t}\n\tconst shared = Math.min(leftSummary.operations.length, rightSummary.operations.length);\n\tfor (let index = 0; index < shared; index++) {\n\t\tdivergences.push(...compareOperation(index, leftSummary.operations[index], rightSummary.operations[index]));\n\t}\n\tconst leftCommands = leftSummary.acceptedCommandIds.join(\",\");\n\tconst rightCommands = rightSummary.acceptedCommandIds.join(\",\");\n\tif (leftCommands !== rightCommands) {\n\t\tdivergences.push({ dimension: \"accepted_commands\", class: \"blocker\", left: leftCommands, right: rightCommands });\n\t}\n\tconst leftWrites = leftSummary.sessionWrites.join(\",\");\n\tconst rightWrites = rightSummary.sessionWrites.join(\",\");\n\tif (leftWrites !== rightWrites) {\n\t\tdivergences.push({ dimension: \"session_write_order\", class: \"blocker\", left: leftWrites, right: rightWrites });\n\t}\n\treturn {\n\t\tequal: false,\n\t\tleftDigest,\n\t\trightDigest,\n\t\tblockers: divergences.filter((item) => item.class === \"blocker\"),\n\t\tnonBlockers: divergences.filter((item) => item.class === \"non_blocker\"),\n\t};\n}\n"]}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Operation trace vocabulary: the normalized event stream two runtimes must
3
+ * agree on before either can be called the operation authority.
4
+ *
5
+ * The CLI `AgentSession` and the core `AgentHarness` both drive an operation
6
+ * lifecycle today. Converging them (FND-003, Stage A) needs a common, timing-
7
+ * free record of what each one did: which operations started, which attempts
8
+ * ran and how they closed, which effects were left uncertain, which deferred
9
+ * commands and session writes were accepted, and how the operation settled.
10
+ * This module fixes that vocabulary and derives it mechanically from the pure
11
+ * lifecycle reducer, so a trace is a function of the command stream and not of
12
+ * whichever runtime happened to emit it.
13
+ *
14
+ * Traces carry no wall-clock data and no human wording: `TraceOutcome` keeps
15
+ * the public failure `code` (a blocker dimension) and drops the message (a
16
+ * non-blocker). Two runtimes that produce the same trace digest for the same
17
+ * scenario are behaviourally indistinguishable at the lifecycle boundary.
18
+ */
19
+ import type { HarnessAttemptOutcome, HarnessAttemptReason, HarnessLifecycleCommand, HarnessLifecycleResult, HarnessLifecycleState, HarnessOperationKind, HarnessOperationOutcome, HarnessOperationStage } from "./operation-lifecycle-types.ts";
20
+ export declare const OPERATION_TRACE_SCHEMA_VERSION: "omk.operation-trace.v1";
21
+ /** Public outcome projection: status and failure code only, never wording. */
22
+ export type TraceOutcome = {
23
+ readonly status: "completed";
24
+ } | {
25
+ readonly status: "failed";
26
+ readonly code: string;
27
+ } | {
28
+ readonly status: "aborted";
29
+ } | {
30
+ readonly status: "cancelled";
31
+ };
32
+ /** Events derived from lifecycle commands. Emitted only by `OperationTraceRecorder.apply`. */
33
+ export type LifecycleTraceEventBody = {
34
+ readonly type: "operation_started";
35
+ readonly operationId: string;
36
+ readonly kind: HarnessOperationKind;
37
+ readonly sequence: number;
38
+ } | {
39
+ readonly type: "stage_changed";
40
+ readonly operationId: string;
41
+ readonly stage: HarnessOperationStage;
42
+ } | {
43
+ readonly type: "attempt_started";
44
+ readonly operationId: string;
45
+ readonly attemptId: string;
46
+ readonly index: number;
47
+ readonly reason: HarnessAttemptReason;
48
+ } | {
49
+ readonly type: "attempt_finished";
50
+ readonly operationId: string;
51
+ readonly attemptId: string;
52
+ readonly outcome: HarnessAttemptOutcome;
53
+ } | {
54
+ readonly type: "abort_requested";
55
+ readonly operationId: string;
56
+ } | {
57
+ readonly type: "settlement_started";
58
+ readonly operationId: string;
59
+ readonly outcome: TraceOutcome;
60
+ } | {
61
+ readonly type: "operation_settled";
62
+ readonly operationId: string;
63
+ readonly outcome: TraceOutcome;
64
+ };
65
+ /** Events reported by subsystems around the lifecycle (effects, deferred commands, session writes). */
66
+ export type ExternalTraceEventBody = {
67
+ readonly type: "effect_prepared";
68
+ readonly operationId: string;
69
+ readonly effectId: string;
70
+ } | {
71
+ readonly type: "effect_uncertain";
72
+ readonly operationId: string;
73
+ readonly effectId: string;
74
+ } | {
75
+ readonly type: "effect_reconciled";
76
+ readonly operationId: string;
77
+ readonly effectId: string;
78
+ } | {
79
+ readonly type: "deferred_command_accepted";
80
+ readonly commandId: string;
81
+ readonly name: string;
82
+ readonly operationId?: string;
83
+ } | {
84
+ readonly type: "session_write_accepted";
85
+ readonly writeSequence: number;
86
+ readonly writeType: string;
87
+ readonly operationId?: string;
88
+ };
89
+ export type OperationTraceEventBody = LifecycleTraceEventBody | ExternalTraceEventBody;
90
+ /** One trace entry. `seq` is 1-based and strictly increasing within a trace. */
91
+ export type OperationTraceEvent = OperationTraceEventBody & {
92
+ readonly seq: number;
93
+ };
94
+ export interface OperationTraceDocument {
95
+ readonly schemaVersion: typeof OPERATION_TRACE_SCHEMA_VERSION;
96
+ readonly events: readonly OperationTraceEvent[];
97
+ }
98
+ export declare function projectTraceOutcome(outcome: HarnessOperationOutcome): TraceOutcome;
99
+ /**
100
+ * The trace events one accepted lifecycle command produces. Pure: it reads the
101
+ * pre-transition state only to recover identities the command omits (the
102
+ * operation of an `attempt_end`, the outcome recorded at `settle_begin`).
103
+ */
104
+ export declare function traceEventsForCommand(before: HarnessLifecycleState, command: HarnessLifecycleCommand): readonly LifecycleTraceEventBody[];
105
+ export type TraceRecordResult = {
106
+ readonly ok: true;
107
+ readonly event: OperationTraceEvent;
108
+ } | {
109
+ readonly ok: false;
110
+ readonly code: "stale_operation" | "no_active_operation";
111
+ readonly message: string;
112
+ };
113
+ /**
114
+ * Drives the pure lifecycle reducer and appends the derived trace. A rejected
115
+ * command records nothing: the trace only ever describes accepted transitions,
116
+ * so two recorders fed the same accepted command stream produce byte-identical
117
+ * traces regardless of how many illegal commands were refused in between.
118
+ */
119
+ export declare class OperationTraceRecorder {
120
+ private readonly events;
121
+ private state;
122
+ constructor(initial?: HarnessLifecycleState);
123
+ get lifecycleState(): HarnessLifecycleState;
124
+ get size(): number;
125
+ apply(command: HarnessLifecycleCommand): HarnessLifecycleResult<HarnessLifecycleState>;
126
+ /** Record a subsystem event. Effect events must name the current operation. */
127
+ record(body: ExternalTraceEventBody): TraceRecordResult;
128
+ snapshot(): OperationTraceDocument;
129
+ digest(): string;
130
+ private push;
131
+ }
132
+ /** Canonical SHA-256 over the schema version and the ordered event list. */
133
+ export declare function computeTraceDigest(events: readonly OperationTraceEvent[]): string;
134
+ //# sourceMappingURL=operation-trace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-trace.d.ts","sourceRoot":"","sources":["../../src/harness/operation-trace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Operation trace vocabulary: the normalized event stream two runtimes must\n * agree on before either can be called the operation authority.\n *\n * The CLI `AgentSession` and the core `AgentHarness` both drive an operation\n * lifecycle today. Converging them (FND-003, Stage A) needs a common, timing-\n * free record of what each one did: which operations started, which attempts\n * ran and how they closed, which effects were left uncertain, which deferred\n * commands and session writes were accepted, and how the operation settled.\n * This module fixes that vocabulary and derives it mechanically from the pure\n * lifecycle reducer, so a trace is a function of the command stream and not of\n * whichever runtime happened to emit it.\n *\n * Traces carry no wall-clock data and no human wording: `TraceOutcome` keeps\n * the public failure `code` (a blocker dimension) and drops the message (a\n * non-blocker). Two runtimes that produce the same trace digest for the same\n * scenario are behaviourally indistinguishable at the lifecycle boundary.\n */\n\nimport { canonicalDigest } from \"./canonical-digest.ts\";\nimport { initialHarnessLifecycleState, reduceHarnessLifecycle } from \"./operation-lifecycle-reducer.ts\";\nimport type {\n\tHarnessAttemptOutcome,\n\tHarnessAttemptReason,\n\tHarnessLifecycleCommand,\n\tHarnessLifecycleResult,\n\tHarnessLifecycleState,\n\tHarnessOperationKind,\n\tHarnessOperationOutcome,\n\tHarnessOperationStage,\n} from \"./operation-lifecycle-types.ts\";\n\nexport const OPERATION_TRACE_SCHEMA_VERSION = \"omk.operation-trace.v1\" as const;\n\n/** Public outcome projection: status and failure code only, never wording. */\nexport type TraceOutcome =\n\t| { readonly status: \"completed\" }\n\t| { readonly status: \"failed\"; readonly code: string }\n\t| { readonly status: \"aborted\" }\n\t| { readonly status: \"cancelled\" };\n\n/** Events derived from lifecycle commands. Emitted only by `OperationTraceRecorder.apply`. */\nexport type LifecycleTraceEventBody =\n\t| {\n\t\t\treadonly type: \"operation_started\";\n\t\t\treadonly operationId: string;\n\t\t\treadonly kind: HarnessOperationKind;\n\t\t\treadonly sequence: number;\n\t }\n\t| { readonly type: \"stage_changed\"; readonly operationId: string; readonly stage: HarnessOperationStage }\n\t| {\n\t\t\treadonly type: \"attempt_started\";\n\t\t\treadonly operationId: string;\n\t\t\treadonly attemptId: string;\n\t\t\treadonly index: number;\n\t\t\treadonly reason: HarnessAttemptReason;\n\t }\n\t| {\n\t\t\treadonly type: \"attempt_finished\";\n\t\t\treadonly operationId: string;\n\t\t\treadonly attemptId: string;\n\t\t\treadonly outcome: HarnessAttemptOutcome;\n\t }\n\t| { readonly type: \"abort_requested\"; readonly operationId: string }\n\t| { readonly type: \"settlement_started\"; readonly operationId: string; readonly outcome: TraceOutcome }\n\t| { readonly type: \"operation_settled\"; readonly operationId: string; readonly outcome: TraceOutcome };\n\n/** Events reported by subsystems around the lifecycle (effects, deferred commands, session writes). */\nexport type ExternalTraceEventBody =\n\t| { readonly type: \"effect_prepared\"; readonly operationId: string; readonly effectId: string }\n\t| { readonly type: \"effect_uncertain\"; readonly operationId: string; readonly effectId: string }\n\t| { readonly type: \"effect_reconciled\"; readonly operationId: string; readonly effectId: string }\n\t| {\n\t\t\treadonly type: \"deferred_command_accepted\";\n\t\t\treadonly commandId: string;\n\t\t\treadonly name: string;\n\t\t\treadonly operationId?: string;\n\t }\n\t| {\n\t\t\treadonly type: \"session_write_accepted\";\n\t\t\treadonly writeSequence: number;\n\t\t\treadonly writeType: string;\n\t\t\treadonly operationId?: string;\n\t };\n\nexport type OperationTraceEventBody = LifecycleTraceEventBody | ExternalTraceEventBody;\n\n/** One trace entry. `seq` is 1-based and strictly increasing within a trace. */\nexport type OperationTraceEvent = OperationTraceEventBody & { readonly seq: number };\n\nexport interface OperationTraceDocument {\n\treadonly schemaVersion: typeof OPERATION_TRACE_SCHEMA_VERSION;\n\treadonly events: readonly OperationTraceEvent[];\n}\n\nexport function projectTraceOutcome(outcome: HarnessOperationOutcome): TraceOutcome {\n\tswitch (outcome.status) {\n\t\tcase \"completed\":\n\t\t\treturn { status: \"completed\" };\n\t\tcase \"failed\":\n\t\t\treturn { status: \"failed\", code: outcome.code };\n\t\tcase \"aborted\":\n\t\t\treturn { status: \"aborted\" };\n\t\tcase \"cancelled\":\n\t\t\treturn { status: \"cancelled\" };\n\t}\n}\n\n/**\n * The trace events one accepted lifecycle command produces. Pure: it reads the\n * pre-transition state only to recover identities the command omits (the\n * operation of an `attempt_end`, the outcome recorded at `settle_begin`).\n */\nexport function traceEventsForCommand(\n\tbefore: HarnessLifecycleState,\n\tcommand: HarnessLifecycleCommand,\n): readonly LifecycleTraceEventBody[] {\n\tswitch (command.type) {\n\t\tcase \"begin\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\toperationId: command.operation.operationId,\n\t\t\t\t\tkind: command.operation.kind,\n\t\t\t\t\tsequence: command.operation.sequence,\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"stage\":\n\t\t\treturn [{ type: \"stage_changed\", operationId: command.operationId, stage: command.stage }];\n\t\tcase \"attempt_begin\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"attempt_started\",\n\t\t\t\t\toperationId: command.attempt.operationId,\n\t\t\t\t\tattemptId: command.attempt.attemptId,\n\t\t\t\t\tindex: command.attempt.index,\n\t\t\t\t\treason: command.attempt.reason,\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"attempt_end\":\n\t\t\tif (before.tag === \"idle\") return [];\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"attempt_finished\",\n\t\t\t\t\toperationId: before.operation.operationId,\n\t\t\t\t\tattemptId: command.attemptId,\n\t\t\t\t\toutcome: command.outcome,\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"abort_request\":\n\t\t\treturn [{ type: \"abort_requested\", operationId: command.operationId }];\n\t\tcase \"settle_begin\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"settlement_started\",\n\t\t\t\t\toperationId: command.operationId,\n\t\t\t\t\toutcome: projectTraceOutcome(command.outcome),\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"settle_finish\":\n\t\t\tif (before.tag !== \"settling\") return [];\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"operation_settled\",\n\t\t\t\t\toperationId: command.operationId,\n\t\t\t\t\toutcome: projectTraceOutcome(before.outcome),\n\t\t\t\t},\n\t\t\t];\n\t}\n}\n\nexport type TraceRecordResult =\n\t| { readonly ok: true; readonly event: OperationTraceEvent }\n\t| { readonly ok: false; readonly code: \"stale_operation\" | \"no_active_operation\"; readonly message: string };\n\n/**\n * Drives the pure lifecycle reducer and appends the derived trace. A rejected\n * command records nothing: the trace only ever describes accepted transitions,\n * so two recorders fed the same accepted command stream produce byte-identical\n * traces regardless of how many illegal commands were refused in between.\n */\nexport class OperationTraceRecorder {\n\tprivate readonly events: OperationTraceEvent[] = [];\n\tprivate state: HarnessLifecycleState;\n\n\tconstructor(initial: HarnessLifecycleState = initialHarnessLifecycleState()) {\n\t\tthis.state = initial;\n\t}\n\n\tget lifecycleState(): HarnessLifecycleState {\n\t\treturn this.state;\n\t}\n\n\tget size(): number {\n\t\treturn this.events.length;\n\t}\n\n\tapply(command: HarnessLifecycleCommand): HarnessLifecycleResult<HarnessLifecycleState> {\n\t\tconst result = reduceHarnessLifecycle(this.state, command);\n\t\tif (!result.ok) return result;\n\t\tfor (const body of traceEventsForCommand(this.state, command)) this.push(body);\n\t\tthis.state = result.value;\n\t\treturn result;\n\t}\n\n\t/** Record a subsystem event. Effect events must name the current operation. */\n\trecord(body: ExternalTraceEventBody): TraceRecordResult {\n\t\tif (body.operationId !== undefined) {\n\t\t\tif (this.state.tag === \"idle\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tcode: \"no_active_operation\",\n\t\t\t\t\tmessage: `${body.type} names ${body.operationId} while idle`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (this.state.operation.operationId !== body.operationId) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tcode: \"stale_operation\",\n\t\t\t\t\tmessage: `${body.type} names ${body.operationId} but current operation is ${this.state.operation.operationId}`,\n\t\t\t\t};\n\t\t\t}\n\t\t} else if (body.type.startsWith(\"effect_\")) {\n\t\t\treturn { ok: false, code: \"no_active_operation\", message: `${body.type} requires an operationId` };\n\t\t}\n\t\treturn { ok: true, event: this.push(body) };\n\t}\n\n\tsnapshot(): OperationTraceDocument {\n\t\treturn { schemaVersion: OPERATION_TRACE_SCHEMA_VERSION, events: [...this.events] };\n\t}\n\n\tdigest(): string {\n\t\treturn computeTraceDigest(this.events);\n\t}\n\n\tprivate push(body: OperationTraceEventBody): OperationTraceEvent {\n\t\tconst event: OperationTraceEvent = Object.freeze({ ...body, seq: this.events.length + 1 });\n\t\tthis.events.push(event);\n\t\treturn event;\n\t}\n}\n\n/** Canonical SHA-256 over the schema version and the ordered event list. */\nexport function computeTraceDigest(events: readonly OperationTraceEvent[]): string {\n\treturn canonicalDigest({ schemaVersion: OPERATION_TRACE_SCHEMA_VERSION, events });\n}\n"]}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Operation trace vocabulary: the normalized event stream two runtimes must
3
+ * agree on before either can be called the operation authority.
4
+ *
5
+ * The CLI `AgentSession` and the core `AgentHarness` both drive an operation
6
+ * lifecycle today. Converging them (FND-003, Stage A) needs a common, timing-
7
+ * free record of what each one did: which operations started, which attempts
8
+ * ran and how they closed, which effects were left uncertain, which deferred
9
+ * commands and session writes were accepted, and how the operation settled.
10
+ * This module fixes that vocabulary and derives it mechanically from the pure
11
+ * lifecycle reducer, so a trace is a function of the command stream and not of
12
+ * whichever runtime happened to emit it.
13
+ *
14
+ * Traces carry no wall-clock data and no human wording: `TraceOutcome` keeps
15
+ * the public failure `code` (a blocker dimension) and drops the message (a
16
+ * non-blocker). Two runtimes that produce the same trace digest for the same
17
+ * scenario are behaviourally indistinguishable at the lifecycle boundary.
18
+ */
19
+ import { canonicalDigest } from "./canonical-digest.js";
20
+ import { initialHarnessLifecycleState, reduceHarnessLifecycle } from "./operation-lifecycle-reducer.js";
21
+ export const OPERATION_TRACE_SCHEMA_VERSION = "omk.operation-trace.v1";
22
+ export function projectTraceOutcome(outcome) {
23
+ switch (outcome.status) {
24
+ case "completed":
25
+ return { status: "completed" };
26
+ case "failed":
27
+ return { status: "failed", code: outcome.code };
28
+ case "aborted":
29
+ return { status: "aborted" };
30
+ case "cancelled":
31
+ return { status: "cancelled" };
32
+ }
33
+ }
34
+ /**
35
+ * The trace events one accepted lifecycle command produces. Pure: it reads the
36
+ * pre-transition state only to recover identities the command omits (the
37
+ * operation of an `attempt_end`, the outcome recorded at `settle_begin`).
38
+ */
39
+ export function traceEventsForCommand(before, command) {
40
+ switch (command.type) {
41
+ case "begin":
42
+ return [
43
+ {
44
+ type: "operation_started",
45
+ operationId: command.operation.operationId,
46
+ kind: command.operation.kind,
47
+ sequence: command.operation.sequence,
48
+ },
49
+ ];
50
+ case "stage":
51
+ return [{ type: "stage_changed", operationId: command.operationId, stage: command.stage }];
52
+ case "attempt_begin":
53
+ return [
54
+ {
55
+ type: "attempt_started",
56
+ operationId: command.attempt.operationId,
57
+ attemptId: command.attempt.attemptId,
58
+ index: command.attempt.index,
59
+ reason: command.attempt.reason,
60
+ },
61
+ ];
62
+ case "attempt_end":
63
+ if (before.tag === "idle")
64
+ return [];
65
+ return [
66
+ {
67
+ type: "attempt_finished",
68
+ operationId: before.operation.operationId,
69
+ attemptId: command.attemptId,
70
+ outcome: command.outcome,
71
+ },
72
+ ];
73
+ case "abort_request":
74
+ return [{ type: "abort_requested", operationId: command.operationId }];
75
+ case "settle_begin":
76
+ return [
77
+ {
78
+ type: "settlement_started",
79
+ operationId: command.operationId,
80
+ outcome: projectTraceOutcome(command.outcome),
81
+ },
82
+ ];
83
+ case "settle_finish":
84
+ if (before.tag !== "settling")
85
+ return [];
86
+ return [
87
+ {
88
+ type: "operation_settled",
89
+ operationId: command.operationId,
90
+ outcome: projectTraceOutcome(before.outcome),
91
+ },
92
+ ];
93
+ }
94
+ }
95
+ /**
96
+ * Drives the pure lifecycle reducer and appends the derived trace. A rejected
97
+ * command records nothing: the trace only ever describes accepted transitions,
98
+ * so two recorders fed the same accepted command stream produce byte-identical
99
+ * traces regardless of how many illegal commands were refused in between.
100
+ */
101
+ export class OperationTraceRecorder {
102
+ events = [];
103
+ state;
104
+ constructor(initial = initialHarnessLifecycleState()) {
105
+ this.state = initial;
106
+ }
107
+ get lifecycleState() {
108
+ return this.state;
109
+ }
110
+ get size() {
111
+ return this.events.length;
112
+ }
113
+ apply(command) {
114
+ const result = reduceHarnessLifecycle(this.state, command);
115
+ if (!result.ok)
116
+ return result;
117
+ for (const body of traceEventsForCommand(this.state, command))
118
+ this.push(body);
119
+ this.state = result.value;
120
+ return result;
121
+ }
122
+ /** Record a subsystem event. Effect events must name the current operation. */
123
+ record(body) {
124
+ if (body.operationId !== undefined) {
125
+ if (this.state.tag === "idle") {
126
+ return {
127
+ ok: false,
128
+ code: "no_active_operation",
129
+ message: `${body.type} names ${body.operationId} while idle`,
130
+ };
131
+ }
132
+ if (this.state.operation.operationId !== body.operationId) {
133
+ return {
134
+ ok: false,
135
+ code: "stale_operation",
136
+ message: `${body.type} names ${body.operationId} but current operation is ${this.state.operation.operationId}`,
137
+ };
138
+ }
139
+ }
140
+ else if (body.type.startsWith("effect_")) {
141
+ return { ok: false, code: "no_active_operation", message: `${body.type} requires an operationId` };
142
+ }
143
+ return { ok: true, event: this.push(body) };
144
+ }
145
+ snapshot() {
146
+ return { schemaVersion: OPERATION_TRACE_SCHEMA_VERSION, events: [...this.events] };
147
+ }
148
+ digest() {
149
+ return computeTraceDigest(this.events);
150
+ }
151
+ push(body) {
152
+ const event = Object.freeze({ ...body, seq: this.events.length + 1 });
153
+ this.events.push(event);
154
+ return event;
155
+ }
156
+ }
157
+ /** Canonical SHA-256 over the schema version and the ordered event list. */
158
+ export function computeTraceDigest(events) {
159
+ return canonicalDigest({ schemaVersion: OPERATION_TRACE_SCHEMA_VERSION, events });
160
+ }
161
+ //# sourceMappingURL=operation-trace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-trace.js","sourceRoot":"","sources":["../../src/harness/operation-trace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Operation trace vocabulary: the normalized event stream two runtimes must\n * agree on before either can be called the operation authority.\n *\n * The CLI `AgentSession` and the core `AgentHarness` both drive an operation\n * lifecycle today. Converging them (FND-003, Stage A) needs a common, timing-\n * free record of what each one did: which operations started, which attempts\n * ran and how they closed, which effects were left uncertain, which deferred\n * commands and session writes were accepted, and how the operation settled.\n * This module fixes that vocabulary and derives it mechanically from the pure\n * lifecycle reducer, so a trace is a function of the command stream and not of\n * whichever runtime happened to emit it.\n *\n * Traces carry no wall-clock data and no human wording: `TraceOutcome` keeps\n * the public failure `code` (a blocker dimension) and drops the message (a\n * non-blocker). Two runtimes that produce the same trace digest for the same\n * scenario are behaviourally indistinguishable at the lifecycle boundary.\n */\n\nimport { canonicalDigest } from \"./canonical-digest.ts\";\nimport { initialHarnessLifecycleState, reduceHarnessLifecycle } from \"./operation-lifecycle-reducer.ts\";\nimport type {\n\tHarnessAttemptOutcome,\n\tHarnessAttemptReason,\n\tHarnessLifecycleCommand,\n\tHarnessLifecycleResult,\n\tHarnessLifecycleState,\n\tHarnessOperationKind,\n\tHarnessOperationOutcome,\n\tHarnessOperationStage,\n} from \"./operation-lifecycle-types.ts\";\n\nexport const OPERATION_TRACE_SCHEMA_VERSION = \"omk.operation-trace.v1\" as const;\n\n/** Public outcome projection: status and failure code only, never wording. */\nexport type TraceOutcome =\n\t| { readonly status: \"completed\" }\n\t| { readonly status: \"failed\"; readonly code: string }\n\t| { readonly status: \"aborted\" }\n\t| { readonly status: \"cancelled\" };\n\n/** Events derived from lifecycle commands. Emitted only by `OperationTraceRecorder.apply`. */\nexport type LifecycleTraceEventBody =\n\t| {\n\t\t\treadonly type: \"operation_started\";\n\t\t\treadonly operationId: string;\n\t\t\treadonly kind: HarnessOperationKind;\n\t\t\treadonly sequence: number;\n\t }\n\t| { readonly type: \"stage_changed\"; readonly operationId: string; readonly stage: HarnessOperationStage }\n\t| {\n\t\t\treadonly type: \"attempt_started\";\n\t\t\treadonly operationId: string;\n\t\t\treadonly attemptId: string;\n\t\t\treadonly index: number;\n\t\t\treadonly reason: HarnessAttemptReason;\n\t }\n\t| {\n\t\t\treadonly type: \"attempt_finished\";\n\t\t\treadonly operationId: string;\n\t\t\treadonly attemptId: string;\n\t\t\treadonly outcome: HarnessAttemptOutcome;\n\t }\n\t| { readonly type: \"abort_requested\"; readonly operationId: string }\n\t| { readonly type: \"settlement_started\"; readonly operationId: string; readonly outcome: TraceOutcome }\n\t| { readonly type: \"operation_settled\"; readonly operationId: string; readonly outcome: TraceOutcome };\n\n/** Events reported by subsystems around the lifecycle (effects, deferred commands, session writes). */\nexport type ExternalTraceEventBody =\n\t| { readonly type: \"effect_prepared\"; readonly operationId: string; readonly effectId: string }\n\t| { readonly type: \"effect_uncertain\"; readonly operationId: string; readonly effectId: string }\n\t| { readonly type: \"effect_reconciled\"; readonly operationId: string; readonly effectId: string }\n\t| {\n\t\t\treadonly type: \"deferred_command_accepted\";\n\t\t\treadonly commandId: string;\n\t\t\treadonly name: string;\n\t\t\treadonly operationId?: string;\n\t }\n\t| {\n\t\t\treadonly type: \"session_write_accepted\";\n\t\t\treadonly writeSequence: number;\n\t\t\treadonly writeType: string;\n\t\t\treadonly operationId?: string;\n\t };\n\nexport type OperationTraceEventBody = LifecycleTraceEventBody | ExternalTraceEventBody;\n\n/** One trace entry. `seq` is 1-based and strictly increasing within a trace. */\nexport type OperationTraceEvent = OperationTraceEventBody & { readonly seq: number };\n\nexport interface OperationTraceDocument {\n\treadonly schemaVersion: typeof OPERATION_TRACE_SCHEMA_VERSION;\n\treadonly events: readonly OperationTraceEvent[];\n}\n\nexport function projectTraceOutcome(outcome: HarnessOperationOutcome): TraceOutcome {\n\tswitch (outcome.status) {\n\t\tcase \"completed\":\n\t\t\treturn { status: \"completed\" };\n\t\tcase \"failed\":\n\t\t\treturn { status: \"failed\", code: outcome.code };\n\t\tcase \"aborted\":\n\t\t\treturn { status: \"aborted\" };\n\t\tcase \"cancelled\":\n\t\t\treturn { status: \"cancelled\" };\n\t}\n}\n\n/**\n * The trace events one accepted lifecycle command produces. Pure: it reads the\n * pre-transition state only to recover identities the command omits (the\n * operation of an `attempt_end`, the outcome recorded at `settle_begin`).\n */\nexport function traceEventsForCommand(\n\tbefore: HarnessLifecycleState,\n\tcommand: HarnessLifecycleCommand,\n): readonly LifecycleTraceEventBody[] {\n\tswitch (command.type) {\n\t\tcase \"begin\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\toperationId: command.operation.operationId,\n\t\t\t\t\tkind: command.operation.kind,\n\t\t\t\t\tsequence: command.operation.sequence,\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"stage\":\n\t\t\treturn [{ type: \"stage_changed\", operationId: command.operationId, stage: command.stage }];\n\t\tcase \"attempt_begin\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"attempt_started\",\n\t\t\t\t\toperationId: command.attempt.operationId,\n\t\t\t\t\tattemptId: command.attempt.attemptId,\n\t\t\t\t\tindex: command.attempt.index,\n\t\t\t\t\treason: command.attempt.reason,\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"attempt_end\":\n\t\t\tif (before.tag === \"idle\") return [];\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"attempt_finished\",\n\t\t\t\t\toperationId: before.operation.operationId,\n\t\t\t\t\tattemptId: command.attemptId,\n\t\t\t\t\toutcome: command.outcome,\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"abort_request\":\n\t\t\treturn [{ type: \"abort_requested\", operationId: command.operationId }];\n\t\tcase \"settle_begin\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"settlement_started\",\n\t\t\t\t\toperationId: command.operationId,\n\t\t\t\t\toutcome: projectTraceOutcome(command.outcome),\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"settle_finish\":\n\t\t\tif (before.tag !== \"settling\") return [];\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"operation_settled\",\n\t\t\t\t\toperationId: command.operationId,\n\t\t\t\t\toutcome: projectTraceOutcome(before.outcome),\n\t\t\t\t},\n\t\t\t];\n\t}\n}\n\nexport type TraceRecordResult =\n\t| { readonly ok: true; readonly event: OperationTraceEvent }\n\t| { readonly ok: false; readonly code: \"stale_operation\" | \"no_active_operation\"; readonly message: string };\n\n/**\n * Drives the pure lifecycle reducer and appends the derived trace. A rejected\n * command records nothing: the trace only ever describes accepted transitions,\n * so two recorders fed the same accepted command stream produce byte-identical\n * traces regardless of how many illegal commands were refused in between.\n */\nexport class OperationTraceRecorder {\n\tprivate readonly events: OperationTraceEvent[] = [];\n\tprivate state: HarnessLifecycleState;\n\n\tconstructor(initial: HarnessLifecycleState = initialHarnessLifecycleState()) {\n\t\tthis.state = initial;\n\t}\n\n\tget lifecycleState(): HarnessLifecycleState {\n\t\treturn this.state;\n\t}\n\n\tget size(): number {\n\t\treturn this.events.length;\n\t}\n\n\tapply(command: HarnessLifecycleCommand): HarnessLifecycleResult<HarnessLifecycleState> {\n\t\tconst result = reduceHarnessLifecycle(this.state, command);\n\t\tif (!result.ok) return result;\n\t\tfor (const body of traceEventsForCommand(this.state, command)) this.push(body);\n\t\tthis.state = result.value;\n\t\treturn result;\n\t}\n\n\t/** Record a subsystem event. Effect events must name the current operation. */\n\trecord(body: ExternalTraceEventBody): TraceRecordResult {\n\t\tif (body.operationId !== undefined) {\n\t\t\tif (this.state.tag === \"idle\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tcode: \"no_active_operation\",\n\t\t\t\t\tmessage: `${body.type} names ${body.operationId} while idle`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (this.state.operation.operationId !== body.operationId) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tcode: \"stale_operation\",\n\t\t\t\t\tmessage: `${body.type} names ${body.operationId} but current operation is ${this.state.operation.operationId}`,\n\t\t\t\t};\n\t\t\t}\n\t\t} else if (body.type.startsWith(\"effect_\")) {\n\t\t\treturn { ok: false, code: \"no_active_operation\", message: `${body.type} requires an operationId` };\n\t\t}\n\t\treturn { ok: true, event: this.push(body) };\n\t}\n\n\tsnapshot(): OperationTraceDocument {\n\t\treturn { schemaVersion: OPERATION_TRACE_SCHEMA_VERSION, events: [...this.events] };\n\t}\n\n\tdigest(): string {\n\t\treturn computeTraceDigest(this.events);\n\t}\n\n\tprivate push(body: OperationTraceEventBody): OperationTraceEvent {\n\t\tconst event: OperationTraceEvent = Object.freeze({ ...body, seq: this.events.length + 1 });\n\t\tthis.events.push(event);\n\t\treturn event;\n\t}\n}\n\n/** Canonical SHA-256 over the schema version and the ordered event list. */\nexport function computeTraceDigest(events: readonly OperationTraceEvent[]): string {\n\treturn canonicalDigest({ schemaVersion: OPERATION_TRACE_SCHEMA_VERSION, events });\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function normalizeReverseSkillText(text: string | undefined): string;
2
+ export declare function normalizeReverseSkillName(name: string): string;
3
+ //# sourceMappingURL=reverse-skill-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverse-skill-text.d.ts","sourceRoot":"","sources":["../../src/harness/reverse-skill-text.ts"],"names":[],"mappings":"AAAA,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAS1E;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ9D","sourcesContent":["export function normalizeReverseSkillText(text: string | undefined): string {\n\treturn (text ?? \"\")\n\t\t.normalize(\"NFKC\")\n\t\t.toLowerCase()\n\t\t.replace(/[\\u2018\\u2019]/g, \"'\")\n\t\t.replace(/[\\u201c\\u201d]/g, '\"')\n\t\t.replace(/[_./\\\\:-]+/g, \" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n}\n\nexport function normalizeReverseSkillName(name: string): string {\n\tconst normalized = normalizeReverseSkillText(name)\n\t\t.replace(/[^a-z0-9-]+/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\")\n\t\t.slice(0, 64)\n\t\t.replace(/-$/g, \"\");\n\treturn normalized || \"reverse-skill\";\n}\n"]}
@@ -0,0 +1,20 @@
1
+ export function normalizeReverseSkillText(text) {
2
+ return (text ?? "")
3
+ .normalize("NFKC")
4
+ .toLowerCase()
5
+ .replace(/[\u2018\u2019]/g, "'")
6
+ .replace(/[\u201c\u201d]/g, '"')
7
+ .replace(/[_./\\:-]+/g, " ")
8
+ .replace(/\s+/g, " ")
9
+ .trim();
10
+ }
11
+ export function normalizeReverseSkillName(name) {
12
+ const normalized = normalizeReverseSkillText(name)
13
+ .replace(/[^a-z0-9-]+/g, "-")
14
+ .replace(/-+/g, "-")
15
+ .replace(/^-|-$/g, "")
16
+ .slice(0, 64)
17
+ .replace(/-$/g, "");
18
+ return normalized || "reverse-skill";
19
+ }
20
+ //# sourceMappingURL=reverse-skill-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverse-skill-text.js","sourceRoot":"","sources":["../../src/harness/reverse-skill-text.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,yBAAyB,CAAC,IAAwB,EAAU;IAC3E,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;SACjB,SAAS,CAAC,MAAM,CAAC;SACjB,WAAW,EAAE;SACb,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC;SAC/B,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC;SAC/B,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AAAA,CACT;AAED,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAU;IAC/D,MAAM,UAAU,GAAG,yBAAyB,CAAC,IAAI,CAAC;SAChD,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrB,OAAO,UAAU,IAAI,eAAe,CAAC;AAAA,CACrC","sourcesContent":["export function normalizeReverseSkillText(text: string | undefined): string {\n\treturn (text ?? \"\")\n\t\t.normalize(\"NFKC\")\n\t\t.toLowerCase()\n\t\t.replace(/[\\u2018\\u2019]/g, \"'\")\n\t\t.replace(/[\\u201c\\u201d]/g, '\"')\n\t\t.replace(/[_./\\\\:-]+/g, \" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n}\n\nexport function normalizeReverseSkillName(name: string): string {\n\tconst normalized = normalizeReverseSkillText(name)\n\t\t.replace(/[^a-z0-9-]+/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\")\n\t\t.slice(0, 64)\n\t\t.replace(/-$/g, \"\");\n\treturn normalized || \"reverse-skill\";\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const TOOL_ALIASES: Record<string, string[]>;
2
+ export declare function getReverseSkillToolAliases(tool: string): string[];
3
+ //# sourceMappingURL=reverse-skill-tool-aliases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverse-skill-tool-aliases.d.ts","sourceRoot":"","sources":["../../src/harness/reverse-skill-tool-aliases.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CA0CjD,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjE","sourcesContent":["export const TOOL_ALIASES: Record<string, string[]> = {\n\t\"anything-analyzer\": [\"anything-analyzer\"],\n\tadb: [\"adb\"],\n\tanalyzeheadless: [\"analyzeHeadless\"],\n\tmitmproxy: [\"mitmproxy\", \"mitmweb\", \"mitmdump\"],\n\tmitmproxy2swagger: [\"mitmproxy2swagger\"],\n\ttshark: [\"tshark\"],\n\ttcpdump: [\"tcpdump\"],\n\tscapy: [\"python3\", \"python\"],\n\tapktool: [\"apktool\"],\n\tapksigner: [\"apksigner\"],\n\tbinwalk: [\"binwalk\"],\n\tburpsuite: [\"burpsuite\"],\n\tchrome: [\"google-chrome\", \"chromium\", \"chrome\"],\n\tfrida: [\"frida\", \"frida-ps\"],\n\tgdb: [\"gdb\"],\n\tgh: [\"gh\"],\n\tgit: [\"git\"],\n\tghidra: [\"ghidraRun\", \"analyzeHeadless\"],\n\tgitleaks: [\"gitleaks\"],\n\tgraphviz: [\"dot\"],\n\tidapro: [\"ida\", \"idat\", \"idat64\"],\n\t\"idalib-mcp\": [\"ida-pro-mcp\", \"idalib-mcp\"],\n\tjadx: [\"jadx\"],\n\tjshookmcp: [\"jshook\", \"npx\"],\n\tnode: [\"node\"],\n\tnuclei: [\"nuclei\"],\n\tpandoc: [\"pandoc\"],\n\tplantuml: [\"plantuml\"],\n\tplaywright: [\"playwright\", \"npx\"],\n\tpwntools: [\"python\"],\n\tpython: [\"python3\", \"python\"],\n\tradare2: [\"radare2\", \"r2\"],\n\trabin2: [\"rabin2\"],\n\tradiff2: [\"radiff2\"],\n\trasm2: [\"rasm2\"],\n\tsemgrep: [\"semgrep\"],\n\tsyft: [\"syft\"],\n\ttrivy: [\"trivy\"],\n\tzap: [\"zap.sh\", \"zap-baseline.py\"],\n\tzipalign: [\"zipalign\"],\n\tz3: [\"z3\"],\n};\n\nexport function getReverseSkillToolAliases(tool: string): string[] {\n\treturn TOOL_ALIASES[tool] ?? [tool];\n}\n"]}