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,148 @@
1
+ /**
2
+ * Legal phase moves for one effect. Pure and total: every command either
3
+ * yields the next phase (plus the reason code the journal should record) or a
4
+ * classified violation, and nothing here touches the chain, a clock, or I/O.
5
+ *
6
+ * The table encodes the two rules the plan refuses to compromise on:
7
+ *
8
+ * - `commit_unknown` is only ever left by inspection, compensation, an
9
+ * explicit abandonment, or — for `pure`/`idempotent` semantics alone — a
10
+ * replay. An `opaque`, `inspectable`, or `compensatable` effect whose commit
11
+ * is unknown cannot be re-dispatched (`unsafe_replay`).
12
+ * - Terminal phases are final. An acknowledged effect cannot be reopened, so a
13
+ * late duplicate observation after recovery cannot resurrect it.
14
+ */
15
+ import { EffectJournalViolation, TERMINAL_EFFECT_PHASES, } from "./effect-types.js";
16
+ const REPLAY_SAFE_SEMANTICS = ["pure", "idempotent"];
17
+ export function isTerminalEffectPhase(phase) {
18
+ return TERMINAL_EFFECT_PHASES.includes(phase);
19
+ }
20
+ export function isReplaySafe(semantics) {
21
+ return REPLAY_SAFE_SEMANTICS.includes(semantics);
22
+ }
23
+ function move(phase, reasonCode) {
24
+ return { ok: true, value: { phase, reasonCode } };
25
+ }
26
+ function invalid(record, command) {
27
+ return {
28
+ ok: false,
29
+ error: new EffectJournalViolation("invalid_transition", `Effect ${record.effectId} cannot ${command.type} from phase ${record.phase}`, record.effectId),
30
+ };
31
+ }
32
+ function reduceObserve(record, command) {
33
+ if (record.phase !== "dispatched")
34
+ return invalid(record, command);
35
+ switch (command.observation) {
36
+ case "committed":
37
+ return move("observed_committed", command.reasonCode ?? "effect.observed_committed");
38
+ case "not_committed":
39
+ return move("observed_not_committed", command.reasonCode ?? "effect.observed_not_committed");
40
+ case "unknown":
41
+ return move("commit_unknown", command.reasonCode ?? "effect.commit_unknown");
42
+ }
43
+ }
44
+ function reduceRedispatch(record, command) {
45
+ if (record.phase === "observed_not_committed")
46
+ return move("dispatched", "effect.retry_not_committed");
47
+ if (record.phase !== "commit_unknown")
48
+ return invalid(record, command);
49
+ if (isReplaySafe(record.semantics))
50
+ return move("dispatched", "effect.replay_safe");
51
+ return {
52
+ ok: false,
53
+ error: new EffectJournalViolation("unsafe_replay", `Effect ${record.effectId} has ${record.semantics} semantics and an unknown commit; blind replay is forbidden`, record.effectId),
54
+ };
55
+ }
56
+ function reduceCompensateBegin(record, command) {
57
+ if (record.phase !== "observed_committed" && record.phase !== "commit_unknown")
58
+ return invalid(record, command);
59
+ if (record.compensationDescriptor === undefined) {
60
+ return {
61
+ ok: false,
62
+ error: new EffectJournalViolation("missing_descriptor", `Effect ${record.effectId} declares no compensation descriptor`, record.effectId),
63
+ };
64
+ }
65
+ return move("compensating", "effect.compensation_started");
66
+ }
67
+ /** Abandonment is an explicit decision (waiver or operator) and must carry its reason. */
68
+ function reduceAbandon(record, command) {
69
+ const allowed = ["prepared", "observed_not_committed", "commit_unknown", "compensating"];
70
+ if (!allowed.includes(record.phase))
71
+ return invalid(record, command);
72
+ if (command.reasonCode.trim().length === 0) {
73
+ return {
74
+ ok: false,
75
+ error: new EffectJournalViolation("invalid_record", `Abandoning effect ${record.effectId} requires a reason code`, record.effectId),
76
+ };
77
+ }
78
+ return move("abandoned", command.reasonCode);
79
+ }
80
+ /**
81
+ * Every phase the effect may legally move to next, derived from the same
82
+ * rules as `reduceEffectPhase`. Replaying a persisted journal uses this to
83
+ * validate a stored record without reconstructing the command that made it.
84
+ */
85
+ export function legalNextPhases(record) {
86
+ switch (record.phase) {
87
+ case "prepared":
88
+ return ["dispatched", "abandoned"];
89
+ case "dispatched":
90
+ return ["observed_committed", "observed_not_committed", "commit_unknown"];
91
+ case "observed_committed":
92
+ return record.compensationDescriptor === undefined ? ["acknowledged"] : ["acknowledged", "compensating"];
93
+ case "observed_not_committed":
94
+ return ["dispatched", "abandoned"];
95
+ case "commit_unknown": {
96
+ const phases = ["observed_committed", "observed_not_committed", "abandoned"];
97
+ if (record.compensationDescriptor !== undefined)
98
+ phases.push("compensating");
99
+ if (isReplaySafe(record.semantics))
100
+ phases.push("dispatched");
101
+ return phases;
102
+ }
103
+ case "compensating":
104
+ return ["compensated", "commit_unknown", "abandoned"];
105
+ case "acknowledged":
106
+ case "compensated":
107
+ case "abandoned":
108
+ return [];
109
+ }
110
+ }
111
+ /** Next phase for `command` applied to the effect's latest record. */
112
+ export function reduceEffectPhase(record, command) {
113
+ if (isTerminalEffectPhase(record.phase))
114
+ return invalid(record, command);
115
+ switch (command.type) {
116
+ case "dispatch":
117
+ return record.phase === "prepared" ? move("dispatched", "effect.dispatched") : invalid(record, command);
118
+ case "observe":
119
+ return reduceObserve(record, command);
120
+ case "acknowledge":
121
+ return record.phase === "observed_committed"
122
+ ? move("acknowledged", "effect.acknowledged")
123
+ : invalid(record, command);
124
+ case "resolve_unknown":
125
+ if (record.phase !== "commit_unknown")
126
+ return invalid(record, command);
127
+ return command.inspection === "committed"
128
+ ? move("observed_committed", "effect.inspection_committed")
129
+ : move("observed_not_committed", "effect.inspection_not_committed");
130
+ case "redispatch":
131
+ return reduceRedispatch(record, command);
132
+ case "compensate_begin":
133
+ return reduceCompensateBegin(record, command);
134
+ case "compensate_end":
135
+ if (record.phase !== "compensating")
136
+ return invalid(record, command);
137
+ return command.result === "compensated"
138
+ ? move("compensated", "effect.compensated")
139
+ : move("commit_unknown", "effect.compensation_unknown");
140
+ case "abandon":
141
+ return reduceAbandon(record, command);
142
+ default: {
143
+ const unknownCommand = command;
144
+ throw new EffectJournalViolation("invalid_transition", `Unknown effect command ${String(unknownCommand)}`);
145
+ }
146
+ }
147
+ }
148
+ //# sourceMappingURL=effect-transitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-transitions.js","sourceRoot":"","sources":["../../src/effects/effect-transitions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Legal phase moves for one effect. Pure and total: every command either\n * yields the next phase (plus the reason code the journal should record) or a\n * classified violation, and nothing here touches the chain, a clock, or I/O.\n *\n * The table encodes the two rules the plan refuses to compromise on:\n *\n * - `commit_unknown` is only ever left by inspection, compensation, an\n * explicit abandonment, or — for `pure`/`idempotent` semantics alone — a\n * replay. An `opaque`, `inspectable`, or `compensatable` effect whose commit\n * is unknown cannot be re-dispatched (`unsafe_replay`).\n * - Terminal phases are final. An acknowledged effect cannot be reopened, so a\n * late duplicate observation after recovery cannot resurrect it.\n */\n\nimport {\n\ttype EffectCommand,\n\ttype EffectJournalResult,\n\tEffectJournalViolation,\n\ttype EffectPhase,\n\ttype EffectRecord,\n\ttype EffectSemantics,\n\tTERMINAL_EFFECT_PHASES,\n} from \"./effect-types.ts\";\n\nexport interface EffectPhaseMove {\n\treadonly phase: EffectPhase;\n\treadonly reasonCode: string;\n}\n\ntype MoveResult = EffectJournalResult<EffectPhaseMove>;\n\nconst REPLAY_SAFE_SEMANTICS: readonly EffectSemantics[] = [\"pure\", \"idempotent\"];\n\nexport function isTerminalEffectPhase(phase: EffectPhase): boolean {\n\treturn TERMINAL_EFFECT_PHASES.includes(phase);\n}\n\nexport function isReplaySafe(semantics: EffectSemantics): boolean {\n\treturn REPLAY_SAFE_SEMANTICS.includes(semantics);\n}\n\nfunction move(phase: EffectPhase, reasonCode: string): MoveResult {\n\treturn { ok: true, value: { phase, reasonCode } };\n}\n\nfunction invalid(record: EffectRecord, command: EffectCommand): MoveResult {\n\treturn {\n\t\tok: false,\n\t\terror: new EffectJournalViolation(\n\t\t\t\"invalid_transition\",\n\t\t\t`Effect ${record.effectId} cannot ${command.type} from phase ${record.phase}`,\n\t\t\trecord.effectId,\n\t\t),\n\t};\n}\n\nfunction reduceObserve(record: EffectRecord, command: Extract<EffectCommand, { type: \"observe\" }>): MoveResult {\n\tif (record.phase !== \"dispatched\") return invalid(record, command);\n\tswitch (command.observation) {\n\t\tcase \"committed\":\n\t\t\treturn move(\"observed_committed\", command.reasonCode ?? \"effect.observed_committed\");\n\t\tcase \"not_committed\":\n\t\t\treturn move(\"observed_not_committed\", command.reasonCode ?? \"effect.observed_not_committed\");\n\t\tcase \"unknown\":\n\t\t\treturn move(\"commit_unknown\", command.reasonCode ?? \"effect.commit_unknown\");\n\t}\n}\n\nfunction reduceRedispatch(record: EffectRecord, command: Extract<EffectCommand, { type: \"redispatch\" }>): MoveResult {\n\tif (record.phase === \"observed_not_committed\") return move(\"dispatched\", \"effect.retry_not_committed\");\n\tif (record.phase !== \"commit_unknown\") return invalid(record, command);\n\tif (isReplaySafe(record.semantics)) return move(\"dispatched\", \"effect.replay_safe\");\n\treturn {\n\t\tok: false,\n\t\terror: new EffectJournalViolation(\n\t\t\t\"unsafe_replay\",\n\t\t\t`Effect ${record.effectId} has ${record.semantics} semantics and an unknown commit; blind replay is forbidden`,\n\t\t\trecord.effectId,\n\t\t),\n\t};\n}\n\nfunction reduceCompensateBegin(\n\trecord: EffectRecord,\n\tcommand: Extract<EffectCommand, { type: \"compensate_begin\" }>,\n): MoveResult {\n\tif (record.phase !== \"observed_committed\" && record.phase !== \"commit_unknown\") return invalid(record, command);\n\tif (record.compensationDescriptor === undefined) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: new EffectJournalViolation(\n\t\t\t\t\"missing_descriptor\",\n\t\t\t\t`Effect ${record.effectId} declares no compensation descriptor`,\n\t\t\t\trecord.effectId,\n\t\t\t),\n\t\t};\n\t}\n\treturn move(\"compensating\", \"effect.compensation_started\");\n}\n\n/** Abandonment is an explicit decision (waiver or operator) and must carry its reason. */\nfunction reduceAbandon(record: EffectRecord, command: Extract<EffectCommand, { type: \"abandon\" }>): MoveResult {\n\tconst allowed: readonly EffectPhase[] = [\"prepared\", \"observed_not_committed\", \"commit_unknown\", \"compensating\"];\n\tif (!allowed.includes(record.phase)) return invalid(record, command);\n\tif (command.reasonCode.trim().length === 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: new EffectJournalViolation(\n\t\t\t\t\"invalid_record\",\n\t\t\t\t`Abandoning effect ${record.effectId} requires a reason code`,\n\t\t\t\trecord.effectId,\n\t\t\t),\n\t\t};\n\t}\n\treturn move(\"abandoned\", command.reasonCode);\n}\n\n/**\n * Every phase the effect may legally move to next, derived from the same\n * rules as `reduceEffectPhase`. Replaying a persisted journal uses this to\n * validate a stored record without reconstructing the command that made it.\n */\nexport function legalNextPhases(record: EffectRecord): readonly EffectPhase[] {\n\tswitch (record.phase) {\n\t\tcase \"prepared\":\n\t\t\treturn [\"dispatched\", \"abandoned\"];\n\t\tcase \"dispatched\":\n\t\t\treturn [\"observed_committed\", \"observed_not_committed\", \"commit_unknown\"];\n\t\tcase \"observed_committed\":\n\t\t\treturn record.compensationDescriptor === undefined ? [\"acknowledged\"] : [\"acknowledged\", \"compensating\"];\n\t\tcase \"observed_not_committed\":\n\t\t\treturn [\"dispatched\", \"abandoned\"];\n\t\tcase \"commit_unknown\": {\n\t\t\tconst phases: EffectPhase[] = [\"observed_committed\", \"observed_not_committed\", \"abandoned\"];\n\t\t\tif (record.compensationDescriptor !== undefined) phases.push(\"compensating\");\n\t\t\tif (isReplaySafe(record.semantics)) phases.push(\"dispatched\");\n\t\t\treturn phases;\n\t\t}\n\t\tcase \"compensating\":\n\t\t\treturn [\"compensated\", \"commit_unknown\", \"abandoned\"];\n\t\tcase \"acknowledged\":\n\t\tcase \"compensated\":\n\t\tcase \"abandoned\":\n\t\t\treturn [];\n\t}\n}\n\n/** Next phase for `command` applied to the effect's latest record. */\nexport function reduceEffectPhase(\n\trecord: EffectRecord,\n\tcommand: Exclude<EffectCommand, { type: \"prepare\" }>,\n): MoveResult {\n\tif (isTerminalEffectPhase(record.phase)) return invalid(record, command);\n\tswitch (command.type) {\n\t\tcase \"dispatch\":\n\t\t\treturn record.phase === \"prepared\" ? move(\"dispatched\", \"effect.dispatched\") : invalid(record, command);\n\t\tcase \"observe\":\n\t\t\treturn reduceObserve(record, command);\n\t\tcase \"acknowledge\":\n\t\t\treturn record.phase === \"observed_committed\"\n\t\t\t\t? move(\"acknowledged\", \"effect.acknowledged\")\n\t\t\t\t: invalid(record, command);\n\t\tcase \"resolve_unknown\":\n\t\t\tif (record.phase !== \"commit_unknown\") return invalid(record, command);\n\t\t\treturn command.inspection === \"committed\"\n\t\t\t\t? move(\"observed_committed\", \"effect.inspection_committed\")\n\t\t\t\t: move(\"observed_not_committed\", \"effect.inspection_not_committed\");\n\t\tcase \"redispatch\":\n\t\t\treturn reduceRedispatch(record, command);\n\t\tcase \"compensate_begin\":\n\t\t\treturn reduceCompensateBegin(record, command);\n\t\tcase \"compensate_end\":\n\t\t\tif (record.phase !== \"compensating\") return invalid(record, command);\n\t\t\treturn command.result === \"compensated\"\n\t\t\t\t? move(\"compensated\", \"effect.compensated\")\n\t\t\t\t: move(\"commit_unknown\", \"effect.compensation_unknown\");\n\t\tcase \"abandon\":\n\t\t\treturn reduceAbandon(record, command);\n\t\tdefault: {\n\t\t\tconst unknownCommand: never = command;\n\t\t\tthrow new EffectJournalViolation(\"invalid_transition\", `Unknown effect command ${String(unknownCommand)}`);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Effect Journal V2 vocabulary: the durable identity and phase model for every
3
+ * side effect a harness operation performs.
4
+ *
5
+ * A side effect is not a tool result. The tool result says what the tool
6
+ * reported; the effect record says what the runtime committed to before,
7
+ * during, and after the action, so that after a crash the question "did it
8
+ * happen?" has one of three honest answers — committed, not committed, or
9
+ * unknown — instead of being overwritten by a retry.
10
+ *
11
+ * This module imports nothing and declares no behaviour. Legal phase moves
12
+ * live in `effect-transitions.ts`, the hash-chained journal in
13
+ * `effect-journal.ts`, recovery in `effect-recovery.ts`.
14
+ */
15
+ export declare const EFFECT_RECORD_SCHEMA_VERSION: 2;
16
+ /** The hash a journal's first record chains to. */
17
+ export declare const EFFECT_JOURNAL_GENESIS_HASH: string;
18
+ /**
19
+ * What the runtime may assume about re-executing the effect.
20
+ *
21
+ * | semantics | automatic recovery |
22
+ * | --------------- | ------------------------------------------------- |
23
+ * | `pure` | re-execute freely |
24
+ * | `idempotent` | re-execute with the same idempotency key |
25
+ * | `inspectable` | inspect the target, then decide |
26
+ * | `compensatable` | run the declared compensation |
27
+ * | `opaque` | never re-execute automatically; operator decides |
28
+ */
29
+ export type EffectSemantics = "pure" | "idempotent" | "inspectable" | "compensatable" | "opaque";
30
+ export type EffectPhase = "prepared" | "dispatched" | "observed_committed" | "observed_not_committed" | "commit_unknown" | "acknowledged" | "compensating" | "compensated" | "abandoned";
31
+ /** Phases after which the journal accepts no further transition for the effect. */
32
+ export declare const TERMINAL_EFFECT_PHASES: readonly EffectPhase[];
33
+ /** Phases whose external outcome is not yet known; a verified verdict needs this set empty. */
34
+ export declare const UNCERTAIN_EFFECT_PHASES: readonly EffectPhase[];
35
+ export interface EffectInspectionDescriptor {
36
+ readonly kind: string;
37
+ readonly targetDigest?: string;
38
+ readonly parameters?: Readonly<Record<string, string>>;
39
+ }
40
+ export interface EffectCompensationDescriptor {
41
+ readonly kind: string;
42
+ readonly parameters?: Readonly<Record<string, string>>;
43
+ }
44
+ /** Who performs the effect, under which operation, attempt, lane epoch, and process incarnation. */
45
+ export interface EffectIdentity {
46
+ readonly effectId: string;
47
+ readonly operationId: string;
48
+ readonly attemptId: string;
49
+ readonly laneId?: string;
50
+ readonly laneEpoch?: number;
51
+ readonly processIncarnation: string;
52
+ }
53
+ /** What the effect intends to do, committed before dispatch and constant for the effect's lifetime. */
54
+ export interface EffectIntent {
55
+ readonly semantics: EffectSemantics;
56
+ readonly capabilityDigest: string;
57
+ readonly intentDigest: string;
58
+ readonly idempotencyKey?: string;
59
+ readonly inspectDescriptor?: EffectInspectionDescriptor;
60
+ readonly compensationDescriptor?: EffectCompensationDescriptor;
61
+ }
62
+ /** One hash-chained journal entry: the effect's identity, intent, and phase at `sequence`. */
63
+ export interface EffectRecord extends EffectIdentity, EffectIntent {
64
+ readonly schemaVersion: typeof EFFECT_RECORD_SCHEMA_VERSION;
65
+ readonly phase: EffectPhase;
66
+ readonly sequence: number;
67
+ readonly timestamp: string;
68
+ readonly reasonCode?: string;
69
+ readonly previousRecordHash: string;
70
+ readonly recordHash: string;
71
+ }
72
+ export type EffectObservation = "committed" | "not_committed" | "unknown";
73
+ export type EffectCommand = {
74
+ readonly type: "prepare";
75
+ readonly identity: EffectIdentity;
76
+ readonly intent: EffectIntent;
77
+ readonly timestamp: string;
78
+ } | {
79
+ readonly type: "dispatch";
80
+ readonly effectId: string;
81
+ readonly timestamp: string;
82
+ } | {
83
+ readonly type: "observe";
84
+ readonly effectId: string;
85
+ readonly observation: EffectObservation;
86
+ readonly timestamp: string;
87
+ readonly reasonCode?: string;
88
+ } | {
89
+ readonly type: "acknowledge";
90
+ readonly effectId: string;
91
+ readonly timestamp: string;
92
+ } | {
93
+ readonly type: "resolve_unknown";
94
+ readonly effectId: string;
95
+ readonly inspection: "committed" | "not_committed";
96
+ readonly timestamp: string;
97
+ } | {
98
+ readonly type: "redispatch";
99
+ readonly effectId: string;
100
+ readonly timestamp: string;
101
+ } | {
102
+ readonly type: "compensate_begin";
103
+ readonly effectId: string;
104
+ readonly timestamp: string;
105
+ } | {
106
+ readonly type: "compensate_end";
107
+ readonly effectId: string;
108
+ readonly result: "compensated" | "unknown";
109
+ readonly timestamp: string;
110
+ } | {
111
+ readonly type: "abandon";
112
+ readonly effectId: string;
113
+ readonly reasonCode: string;
114
+ readonly timestamp: string;
115
+ };
116
+ export type EffectViolationCode = "unknown_effect" | "duplicate_effect" | "invalid_transition" | "unsafe_replay" | "missing_descriptor" | "identity_mismatch" | "sequence_violation" | "chain_break" | "hash_mismatch" | "invalid_record";
117
+ export declare class EffectJournalViolation extends Error {
118
+ readonly code: EffectViolationCode;
119
+ readonly effectId?: string;
120
+ constructor(code: EffectViolationCode, message: string, effectId?: string);
121
+ }
122
+ export type EffectJournalResult<T> = {
123
+ readonly ok: true;
124
+ readonly value: T;
125
+ } | {
126
+ readonly ok: false;
127
+ readonly error: EffectJournalViolation;
128
+ };
129
+ /** Latest record per effect plus the chain head; the whole thing is plain data. */
130
+ export interface EffectJournalState {
131
+ readonly headHash: string;
132
+ readonly lastSequence: number;
133
+ readonly effects: Readonly<Record<string, EffectRecord>>;
134
+ }
135
+ //# sourceMappingURL=effect-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-types.d.ts","sourceRoot":"","sources":["../../src/effects/effect-types.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Effect Journal V2 vocabulary: the durable identity and phase model for every\n * side effect a harness operation performs.\n *\n * A side effect is not a tool result. The tool result says what the tool\n * reported; the effect record says what the runtime committed to before,\n * during, and after the action, so that after a crash the question \"did it\n * happen?\" has one of three honest answers — committed, not committed, or\n * unknown — instead of being overwritten by a retry.\n *\n * This module imports nothing and declares no behaviour. Legal phase moves\n * live in `effect-transitions.ts`, the hash-chained journal in\n * `effect-journal.ts`, recovery in `effect-recovery.ts`.\n */\n\nexport const EFFECT_RECORD_SCHEMA_VERSION = 2 as const;\n\n/** The hash a journal's first record chains to. */\nexport const EFFECT_JOURNAL_GENESIS_HASH = \"0\".repeat(64);\n\n/**\n * What the runtime may assume about re-executing the effect.\n *\n * | semantics | automatic recovery |\n * | --------------- | ------------------------------------------------- |\n * | `pure` | re-execute freely |\n * | `idempotent` | re-execute with the same idempotency key |\n * | `inspectable` | inspect the target, then decide |\n * | `compensatable` | run the declared compensation |\n * | `opaque` | never re-execute automatically; operator decides |\n */\nexport type EffectSemantics = \"pure\" | \"idempotent\" | \"inspectable\" | \"compensatable\" | \"opaque\";\n\nexport type EffectPhase =\n\t| \"prepared\"\n\t| \"dispatched\"\n\t| \"observed_committed\"\n\t| \"observed_not_committed\"\n\t| \"commit_unknown\"\n\t| \"acknowledged\"\n\t| \"compensating\"\n\t| \"compensated\"\n\t| \"abandoned\";\n\n/** Phases after which the journal accepts no further transition for the effect. */\nexport const TERMINAL_EFFECT_PHASES: readonly EffectPhase[] = [\"acknowledged\", \"compensated\", \"abandoned\"];\n\n/** Phases whose external outcome is not yet known; a verified verdict needs this set empty. */\nexport const UNCERTAIN_EFFECT_PHASES: readonly EffectPhase[] = [\"dispatched\", \"commit_unknown\", \"compensating\"];\n\nexport interface EffectInspectionDescriptor {\n\treadonly kind: string;\n\treadonly targetDigest?: string;\n\treadonly parameters?: Readonly<Record<string, string>>;\n}\n\nexport interface EffectCompensationDescriptor {\n\treadonly kind: string;\n\treadonly parameters?: Readonly<Record<string, string>>;\n}\n\n/** Who performs the effect, under which operation, attempt, lane epoch, and process incarnation. */\nexport interface EffectIdentity {\n\treadonly effectId: string;\n\treadonly operationId: string;\n\treadonly attemptId: string;\n\treadonly laneId?: string;\n\treadonly laneEpoch?: number;\n\treadonly processIncarnation: string;\n}\n\n/** What the effect intends to do, committed before dispatch and constant for the effect's lifetime. */\nexport interface EffectIntent {\n\treadonly semantics: EffectSemantics;\n\treadonly capabilityDigest: string;\n\treadonly intentDigest: string;\n\treadonly idempotencyKey?: string;\n\treadonly inspectDescriptor?: EffectInspectionDescriptor;\n\treadonly compensationDescriptor?: EffectCompensationDescriptor;\n}\n\n/** One hash-chained journal entry: the effect's identity, intent, and phase at `sequence`. */\nexport interface EffectRecord extends EffectIdentity, EffectIntent {\n\treadonly schemaVersion: typeof EFFECT_RECORD_SCHEMA_VERSION;\n\treadonly phase: EffectPhase;\n\treadonly sequence: number;\n\treadonly timestamp: string;\n\treadonly reasonCode?: string;\n\treadonly previousRecordHash: string;\n\treadonly recordHash: string;\n}\n\nexport type EffectObservation = \"committed\" | \"not_committed\" | \"unknown\";\n\nexport type EffectCommand =\n\t| {\n\t\t\treadonly type: \"prepare\";\n\t\t\treadonly identity: EffectIdentity;\n\t\t\treadonly intent: EffectIntent;\n\t\t\treadonly timestamp: string;\n\t }\n\t| { readonly type: \"dispatch\"; readonly effectId: string; readonly timestamp: string }\n\t| {\n\t\t\treadonly type: \"observe\";\n\t\t\treadonly effectId: string;\n\t\t\treadonly observation: EffectObservation;\n\t\t\treadonly timestamp: string;\n\t\t\treadonly reasonCode?: string;\n\t }\n\t| { readonly type: \"acknowledge\"; readonly effectId: string; readonly timestamp: string }\n\t| {\n\t\t\treadonly type: \"resolve_unknown\";\n\t\t\treadonly effectId: string;\n\t\t\treadonly inspection: \"committed\" | \"not_committed\";\n\t\t\treadonly timestamp: string;\n\t }\n\t| { readonly type: \"redispatch\"; readonly effectId: string; readonly timestamp: string }\n\t| { readonly type: \"compensate_begin\"; readonly effectId: string; readonly timestamp: string }\n\t| {\n\t\t\treadonly type: \"compensate_end\";\n\t\t\treadonly effectId: string;\n\t\t\treadonly result: \"compensated\" | \"unknown\";\n\t\t\treadonly timestamp: string;\n\t }\n\t| { readonly type: \"abandon\"; readonly effectId: string; readonly reasonCode: string; readonly timestamp: string };\n\nexport type EffectViolationCode =\n\t| \"unknown_effect\"\n\t| \"duplicate_effect\"\n\t| \"invalid_transition\"\n\t| \"unsafe_replay\"\n\t| \"missing_descriptor\"\n\t| \"identity_mismatch\"\n\t| \"sequence_violation\"\n\t| \"chain_break\"\n\t| \"hash_mismatch\"\n\t| \"invalid_record\";\n\nexport class EffectJournalViolation extends Error {\n\tpublic readonly code: EffectViolationCode;\n\tpublic readonly effectId?: string;\n\n\tconstructor(code: EffectViolationCode, message: string, effectId?: string) {\n\t\tsuper(message);\n\t\tthis.name = \"EffectJournalViolation\";\n\t\tthis.code = code;\n\t\tthis.effectId = effectId;\n\t}\n}\n\nexport type EffectJournalResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly error: EffectJournalViolation };\n\n/** Latest record per effect plus the chain head; the whole thing is plain data. */\nexport interface EffectJournalState {\n\treadonly headHash: string;\n\treadonly lastSequence: number;\n\treadonly effects: Readonly<Record<string, EffectRecord>>;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Effect Journal V2 vocabulary: the durable identity and phase model for every
3
+ * side effect a harness operation performs.
4
+ *
5
+ * A side effect is not a tool result. The tool result says what the tool
6
+ * reported; the effect record says what the runtime committed to before,
7
+ * during, and after the action, so that after a crash the question "did it
8
+ * happen?" has one of three honest answers — committed, not committed, or
9
+ * unknown — instead of being overwritten by a retry.
10
+ *
11
+ * This module imports nothing and declares no behaviour. Legal phase moves
12
+ * live in `effect-transitions.ts`, the hash-chained journal in
13
+ * `effect-journal.ts`, recovery in `effect-recovery.ts`.
14
+ */
15
+ export const EFFECT_RECORD_SCHEMA_VERSION = 2;
16
+ /** The hash a journal's first record chains to. */
17
+ export const EFFECT_JOURNAL_GENESIS_HASH = "0".repeat(64);
18
+ /** Phases after which the journal accepts no further transition for the effect. */
19
+ export const TERMINAL_EFFECT_PHASES = ["acknowledged", "compensated", "abandoned"];
20
+ /** Phases whose external outcome is not yet known; a verified verdict needs this set empty. */
21
+ export const UNCERTAIN_EFFECT_PHASES = ["dispatched", "commit_unknown", "compensating"];
22
+ export class EffectJournalViolation extends Error {
23
+ code;
24
+ effectId;
25
+ constructor(code, message, effectId) {
26
+ super(message);
27
+ this.name = "EffectJournalViolation";
28
+ this.code = code;
29
+ this.effectId = effectId;
30
+ }
31
+ }
32
+ //# sourceMappingURL=effect-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-types.js","sourceRoot":"","sources":["../../src/effects/effect-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAU,CAAC;AAEvD,mDAAmD;AACnD,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AA0B1D,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAA2B,CAAC,cAAc,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;AAE3G,+FAA+F;AAC/F,MAAM,CAAC,MAAM,uBAAuB,GAA2B,CAAC,YAAY,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC;AA0FhH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAChC,IAAI,CAAsB;IAC1B,QAAQ,CAAU;IAElC,YAAY,IAAyB,EAAE,OAAe,EAAE,QAAiB,EAAE;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAAA,CACzB;CACD","sourcesContent":["/**\n * Effect Journal V2 vocabulary: the durable identity and phase model for every\n * side effect a harness operation performs.\n *\n * A side effect is not a tool result. The tool result says what the tool\n * reported; the effect record says what the runtime committed to before,\n * during, and after the action, so that after a crash the question \"did it\n * happen?\" has one of three honest answers — committed, not committed, or\n * unknown — instead of being overwritten by a retry.\n *\n * This module imports nothing and declares no behaviour. Legal phase moves\n * live in `effect-transitions.ts`, the hash-chained journal in\n * `effect-journal.ts`, recovery in `effect-recovery.ts`.\n */\n\nexport const EFFECT_RECORD_SCHEMA_VERSION = 2 as const;\n\n/** The hash a journal's first record chains to. */\nexport const EFFECT_JOURNAL_GENESIS_HASH = \"0\".repeat(64);\n\n/**\n * What the runtime may assume about re-executing the effect.\n *\n * | semantics | automatic recovery |\n * | --------------- | ------------------------------------------------- |\n * | `pure` | re-execute freely |\n * | `idempotent` | re-execute with the same idempotency key |\n * | `inspectable` | inspect the target, then decide |\n * | `compensatable` | run the declared compensation |\n * | `opaque` | never re-execute automatically; operator decides |\n */\nexport type EffectSemantics = \"pure\" | \"idempotent\" | \"inspectable\" | \"compensatable\" | \"opaque\";\n\nexport type EffectPhase =\n\t| \"prepared\"\n\t| \"dispatched\"\n\t| \"observed_committed\"\n\t| \"observed_not_committed\"\n\t| \"commit_unknown\"\n\t| \"acknowledged\"\n\t| \"compensating\"\n\t| \"compensated\"\n\t| \"abandoned\";\n\n/** Phases after which the journal accepts no further transition for the effect. */\nexport const TERMINAL_EFFECT_PHASES: readonly EffectPhase[] = [\"acknowledged\", \"compensated\", \"abandoned\"];\n\n/** Phases whose external outcome is not yet known; a verified verdict needs this set empty. */\nexport const UNCERTAIN_EFFECT_PHASES: readonly EffectPhase[] = [\"dispatched\", \"commit_unknown\", \"compensating\"];\n\nexport interface EffectInspectionDescriptor {\n\treadonly kind: string;\n\treadonly targetDigest?: string;\n\treadonly parameters?: Readonly<Record<string, string>>;\n}\n\nexport interface EffectCompensationDescriptor {\n\treadonly kind: string;\n\treadonly parameters?: Readonly<Record<string, string>>;\n}\n\n/** Who performs the effect, under which operation, attempt, lane epoch, and process incarnation. */\nexport interface EffectIdentity {\n\treadonly effectId: string;\n\treadonly operationId: string;\n\treadonly attemptId: string;\n\treadonly laneId?: string;\n\treadonly laneEpoch?: number;\n\treadonly processIncarnation: string;\n}\n\n/** What the effect intends to do, committed before dispatch and constant for the effect's lifetime. */\nexport interface EffectIntent {\n\treadonly semantics: EffectSemantics;\n\treadonly capabilityDigest: string;\n\treadonly intentDigest: string;\n\treadonly idempotencyKey?: string;\n\treadonly inspectDescriptor?: EffectInspectionDescriptor;\n\treadonly compensationDescriptor?: EffectCompensationDescriptor;\n}\n\n/** One hash-chained journal entry: the effect's identity, intent, and phase at `sequence`. */\nexport interface EffectRecord extends EffectIdentity, EffectIntent {\n\treadonly schemaVersion: typeof EFFECT_RECORD_SCHEMA_VERSION;\n\treadonly phase: EffectPhase;\n\treadonly sequence: number;\n\treadonly timestamp: string;\n\treadonly reasonCode?: string;\n\treadonly previousRecordHash: string;\n\treadonly recordHash: string;\n}\n\nexport type EffectObservation = \"committed\" | \"not_committed\" | \"unknown\";\n\nexport type EffectCommand =\n\t| {\n\t\t\treadonly type: \"prepare\";\n\t\t\treadonly identity: EffectIdentity;\n\t\t\treadonly intent: EffectIntent;\n\t\t\treadonly timestamp: string;\n\t }\n\t| { readonly type: \"dispatch\"; readonly effectId: string; readonly timestamp: string }\n\t| {\n\t\t\treadonly type: \"observe\";\n\t\t\treadonly effectId: string;\n\t\t\treadonly observation: EffectObservation;\n\t\t\treadonly timestamp: string;\n\t\t\treadonly reasonCode?: string;\n\t }\n\t| { readonly type: \"acknowledge\"; readonly effectId: string; readonly timestamp: string }\n\t| {\n\t\t\treadonly type: \"resolve_unknown\";\n\t\t\treadonly effectId: string;\n\t\t\treadonly inspection: \"committed\" | \"not_committed\";\n\t\t\treadonly timestamp: string;\n\t }\n\t| { readonly type: \"redispatch\"; readonly effectId: string; readonly timestamp: string }\n\t| { readonly type: \"compensate_begin\"; readonly effectId: string; readonly timestamp: string }\n\t| {\n\t\t\treadonly type: \"compensate_end\";\n\t\t\treadonly effectId: string;\n\t\t\treadonly result: \"compensated\" | \"unknown\";\n\t\t\treadonly timestamp: string;\n\t }\n\t| { readonly type: \"abandon\"; readonly effectId: string; readonly reasonCode: string; readonly timestamp: string };\n\nexport type EffectViolationCode =\n\t| \"unknown_effect\"\n\t| \"duplicate_effect\"\n\t| \"invalid_transition\"\n\t| \"unsafe_replay\"\n\t| \"missing_descriptor\"\n\t| \"identity_mismatch\"\n\t| \"sequence_violation\"\n\t| \"chain_break\"\n\t| \"hash_mismatch\"\n\t| \"invalid_record\";\n\nexport class EffectJournalViolation extends Error {\n\tpublic readonly code: EffectViolationCode;\n\tpublic readonly effectId?: string;\n\n\tconstructor(code: EffectViolationCode, message: string, effectId?: string) {\n\t\tsuper(message);\n\t\tthis.name = \"EffectJournalViolation\";\n\t\tthis.code = code;\n\t\tthis.effectId = effectId;\n\t}\n}\n\nexport type EffectJournalResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly error: EffectJournalViolation };\n\n/** Latest record per effect plus the chain head; the whole thing is plain data. */\nexport interface EffectJournalState {\n\treadonly headHash: string;\n\treadonly lastSequence: number;\n\treadonly effects: Readonly<Record<string, EffectRecord>>;\n}\n"]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Pure abort-delivery vocabulary, extracted from `AgentHarness`.
3
+ *
4
+ * Aborting has two halves that callers need separately: delivering the signal
5
+ * (safe from anywhere, including an operation's own callbacks, because it never
6
+ * waits) and waiting for the target's settlement (which a callback of that same
7
+ * operation must never do — settlement awaits the callback). Keeping the refusal
8
+ * table and the delivery description here means the oversized harness module only
9
+ * gains thin delegation, and the rules stay testable without a harness instance.
10
+ */
11
+ import type { HarnessAbortCapture } from "./operation-lifecycle-controller.ts";
12
+ import type { HarnessLifecycleState } from "./operation-lifecycle-types.ts";
13
+ /** What one abort-signal delivery did, without waiting for anything. */
14
+ export interface AbortSignalDeliveryResult {
15
+ /** The operation the signal targeted, when one was active or settling. */
16
+ readonly operationId?: string;
17
+ /** True only when the abort signal was newly delivered to that operation. */
18
+ readonly signalDelivered: boolean;
19
+ /** True when the target was already settling, so no signal could be delivered. */
20
+ readonly alreadySettling: boolean;
21
+ }
22
+ /** Throw when the active operation is one that refuses an abort. */
23
+ export declare function assertAbortAllowed(snapshot: Readonly<HarnessLifecycleState>): void;
24
+ /** Describe one captured abort delivery for a public, wait-free result. */
25
+ export declare function describeAbortDelivery(capture: HarnessAbortCapture): AbortSignalDeliveryResult;
26
+ //# sourceMappingURL=abort-delivery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-delivery.d.ts","sourceRoot":"","sources":["../../src/harness/abort-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,KAAK,EAAE,qBAAqB,EAAwB,MAAM,gCAAgC,CAAC;AAElG,wEAAwE;AACxE,MAAM,WAAW,yBAAyB;IACzC,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,kFAAkF;IAClF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CAClC;AAWD,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,IAAI,CAIlF;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,mBAAmB,GAAG,yBAAyB,CAM7F","sourcesContent":["/**\n * Pure abort-delivery vocabulary, extracted from `AgentHarness`.\n *\n * Aborting has two halves that callers need separately: delivering the signal\n * (safe from anywhere, including an operation's own callbacks, because it never\n * waits) and waiting for the target's settlement (which a callback of that same\n * operation must never do — settlement awaits the callback). Keeping the refusal\n * table and the delivery description here means the oversized harness module only\n * gains thin delegation, and the rules stay testable without a harness instance.\n */\n\nimport { AgentHarnessError } from \"./errors.ts\";\nimport type { HarnessAbortCapture } from \"./operation-lifecycle-controller.ts\";\nimport type { HarnessLifecycleState, HarnessOperationKind } from \"./operation-lifecycle-types.ts\";\n\n/** What one abort-signal delivery did, without waiting for anything. */\nexport interface AbortSignalDeliveryResult {\n\t/** The operation the signal targeted, when one was active or settling. */\n\treadonly operationId?: string;\n\t/** True only when the abort signal was newly delivered to that operation. */\n\treadonly signalDelivered: boolean;\n\t/** True when the target was already settling, so no signal could be delivered. */\n\treadonly alreadySettling: boolean;\n}\n\n/**\n * Operations that refuse an abort: cancelling them mid-flight would leave their\n * work half-applied, so the caller gets an explicit `invalid_state` instead.\n */\nconst ABORT_REFUSED_OPERATIONS: ReadonlyMap<HarnessOperationKind, string> = new Map([\n\t[\"manual_compaction\", \"Cannot abort during compaction\"],\n\t[\"tree_navigation\", \"Cannot abort during branch_summary\"],\n]);\n\n/** Throw when the active operation is one that refuses an abort. */\nexport function assertAbortAllowed(snapshot: Readonly<HarnessLifecycleState>): void {\n\tif (snapshot.tag !== \"active\") return;\n\tconst refused = ABORT_REFUSED_OPERATIONS.get(snapshot.operation.kind);\n\tif (refused !== undefined) throw new AgentHarnessError(\"invalid_state\", refused);\n}\n\n/** Describe one captured abort delivery for a public, wait-free result. */\nexport function describeAbortDelivery(capture: HarnessAbortCapture): AbortSignalDeliveryResult {\n\treturn {\n\t\t...(capture.target === undefined ? {} : { operationId: capture.target.operation.operationId }),\n\t\tsignalDelivered: capture.signalDelivered,\n\t\talreadySettling: capture.target !== undefined && !capture.signalDelivered,\n\t};\n}\n"]}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure abort-delivery vocabulary, extracted from `AgentHarness`.
3
+ *
4
+ * Aborting has two halves that callers need separately: delivering the signal
5
+ * (safe from anywhere, including an operation's own callbacks, because it never
6
+ * waits) and waiting for the target's settlement (which a callback of that same
7
+ * operation must never do — settlement awaits the callback). Keeping the refusal
8
+ * table and the delivery description here means the oversized harness module only
9
+ * gains thin delegation, and the rules stay testable without a harness instance.
10
+ */
11
+ import { AgentHarnessError } from "./errors.js";
12
+ /**
13
+ * Operations that refuse an abort: cancelling them mid-flight would leave their
14
+ * work half-applied, so the caller gets an explicit `invalid_state` instead.
15
+ */
16
+ const ABORT_REFUSED_OPERATIONS = new Map([
17
+ ["manual_compaction", "Cannot abort during compaction"],
18
+ ["tree_navigation", "Cannot abort during branch_summary"],
19
+ ]);
20
+ /** Throw when the active operation is one that refuses an abort. */
21
+ export function assertAbortAllowed(snapshot) {
22
+ if (snapshot.tag !== "active")
23
+ return;
24
+ const refused = ABORT_REFUSED_OPERATIONS.get(snapshot.operation.kind);
25
+ if (refused !== undefined)
26
+ throw new AgentHarnessError("invalid_state", refused);
27
+ }
28
+ /** Describe one captured abort delivery for a public, wait-free result. */
29
+ export function describeAbortDelivery(capture) {
30
+ return {
31
+ ...(capture.target === undefined ? {} : { operationId: capture.target.operation.operationId }),
32
+ signalDelivered: capture.signalDelivered,
33
+ alreadySettling: capture.target !== undefined && !capture.signalDelivered,
34
+ };
35
+ }
36
+ //# sourceMappingURL=abort-delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-delivery.js","sourceRoot":"","sources":["../../src/harness/abort-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAchD;;;GAGG;AACH,MAAM,wBAAwB,GAA8C,IAAI,GAAG,CAAC;IACnF,CAAC,mBAAmB,EAAE,gCAAgC,CAAC;IACvD,CAAC,iBAAiB,EAAE,oCAAoC,CAAC;CACzD,CAAC,CAAC;AAEH,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAAC,QAAyC,EAAQ;IACnF,IAAI,QAAQ,CAAC,GAAG,KAAK,QAAQ;QAAE,OAAO;IACtC,MAAM,OAAO,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtE,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,IAAI,iBAAiB,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAAA,CACjF;AAED,2EAA2E;AAC3E,MAAM,UAAU,qBAAqB,CAAC,OAA4B,EAA6B;IAC9F,OAAO;QACN,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAC9F,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,eAAe,EAAE,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,eAAe;KACzE,CAAC;AAAA,CACF","sourcesContent":["/**\n * Pure abort-delivery vocabulary, extracted from `AgentHarness`.\n *\n * Aborting has two halves that callers need separately: delivering the signal\n * (safe from anywhere, including an operation's own callbacks, because it never\n * waits) and waiting for the target's settlement (which a callback of that same\n * operation must never do — settlement awaits the callback). Keeping the refusal\n * table and the delivery description here means the oversized harness module only\n * gains thin delegation, and the rules stay testable without a harness instance.\n */\n\nimport { AgentHarnessError } from \"./errors.ts\";\nimport type { HarnessAbortCapture } from \"./operation-lifecycle-controller.ts\";\nimport type { HarnessLifecycleState, HarnessOperationKind } from \"./operation-lifecycle-types.ts\";\n\n/** What one abort-signal delivery did, without waiting for anything. */\nexport interface AbortSignalDeliveryResult {\n\t/** The operation the signal targeted, when one was active or settling. */\n\treadonly operationId?: string;\n\t/** True only when the abort signal was newly delivered to that operation. */\n\treadonly signalDelivered: boolean;\n\t/** True when the target was already settling, so no signal could be delivered. */\n\treadonly alreadySettling: boolean;\n}\n\n/**\n * Operations that refuse an abort: cancelling them mid-flight would leave their\n * work half-applied, so the caller gets an explicit `invalid_state` instead.\n */\nconst ABORT_REFUSED_OPERATIONS: ReadonlyMap<HarnessOperationKind, string> = new Map([\n\t[\"manual_compaction\", \"Cannot abort during compaction\"],\n\t[\"tree_navigation\", \"Cannot abort during branch_summary\"],\n]);\n\n/** Throw when the active operation is one that refuses an abort. */\nexport function assertAbortAllowed(snapshot: Readonly<HarnessLifecycleState>): void {\n\tif (snapshot.tag !== \"active\") return;\n\tconst refused = ABORT_REFUSED_OPERATIONS.get(snapshot.operation.kind);\n\tif (refused !== undefined) throw new AgentHarnessError(\"invalid_state\", refused);\n}\n\n/** Describe one captured abort delivery for a public, wait-free result. */\nexport function describeAbortDelivery(capture: HarnessAbortCapture): AbortSignalDeliveryResult {\n\treturn {\n\t\t...(capture.target === undefined ? {} : { operationId: capture.target.operation.operationId }),\n\t\tsignalDelivered: capture.signalDelivered,\n\t\talreadySettling: capture.target !== undefined && !capture.signalDelivered,\n\t};\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  import { type AssistantMessage, type ImageContent, type Model } from "omk-ai";
2
2
  import type { AgentMessage, AgentTool, QueueMode, ThinkingLevel } from "../types.ts";
