omk-agent-core 0.98.0 → 0.98.2

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 (77) hide show
  1. package/dist/harness/agent-harness.d.ts +29 -14
  2. package/dist/harness/agent-harness.d.ts.map +1 -1
  3. package/dist/harness/agent-harness.js +409 -453
  4. package/dist/harness/agent-harness.js.map +1 -1
  5. package/dist/harness/compaction/branch-summarization.d.ts +5 -1
  6. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  7. package/dist/harness/compaction/branch-summarization.js +3 -3
  8. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  9. package/dist/harness/compaction/compaction.d.ts +8 -4
  10. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  11. package/dist/harness/compaction/compaction.js +12 -97
  12. package/dist/harness/compaction/compaction.js.map +1 -1
  13. package/dist/harness/compaction/operation.d.ts +13 -0
  14. package/dist/harness/compaction/operation.d.ts.map +1 -0
  15. package/dist/harness/compaction/operation.js +2 -0
  16. package/dist/harness/compaction/operation.js.map +1 -0
  17. package/dist/harness/compaction/summarization-prompts.d.ts +5 -0
  18. package/dist/harness/compaction/summarization-prompts.d.ts.map +1 -0
  19. package/dist/harness/compaction/summarization-prompts.js +88 -0
  20. package/dist/harness/compaction/summarization-prompts.js.map +1 -0
  21. package/dist/harness/errors.d.ts +15 -0
  22. package/dist/harness/errors.d.ts.map +1 -0
  23. package/dist/harness/errors.js +32 -0
  24. package/dist/harness/errors.js.map +1 -0
  25. package/dist/harness/harness-session.d.ts +52 -0
  26. package/dist/harness/harness-session.d.ts.map +1 -0
  27. package/dist/harness/harness-session.js +113 -0
  28. package/dist/harness/harness-session.js.map +1 -0
  29. package/dist/harness/messages.d.ts +3 -1
  30. package/dist/harness/messages.d.ts.map +1 -1
  31. package/dist/harness/messages.js +26 -0
  32. package/dist/harness/messages.js.map +1 -1
  33. package/dist/harness/name-validation.d.ts +2 -0
  34. package/dist/harness/name-validation.d.ts.map +1 -0
  35. package/dist/harness/name-validation.js +11 -0
  36. package/dist/harness/name-validation.js.map +1 -0
  37. package/dist/harness/operation-lifecycle-controller.d.ts +68 -0
  38. package/dist/harness/operation-lifecycle-controller.d.ts.map +1 -0
  39. package/dist/harness/operation-lifecycle-controller.js +199 -0
  40. package/dist/harness/operation-lifecycle-controller.js.map +1 -0
  41. package/dist/harness/operation-lifecycle-reducer.d.ts +19 -0
  42. package/dist/harness/operation-lifecycle-reducer.d.ts.map +1 -0
  43. package/dist/harness/operation-lifecycle-reducer.js +201 -0
  44. package/dist/harness/operation-lifecycle-reducer.js.map +1 -0
  45. package/dist/harness/operation-lifecycle-types.d.ts +130 -0
  46. package/dist/harness/operation-lifecycle-types.d.ts.map +1 -0
  47. package/dist/harness/operation-lifecycle-types.js +34 -0
  48. package/dist/harness/operation-lifecycle-types.js.map +1 -0
  49. package/dist/harness/operation-outcome.d.ts +71 -0
  50. package/dist/harness/operation-outcome.d.ts.map +1 -0
  51. package/dist/harness/operation-outcome.js +131 -0
  52. package/dist/harness/operation-outcome.js.map +1 -0
  53. package/dist/harness/session-write-coordinator.d.ts +76 -0
  54. package/dist/harness/session-write-coordinator.d.ts.map +1 -0
  55. package/dist/harness/session-write-coordinator.js +126 -0
  56. package/dist/harness/session-write-coordinator.js.map +1 -0
  57. package/dist/harness/stream-options.d.ts +31 -0
  58. package/dist/harness/stream-options.d.ts.map +1 -0
  59. package/dist/harness/stream-options.js +66 -0
  60. package/dist/harness/stream-options.js.map +1 -0
  61. package/dist/harness/subscriber-fanout.d.ts +37 -0
  62. package/dist/harness/subscriber-fanout.d.ts.map +1 -0
  63. package/dist/harness/subscriber-fanout.js +73 -0
  64. package/dist/harness/subscriber-fanout.js.map +1 -0
  65. package/dist/harness/summarization-retry.d.ts +29 -0
  66. package/dist/harness/summarization-retry.d.ts.map +1 -0
  67. package/dist/harness/summarization-retry.js +20 -0
  68. package/dist/harness/summarization-retry.js.map +1 -0
  69. package/dist/harness/tree-navigation.d.ts +45 -0
  70. package/dist/harness/tree-navigation.d.ts.map +1 -0
  71. package/dist/harness/tree-navigation.js +59 -0
  72. package/dist/harness/tree-navigation.js.map +1 -0
  73. package/dist/harness/types.d.ts +69 -43
  74. package/dist/harness/types.d.ts.map +1 -1
  75. package/dist/harness/types.js +1 -32
  76. package/dist/harness/types.js.map +1 -1
  77. package/package.json +2 -2
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Pure transition function for the harness operation lifecycle.
3
+ *
4
+ * Given the current state and one command, returns the next state or a
5
+ * classified violation. It performs no side effects: it does not read a
6
+ * clock, allocate identifiers, emit events, flush sessions, or touch abort
7
+ * signals. The controller supplies all of those around it.
8
+ *
9
+ * The transition table encodes the plan's legal moves plus one documented
10
+ * conservative addition: `structural_running -> committing` marks the single
11
+ * declared commit point of a structural operation. Every other transition not
12
+ * listed here is rejected, and rejection never mutates the input state.
13
+ */
14
+ import { type HarnessLifecycleCommand, type HarnessLifecycleResult, type HarnessLifecycleState } from "./operation-lifecycle-types.ts";
15
+ type Result = HarnessLifecycleResult<HarnessLifecycleState>;
16
+ export declare function initialHarnessLifecycleState(): HarnessLifecycleState;
17
+ export declare function reduceHarnessLifecycle(state: HarnessLifecycleState, command: HarnessLifecycleCommand): Result;
18
+ export {};
19
+ //# sourceMappingURL=operation-lifecycle-reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-lifecycle-reducer.d.ts","sourceRoot":"","sources":["../../src/harness/operation-lifecycle-reducer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACN,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAK1B,MAAM,gCAAgC,CAAC;AAIxC,KAAK,MAAM,GAAG,sBAAsB,CAAC,qBAAqB,CAAC,CAAC;AAE5D,wBAAgB,4BAA4B,IAAI,qBAAqB,CAEpE;AAkOD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAqB7G","sourcesContent":["/**\n * Pure transition function for the harness operation lifecycle.\n *\n * Given the current state and one command, returns the next state or a\n * classified violation. It performs no side effects: it does not read a\n * clock, allocate identifiers, emit events, flush sessions, or touch abort\n * signals. The controller supplies all of those around it.\n *\n * The transition table encodes the plan's legal moves plus one documented\n * conservative addition: `structural_running -> committing` marks the single\n * declared commit point of a structural operation. Every other transition not\n * listed here is rejected, and rejection never mutates the input state.\n */\n\nimport {\n\ttype HarnessLifecycleCommand,\n\ttype HarnessLifecycleResult,\n\ttype HarnessLifecycleState,\n\tHarnessLifecycleViolation,\n\ttype HarnessLifecycleViolationCode,\n\ttype HarnessOperationKind,\n\tPROMPT_FAMILY_KINDS,\n} from \"./operation-lifecycle-types.ts\";\n\ntype ActiveState = Extract<HarnessLifecycleState, { tag: \"active\" }>;\ntype OwnedState = Extract<HarnessLifecycleState, { tag: \"active\" | \"settling\" }>;\ntype Result = HarnessLifecycleResult<HarnessLifecycleState>;\n\nexport function initialHarnessLifecycleState(): HarnessLifecycleState {\n\treturn { tag: \"idle\", lastSequence: 0 };\n}\n\nfunction reject(\n\tcode: HarnessLifecycleViolationCode,\n\tmessage: string,\n\tstate: HarnessLifecycleState,\n\tcommand: HarnessLifecycleCommand,\n): HarnessLifecycleResult<never> {\n\treturn { ok: false, error: new HarnessLifecycleViolation(code, message, state, command) };\n}\n\nfunction isPromptFamily(kind: HarnessOperationKind): boolean {\n\treturn PROMPT_FAMILY_KINDS.includes(kind);\n}\n\nfunction notIdle(state: HarnessLifecycleState, command: HarnessLifecycleCommand, action: string): Result {\n\treturn reject(\"invalid_transition\", `Cannot ${action} without an active operation`, state, command);\n}\n\n/** Commands carrying an operationId must name the current operation. */\nfunction expectCurrentOperation(\n\tstate: OwnedState,\n\toperationId: string,\n\tcommand: HarnessLifecycleCommand,\n): HarnessLifecycleResult<OwnedState> {\n\tif (state.operation.operationId !== operationId) {\n\t\treturn reject(\n\t\t\t\"stale_operation\",\n\t\t\t`Command targets operation ${operationId} but current operation is ${state.operation.operationId}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn { ok: true, value: state };\n}\n\nfunction reduceBegin(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"begin\" }>,\n): Result {\n\tif (state.tag !== \"idle\") {\n\t\treturn reject(\"busy\", \"Cannot begin a new operation while another is active or settling\", state, command);\n\t}\n\tif (command.operation.sequence !== state.lastSequence + 1) {\n\t\treturn reject(\n\t\t\t\"sequence_violation\",\n\t\t\t`Operation sequence ${command.operation.sequence} is not lastSequence + 1 (${state.lastSequence + 1})`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn {\n\t\tok: true,\n\t\tvalue: { tag: \"active\", operation: command.operation, stage: \"preparing\", attempts: [], abortRequested: false },\n\t};\n}\n\n/**\n * Overflow recovery answers an observed overflow. It is legal only when the\n * most recently closed attempt ended with `overflow`; otherwise the operation\n * would sit in a stage from which no attempt can begin (index 0 must start\n * from `preparing`, index > 0 needs a prior overflow) and could only settle.\n */\nfunction lastAttemptOverflowed(state: ActiveState): boolean {\n\treturn state.attempts.at(-1)?.outcome === \"overflow\";\n}\n\nfunction isLegalStageMove(state: ActiveState, to: ActiveState[\"stage\"]): boolean {\n\tconst from = state.stage;\n\tconst kind = state.operation.kind;\n\tif (from === \"attempt_running\" && to === \"save_point\") return state.attempt !== undefined;\n\tif (from === \"save_point\" && to === \"attempt_running\") return state.attempt !== undefined;\n\tif (from === \"preparing\" && to === \"recovering_overflow\") {\n\t\treturn isPromptFamily(kind) && state.attempt === undefined && lastAttemptOverflowed(state);\n\t}\n\tif (from === \"preparing\" && to === \"structural_running\") return !isPromptFamily(kind);\n\treturn from === \"structural_running\" && to === \"committing\";\n}\n\nfunction reduceStage(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"stage\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"change stage\");\n\tif (state.tag === \"settling\")\n\t\treturn reject(\"invalid_transition\", \"Cannot change stage while settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\tif (!isLegalStageMove(state, command.stage)) {\n\t\treturn reject(\n\t\t\t\"invalid_transition\",\n\t\t\t`Illegal stage transition ${state.stage} -> ${command.stage} for ${state.operation.kind}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn { ok: true, value: { ...state, stage: command.stage } };\n}\n\nfunction reduceAttemptBegin(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"attempt_begin\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"begin an attempt\");\n\tif (state.tag === \"settling\")\n\t\treturn reject(\"invalid_transition\", \"Cannot begin an attempt while settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.attempt.operationId, command);\n\tif (!current.ok) return current;\n\tif (state.attempt !== undefined) {\n\t\treturn reject(\"attempt_mismatch\", `Attempt ${state.attempt.attemptId} is still active`, state, command);\n\t}\n\tif (!isPromptFamily(state.operation.kind)) {\n\t\treturn reject(\"invalid_transition\", `${state.operation.kind} operations do not run attempts`, state, command);\n\t}\n\tif (command.attempt.index !== state.attempts.length) {\n\t\treturn reject(\n\t\t\t\"sequence_violation\",\n\t\t\t`Attempt index ${command.attempt.index} is not the next index ${state.attempts.length}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\tconst fromPreparing =\n\t\tstate.stage === \"preparing\" && command.attempt.index === 0 && command.attempt.reason === \"initial\";\n\tconst fromRecovery =\n\t\tstate.stage === \"recovering_overflow\" &&\n\t\tcommand.attempt.index > 0 &&\n\t\tcommand.attempt.reason === \"context_overflow_recovery\";\n\tif (!fromPreparing && !fromRecovery) {\n\t\treturn reject(\n\t\t\t\"invalid_transition\",\n\t\t\t`Cannot begin attempt ${command.attempt.attemptId} (${command.attempt.reason}) from stage ${state.stage}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn { ok: true, value: { ...state, stage: \"attempt_running\", attempt: command.attempt } };\n}\n\nfunction reduceAttemptEnd(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"attempt_end\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"end an attempt\");\n\tif (state.tag === \"settling\")\n\t\treturn reject(\"invalid_transition\", \"Cannot end an attempt while settling\", state, command);\n\tif (state.attempt === undefined) return reject(\"attempt_mismatch\", \"No active attempt to end\", state, command);\n\tif (state.attempt.attemptId !== command.attemptId) {\n\t\treturn reject(\n\t\t\t\"attempt_mismatch\",\n\t\t\t`Attempt end names ${command.attemptId} but active attempt is ${state.attempt.attemptId}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\t// save_point is an intra-attempt stage between provider turns; the attempt\n\t// remains logically running there, so it may end from either stage.\n\tif (state.stage !== \"attempt_running\" && state.stage !== \"save_point\") {\n\t\treturn reject(\"invalid_transition\", `Cannot end an attempt from stage ${state.stage}`, state, command);\n\t}\n\tconst { attempt } = state;\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\t...state,\n\t\t\tstage: \"preparing\",\n\t\t\tattempt: undefined,\n\t\t\tattempts: [...state.attempts, { attempt, outcome: command.outcome }],\n\t\t},\n\t};\n}\n\nfunction reduceAbortRequest(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"abort_request\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"abort\");\n\tif (state.tag === \"settling\") return reject(\"invalid_transition\", \"Cannot abort while settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\treturn { ok: true, value: { ...state, abortRequested: true } };\n}\n\nfunction reduceSettleBegin(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"settle_begin\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"settle\");\n\tif (state.tag === \"settling\") return reject(\"invalid_transition\", \"Operation is already settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\t// Last line of defence against a silently orphaned attempt: an integration\n\t// bug that drops `attempt_end` must fail loudly here, not settle an\n\t// operation whose summary is missing a started attempt.\n\tif (state.attempt !== undefined) {\n\t\treturn reject(\n\t\t\t\"attempt_mismatch\",\n\t\t\t`Cannot settle operation ${state.operation.operationId} while attempt ${state.attempt.attemptId} is active`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\ttag: \"settling\",\n\t\t\toperation: state.operation,\n\t\t\toutcome: command.outcome,\n\t\t\tattempts: state.attempts,\n\t\t\tabortRequested: state.abortRequested,\n\t\t},\n\t};\n}\n\nfunction reduceSettleFinish(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"settle_finish\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"finish settling\");\n\tif (state.tag === \"active\")\n\t\treturn reject(\"invalid_transition\", \"Operation must pass through settling before finishing\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\treturn { ok: true, value: { tag: \"idle\", lastSequence: state.operation.sequence } };\n}\n\nexport function reduceHarnessLifecycle(state: HarnessLifecycleState, command: HarnessLifecycleCommand): Result {\n\tswitch (command.type) {\n\t\tcase \"begin\":\n\t\t\treturn reduceBegin(state, command);\n\t\tcase \"stage\":\n\t\t\treturn reduceStage(state, command);\n\t\tcase \"attempt_begin\":\n\t\t\treturn reduceAttemptBegin(state, command);\n\t\tcase \"attempt_end\":\n\t\t\treturn reduceAttemptEnd(state, command);\n\t\tcase \"abort_request\":\n\t\t\treturn reduceAbortRequest(state, command);\n\t\tcase \"settle_begin\":\n\t\t\treturn reduceSettleBegin(state, command);\n\t\tcase \"settle_finish\":\n\t\t\treturn reduceSettleFinish(state, command);\n\t\tdefault: {\n\t\t\tconst unknownCommand: never = command;\n\t\t\tthrow new HarnessLifecycleViolation(\"invalid_transition\", \"Unknown lifecycle command\", state, unknownCommand);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Pure transition function for the harness operation lifecycle.
3
+ *
4
+ * Given the current state and one command, returns the next state or a
5
+ * classified violation. It performs no side effects: it does not read a
6
+ * clock, allocate identifiers, emit events, flush sessions, or touch abort
7
+ * signals. The controller supplies all of those around it.
8
+ *
9
+ * The transition table encodes the plan's legal moves plus one documented
10
+ * conservative addition: `structural_running -> committing` marks the single
11
+ * declared commit point of a structural operation. Every other transition not
12
+ * listed here is rejected, and rejection never mutates the input state.
13
+ */
14
+ import { HarnessLifecycleViolation, PROMPT_FAMILY_KINDS, } from "./operation-lifecycle-types.js";
15
+ export function initialHarnessLifecycleState() {
16
+ return { tag: "idle", lastSequence: 0 };
17
+ }
18
+ function reject(code, message, state, command) {
19
+ return { ok: false, error: new HarnessLifecycleViolation(code, message, state, command) };
20
+ }
21
+ function isPromptFamily(kind) {
22
+ return PROMPT_FAMILY_KINDS.includes(kind);
23
+ }
24
+ function notIdle(state, command, action) {
25
+ return reject("invalid_transition", `Cannot ${action} without an active operation`, state, command);
26
+ }
27
+ /** Commands carrying an operationId must name the current operation. */
28
+ function expectCurrentOperation(state, operationId, command) {
29
+ if (state.operation.operationId !== operationId) {
30
+ return reject("stale_operation", `Command targets operation ${operationId} but current operation is ${state.operation.operationId}`, state, command);
31
+ }
32
+ return { ok: true, value: state };
33
+ }
34
+ function reduceBegin(state, command) {
35
+ if (state.tag !== "idle") {
36
+ return reject("busy", "Cannot begin a new operation while another is active or settling", state, command);
37
+ }
38
+ if (command.operation.sequence !== state.lastSequence + 1) {
39
+ return reject("sequence_violation", `Operation sequence ${command.operation.sequence} is not lastSequence + 1 (${state.lastSequence + 1})`, state, command);
40
+ }
41
+ return {
42
+ ok: true,
43
+ value: { tag: "active", operation: command.operation, stage: "preparing", attempts: [], abortRequested: false },
44
+ };
45
+ }
46
+ /**
47
+ * Overflow recovery answers an observed overflow. It is legal only when the
48
+ * most recently closed attempt ended with `overflow`; otherwise the operation
49
+ * would sit in a stage from which no attempt can begin (index 0 must start
50
+ * from `preparing`, index > 0 needs a prior overflow) and could only settle.
51
+ */
52
+ function lastAttemptOverflowed(state) {
53
+ return state.attempts.at(-1)?.outcome === "overflow";
54
+ }
55
+ function isLegalStageMove(state, to) {
56
+ const from = state.stage;
57
+ const kind = state.operation.kind;
58
+ if (from === "attempt_running" && to === "save_point")
59
+ return state.attempt !== undefined;
60
+ if (from === "save_point" && to === "attempt_running")
61
+ return state.attempt !== undefined;
62
+ if (from === "preparing" && to === "recovering_overflow") {
63
+ return isPromptFamily(kind) && state.attempt === undefined && lastAttemptOverflowed(state);
64
+ }
65
+ if (from === "preparing" && to === "structural_running")
66
+ return !isPromptFamily(kind);
67
+ return from === "structural_running" && to === "committing";
68
+ }
69
+ function reduceStage(state, command) {
70
+ if (state.tag === "idle")
71
+ return notIdle(state, command, "change stage");
72
+ if (state.tag === "settling")
73
+ return reject("invalid_transition", "Cannot change stage while settling", state, command);
74
+ const current = expectCurrentOperation(state, command.operationId, command);
75
+ if (!current.ok)
76
+ return current;
77
+ if (!isLegalStageMove(state, command.stage)) {
78
+ return reject("invalid_transition", `Illegal stage transition ${state.stage} -> ${command.stage} for ${state.operation.kind}`, state, command);
79
+ }
80
+ return { ok: true, value: { ...state, stage: command.stage } };
81
+ }
82
+ function reduceAttemptBegin(state, command) {
83
+ if (state.tag === "idle")
84
+ return notIdle(state, command, "begin an attempt");
85
+ if (state.tag === "settling")
86
+ return reject("invalid_transition", "Cannot begin an attempt while settling", state, command);
87
+ const current = expectCurrentOperation(state, command.attempt.operationId, command);
88
+ if (!current.ok)
89
+ return current;
90
+ if (state.attempt !== undefined) {
91
+ return reject("attempt_mismatch", `Attempt ${state.attempt.attemptId} is still active`, state, command);
92
+ }
93
+ if (!isPromptFamily(state.operation.kind)) {
94
+ return reject("invalid_transition", `${state.operation.kind} operations do not run attempts`, state, command);
95
+ }
96
+ if (command.attempt.index !== state.attempts.length) {
97
+ return reject("sequence_violation", `Attempt index ${command.attempt.index} is not the next index ${state.attempts.length}`, state, command);
98
+ }
99
+ const fromPreparing = state.stage === "preparing" && command.attempt.index === 0 && command.attempt.reason === "initial";
100
+ const fromRecovery = state.stage === "recovering_overflow" &&
101
+ command.attempt.index > 0 &&
102
+ command.attempt.reason === "context_overflow_recovery";
103
+ if (!fromPreparing && !fromRecovery) {
104
+ return reject("invalid_transition", `Cannot begin attempt ${command.attempt.attemptId} (${command.attempt.reason}) from stage ${state.stage}`, state, command);
105
+ }
106
+ return { ok: true, value: { ...state, stage: "attempt_running", attempt: command.attempt } };
107
+ }
108
+ function reduceAttemptEnd(state, command) {
109
+ if (state.tag === "idle")
110
+ return notIdle(state, command, "end an attempt");
111
+ if (state.tag === "settling")
112
+ return reject("invalid_transition", "Cannot end an attempt while settling", state, command);
113
+ if (state.attempt === undefined)
114
+ return reject("attempt_mismatch", "No active attempt to end", state, command);
115
+ if (state.attempt.attemptId !== command.attemptId) {
116
+ return reject("attempt_mismatch", `Attempt end names ${command.attemptId} but active attempt is ${state.attempt.attemptId}`, state, command);
117
+ }
118
+ // save_point is an intra-attempt stage between provider turns; the attempt
119
+ // remains logically running there, so it may end from either stage.
120
+ if (state.stage !== "attempt_running" && state.stage !== "save_point") {
121
+ return reject("invalid_transition", `Cannot end an attempt from stage ${state.stage}`, state, command);
122
+ }
123
+ const { attempt } = state;
124
+ return {
125
+ ok: true,
126
+ value: {
127
+ ...state,
128
+ stage: "preparing",
129
+ attempt: undefined,
130
+ attempts: [...state.attempts, { attempt, outcome: command.outcome }],
131
+ },
132
+ };
133
+ }
134
+ function reduceAbortRequest(state, command) {
135
+ if (state.tag === "idle")
136
+ return notIdle(state, command, "abort");
137
+ if (state.tag === "settling")
138
+ return reject("invalid_transition", "Cannot abort while settling", state, command);
139
+ const current = expectCurrentOperation(state, command.operationId, command);
140
+ if (!current.ok)
141
+ return current;
142
+ return { ok: true, value: { ...state, abortRequested: true } };
143
+ }
144
+ function reduceSettleBegin(state, command) {
145
+ if (state.tag === "idle")
146
+ return notIdle(state, command, "settle");
147
+ if (state.tag === "settling")
148
+ return reject("invalid_transition", "Operation is already settling", state, command);
149
+ const current = expectCurrentOperation(state, command.operationId, command);
150
+ if (!current.ok)
151
+ return current;
152
+ // Last line of defence against a silently orphaned attempt: an integration
153
+ // bug that drops `attempt_end` must fail loudly here, not settle an
154
+ // operation whose summary is missing a started attempt.
155
+ if (state.attempt !== undefined) {
156
+ return reject("attempt_mismatch", `Cannot settle operation ${state.operation.operationId} while attempt ${state.attempt.attemptId} is active`, state, command);
157
+ }
158
+ return {
159
+ ok: true,
160
+ value: {
161
+ tag: "settling",
162
+ operation: state.operation,
163
+ outcome: command.outcome,
164
+ attempts: state.attempts,
165
+ abortRequested: state.abortRequested,
166
+ },
167
+ };
168
+ }
169
+ function reduceSettleFinish(state, command) {
170
+ if (state.tag === "idle")
171
+ return notIdle(state, command, "finish settling");
172
+ if (state.tag === "active")
173
+ return reject("invalid_transition", "Operation must pass through settling before finishing", state, command);
174
+ const current = expectCurrentOperation(state, command.operationId, command);
175
+ if (!current.ok)
176
+ return current;
177
+ return { ok: true, value: { tag: "idle", lastSequence: state.operation.sequence } };
178
+ }
179
+ export function reduceHarnessLifecycle(state, command) {
180
+ switch (command.type) {
181
+ case "begin":
182
+ return reduceBegin(state, command);
183
+ case "stage":
184
+ return reduceStage(state, command);
185
+ case "attempt_begin":
186
+ return reduceAttemptBegin(state, command);
187
+ case "attempt_end":
188
+ return reduceAttemptEnd(state, command);
189
+ case "abort_request":
190
+ return reduceAbortRequest(state, command);
191
+ case "settle_begin":
192
+ return reduceSettleBegin(state, command);
193
+ case "settle_finish":
194
+ return reduceSettleFinish(state, command);
195
+ default: {
196
+ const unknownCommand = command;
197
+ throw new HarnessLifecycleViolation("invalid_transition", "Unknown lifecycle command", state, unknownCommand);
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=operation-lifecycle-reducer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-lifecycle-reducer.js","sourceRoot":"","sources":["../../src/harness/operation-lifecycle-reducer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure transition function for the harness operation lifecycle.\n *\n * Given the current state and one command, returns the next state or a\n * classified violation. It performs no side effects: it does not read a\n * clock, allocate identifiers, emit events, flush sessions, or touch abort\n * signals. The controller supplies all of those around it.\n *\n * The transition table encodes the plan's legal moves plus one documented\n * conservative addition: `structural_running -> committing` marks the single\n * declared commit point of a structural operation. Every other transition not\n * listed here is rejected, and rejection never mutates the input state.\n */\n\nimport {\n\ttype HarnessLifecycleCommand,\n\ttype HarnessLifecycleResult,\n\ttype HarnessLifecycleState,\n\tHarnessLifecycleViolation,\n\ttype HarnessLifecycleViolationCode,\n\ttype HarnessOperationKind,\n\tPROMPT_FAMILY_KINDS,\n} from \"./operation-lifecycle-types.ts\";\n\ntype ActiveState = Extract<HarnessLifecycleState, { tag: \"active\" }>;\ntype OwnedState = Extract<HarnessLifecycleState, { tag: \"active\" | \"settling\" }>;\ntype Result = HarnessLifecycleResult<HarnessLifecycleState>;\n\nexport function initialHarnessLifecycleState(): HarnessLifecycleState {\n\treturn { tag: \"idle\", lastSequence: 0 };\n}\n\nfunction reject(\n\tcode: HarnessLifecycleViolationCode,\n\tmessage: string,\n\tstate: HarnessLifecycleState,\n\tcommand: HarnessLifecycleCommand,\n): HarnessLifecycleResult<never> {\n\treturn { ok: false, error: new HarnessLifecycleViolation(code, message, state, command) };\n}\n\nfunction isPromptFamily(kind: HarnessOperationKind): boolean {\n\treturn PROMPT_FAMILY_KINDS.includes(kind);\n}\n\nfunction notIdle(state: HarnessLifecycleState, command: HarnessLifecycleCommand, action: string): Result {\n\treturn reject(\"invalid_transition\", `Cannot ${action} without an active operation`, state, command);\n}\n\n/** Commands carrying an operationId must name the current operation. */\nfunction expectCurrentOperation(\n\tstate: OwnedState,\n\toperationId: string,\n\tcommand: HarnessLifecycleCommand,\n): HarnessLifecycleResult<OwnedState> {\n\tif (state.operation.operationId !== operationId) {\n\t\treturn reject(\n\t\t\t\"stale_operation\",\n\t\t\t`Command targets operation ${operationId} but current operation is ${state.operation.operationId}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn { ok: true, value: state };\n}\n\nfunction reduceBegin(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"begin\" }>,\n): Result {\n\tif (state.tag !== \"idle\") {\n\t\treturn reject(\"busy\", \"Cannot begin a new operation while another is active or settling\", state, command);\n\t}\n\tif (command.operation.sequence !== state.lastSequence + 1) {\n\t\treturn reject(\n\t\t\t\"sequence_violation\",\n\t\t\t`Operation sequence ${command.operation.sequence} is not lastSequence + 1 (${state.lastSequence + 1})`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn {\n\t\tok: true,\n\t\tvalue: { tag: \"active\", operation: command.operation, stage: \"preparing\", attempts: [], abortRequested: false },\n\t};\n}\n\n/**\n * Overflow recovery answers an observed overflow. It is legal only when the\n * most recently closed attempt ended with `overflow`; otherwise the operation\n * would sit in a stage from which no attempt can begin (index 0 must start\n * from `preparing`, index > 0 needs a prior overflow) and could only settle.\n */\nfunction lastAttemptOverflowed(state: ActiveState): boolean {\n\treturn state.attempts.at(-1)?.outcome === \"overflow\";\n}\n\nfunction isLegalStageMove(state: ActiveState, to: ActiveState[\"stage\"]): boolean {\n\tconst from = state.stage;\n\tconst kind = state.operation.kind;\n\tif (from === \"attempt_running\" && to === \"save_point\") return state.attempt !== undefined;\n\tif (from === \"save_point\" && to === \"attempt_running\") return state.attempt !== undefined;\n\tif (from === \"preparing\" && to === \"recovering_overflow\") {\n\t\treturn isPromptFamily(kind) && state.attempt === undefined && lastAttemptOverflowed(state);\n\t}\n\tif (from === \"preparing\" && to === \"structural_running\") return !isPromptFamily(kind);\n\treturn from === \"structural_running\" && to === \"committing\";\n}\n\nfunction reduceStage(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"stage\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"change stage\");\n\tif (state.tag === \"settling\")\n\t\treturn reject(\"invalid_transition\", \"Cannot change stage while settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\tif (!isLegalStageMove(state, command.stage)) {\n\t\treturn reject(\n\t\t\t\"invalid_transition\",\n\t\t\t`Illegal stage transition ${state.stage} -> ${command.stage} for ${state.operation.kind}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn { ok: true, value: { ...state, stage: command.stage } };\n}\n\nfunction reduceAttemptBegin(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"attempt_begin\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"begin an attempt\");\n\tif (state.tag === \"settling\")\n\t\treturn reject(\"invalid_transition\", \"Cannot begin an attempt while settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.attempt.operationId, command);\n\tif (!current.ok) return current;\n\tif (state.attempt !== undefined) {\n\t\treturn reject(\"attempt_mismatch\", `Attempt ${state.attempt.attemptId} is still active`, state, command);\n\t}\n\tif (!isPromptFamily(state.operation.kind)) {\n\t\treturn reject(\"invalid_transition\", `${state.operation.kind} operations do not run attempts`, state, command);\n\t}\n\tif (command.attempt.index !== state.attempts.length) {\n\t\treturn reject(\n\t\t\t\"sequence_violation\",\n\t\t\t`Attempt index ${command.attempt.index} is not the next index ${state.attempts.length}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\tconst fromPreparing =\n\t\tstate.stage === \"preparing\" && command.attempt.index === 0 && command.attempt.reason === \"initial\";\n\tconst fromRecovery =\n\t\tstate.stage === \"recovering_overflow\" &&\n\t\tcommand.attempt.index > 0 &&\n\t\tcommand.attempt.reason === \"context_overflow_recovery\";\n\tif (!fromPreparing && !fromRecovery) {\n\t\treturn reject(\n\t\t\t\"invalid_transition\",\n\t\t\t`Cannot begin attempt ${command.attempt.attemptId} (${command.attempt.reason}) from stage ${state.stage}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn { ok: true, value: { ...state, stage: \"attempt_running\", attempt: command.attempt } };\n}\n\nfunction reduceAttemptEnd(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"attempt_end\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"end an attempt\");\n\tif (state.tag === \"settling\")\n\t\treturn reject(\"invalid_transition\", \"Cannot end an attempt while settling\", state, command);\n\tif (state.attempt === undefined) return reject(\"attempt_mismatch\", \"No active attempt to end\", state, command);\n\tif (state.attempt.attemptId !== command.attemptId) {\n\t\treturn reject(\n\t\t\t\"attempt_mismatch\",\n\t\t\t`Attempt end names ${command.attemptId} but active attempt is ${state.attempt.attemptId}`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\t// save_point is an intra-attempt stage between provider turns; the attempt\n\t// remains logically running there, so it may end from either stage.\n\tif (state.stage !== \"attempt_running\" && state.stage !== \"save_point\") {\n\t\treturn reject(\"invalid_transition\", `Cannot end an attempt from stage ${state.stage}`, state, command);\n\t}\n\tconst { attempt } = state;\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\t...state,\n\t\t\tstage: \"preparing\",\n\t\t\tattempt: undefined,\n\t\t\tattempts: [...state.attempts, { attempt, outcome: command.outcome }],\n\t\t},\n\t};\n}\n\nfunction reduceAbortRequest(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"abort_request\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"abort\");\n\tif (state.tag === \"settling\") return reject(\"invalid_transition\", \"Cannot abort while settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\treturn { ok: true, value: { ...state, abortRequested: true } };\n}\n\nfunction reduceSettleBegin(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"settle_begin\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"settle\");\n\tif (state.tag === \"settling\") return reject(\"invalid_transition\", \"Operation is already settling\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\t// Last line of defence against a silently orphaned attempt: an integration\n\t// bug that drops `attempt_end` must fail loudly here, not settle an\n\t// operation whose summary is missing a started attempt.\n\tif (state.attempt !== undefined) {\n\t\treturn reject(\n\t\t\t\"attempt_mismatch\",\n\t\t\t`Cannot settle operation ${state.operation.operationId} while attempt ${state.attempt.attemptId} is active`,\n\t\t\tstate,\n\t\t\tcommand,\n\t\t);\n\t}\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\ttag: \"settling\",\n\t\t\toperation: state.operation,\n\t\t\toutcome: command.outcome,\n\t\t\tattempts: state.attempts,\n\t\t\tabortRequested: state.abortRequested,\n\t\t},\n\t};\n}\n\nfunction reduceSettleFinish(\n\tstate: HarnessLifecycleState,\n\tcommand: Extract<HarnessLifecycleCommand, { type: \"settle_finish\" }>,\n): Result {\n\tif (state.tag === \"idle\") return notIdle(state, command, \"finish settling\");\n\tif (state.tag === \"active\")\n\t\treturn reject(\"invalid_transition\", \"Operation must pass through settling before finishing\", state, command);\n\tconst current = expectCurrentOperation(state, command.operationId, command);\n\tif (!current.ok) return current;\n\treturn { ok: true, value: { tag: \"idle\", lastSequence: state.operation.sequence } };\n}\n\nexport function reduceHarnessLifecycle(state: HarnessLifecycleState, command: HarnessLifecycleCommand): Result {\n\tswitch (command.type) {\n\t\tcase \"begin\":\n\t\t\treturn reduceBegin(state, command);\n\t\tcase \"stage\":\n\t\t\treturn reduceStage(state, command);\n\t\tcase \"attempt_begin\":\n\t\t\treturn reduceAttemptBegin(state, command);\n\t\tcase \"attempt_end\":\n\t\t\treturn reduceAttemptEnd(state, command);\n\t\tcase \"abort_request\":\n\t\t\treturn reduceAbortRequest(state, command);\n\t\tcase \"settle_begin\":\n\t\t\treturn reduceSettleBegin(state, command);\n\t\tcase \"settle_finish\":\n\t\t\treturn reduceSettleFinish(state, command);\n\t\tdefault: {\n\t\t\tconst unknownCommand: never = command;\n\t\t\tthrow new HarnessLifecycleViolation(\"invalid_transition\", \"Unknown lifecycle command\", state, unknownCommand);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Pure operation-lifecycle model for AgentHarness public operations.
3
+ *
4
+ * One public operation (`prompt`, `skill`, `promptFromTemplate`, `compact`,
5
+ * `navigateTree`) may span several low-level agent attempts. This module
6
+ * declares the identity, state, command, and violation vocabulary used to
7
+ * keep that distinction provable. It imports nothing: no harness, session,
8
+ * provider, or Node types, so it stays a leaf for the import-cycle ratchet
9
+ * and is safe to export from a browser entry point.
10
+ *
11
+ * Reducer-purity note: the reducer never reads a clock, so its `settling`
12
+ * state carries `HarnessSettledAttempt` records (attempt + outcome) rather
13
+ * than wall-clock summaries. The controller, which owns the clock, renders
14
+ * public `HarnessAttemptSummary` values from those records.
15
+ */
16
+ /** Public operation kinds that own the harness at most one at a time. */
17
+ export type HarnessOperationKind = "prompt" | "skill" | "prompt_template" | "manual_compaction" | "tree_navigation";
18
+ /** Prompt-family kinds run agent attempts; structural kinds do not. */
19
+ export declare const PROMPT_FAMILY_KINDS: readonly HarnessOperationKind[];
20
+ export type HarnessOperationStage = "preparing" | "attempt_running" | "save_point" | "recovering_overflow" | "structural_running" | "committing" | "settling";
21
+ /** Correlation identity of one public operation. `sequence` is monotonic per harness instance. */
22
+ export interface HarnessOperationRef {
23
+ readonly operationId: string;
24
+ readonly sequence: number;
25
+ readonly kind: HarnessOperationKind;
26
+ readonly startedAtMs: number;
27
+ }
28
+ /** Overflow recovery is a substage of the originating prompt operation, never its own operation. */
29
+ export type HarnessAttemptReason = "initial" | "context_overflow_recovery";
30
+ export interface HarnessAttemptRef {
31
+ readonly operationId: string;
32
+ readonly attemptId: string;
33
+ readonly index: number;
34
+ readonly reason: HarnessAttemptReason;
35
+ readonly startedAtMs: number;
36
+ }
37
+ export type HarnessAttemptOutcome = "completed" | "failed" | "aborted" | "overflow";
38
+ /** Reducer-side record of a finished attempt; carries no wall-clock data beyond what the attempt ref captured. */
39
+ export interface HarnessSettledAttempt {
40
+ readonly attempt: HarnessAttemptRef;
41
+ readonly outcome: HarnessAttemptOutcome;
42
+ }
43
+ /** Public attempt summary rendered by the controller from settled records plus its own clock readings. */
44
+ export interface HarnessAttemptSummary {
45
+ readonly attemptId: string;
46
+ readonly index: number;
47
+ readonly reason: HarnessAttemptReason;
48
+ readonly outcome: HarnessAttemptOutcome;
49
+ readonly startedAtMs: number;
50
+ readonly finishedAtMs: number;
51
+ }
52
+ export type HarnessOperationOutcome = {
53
+ readonly status: "completed";
54
+ } | {
55
+ readonly status: "failed";
56
+ readonly code: string;
57
+ readonly message: string;
58
+ } | {
59
+ readonly status: "aborted";
60
+ readonly reason?: string;
61
+ } | {
62
+ readonly status: "cancelled";
63
+ readonly reason: string;
64
+ };
65
+ export type HarnessLifecycleState = {
66
+ readonly tag: "idle";
67
+ readonly lastSequence: number;
68
+ } | {
69
+ readonly tag: "active";
70
+ readonly operation: HarnessOperationRef;
71
+ readonly stage: HarnessOperationStage;
72
+ readonly attempt?: HarnessAttemptRef;
73
+ readonly attempts: readonly HarnessSettledAttempt[];
74
+ readonly abortRequested: boolean;
75
+ } | {
76
+ readonly tag: "settling";
77
+ readonly operation: HarnessOperationRef;
78
+ readonly outcome: HarnessOperationOutcome;
79
+ readonly attempts: readonly HarnessSettledAttempt[];
80
+ readonly abortRequested: boolean;
81
+ };
82
+ export type HarnessLifecycleCommand = {
83
+ readonly type: "begin";
84
+ readonly operation: HarnessOperationRef;
85
+ } | {
86
+ readonly type: "stage";
87
+ readonly operationId: string;
88
+ readonly stage: HarnessOperationStage;
89
+ } | {
90
+ readonly type: "attempt_begin";
91
+ readonly attempt: HarnessAttemptRef;
92
+ } | {
93
+ readonly type: "attempt_end";
94
+ readonly attemptId: string;
95
+ readonly outcome: HarnessAttemptOutcome;
96
+ } | {
97
+ readonly type: "abort_request";
98
+ readonly operationId: string;
99
+ } | {
100
+ readonly type: "settle_begin";
101
+ readonly operationId: string;
102
+ readonly outcome: HarnessOperationOutcome;
103
+ } | {
104
+ readonly type: "settle_finish";
105
+ readonly operationId: string;
106
+ };
107
+ export type HarnessLifecycleViolationCode = "busy" | "stale_operation" | "invalid_transition" | "attempt_mismatch" | "sequence_violation";
108
+ /**
109
+ * Illegal lifecycle transition rejected by the reducer. Extends `Error` so the
110
+ * controller can attach it as a preserved `cause` on public boundary errors.
111
+ */
112
+ export declare class HarnessLifecycleViolation extends Error {
113
+ readonly code: HarnessLifecycleViolationCode;
114
+ readonly state: HarnessLifecycleState;
115
+ readonly command: HarnessLifecycleCommand;
116
+ constructor(code: HarnessLifecycleViolationCode, message: string, state: HarnessLifecycleState, command: HarnessLifecycleCommand);
117
+ }
118
+ export type HarnessLifecycleResult<T> = {
119
+ readonly ok: true;
120
+ readonly value: T;
121
+ } | {
122
+ readonly ok: false;
123
+ readonly error: HarnessLifecycleViolation;
124
+ };
125
+ /** Clock and identity factories injected so lifecycle behavior is deterministic under test. */
126
+ export interface HarnessLifecycleDependencies {
127
+ readonly createOperationId: () => string;
128
+ readonly now: () => number;
129
+ }
130
+ //# sourceMappingURL=operation-lifecycle-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-lifecycle-types.d.ts","sourceRoot":"","sources":["../../src/harness/operation-lifecycle-types.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure operation-lifecycle model for AgentHarness public operations.\n *\n * One public operation (`prompt`, `skill`, `promptFromTemplate`, `compact`,\n * `navigateTree`) may span several low-level agent attempts. This module\n * declares the identity, state, command, and violation vocabulary used to\n * keep that distinction provable. It imports nothing: no harness, session,\n * provider, or Node types, so it stays a leaf for the import-cycle ratchet\n * and is safe to export from a browser entry point.\n *\n * Reducer-purity note: the reducer never reads a clock, so its `settling`\n * state carries `HarnessSettledAttempt` records (attempt + outcome) rather\n * than wall-clock summaries. The controller, which owns the clock, renders\n * public `HarnessAttemptSummary` values from those records.\n */\n\n/** Public operation kinds that own the harness at most one at a time. */\nexport type HarnessOperationKind = \"prompt\" | \"skill\" | \"prompt_template\" | \"manual_compaction\" | \"tree_navigation\";\n\n/** Prompt-family kinds run agent attempts; structural kinds do not. */\nexport const PROMPT_FAMILY_KINDS: readonly HarnessOperationKind[] = [\"prompt\", \"skill\", \"prompt_template\"];\n\nexport type HarnessOperationStage =\n\t| \"preparing\"\n\t| \"attempt_running\"\n\t| \"save_point\"\n\t| \"recovering_overflow\"\n\t| \"structural_running\"\n\t| \"committing\"\n\t| \"settling\";\n\n/** Correlation identity of one public operation. `sequence` is monotonic per harness instance. */\nexport interface HarnessOperationRef {\n\treadonly operationId: string;\n\treadonly sequence: number;\n\treadonly kind: HarnessOperationKind;\n\treadonly startedAtMs: number;\n}\n\n/** Overflow recovery is a substage of the originating prompt operation, never its own operation. */\nexport type HarnessAttemptReason = \"initial\" | \"context_overflow_recovery\";\n\nexport interface HarnessAttemptRef {\n\treadonly operationId: string;\n\treadonly attemptId: string;\n\treadonly index: number;\n\treadonly reason: HarnessAttemptReason;\n\treadonly startedAtMs: number;\n}\n\nexport type HarnessAttemptOutcome = \"completed\" | \"failed\" | \"aborted\" | \"overflow\";\n\n/** Reducer-side record of a finished attempt; carries no wall-clock data beyond what the attempt ref captured. */\nexport interface HarnessSettledAttempt {\n\treadonly attempt: HarnessAttemptRef;\n\treadonly outcome: HarnessAttemptOutcome;\n}\n\n/** Public attempt summary rendered by the controller from settled records plus its own clock readings. */\nexport interface HarnessAttemptSummary {\n\treadonly attemptId: string;\n\treadonly index: number;\n\treadonly reason: HarnessAttemptReason;\n\treadonly outcome: HarnessAttemptOutcome;\n\treadonly startedAtMs: number;\n\treadonly finishedAtMs: number;\n}\n\nexport type HarnessOperationOutcome =\n\t| { readonly status: \"completed\" }\n\t| { readonly status: \"failed\"; readonly code: string; readonly message: string }\n\t| { readonly status: \"aborted\"; readonly reason?: string }\n\t| { readonly status: \"cancelled\"; readonly reason: string };\n\nexport type HarnessLifecycleState =\n\t| { readonly tag: \"idle\"; readonly lastSequence: number }\n\t| {\n\t\t\treadonly tag: \"active\";\n\t\t\treadonly operation: HarnessOperationRef;\n\t\t\treadonly stage: HarnessOperationStage;\n\t\t\treadonly attempt?: HarnessAttemptRef;\n\t\t\treadonly attempts: readonly HarnessSettledAttempt[];\n\t\t\treadonly abortRequested: boolean;\n\t }\n\t| {\n\t\t\treadonly tag: \"settling\";\n\t\t\treadonly operation: HarnessOperationRef;\n\t\t\treadonly outcome: HarnessOperationOutcome;\n\t\t\treadonly attempts: readonly HarnessSettledAttempt[];\n\t\t\treadonly abortRequested: boolean;\n\t };\n\nexport type HarnessLifecycleCommand =\n\t| { readonly type: \"begin\"; readonly operation: HarnessOperationRef }\n\t| { readonly type: \"stage\"; readonly operationId: string; readonly stage: HarnessOperationStage }\n\t| { readonly type: \"attempt_begin\"; readonly attempt: HarnessAttemptRef }\n\t| { readonly type: \"attempt_end\"; readonly attemptId: string; readonly outcome: HarnessAttemptOutcome }\n\t| { readonly type: \"abort_request\"; readonly operationId: string }\n\t| { readonly type: \"settle_begin\"; readonly operationId: string; readonly outcome: HarnessOperationOutcome }\n\t| { readonly type: \"settle_finish\"; readonly operationId: string };\n\nexport type HarnessLifecycleViolationCode =\n\t| \"busy\"\n\t| \"stale_operation\"\n\t| \"invalid_transition\"\n\t| \"attempt_mismatch\"\n\t| \"sequence_violation\";\n\n/**\n * Illegal lifecycle transition rejected by the reducer. Extends `Error` so the\n * controller can attach it as a preserved `cause` on public boundary errors.\n */\nexport class HarnessLifecycleViolation extends Error {\n\tpublic readonly code: HarnessLifecycleViolationCode;\n\tpublic readonly state: HarnessLifecycleState;\n\tpublic readonly command: HarnessLifecycleCommand;\n\n\tconstructor(\n\t\tcode: HarnessLifecycleViolationCode,\n\t\tmessage: string,\n\t\tstate: HarnessLifecycleState,\n\t\tcommand: HarnessLifecycleCommand,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"HarnessLifecycleViolation\";\n\t\tthis.code = code;\n\t\tthis.state = state;\n\t\tthis.command = command;\n\t}\n}\n\nexport type HarnessLifecycleResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly error: HarnessLifecycleViolation };\n\n/** Clock and identity factories injected so lifecycle behavior is deterministic under test. */\nexport interface HarnessLifecycleDependencies {\n\treadonly createOperationId: () => string;\n\treadonly now: () => number;\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pure operation-lifecycle model for AgentHarness public operations.
3
+ *
4
+ * One public operation (`prompt`, `skill`, `promptFromTemplate`, `compact`,
5
+ * `navigateTree`) may span several low-level agent attempts. This module
6
+ * declares the identity, state, command, and violation vocabulary used to
7
+ * keep that distinction provable. It imports nothing: no harness, session,
8
+ * provider, or Node types, so it stays a leaf for the import-cycle ratchet
9
+ * and is safe to export from a browser entry point.
10
+ *
11
+ * Reducer-purity note: the reducer never reads a clock, so its `settling`
12
+ * state carries `HarnessSettledAttempt` records (attempt + outcome) rather
13
+ * than wall-clock summaries. The controller, which owns the clock, renders
14
+ * public `HarnessAttemptSummary` values from those records.
15
+ */
16
+ /** Prompt-family kinds run agent attempts; structural kinds do not. */
17
+ export const PROMPT_FAMILY_KINDS = ["prompt", "skill", "prompt_template"];
18
+ /**
19
+ * Illegal lifecycle transition rejected by the reducer. Extends `Error` so the
20
+ * controller can attach it as a preserved `cause` on public boundary errors.
21
+ */
22
+ export class HarnessLifecycleViolation extends Error {
23
+ code;
24
+ state;
25
+ command;
26
+ constructor(code, message, state, command) {
27
+ super(message);
28
+ this.name = "HarnessLifecycleViolation";
29
+ this.code = code;
30
+ this.state = state;
31
+ this.command = command;
32
+ }
33
+ }
34
+ //# sourceMappingURL=operation-lifecycle-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-lifecycle-types.js","sourceRoot":"","sources":["../../src/harness/operation-lifecycle-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,uEAAuE;AACvE,MAAM,CAAC,MAAM,mBAAmB,GAAoC,CAAC,QAAQ,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;AAwF3G;;;GAGG;AACH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IACnC,IAAI,CAAgC;IACpC,KAAK,CAAwB;IAC7B,OAAO,CAA0B;IAEjD,YACC,IAAmC,EACnC,OAAe,EACf,KAA4B,EAC5B,OAAgC,EAC/B;QACD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAAA,CACvB;CACD","sourcesContent":["/**\n * Pure operation-lifecycle model for AgentHarness public operations.\n *\n * One public operation (`prompt`, `skill`, `promptFromTemplate`, `compact`,\n * `navigateTree`) may span several low-level agent attempts. This module\n * declares the identity, state, command, and violation vocabulary used to\n * keep that distinction provable. It imports nothing: no harness, session,\n * provider, or Node types, so it stays a leaf for the import-cycle ratchet\n * and is safe to export from a browser entry point.\n *\n * Reducer-purity note: the reducer never reads a clock, so its `settling`\n * state carries `HarnessSettledAttempt` records (attempt + outcome) rather\n * than wall-clock summaries. The controller, which owns the clock, renders\n * public `HarnessAttemptSummary` values from those records.\n */\n\n/** Public operation kinds that own the harness at most one at a time. */\nexport type HarnessOperationKind = \"prompt\" | \"skill\" | \"prompt_template\" | \"manual_compaction\" | \"tree_navigation\";\n\n/** Prompt-family kinds run agent attempts; structural kinds do not. */\nexport const PROMPT_FAMILY_KINDS: readonly HarnessOperationKind[] = [\"prompt\", \"skill\", \"prompt_template\"];\n\nexport type HarnessOperationStage =\n\t| \"preparing\"\n\t| \"attempt_running\"\n\t| \"save_point\"\n\t| \"recovering_overflow\"\n\t| \"structural_running\"\n\t| \"committing\"\n\t| \"settling\";\n\n/** Correlation identity of one public operation. `sequence` is monotonic per harness instance. */\nexport interface HarnessOperationRef {\n\treadonly operationId: string;\n\treadonly sequence: number;\n\treadonly kind: HarnessOperationKind;\n\treadonly startedAtMs: number;\n}\n\n/** Overflow recovery is a substage of the originating prompt operation, never its own operation. */\nexport type HarnessAttemptReason = \"initial\" | \"context_overflow_recovery\";\n\nexport interface HarnessAttemptRef {\n\treadonly operationId: string;\n\treadonly attemptId: string;\n\treadonly index: number;\n\treadonly reason: HarnessAttemptReason;\n\treadonly startedAtMs: number;\n}\n\nexport type HarnessAttemptOutcome = \"completed\" | \"failed\" | \"aborted\" | \"overflow\";\n\n/** Reducer-side record of a finished attempt; carries no wall-clock data beyond what the attempt ref captured. */\nexport interface HarnessSettledAttempt {\n\treadonly attempt: HarnessAttemptRef;\n\treadonly outcome: HarnessAttemptOutcome;\n}\n\n/** Public attempt summary rendered by the controller from settled records plus its own clock readings. */\nexport interface HarnessAttemptSummary {\n\treadonly attemptId: string;\n\treadonly index: number;\n\treadonly reason: HarnessAttemptReason;\n\treadonly outcome: HarnessAttemptOutcome;\n\treadonly startedAtMs: number;\n\treadonly finishedAtMs: number;\n}\n\nexport type HarnessOperationOutcome =\n\t| { readonly status: \"completed\" }\n\t| { readonly status: \"failed\"; readonly code: string; readonly message: string }\n\t| { readonly status: \"aborted\"; readonly reason?: string }\n\t| { readonly status: \"cancelled\"; readonly reason: string };\n\nexport type HarnessLifecycleState =\n\t| { readonly tag: \"idle\"; readonly lastSequence: number }\n\t| {\n\t\t\treadonly tag: \"active\";\n\t\t\treadonly operation: HarnessOperationRef;\n\t\t\treadonly stage: HarnessOperationStage;\n\t\t\treadonly attempt?: HarnessAttemptRef;\n\t\t\treadonly attempts: readonly HarnessSettledAttempt[];\n\t\t\treadonly abortRequested: boolean;\n\t }\n\t| {\n\t\t\treadonly tag: \"settling\";\n\t\t\treadonly operation: HarnessOperationRef;\n\t\t\treadonly outcome: HarnessOperationOutcome;\n\t\t\treadonly attempts: readonly HarnessSettledAttempt[];\n\t\t\treadonly abortRequested: boolean;\n\t };\n\nexport type HarnessLifecycleCommand =\n\t| { readonly type: \"begin\"; readonly operation: HarnessOperationRef }\n\t| { readonly type: \"stage\"; readonly operationId: string; readonly stage: HarnessOperationStage }\n\t| { readonly type: \"attempt_begin\"; readonly attempt: HarnessAttemptRef }\n\t| { readonly type: \"attempt_end\"; readonly attemptId: string; readonly outcome: HarnessAttemptOutcome }\n\t| { readonly type: \"abort_request\"; readonly operationId: string }\n\t| { readonly type: \"settle_begin\"; readonly operationId: string; readonly outcome: HarnessOperationOutcome }\n\t| { readonly type: \"settle_finish\"; readonly operationId: string };\n\nexport type HarnessLifecycleViolationCode =\n\t| \"busy\"\n\t| \"stale_operation\"\n\t| \"invalid_transition\"\n\t| \"attempt_mismatch\"\n\t| \"sequence_violation\";\n\n/**\n * Illegal lifecycle transition rejected by the reducer. Extends `Error` so the\n * controller can attach it as a preserved `cause` on public boundary errors.\n */\nexport class HarnessLifecycleViolation extends Error {\n\tpublic readonly code: HarnessLifecycleViolationCode;\n\tpublic readonly state: HarnessLifecycleState;\n\tpublic readonly command: HarnessLifecycleCommand;\n\n\tconstructor(\n\t\tcode: HarnessLifecycleViolationCode,\n\t\tmessage: string,\n\t\tstate: HarnessLifecycleState,\n\t\tcommand: HarnessLifecycleCommand,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"HarnessLifecycleViolation\";\n\t\tthis.code = code;\n\t\tthis.state = state;\n\t\tthis.command = command;\n\t}\n}\n\nexport type HarnessLifecycleResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly error: HarnessLifecycleViolation };\n\n/** Clock and identity factories injected so lifecycle behavior is deterministic under test. */\nexport interface HarnessLifecycleDependencies {\n\treadonly createOperationId: () => string;\n\treadonly now: () => number;\n}\n"]}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Pure outcome classification and error aggregation for harness operations.
3
+ *
4
+ * Everything here is a total function over already-observed results: it never
5
+ * touches lifecycle state, sessions, providers, or clocks. Keeping the rules in
6
+ * one leaf module makes the precedence auditable in isolation and keeps
7
+ * `agent-harness.ts` free of the branch-heavy classification tables.
8
+ */
9
+ import { type AssistantMessage } from "omk-ai";
10
+ import type { HarnessAttemptOutcome, HarnessOperationOutcome } from "./operation-lifecycle-types.ts";
11
+ import type { NavigateTreeResult } from "./types.ts";
12
+ import { AgentHarnessError } from "./types.ts";
13
+ /**
14
+ * True only for an error that *is* an abort, not merely an error raised while
15
+ * an abort signal happened to be up. `AgentHarnessError` has no "aborted" code,
16
+ * so an explicit abort reaches us as a subsystem error carrying code "aborted"
17
+ * or as a DOM-style `AbortError`.
18
+ */
19
+ export declare function isExplicitAbortError(error: unknown): boolean;
20
+ /** Map a subsystem failure onto the harness' stable top-level classification. */
21
+ export declare function normalizeHarnessError(error: unknown, fallbackCode: AgentHarnessError["code"]): AgentHarnessError;
22
+ /** Result-based outcome for prompt-family operations that resolve with a failure/abort assistant message. */
23
+ export declare function classifyAssistantOutcome(message: AssistantMessage): HarnessOperationOutcome | undefined;
24
+ /** Structural cancellation is a distinct, non-failure terminal outcome. */
25
+ export declare function classifyNavigateTreeOutcome(result: NavigateTreeResult): HarnessOperationOutcome;
26
+ /** A thrown attempt body is an aborted attempt only when the error itself is an abort. */
27
+ export declare function classifyAttemptFailure(error: unknown): HarnessAttemptOutcome;
28
+ /** Context overflow is a recoverable attempt outcome, not an attempt failure. */
29
+ export declare function classifyAttemptOutcome(message: AssistantMessage, contextWindow: number | undefined): HarnessAttemptOutcome;
30
+ /**
31
+ * Single outcome-precedence rule for every public operation:
32
+ *
33
+ * session persistence failure > non-abort body/hook failure >
34
+ * explicit abort > result-classified outcome > completed
35
+ *
36
+ * A raised abort signal alone never downgrades another failure to "aborted":
37
+ * only an error that *is* an abort does. Otherwise a flush failure during an
38
+ * aborted turn would settle as "aborted" while the public promise rejected
39
+ * with "session".
40
+ */
41
+ export declare function resolveOperationOutcome<T>(input: {
42
+ readonly signalAborted: boolean;
43
+ readonly result: T | undefined;
44
+ readonly bodyError: unknown;
45
+ readonly flushError: unknown;
46
+ readonly classifyResult: ((result: T) => HarnessOperationOutcome | undefined) | undefined;
47
+ readonly fallbackCode: AgentHarnessError["code"];
48
+ }): HarnessOperationOutcome;
49
+ /**
50
+ * The error a boundary should throw after several steps may have failed, or
51
+ * `undefined` when none did. A single failure is returned untouched so its
52
+ * own classification survives; several are kept reachable through one
53
+ * `AggregateError`, classified by the first (primary) failure. This is what
54
+ * lets a failing boundary flush report *alongside* the body or listener error
55
+ * it followed instead of erasing it.
56
+ */
57
+ export declare function combineBoundaryErrors(errors: readonly unknown[], message: string, fallbackCode: AgentHarnessError["code"]): unknown;
58
+ /**
59
+ * Which error a public operation rejects with, or `undefined` on success.
60
+ *
61
+ * Mirrors the outcome precedence, but every concurrent cause is preserved in an
62
+ * `AggregateError` so an audit can still see that, say, the body and the final
63
+ * flush failed together.
64
+ */
65
+ export declare function resolveOperationFailure(input: {
66
+ readonly bodyError: unknown;
67
+ readonly flushError: unknown;
68
+ readonly settleError: unknown;
69
+ readonly fallbackCode: AgentHarnessError["code"];
70
+ }): AgentHarnessError | undefined;
71
+ //# sourceMappingURL=operation-outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-outcome.d.ts","sourceRoot":"","sources":["../../src/harness/operation-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,QAAQ,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACrG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAA8D,MAAM,YAAY,CAAC;AAE3G;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI5D;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,iBAAiB,CAOhH;AAED,6GAA6G;AAC7G,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,gBAAgB,GAAG,uBAAuB,GAAG,SAAS,CAMvG;AAED,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,kBAAkB,GAAG,uBAAuB,CAE/F;AAED,0FAA0F;AAC1F,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAE5E;AAED,iFAAiF;AACjF,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,gBAAgB,EACzB,aAAa,EAAE,MAAM,GAAG,SAAS,GAC/B,qBAAqB,CAKvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE;IACjD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,uBAAuB,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IAC1F,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CACjD,GAAG,uBAAuB,CAiB1B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,SAAS,OAAO,EAAE,EAC1B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,GACrC,OAAO,CAKT;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CACjD,GAAG,iBAAiB,GAAG,SAAS,CAoBhC","sourcesContent":["/**\n * Pure outcome classification and error aggregation for harness operations.\n *\n * Everything here is a total function over already-observed results: it never\n * touches lifecycle state, sessions, providers, or clocks. Keeping the rules in\n * one leaf module makes the precedence auditable in isolation and keeps\n * `agent-harness.ts` free of the branch-heavy classification tables.\n */\n\nimport { type AssistantMessage, isContextOverflow } from \"omk-ai\";\nimport type { HarnessAttemptOutcome, HarnessOperationOutcome } from \"./operation-lifecycle-types.ts\";\nimport type { NavigateTreeResult } from \"./types.ts\";\nimport { AgentHarnessError, BranchSummaryError, CompactionError, SessionError, toError } from \"./types.ts\";\n\n/**\n * True only for an error that *is* an abort, not merely an error raised while\n * an abort signal happened to be up. `AgentHarnessError` has no \"aborted\" code,\n * so an explicit abort reaches us as a subsystem error carrying code \"aborted\"\n * or as a DOM-style `AbortError`.\n */\nexport function isExplicitAbortError(error: unknown): boolean {\n\tconst cause = toError(error);\n\tif (cause.name === \"AbortError\") return true;\n\treturn (cause instanceof CompactionError || cause instanceof BranchSummaryError) && cause.code === \"aborted\";\n}\n\n/** Map a subsystem failure onto the harness' stable top-level classification. */\nexport function normalizeHarnessError(error: unknown, fallbackCode: AgentHarnessError[\"code\"]): AgentHarnessError {\n\tif (error instanceof AgentHarnessError) return error;\n\tconst cause = toError(error);\n\tif (cause instanceof SessionError) return new AgentHarnessError(\"session\", cause.message, cause);\n\tif (cause instanceof CompactionError) return new AgentHarnessError(\"compaction\", cause.message, cause);\n\tif (cause instanceof BranchSummaryError) return new AgentHarnessError(\"branch_summary\", cause.message, cause);\n\treturn new AgentHarnessError(fallbackCode, cause.message, cause);\n}\n\n/** Result-based outcome for prompt-family operations that resolve with a failure/abort assistant message. */\nexport function classifyAssistantOutcome(message: AssistantMessage): HarnessOperationOutcome | undefined {\n\tif (message.stopReason === \"aborted\") return { status: \"aborted\" };\n\tif (message.stopReason === \"error\") {\n\t\treturn { status: \"failed\", code: \"provider\", message: message.errorMessage ?? \"Provider error\" };\n\t}\n\treturn undefined;\n}\n\n/** Structural cancellation is a distinct, non-failure terminal outcome. */\nexport function classifyNavigateTreeOutcome(result: NavigateTreeResult): HarnessOperationOutcome {\n\treturn result.cancelled ? { status: \"cancelled\", reason: \"tree_navigation_cancelled\" } : { status: \"completed\" };\n}\n\n/** A thrown attempt body is an aborted attempt only when the error itself is an abort. */\nexport function classifyAttemptFailure(error: unknown): HarnessAttemptOutcome {\n\treturn isExplicitAbortError(error) ? \"aborted\" : \"failed\";\n}\n\n/** Context overflow is a recoverable attempt outcome, not an attempt failure. */\nexport function classifyAttemptOutcome(\n\tmessage: AssistantMessage,\n\tcontextWindow: number | undefined,\n): HarnessAttemptOutcome {\n\tif (message.stopReason === \"aborted\") return \"aborted\";\n\tif (isContextOverflow(message, contextWindow)) return \"overflow\";\n\tif (message.stopReason === \"error\") return \"failed\";\n\treturn \"completed\";\n}\n\n/**\n * Single outcome-precedence rule for every public operation:\n *\n * session persistence failure > non-abort body/hook failure >\n * explicit abort > result-classified outcome > completed\n *\n * A raised abort signal alone never downgrades another failure to \"aborted\":\n * only an error that *is* an abort does. Otherwise a flush failure during an\n * aborted turn would settle as \"aborted\" while the public promise rejected\n * with \"session\".\n */\nexport function resolveOperationOutcome<T>(input: {\n\treadonly signalAborted: boolean;\n\treadonly result: T | undefined;\n\treadonly bodyError: unknown;\n\treadonly flushError: unknown;\n\treadonly classifyResult: ((result: T) => HarnessOperationOutcome | undefined) | undefined;\n\treadonly fallbackCode: AgentHarnessError[\"code\"];\n}): HarnessOperationOutcome {\n\tif (input.flushError !== undefined) {\n\t\t// Mirror `resolveOperationFailure`: a flush error that already carries a\n\t\t// harness classification (e.g. an `invalid_state` coordinator reentry)\n\t\t// keeps it, so the recorded outcome and the rejection never disagree.\n\t\tconst error = normalizeHarnessError(input.flushError, \"session\");\n\t\treturn { status: \"failed\", code: error.code, message: error.message };\n\t}\n\tif (input.bodyError !== undefined) {\n\t\tif (isExplicitAbortError(input.bodyError)) return { status: \"aborted\" };\n\t\tconst error = normalizeHarnessError(input.bodyError, input.fallbackCode);\n\t\treturn { status: \"failed\", code: error.code, message: error.message };\n\t}\n\treturn (\n\t\tinput.classifyResult?.(input.result as T) ??\n\t\t(input.signalAborted ? { status: \"aborted\" } : { status: \"completed\" })\n\t);\n}\n\n/**\n * The error a boundary should throw after several steps may have failed, or\n * `undefined` when none did. A single failure is returned untouched so its\n * own classification survives; several are kept reachable through one\n * `AggregateError`, classified by the first (primary) failure. This is what\n * lets a failing boundary flush report *alongside* the body or listener error\n * it followed instead of erasing it.\n */\nexport function combineBoundaryErrors(\n\terrors: readonly unknown[],\n\tmessage: string,\n\tfallbackCode: AgentHarnessError[\"code\"],\n): unknown {\n\tconst present = errors.filter((error) => error !== undefined);\n\tif (present.length <= 1) return present[0];\n\tconst cause = new AggregateError(present.map(toError), message);\n\treturn new AgentHarnessError(normalizeHarnessError(present[0], fallbackCode).code, cause.message, cause);\n}\n\n/**\n * Which error a public operation rejects with, or `undefined` on success.\n *\n * Mirrors the outcome precedence, but every concurrent cause is preserved in an\n * `AggregateError` so an audit can still see that, say, the body and the final\n * flush failed together.\n */\nexport function resolveOperationFailure(input: {\n\treadonly bodyError: unknown;\n\treadonly flushError: unknown;\n\treadonly settleError: unknown;\n\treadonly fallbackCode: AgentHarnessError[\"code\"];\n}): AgentHarnessError | undefined {\n\tconst primaryError = input.bodyError ?? input.flushError;\n\tif (primaryError !== undefined && input.settleError !== undefined) {\n\t\tconst cause = new AggregateError(\n\t\t\t[toError(primaryError), toError(input.settleError)],\n\t\t\t\"Operation failed and settlement failed\",\n\t\t);\n\t\treturn new AgentHarnessError(normalizeHarnessError(primaryError, input.fallbackCode).code, cause.message, cause);\n\t}\n\tif (input.settleError !== undefined) return normalizeHarnessError(input.settleError, \"hook\");\n\tif (input.flushError !== undefined) {\n\t\tif (input.bodyError === undefined) return normalizeHarnessError(input.flushError, \"session\");\n\t\tconst cause = new AggregateError(\n\t\t\t[toError(input.bodyError), toError(input.flushError)],\n\t\t\t\"Operation failed and the final flush failed\",\n\t\t);\n\t\treturn new AgentHarnessError(\"session\", cause.message, cause);\n\t}\n\tif (input.bodyError !== undefined) return normalizeHarnessError(input.bodyError, input.fallbackCode);\n\treturn undefined;\n}\n"]}