3
+ import { type AbortSignalDeliveryResult } from "./abort-delivery.ts";
4
+ import { type CommandRef, type DeferredHarnessCommand } from "./deferred-commands.ts";
3
5
  import type { AbortResult, AgentHarnessEvent, AgentHarnessEventResultMap, AgentHarnessOptions, AgentHarnessOwnEvent, AgentHarnessResources, AgentHarnessStreamOptions, CompactResult, ExecutionEnv, HarnessSession, NavigateTreeResult, PromptTemplate, Skill } from "./types.ts";
4
6
  export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate extends PromptTemplate = PromptTemplate, TTool extends AgentTool = AgentTool> {
5
7
  readonly env: ExecutionEnv;
@@ -23,6 +25,7 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
23
25
  private nextTurnQueue;
24
26
  private handlers;
25
27
  private readonly subscribers;
28
+ private readonly deferredCommands;
26
29
  constructor(options: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>);
27
30
  private emitOwn;
28
31
  private emitAny;
@@ -100,6 +103,21 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
100
103
  setResources(resources: AgentHarnessResources<TSkill, TPromptTemplate>): Promise<void>;
101
104
  getStreamOptions(): AgentHarnessStreamOptions;
102
105
  setStreamOptions(streamOptions: AgentHarnessStreamOptions): Promise<void>;
106
+ /**
107
+ * Deliver the abort signal to the current operation without waiting for it to
108
+ * settle. Safe from an operation's own callbacks: nothing here awaits
109
+ * settlement, so it cannot form the cycle `abort()` has to refuse.
110
+ */
111
+ requestAbort(): AbortSignalDeliveryResult;
112
+ /**
113
+ * Queue work to run once the harness is idle and return its ref immediately.
114
+ *
115
+ * This is the callback-safe way to schedule follow-up work: awaiting
116
+ * `waitForIdle()` or `abort()` from a callback of the operation being settled
117
+ * deadlocks, because settlement awaits that callback. The ref reports the
118
+ * command's outcome and cancels it while it is still queued.
119
+ */
120
+ runWhenIdle(command: DeferredHarnessCommand): Promise<CommandRef>;
103
121
  abort(): Promise<AbortResult>;
104
122
  waitForIdle(): Promise<void>;
105
123
  subscribe(listener: (event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void): () => void;