omk-agent-core 0.98.1 → 0.98.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +644 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +3 -5
  4. package/dist/agent.js.map +1 -1
  5. package/dist/effects/effect-journal.d.ts +43 -0
  6. package/dist/effects/effect-journal.d.ts.map +1 -0
  7. package/dist/effects/effect-journal.js +186 -0
  8. package/dist/effects/effect-journal.js.map +1 -0
  9. package/dist/effects/effect-recovery.d.ts +70 -0
  10. package/dist/effects/effect-recovery.d.ts.map +1 -0
  11. package/dist/effects/effect-recovery.js +120 -0
  12. package/dist/effects/effect-recovery.js.map +1 -0
  13. package/dist/effects/effect-transitions.d.ts +34 -0
  14. package/dist/effects/effect-transitions.d.ts.map +1 -0
  15. package/dist/effects/effect-transitions.js +148 -0
  16. package/dist/effects/effect-transitions.js.map +1 -0
  17. package/dist/effects/effect-types.d.ts +135 -0
  18. package/dist/effects/effect-types.d.ts.map +1 -0
  19. package/dist/effects/effect-types.js +32 -0
  20. package/dist/effects/effect-types.js.map +1 -0
  21. package/dist/harness/abort-delivery.d.ts +26 -0
  22. package/dist/harness/abort-delivery.d.ts.map +1 -0
  23. package/dist/harness/abort-delivery.js +36 -0
  24. package/dist/harness/abort-delivery.js.map +1 -0
  25. package/dist/harness/agent-harness.d.ts +38 -6
  26. package/dist/harness/agent-harness.d.ts.map +1 -1
  27. package/dist/harness/agent-harness.js +327 -325
  28. package/dist/harness/agent-harness.js.map +1 -1
  29. package/dist/harness/canonical-digest.d.ts +32 -0
  30. package/dist/harness/canonical-digest.d.ts.map +1 -0
  31. package/dist/harness/canonical-digest.js +164 -0
  32. package/dist/harness/canonical-digest.js.map +1 -0
  33. package/dist/harness/compaction/operation.d.ts +7 -0
  34. package/dist/harness/compaction/operation.d.ts.map +1 -1
  35. package/dist/harness/compaction/operation.js.map +1 -1
  36. package/dist/harness/deferred-commands.d.ts +53 -0
  37. package/dist/harness/deferred-commands.d.ts.map +1 -0
  38. package/dist/harness/deferred-commands.js +96 -0
  39. package/dist/harness/deferred-commands.js.map +1 -0
  40. package/dist/harness/harness-session.d.ts +5 -58
  41. package/dist/harness/harness-session.d.ts.map +1 -1
  42. package/dist/harness/harness-session.js +15 -18
  43. package/dist/harness/harness-session.js.map +1 -1
  44. package/dist/harness/operation-lifecycle-controller.d.ts +68 -0
  45. package/dist/harness/operation-lifecycle-controller.d.ts.map +1 -0
  46. package/dist/harness/operation-lifecycle-controller.js +199 -0
  47. package/dist/harness/operation-lifecycle-controller.js.map +1 -0
  48. package/dist/harness/operation-lifecycle-reducer.d.ts +19 -0
  49. package/dist/harness/operation-lifecycle-reducer.d.ts.map +1 -0
  50. package/dist/harness/operation-lifecycle-reducer.js +201 -0
  51. package/dist/harness/operation-lifecycle-reducer.js.map +1 -0
  52. package/dist/harness/operation-lifecycle-types.d.ts +130 -0
  53. package/dist/harness/operation-lifecycle-types.d.ts.map +1 -0
  54. package/dist/harness/operation-lifecycle-types.js +34 -0
  55. package/dist/harness/operation-lifecycle-types.js.map +1 -0
  56. package/dist/harness/operation-outcome.d.ts +79 -0
  57. package/dist/harness/operation-outcome.d.ts.map +1 -0
  58. package/dist/harness/operation-outcome.js +167 -0
  59. package/dist/harness/operation-outcome.js.map +1 -0
  60. package/dist/harness/operation-trace-divergence.d.ts +60 -0
  61. package/dist/harness/operation-trace-divergence.d.ts.map +1 -0
  62. package/dist/harness/operation-trace-divergence.js +199 -0
  63. package/dist/harness/operation-trace-divergence.js.map +1 -0
  64. package/dist/harness/operation-trace.d.ts +134 -0
  65. package/dist/harness/operation-trace.d.ts.map +1 -0
  66. package/dist/harness/operation-trace.js +161 -0
  67. package/dist/harness/operation-trace.js.map +1 -0
  68. package/dist/harness/session-write-coordinator.d.ts +76 -0
  69. package/dist/harness/session-write-coordinator.d.ts.map +1 -0
  70. package/dist/harness/session-write-coordinator.js +126 -0
  71. package/dist/harness/session-write-coordinator.js.map +1 -0
  72. package/dist/harness/subscriber-fanout.d.ts +50 -0
  73. package/dist/harness/subscriber-fanout.d.ts.map +1 -0
  74. package/dist/harness/subscriber-fanout.js +92 -0
  75. package/dist/harness/subscriber-fanout.js.map +1 -0
  76. package/dist/harness/tree-navigation.d.ts +45 -0
  77. package/dist/harness/tree-navigation.d.ts.map +1 -0
  78. package/dist/harness/tree-navigation.js +59 -0
  79. package/dist/harness/tree-navigation.js.map +1 -0
  80. package/dist/harness/types.d.ts +34 -3
  81. package/dist/harness/types.d.ts.map +1 -1
  82. package/dist/harness/types.js.map +1 -1
  83. package/dist/index.d.ts +2 -0
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +2 -0
  86. package/dist/index.js.map +1 -1
  87. package/dist/listener-delivery.d.ts +22 -0
  88. package/dist/listener-delivery.d.ts.map +1 -0
  89. package/dist/listener-delivery.js +36 -0
  90. package/dist/listener-delivery.js.map +1 -0
  91. package/package.json +4 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-digest.js","sourceRoot":"","sources":["../../src/harness/canonical-digest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Browser-safe canonical serialization and SHA-256 for harness identities.\n *\n * Every digest the harness binds to an operation, attempt, effect, or trace\n * must be reproducible from the same logical value on any host, so this module\n * fixes two things and imports nothing:\n *\n * 1. `canonicalJson` — a JCS-style (RFC 8785) serialization: object keys sorted\n * by UTF-16 code unit, no whitespace, ES number formatting, `undefined`\n * properties omitted. Non-finite numbers, bigint, functions, symbols, and\n * non-plain objects are rejected instead of silently coerced, because a\n * digest over a lossy encoding is not a commitment to the value.\n * 2. `sha256Hex` — a synchronous pure-TypeScript SHA-256, so pure reducers can\n * derive identities without an async `crypto.subtle` round trip or a Node\n * `node:crypto` import that the browser entry point cannot carry.\n *\n * `domainDigest` adds domain separation: the same parts hashed under two\n * domains never collide, and a part list is length-delimited through the\n * canonical encoding so `[\"ab\",\"c\"]` and `[\"a\",\"bc\"]` differ.\n */\n\nexport function canonicalJson(value: unknown): string {\n\treturn serialize(value, new WeakSet<object>());\n}\n\nfunction serialize(value: unknown, ancestors: WeakSet<object>): string {\n\tif (value === null) return \"null\";\n\tswitch (typeof value) {\n\t\tcase \"string\":\n\t\t\treturn JSON.stringify(value);\n\t\tcase \"boolean\":\n\t\t\treturn value ? \"true\" : \"false\";\n\t\tcase \"number\":\n\t\t\tif (!Number.isFinite(value)) throw new TypeError(\"canonicalJson: non-finite numbers are not representable\");\n\t\t\treturn JSON.stringify(value);\n\t\tcase \"object\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new TypeError(`canonicalJson: unsupported value of type ${typeof value}`);\n\t}\n\tconst object = value as object;\n\tif (ancestors.has(object)) throw new TypeError(\"canonicalJson: cyclic values are not representable\");\n\tancestors.add(object);\n\ttry {\n\t\tif (Array.isArray(object)) {\n\t\t\tconst items = object.map((item) => {\n\t\t\t\tif (item === undefined)\n\t\t\t\t\tthrow new TypeError(\"canonicalJson: undefined array elements are not representable\");\n\t\t\t\treturn serialize(item, ancestors);\n\t\t\t});\n\t\t\treturn `[${items.join(\",\")}]`;\n\t\t}\n\t\tconst prototype = Object.getPrototypeOf(object);\n\t\tif (prototype !== Object.prototype && prototype !== null) {\n\t\t\tthrow new TypeError(\"canonicalJson: only plain objects are representable\");\n\t\t}\n\t\tconst record = object as Record<string, unknown>;\n\t\tconst keys = Object.keys(record)\n\t\t\t.filter((key) => record[key] !== undefined)\n\t\t\t.sort(compareCodeUnits);\n\t\tconst members = keys.map((key) => `${JSON.stringify(key)}:${serialize(record[key], ancestors)}`);\n\t\treturn `{${members.join(\",\")}}`;\n\t} finally {\n\t\tancestors.delete(object);\n\t}\n}\n\n/** RFC 8785 orders keys by UTF-16 code unit, which is what `<` gives on JS strings. */\nfunction compareCodeUnits(left: string, right: string): number {\n\treturn left < right ? -1 : left > right ? 1 : 0;\n}\n\nconst ROUND_CONSTANTS = new Uint32Array([\n\t0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,\n\t0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n\t0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,\n\t0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n\t0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,\n\t0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n\t0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,\n\t0xc67178f2,\n]);\n\nconst INITIAL_STATE = new Uint32Array([\n\t0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\nfunction rotateRight(value: number, bits: number): number {\n\treturn (value >>> bits) | (value << (32 - bits));\n}\n\n/** Pad the message per FIPS 180-4 §5.1.1: 0x80, zeros, then the 64-bit big-endian bit length. */\nfunction padMessage(bytes: Uint8Array): DataView {\n\tconst bitLength = bytes.length * 8;\n\tconst paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;\n\tconst padded = new Uint8Array(paddedLength);\n\tpadded.set(bytes);\n\tpadded[bytes.length] = 0x80;\n\tconst view = new DataView(padded.buffer);\n\tview.setUint32(paddedLength - 8, Math.floor(bitLength / 0x100000000), false);\n\tview.setUint32(paddedLength - 4, bitLength >>> 0, false);\n\treturn view;\n}\n\nfunction compressBlock(state: Uint32Array, schedule: Uint32Array, view: DataView, offset: number): void {\n\tfor (let index = 0; index < 16; index++) schedule[index] = view.getUint32(offset + index * 4, false);\n\tfor (let index = 16; index < 64; index++) {\n\t\tconst w15 = schedule[index - 15];\n\t\tconst w2 = schedule[index - 2];\n\t\tconst sigma0 = rotateRight(w15, 7) ^ rotateRight(w15, 18) ^ (w15 >>> 3);\n\t\tconst sigma1 = rotateRight(w2, 17) ^ rotateRight(w2, 19) ^ (w2 >>> 10);\n\t\tschedule[index] = (schedule[index - 16] + sigma0 + schedule[index - 7] + sigma1) >>> 0;\n\t}\n\tlet [a, b, c, d, e, f, g, h] = state;\n\tfor (let index = 0; index < 64; index++) {\n\t\tconst bigSigma1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);\n\t\tconst choose = (e & f) ^ (~e & g);\n\t\tconst temp1 = (h + bigSigma1 + choose + ROUND_CONSTANTS[index] + schedule[index]) >>> 0;\n\t\tconst bigSigma0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);\n\t\tconst majority = (a & b) ^ (a & c) ^ (b & c);\n\t\tconst temp2 = (bigSigma0 + majority) >>> 0;\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = (d + temp1) >>> 0;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = (temp1 + temp2) >>> 0;\n\t}\n\tstate[0] = (state[0] + a) >>> 0;\n\tstate[1] = (state[1] + b) >>> 0;\n\tstate[2] = (state[2] + c) >>> 0;\n\tstate[3] = (state[3] + d) >>> 0;\n\tstate[4] = (state[4] + e) >>> 0;\n\tstate[5] = (state[5] + f) >>> 0;\n\tstate[6] = (state[6] + g) >>> 0;\n\tstate[7] = (state[7] + h) >>> 0;\n}\n\n/** Lowercase hex SHA-256 of a UTF-8 string or raw bytes. Synchronous and dependency-free. */\nexport function sha256Hex(input: string | Uint8Array): string {\n\tconst bytes = typeof input === \"string\" ? new TextEncoder().encode(input) : input;\n\tconst view = padMessage(bytes);\n\tconst state = new Uint32Array(INITIAL_STATE);\n\tconst schedule = new Uint32Array(64);\n\tfor (let offset = 0; offset < view.byteLength; offset += 64) compressBlock(state, schedule, view, offset);\n\tlet hex = \"\";\n\tfor (const word of state) hex += word.toString(16).padStart(8, \"0\");\n\treturn hex;\n}\n\n/** SHA-256 of the canonical JSON encoding of `value`. */\nexport function canonicalDigest(value: unknown): string {\n\treturn sha256Hex(canonicalJson(value));\n}\n\n/**\n * Domain-separated digest over an ordered part list. The parts are committed\n * through canonical JSON, so boundaries between parts are unambiguous and a\n * different domain string always yields a different digest.\n */\nexport function domainDigest(domain: string, parts: readonly string[]): string {\n\tif (domain.length === 0) throw new TypeError(\"domainDigest: domain must be non-empty\");\n\treturn sha256Hex(canonicalJson({ domain, parts }));\n}\n"]}
@@ -2,5 +2,12 @@ export interface HarnessCompactionRunOptions {
2
2
  readonly automatic: boolean;
3
3
  readonly customInstructions?: string;
4
4
  readonly signal?: AbortSignal;
5
+ /**
6
+ * Invoked immediately before the compaction entry is appended, i.e. at the
7
+ * single declared commit point. Manual compaction uses it to enter the
8
+ * `committing` lifecycle stage; automatic compaction runs inside a prompt
9
+ * attempt and leaves it unset.
10
+ */
11
+ readonly beforeCommit?: () => void;
5
12
  }
6
13
  //# sourceMappingURL=operation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"operation.d.ts","sourceRoot":"","sources":["../../../src/harness/compaction/operation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B","sourcesContent":["export interface HarnessCompactionRunOptions {\n\treadonly automatic: boolean;\n\treadonly customInstructions?: string;\n\treadonly signal?: AbortSignal;\n}\n"]}
1
+ {"version":3,"file":"operation.d.ts","sourceRoot":"","sources":["../../../src/harness/compaction/operation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC","sourcesContent":["export interface HarnessCompactionRunOptions {\n\treadonly automatic: boolean;\n\treadonly customInstructions?: string;\n\treadonly signal?: AbortSignal;\n\t/**\n\t * Invoked immediately before the compaction entry is appended, i.e. at the\n\t * single declared commit point. Manual compaction uses it to enter the\n\t * `committing` lifecycle stage; automatic compaction runs inside a prompt\n\t * attempt and leaves it unset.\n\t */\n\treadonly beforeCommit?: () => void;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"operation.js","sourceRoot":"","sources":["../../../src/harness/compaction/operation.ts"],"names":[],"mappings":"","sourcesContent":["export interface HarnessCompactionRunOptions {\n\treadonly automatic: boolean;\n\treadonly customInstructions?: string;\n\treadonly signal?: AbortSignal;\n}\n"]}
1
+ {"version":3,"file":"operation.js","sourceRoot":"","sources":["../../../src/harness/compaction/operation.ts"],"names":[],"mappings":"","sourcesContent":["export interface HarnessCompactionRunOptions {\n\treadonly automatic: boolean;\n\treadonly customInstructions?: string;\n\treadonly signal?: AbortSignal;\n\t/**\n\t * Invoked immediately before the compaction entry is appended, i.e. at the\n\t * single declared commit point. Manual compaction uses it to enter the\n\t * `committing` lifecycle stage; automatic compaction runs inside a prompt\n\t * attempt and leaves it unset.\n\t */\n\treadonly beforeCommit?: () => void;\n}\n"]}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Deferred harness commands: the callback-safe alternative to waiting on the
3
+ * operation a callback is inside.
4
+ *
5
+ * A listener that awaits `waitForIdle()` or `abortAndWait()` for the operation
6
+ * whose emission it is blocking forms a cycle: settlement awaits the listener
7
+ * and the listener awaits settlement. `AgentHarness.runWhenIdle()` breaks that
8
+ * cycle by enqueueing the work and returning a ref without waiting, so the
9
+ * callback never depends on the operation it is inside.
10
+ *
11
+ * Commands run in registration order, only while the harness reports idle, and
12
+ * each outcome is captured in the ref instead of rejecting — a deferred command
13
+ * is nobody's caller, so an unhandled rejection would be invisible.
14
+ */
15
+ export type DeferredCommandStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
16
+ export interface DeferredHarnessCommand {
17
+ /** Short label used in the ref and in diagnostics. */
18
+ readonly name: string;
19
+ /** Runs once the harness is idle. */
20
+ run(): Promise<unknown> | unknown;
21
+ }
22
+ export interface DeferredCommandOutcome {
23
+ readonly status: "completed" | "failed" | "cancelled";
24
+ readonly value?: unknown;
25
+ readonly error?: unknown;
26
+ }
27
+ export interface CommandRef {
28
+ readonly commandId: string;
29
+ readonly name: string;
30
+ readonly status: DeferredCommandStatus;
31
+ /** Settles once the command completed, failed, or was cancelled. Never rejects. */
32
+ readonly done: Promise<DeferredCommandOutcome>;
33
+ /**
34
+ * Cancel while the command is still queued. Returns false once it has started:
35
+ * a running command owns its own lifecycle (a deferred command that begins a
36
+ * harness operation is stopped through the normal abort path instead).
37
+ */
38
+ cancel(): boolean;
39
+ }
40
+ export declare class DeferredCommandQueue {
41
+ /** Insertion-ordered, so registration order is the execution order. */
42
+ private readonly entries;
43
+ private readonly isIdle;
44
+ private nextSequence;
45
+ private draining;
46
+ constructor(isIdle: () => boolean);
47
+ get size(): number;
48
+ enqueue(command: DeferredHarnessCommand): CommandRef;
49
+ /** Run queued commands in order while the harness stays idle. Never throws. */
50
+ drain(): Promise<void>;
51
+ private cancelEntry;
52
+ }
53
+ //# sourceMappingURL=deferred-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred-commands.d.ts","sourceRoot":"","sources":["../../src/harness/deferred-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEhG,MAAM,WAAW,sBAAsB;IACtC,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/C;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC;CAClB;AAWD,qBAAa,oBAAoB;IAChC,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,MAAM,EAAE,MAAM,OAAO,EAEhC;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,UAAU,CA4BnD;IAED,+EAA+E;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAwB3B;IAED,OAAO,CAAC,WAAW;CAOnB","sourcesContent":["/**\n * Deferred harness commands: the callback-safe alternative to waiting on the\n * operation a callback is inside.\n *\n * A listener that awaits `waitForIdle()` or `abortAndWait()` for the operation\n * whose emission it is blocking forms a cycle: settlement awaits the listener\n * and the listener awaits settlement. `AgentHarness.runWhenIdle()` breaks that\n * cycle by enqueueing the work and returning a ref without waiting, so the\n * callback never depends on the operation it is inside.\n *\n * Commands run in registration order, only while the harness reports idle, and\n * each outcome is captured in the ref instead of rejecting — a deferred command\n * is nobody's caller, so an unhandled rejection would be invisible.\n */\n\nexport type DeferredCommandStatus = \"queued\" | \"running\" | \"completed\" | \"failed\" | \"cancelled\";\n\nexport interface DeferredHarnessCommand {\n\t/** Short label used in the ref and in diagnostics. */\n\treadonly name: string;\n\t/** Runs once the harness is idle. */\n\trun(): Promise<unknown> | unknown;\n}\n\nexport interface DeferredCommandOutcome {\n\treadonly status: \"completed\" | \"failed\" | \"cancelled\";\n\treadonly value?: unknown;\n\treadonly error?: unknown;\n}\n\nexport interface CommandRef {\n\treadonly commandId: string;\n\treadonly name: string;\n\treadonly status: DeferredCommandStatus;\n\t/** Settles once the command completed, failed, or was cancelled. Never rejects. */\n\treadonly done: Promise<DeferredCommandOutcome>;\n\t/**\n\t * Cancel while the command is still queued. Returns false once it has started:\n\t * a running command owns its own lifecycle (a deferred command that begins a\n\t * harness operation is stopped through the normal abort path instead).\n\t */\n\tcancel(): boolean;\n}\n\ninterface Entry {\n\treadonly commandId: string;\n\treadonly name: string;\n\treadonly command: DeferredHarnessCommand;\n\treadonly done: Promise<DeferredCommandOutcome>;\n\treadonly resolveDone: (outcome: DeferredCommandOutcome) => void;\n\tstatus: DeferredCommandStatus;\n}\n\nexport class DeferredCommandQueue {\n\t/** Insertion-ordered, so registration order is the execution order. */\n\tprivate readonly entries = new Map<string, Entry>();\n\tprivate readonly isIdle: () => boolean;\n\tprivate nextSequence = 0;\n\tprivate draining = false;\n\n\tconstructor(isIdle: () => boolean) {\n\t\tthis.isIdle = isIdle;\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\tenqueue(command: DeferredHarnessCommand): CommandRef {\n\t\tthis.nextSequence += 1;\n\t\tconst commandId = `cmd-${this.nextSequence}`;\n\t\tlet resolveDone: (outcome: DeferredCommandOutcome) => void = () => undefined;\n\t\tconst done = new Promise<DeferredCommandOutcome>((resolve) => {\n\t\t\tresolveDone = resolve;\n\t\t});\n\t\tconst entry: Entry = {\n\t\t\tcommandId,\n\t\t\tname: command.name,\n\t\t\tcommand,\n\t\t\tdone,\n\t\t\tresolveDone,\n\t\t\tstatus: \"queued\",\n\t\t};\n\t\tthis.entries.set(commandId, entry);\n\t\t// \"When idle\" includes now: an idle harness starts the command immediately,\n\t\t// while a busy one leaves it queued for the next idle transition.\n\t\tvoid this.drain();\n\t\treturn {\n\t\t\tcommandId,\n\t\t\tname: entry.name,\n\t\t\tget status() {\n\t\t\t\treturn entry.status;\n\t\t\t},\n\t\t\tdone,\n\t\t\tcancel: () => this.cancelEntry(entry),\n\t\t};\n\t}\n\n\t/** Run queued commands in order while the harness stays idle. Never throws. */\n\tasync drain(): Promise<void> {\n\t\tif (this.draining) return;\n\t\tthis.draining = true;\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\t// A command may start a new operation; the rest of the queue then waits\n\t\t\t\t// for the next idle transition instead of reentering the lifecycle.\n\t\t\t\tif (!this.isIdle()) return;\n\t\t\t\tconst entry = this.entries.values().next().value as Entry | undefined;\n\t\t\t\tif (entry === undefined) return;\n\t\t\t\tthis.entries.delete(entry.commandId);\n\t\t\t\tentry.status = \"running\";\n\t\t\t\ttry {\n\t\t\t\t\tconst value = await entry.command.run();\n\t\t\t\t\tentry.status = \"completed\";\n\t\t\t\t\tentry.resolveDone({ status: \"completed\", value });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tentry.status = \"failed\";\n\t\t\t\t\tentry.resolveDone({ status: \"failed\", error });\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.draining = false;\n\t\t}\n\t}\n\n\tprivate cancelEntry(entry: Entry): boolean {\n\t\tif (entry.status !== \"queued\") return false;\n\t\tthis.entries.delete(entry.commandId);\n\t\tentry.status = \"cancelled\";\n\t\tentry.resolveDone({ status: \"cancelled\" });\n\t\treturn true;\n\t}\n}\n"]}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Deferred harness commands: the callback-safe alternative to waiting on the
3
+ * operation a callback is inside.
4
+ *
5
+ * A listener that awaits `waitForIdle()` or `abortAndWait()` for the operation
6
+ * whose emission it is blocking forms a cycle: settlement awaits the listener
7
+ * and the listener awaits settlement. `AgentHarness.runWhenIdle()` breaks that
8
+ * cycle by enqueueing the work and returning a ref without waiting, so the
9
+ * callback never depends on the operation it is inside.
10
+ *
11
+ * Commands run in registration order, only while the harness reports idle, and
12
+ * each outcome is captured in the ref instead of rejecting — a deferred command
13
+ * is nobody's caller, so an unhandled rejection would be invisible.
14
+ */
15
+ export class DeferredCommandQueue {
16
+ /** Insertion-ordered, so registration order is the execution order. */
17
+ entries = new Map();
18
+ isIdle;
19
+ nextSequence = 0;
20
+ draining = false;
21
+ constructor(isIdle) {
22
+ this.isIdle = isIdle;
23
+ }
24
+ get size() {
25
+ return this.entries.size;
26
+ }
27
+ enqueue(command) {
28
+ this.nextSequence += 1;
29
+ const commandId = `cmd-${this.nextSequence}`;
30
+ let resolveDone = () => undefined;
31
+ const done = new Promise((resolve) => {
32
+ resolveDone = resolve;
33
+ });
34
+ const entry = {
35
+ commandId,
36
+ name: command.name,
37
+ command,
38
+ done,
39
+ resolveDone,
40
+ status: "queued",
41
+ };
42
+ this.entries.set(commandId, entry);
43
+ // "When idle" includes now: an idle harness starts the command immediately,
44
+ // while a busy one leaves it queued for the next idle transition.
45
+ void this.drain();
46
+ return {
47
+ commandId,
48
+ name: entry.name,
49
+ get status() {
50
+ return entry.status;
51
+ },
52
+ done,
53
+ cancel: () => this.cancelEntry(entry),
54
+ };
55
+ }
56
+ /** Run queued commands in order while the harness stays idle. Never throws. */
57
+ async drain() {
58
+ if (this.draining)
59
+ return;
60
+ this.draining = true;
61
+ try {
62
+ for (;;) {
63
+ // A command may start a new operation; the rest of the queue then waits
64
+ // for the next idle transition instead of reentering the lifecycle.
65
+ if (!this.isIdle())
66
+ return;
67
+ const entry = this.entries.values().next().value;
68
+ if (entry === undefined)
69
+ return;
70
+ this.entries.delete(entry.commandId);
71
+ entry.status = "running";
72
+ try {
73
+ const value = await entry.command.run();
74
+ entry.status = "completed";
75
+ entry.resolveDone({ status: "completed", value });
76
+ }
77
+ catch (error) {
78
+ entry.status = "failed";
79
+ entry.resolveDone({ status: "failed", error });
80
+ }
81
+ }
82
+ }
83
+ finally {
84
+ this.draining = false;
85
+ }
86
+ }
87
+ cancelEntry(entry) {
88
+ if (entry.status !== "queued")
89
+ return false;
90
+ this.entries.delete(entry.commandId);
91
+ entry.status = "cancelled";
92
+ entry.resolveDone({ status: "cancelled" });
93
+ return true;
94
+ }
95
+ }
96
+ //# sourceMappingURL=deferred-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred-commands.js","sourceRoot":"","sources":["../../src/harness/deferred-commands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Deferred harness commands: the callback-safe alternative to waiting on the\n * operation a callback is inside.\n *\n * A listener that awaits `waitForIdle()` or `abortAndWait()` for the operation\n * whose emission it is blocking forms a cycle: settlement awaits the listener\n * and the listener awaits settlement. `AgentHarness.runWhenIdle()` breaks that\n * cycle by enqueueing the work and returning a ref without waiting, so the\n * callback never depends on the operation it is inside.\n *\n * Commands run in registration order, only while the harness reports idle, and\n * each outcome is captured in the ref instead of rejecting — a deferred command\n * is nobody's caller, so an unhandled rejection would be invisible.\n */\n\nexport type DeferredCommandStatus = \"queued\" | \"running\" | \"completed\" | \"failed\" | \"cancelled\";\n\nexport interface DeferredHarnessCommand {\n\t/** Short label used in the ref and in diagnostics. */\n\treadonly name: string;\n\t/** Runs once the harness is idle. */\n\trun(): Promise<unknown> | unknown;\n}\n\nexport interface DeferredCommandOutcome {\n\treadonly status: \"completed\" | \"failed\" | \"cancelled\";\n\treadonly value?: unknown;\n\treadonly error?: unknown;\n}\n\nexport interface CommandRef {\n\treadonly commandId: string;\n\treadonly name: string;\n\treadonly status: DeferredCommandStatus;\n\t/** Settles once the command completed, failed, or was cancelled. Never rejects. */\n\treadonly done: Promise<DeferredCommandOutcome>;\n\t/**\n\t * Cancel while the command is still queued. Returns false once it has started:\n\t * a running command owns its own lifecycle (a deferred command that begins a\n\t * harness operation is stopped through the normal abort path instead).\n\t */\n\tcancel(): boolean;\n}\n\ninterface Entry {\n\treadonly commandId: string;\n\treadonly name: string;\n\treadonly command: DeferredHarnessCommand;\n\treadonly done: Promise<DeferredCommandOutcome>;\n\treadonly resolveDone: (outcome: DeferredCommandOutcome) => void;\n\tstatus: DeferredCommandStatus;\n}\n\nexport class DeferredCommandQueue {\n\t/** Insertion-ordered, so registration order is the execution order. */\n\tprivate readonly entries = new Map<string, Entry>();\n\tprivate readonly isIdle: () => boolean;\n\tprivate nextSequence = 0;\n\tprivate draining = false;\n\n\tconstructor(isIdle: () => boolean) {\n\t\tthis.isIdle = isIdle;\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\tenqueue(command: DeferredHarnessCommand): CommandRef {\n\t\tthis.nextSequence += 1;\n\t\tconst commandId = `cmd-${this.nextSequence}`;\n\t\tlet resolveDone: (outcome: DeferredCommandOutcome) => void = () => undefined;\n\t\tconst done = new Promise<DeferredCommandOutcome>((resolve) => {\n\t\t\tresolveDone = resolve;\n\t\t});\n\t\tconst entry: Entry = {\n\t\t\tcommandId,\n\t\t\tname: command.name,\n\t\t\tcommand,\n\t\t\tdone,\n\t\t\tresolveDone,\n\t\t\tstatus: \"queued\",\n\t\t};\n\t\tthis.entries.set(commandId, entry);\n\t\t// \"When idle\" includes now: an idle harness starts the command immediately,\n\t\t// while a busy one leaves it queued for the next idle transition.\n\t\tvoid this.drain();\n\t\treturn {\n\t\t\tcommandId,\n\t\t\tname: entry.name,\n\t\t\tget status() {\n\t\t\t\treturn entry.status;\n\t\t\t},\n\t\t\tdone,\n\t\t\tcancel: () => this.cancelEntry(entry),\n\t\t};\n\t}\n\n\t/** Run queued commands in order while the harness stays idle. Never throws. */\n\tasync drain(): Promise<void> {\n\t\tif (this.draining) return;\n\t\tthis.draining = true;\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\t// A command may start a new operation; the rest of the queue then waits\n\t\t\t\t// for the next idle transition instead of reentering the lifecycle.\n\t\t\t\tif (!this.isIdle()) return;\n\t\t\t\tconst entry = this.entries.values().next().value as Entry | undefined;\n\t\t\t\tif (entry === undefined) return;\n\t\t\t\tthis.entries.delete(entry.commandId);\n\t\t\t\tentry.status = \"running\";\n\t\t\t\ttry {\n\t\t\t\t\tconst value = await entry.command.run();\n\t\t\t\t\tentry.status = \"completed\";\n\t\t\t\t\tentry.resolveDone({ status: \"completed\", value });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tentry.status = \"failed\";\n\t\t\t\t\tentry.resolveDone({ status: \"failed\", error });\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.draining = false;\n\t\t}\n\t}\n\n\tprivate cancelEntry(entry: Entry): boolean {\n\t\tif (entry.status !== \"queued\") return false;\n\t\tthis.entries.delete(entry.commandId);\n\t\tentry.status = \"cancelled\";\n\t\tentry.resolveDone({ status: \"cancelled\" });\n\t\treturn true;\n\t}\n}\n"]}
@@ -1,53 +1,6 @@
1
1
  import type { ImageContent, TextContent } from "omk-ai";
2
- type FacadeWrite<TMessage> = {
3
- readonly type: "message";
4
- readonly message: TMessage;
5
- } | {
6
- readonly type: "custom";
7
- readonly customType: string;
8
- readonly data?: unknown;
9
- } | {
10
- readonly type: "custom_message";
11
- readonly customType: string;
12
- readonly content: string | (TextContent | ImageContent)[];
13
- readonly details?: unknown;
14
- readonly display: boolean;
15
- } | {
16
- readonly type: "label";
17
- readonly targetId: string;
18
- readonly label: string | undefined;
19
- } | {
20
- readonly type: "session_info";
21
- readonly name?: string;
22
- } | {
23
- readonly type: "leaf";
24
- readonly targetId: string | null;
25
- };
26
- type PendingWrite<TMessage> = FacadeWrite<TMessage> | {
27
- readonly type: "thinking_level_change";
28
- readonly thinkingLevel: string;
29
- } | {
30
- readonly type: "model_change";
31
- readonly provider: string;
32
- readonly modelId: string;
33
- } | {
34
- readonly type: "active_tools_change";
35
- readonly activeToolNames: string[];
36
- } | {
37
- readonly type: "compaction";
38
- readonly summary: string;
39
- readonly firstKeptEntryId: string;
40
- readonly tokensBefore: number;
41
- readonly details?: unknown;
42
- readonly fromHook?: boolean;
43
- } | {
44
- readonly type: "branch_summary";
45
- readonly fromId: string;
46
- readonly summary: string;
47
- readonly details?: unknown;
48
- readonly fromHook?: boolean;
49
- };
50
- interface SessionPort<TMessage, TEntry, TContext, TMetadata> {
2
+ import type { QueuedSessionWrite, SessionWriteCoordinator } from "./session-write-coordinator.ts";
3
+ interface SessionPort<TEntry, TContext, TMetadata> {
51
4
  getMetadata(): Promise<TMetadata>;
52
5
  getLeafId(): Promise<string | null>;
53
6
  getEntry(id: string): Promise<TEntry | undefined>;
@@ -56,12 +9,6 @@ interface SessionPort<TMessage, TEntry, TContext, TMetadata> {
56
9
  buildContext(): Promise<TContext>;
57
10
  getLabel(id: string): Promise<string | undefined>;
58
11
  getSessionName(): Promise<string | undefined>;
59
- appendMessage(message: TMessage): Promise<string>;
60
- appendCustomEntry(customType: string, data?: unknown): Promise<string>;
61
- appendCustomMessageEntry(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details?: unknown): Promise<string>;
62
- appendLabel(targetId: string, label: string | undefined): Promise<string>;
63
- appendSessionName(name: string): Promise<string>;
64
- moveTo(entryId: string | null): Promise<string | undefined>;
65
12
  }
66
13
  interface CustomMessageInput<T = unknown> {
67
14
  readonly customType: string;
@@ -80,8 +27,8 @@ interface CustomMessageInput<T = unknown> {
80
27
  export declare class HarnessSessionFacade<TMessage, TEntry, TContext, TMetadata> {
81
28
  private readonly session;
82
29
  private readonly getPhase;
83
- private readonly pendingWrites;
84
- constructor(session: SessionPort<TMessage, TEntry, TContext, TMetadata>, getPhase: () => string, pendingWrites: PendingWrite<TMessage>[]);
30
+ private readonly sessionWrites;
31
+ constructor(session: SessionPort<TEntry, TContext, TMetadata>, getPhase: () => string, sessionWrites: SessionWriteCoordinator<TMessage>);
85
32
  getMetadata(): Promise<Readonly<TMetadata>>;
86
33
  getLeafId(): Promise<string | null>;
87
34
  getEntry(id: string): Promise<Readonly<TEntry> | undefined>;
@@ -90,7 +37,7 @@ export declare class HarnessSessionFacade<TMessage, TEntry, TContext, TMetadata>
90
37
  buildContext(): Promise<Readonly<TContext>>;
91
38
  getLabel(id: string): Promise<string | undefined>;
92
39
  getSessionName(): Promise<string | undefined>;
93
- getPendingWrites(): readonly PendingWrite<TMessage>[];
40
+ getPendingWrites(): readonly QueuedSessionWrite<TMessage>[];
94
41
  appendMessage(message: TMessage): Promise<void>;
95
42
  appendCustomEntry(customType: string, data?: unknown): Promise<void>;
96
43
  appendCustomMessage<T = unknown>(input: CustomMessageInput<T>): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../../src/harness/harness-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAIxD,KAAK,WAAW,CAAC,QAAQ,IACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GACjF;IACA,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzF;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE/D,KAAK,YAAY,CAAC,QAAQ,IACvB,WAAW,CAAC,QAAQ,CAAC,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtF;IAAE,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5E;IACA,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC3B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEL,UAAU,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS;IAC1D,WAAW,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAClD,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAClD,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9C,aAAa,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,wBAAwB,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,EAChD,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1E,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5D;AAED,UAAU,kBAAkB,CAAC,CAAC,GAAG,OAAO;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACnE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqD;IAC7E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IAEzD,YACC,OAAO,EAAE,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,EAC3D,QAAQ,EAAE,MAAM,MAAM,EACtB,aAAa,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,EAKvC;IAEK,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAEhD;IAEK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExC;IAEK,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAEhE;IAEK,UAAU,IAAI,OAAO,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAEvD;IAEK,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAErE;IAEK,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAEhD;IAEK,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAEtD;IAEK,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAElD;IAED,gBAAgB,IAAI,SAAS,YAAY,CAAC,QAAQ,CAAC,EAAE,CAEpD;IAEK,aAAa,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpD;IAEK,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzE;IAEK,mBAAmB,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlF;IAEK,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5E;IAEK,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnD;IAEK,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAMtD;YAEa,IAAI;IAQlB,OAAO,CAAC,QAAQ;YAQF,KAAK;CAenB","sourcesContent":["import type { ImageContent, TextContent } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"../plain-data.ts\";\nimport { AgentHarnessError, toError } from \"./errors.ts\";\n\ntype FacadeWrite<TMessage> =\n\t| { readonly type: \"message\"; readonly message: TMessage }\n\t| { readonly type: \"custom\"; readonly customType: string; readonly data?: unknown }\n\t| {\n\t\t\treadonly type: \"custom_message\";\n\t\t\treadonly customType: string;\n\t\t\treadonly content: string | (TextContent | ImageContent)[];\n\t\t\treadonly details?: unknown;\n\t\t\treadonly display: boolean;\n\t }\n\t| { readonly type: \"label\"; readonly targetId: string; readonly label: string | undefined }\n\t| { readonly type: \"session_info\"; readonly name?: string }\n\t| { readonly type: \"leaf\"; readonly targetId: string | null };\n\ntype PendingWrite<TMessage> =\n\t| FacadeWrite<TMessage>\n\t| { readonly type: \"thinking_level_change\"; readonly thinkingLevel: string }\n\t| { readonly type: \"model_change\"; readonly provider: string; readonly modelId: string }\n\t| { readonly type: \"active_tools_change\"; readonly activeToolNames: string[] }\n\t| {\n\t\t\treadonly type: \"compaction\";\n\t\t\treadonly summary: string;\n\t\t\treadonly firstKeptEntryId: string;\n\t\t\treadonly tokensBefore: number;\n\t\t\treadonly details?: unknown;\n\t\t\treadonly fromHook?: boolean;\n\t }\n\t| {\n\t\t\treadonly type: \"branch_summary\";\n\t\t\treadonly fromId: string;\n\t\t\treadonly summary: string;\n\t\t\treadonly details?: unknown;\n\t\t\treadonly fromHook?: boolean;\n\t };\n\ninterface SessionPort<TMessage, TEntry, TContext, TMetadata> {\n\tgetMetadata(): Promise<TMetadata>;\n\tgetLeafId(): Promise<string | null>;\n\tgetEntry(id: string): Promise<TEntry | undefined>;\n\tgetEntries(): Promise<TEntry[]>;\n\tgetBranch(fromId?: string): Promise<TEntry[]>;\n\tbuildContext(): Promise<TContext>;\n\tgetLabel(id: string): Promise<string | undefined>;\n\tgetSessionName(): Promise<string | undefined>;\n\tappendMessage(message: TMessage): Promise<string>;\n\tappendCustomEntry(customType: string, data?: unknown): Promise<string>;\n\tappendCustomMessageEntry(\n\t\tcustomType: string,\n\t\tcontent: string | (TextContent | ImageContent)[],\n\t\tdisplay: boolean,\n\t\tdetails?: unknown,\n\t): Promise<string>;\n\tappendLabel(targetId: string, label: string | undefined): Promise<string>;\n\tappendSessionName(name: string): Promise<string>;\n\tmoveTo(entryId: string | null): Promise<string | undefined>;\n}\n\ninterface CustomMessageInput<T = unknown> {\n\treadonly customType: string;\n\treadonly content: string | readonly (TextContent | ImageContent)[];\n\treadonly display: boolean;\n\treadonly details?: T;\n}\n\n/**\n * Internal implementation of the public `HarnessSession` structural interface.\n *\n * This module intentionally imports no harness/session types. `AgentHarness` and the\n * raw session are in a legacy import-cycle component; importing their types here would\n * pull this new boundary into that component because the cycle ratchet treats static\n * type imports as architectural edges. Generic ports keep the implementation a leaf.\n */\nexport class HarnessSessionFacade<TMessage, TEntry, TContext, TMetadata> {\n\tprivate readonly session: SessionPort<TMessage, TEntry, TContext, TMetadata>;\n\tprivate readonly getPhase: () => string;\n\tprivate readonly pendingWrites: PendingWrite<TMessage>[];\n\n\tconstructor(\n\t\tsession: SessionPort<TMessage, TEntry, TContext, TMetadata>,\n\t\tgetPhase: () => string,\n\t\tpendingWrites: PendingWrite<TMessage>[],\n\t) {\n\t\tthis.session = session;\n\t\tthis.getPhase = getPhase;\n\t\tthis.pendingWrites = pendingWrites;\n\t}\n\n\tasync getMetadata(): Promise<Readonly<TMetadata>> {\n\t\treturn this.read(() => this.session.getMetadata());\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\treturn this.read(() => this.session.getLeafId());\n\t}\n\n\tasync getEntry(id: string): Promise<Readonly<TEntry> | undefined> {\n\t\treturn this.read(() => this.session.getEntry(id));\n\t}\n\n\tasync getEntries(): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getEntries());\n\t}\n\n\tasync getBranch(fromId?: string): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getBranch(fromId));\n\t}\n\n\tasync buildContext(): Promise<Readonly<TContext>> {\n\t\treturn this.read(() => this.session.buildContext());\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getLabel(id));\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getSessionName());\n\t}\n\n\tgetPendingWrites(): readonly PendingWrite<TMessage>[] {\n\t\treturn createImmutableSnapshot(this.pendingWrites);\n\t}\n\n\tasync appendMessage(message: TMessage): Promise<void> {\n\t\tconst write = this.snapshot({ type: \"message\", message });\n\t\tawait this.write(write, () => this.session.appendMessage(write.message));\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<void> {\n\t\tconst write = this.snapshot({ type: \"custom\", customType, data });\n\t\tawait this.write(write, () => this.session.appendCustomEntry(write.customType, write.data));\n\t}\n\n\tasync appendCustomMessage<T = unknown>(input: CustomMessageInput<T>): Promise<void> {\n\t\tconst write = this.snapshot({\n\t\t\ttype: \"custom_message\",\n\t\t\tcustomType: input.customType,\n\t\t\tcontent: typeof input.content === \"string\" ? input.content : [...input.content],\n\t\t\tdisplay: input.display,\n\t\t\tdetails: input.details,\n\t\t});\n\t\tawait this.write(write, () =>\n\t\t\tthis.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details),\n\t\t);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<void> {\n\t\tif (!(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tconst write = this.snapshot({ type: \"label\", targetId, label });\n\t\tawait this.write(write, () => this.session.appendLabel(write.targetId, write.label));\n\t}\n\n\tasync appendSessionName(name: string): Promise<void> {\n\t\tconst write = this.snapshot({ type: \"session_info\", name: name.trim() });\n\t\tawait this.write(write, () => this.session.appendSessionName(write.name ?? \"\"));\n\t}\n\n\tasync setLeafId(targetId: string | null): Promise<void> {\n\t\tif (targetId !== null && !(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tconst write = this.snapshot({ type: \"leaf\", targetId });\n\t\tawait this.write(write, () => this.session.moveTo(write.targetId));\n\t}\n\n\tprivate async read<T>(operation: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(await operation());\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session read failed\", toError(error));\n\t\t}\n\t}\n\n\tprivate snapshot<TWrite extends FacadeWrite<TMessage>>(write: TWrite): TWrite {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(write);\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", \"Session writes must contain plain data\", toError(error));\n\t\t}\n\t}\n\n\tprivate async write(write: FacadeWrite<TMessage>, persist: () => Promise<unknown>): Promise<void> {\n\t\tconst phase = this.getPhase();\n\t\tif (phase === \"turn\") {\n\t\t\tthis.pendingWrites.push(write);\n\t\t\treturn;\n\t\t}\n\t\tif (phase !== \"idle\") {\n\t\t\tthrow new AgentHarnessError(\"invalid_state\", `Cannot write session state during ${phase}`);\n\t\t}\n\t\ttry {\n\t\t\tawait persist();\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session write failed\", toError(error));\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"harness-session.d.ts","sourceRoot":"","sources":["../../src/harness/harness-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"../plain-data.ts\";\nimport { AgentHarnessError, toError } from \"./errors.ts\";\nimport type { QueuedSessionWrite, SessionWriteCoordinator } from \"./session-write-coordinator.ts\";\n\ntype FacadeWrite<TMessage> =\n\t| { readonly type: \"message\"; readonly message: TMessage }\n\t| { readonly type: \"custom\"; readonly customType: string; readonly data?: unknown }\n\t| {\n\t\t\treadonly type: \"custom_message\";\n\t\t\treadonly customType: string;\n\t\t\treadonly content: string | (TextContent | ImageContent)[];\n\t\t\treadonly details?: unknown;\n\t\t\treadonly display: boolean;\n\t }\n\t| { readonly type: \"label\"; readonly targetId: string; readonly label: string | undefined }\n\t| { readonly type: \"session_info\"; readonly name?: string }\n\t| { readonly type: \"leaf\"; readonly targetId: string | null };\n\ninterface SessionPort<TEntry, TContext, TMetadata> {\n\tgetMetadata(): Promise<TMetadata>;\n\tgetLeafId(): Promise<string | null>;\n\tgetEntry(id: string): Promise<TEntry | undefined>;\n\tgetEntries(): Promise<TEntry[]>;\n\tgetBranch(fromId?: string): Promise<TEntry[]>;\n\tbuildContext(): Promise<TContext>;\n\tgetLabel(id: string): Promise<string | undefined>;\n\tgetSessionName(): Promise<string | undefined>;\n}\n\ninterface CustomMessageInput<T = unknown> {\n\treadonly customType: string;\n\treadonly content: string | readonly (TextContent | ImageContent)[];\n\treadonly display: boolean;\n\treadonly details?: T;\n}\n\n/**\n * Internal implementation of the public `HarnessSession` structural interface.\n *\n * This module intentionally imports no harness/session types. `AgentHarness` and the\n * raw session are in a legacy import-cycle component; importing their types here would\n * pull this new boundary into that component because the cycle ratchet treats static\n * type imports as architectural edges. Generic ports keep the implementation a leaf.\n */\nexport class HarnessSessionFacade<TMessage, TEntry, TContext, TMetadata> {\n\tprivate readonly session: SessionPort<TEntry, TContext, TMetadata>;\n\tprivate readonly getPhase: () => string;\n\tprivate readonly sessionWrites: SessionWriteCoordinator<TMessage>;\n\n\tconstructor(\n\t\tsession: SessionPort<TEntry, TContext, TMetadata>,\n\t\tgetPhase: () => string,\n\t\tsessionWrites: SessionWriteCoordinator<TMessage>,\n\t) {\n\t\tthis.session = session;\n\t\tthis.getPhase = getPhase;\n\t\tthis.sessionWrites = sessionWrites;\n\t}\n\n\tasync getMetadata(): Promise<Readonly<TMetadata>> {\n\t\treturn this.read(() => this.session.getMetadata());\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\treturn this.read(() => this.session.getLeafId());\n\t}\n\n\tasync getEntry(id: string): Promise<Readonly<TEntry> | undefined> {\n\t\treturn this.read(() => this.session.getEntry(id));\n\t}\n\n\tasync getEntries(): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getEntries());\n\t}\n\n\tasync getBranch(fromId?: string): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getBranch(fromId));\n\t}\n\n\tasync buildContext(): Promise<Readonly<TContext>> {\n\t\treturn this.read(() => this.session.buildContext());\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getLabel(id));\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getSessionName());\n\t}\n\n\tgetPendingWrites(): readonly QueuedSessionWrite<TMessage>[] {\n\t\treturn this.sessionWrites.snapshot();\n\t}\n\n\tasync appendMessage(message: TMessage): Promise<void> {\n\t\tawait this.write(this.snapshot({ type: \"message\", message }));\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<void> {\n\t\tawait this.write(this.snapshot({ type: \"custom\", customType, data }));\n\t}\n\n\tasync appendCustomMessage<T = unknown>(input: CustomMessageInput<T>): Promise<void> {\n\t\tconst write = this.snapshot({\n\t\t\ttype: \"custom_message\",\n\t\t\tcustomType: input.customType,\n\t\t\tcontent: typeof input.content === \"string\" ? input.content : [...input.content],\n\t\t\tdisplay: input.display,\n\t\t\tdetails: input.details,\n\t\t});\n\t\tawait this.write(write);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<void> {\n\t\tif (!(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tawait this.write(this.snapshot({ type: \"label\", targetId, label }));\n\t}\n\n\tasync appendSessionName(name: string): Promise<void> {\n\t\tawait this.write(this.snapshot({ type: \"session_info\", name: name.trim() }));\n\t}\n\n\tasync setLeafId(targetId: string | null): Promise<void> {\n\t\tif (targetId !== null && !(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tawait this.write(this.snapshot({ type: \"leaf\", targetId }));\n\t}\n\n\tprivate async read<T>(operation: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(await operation());\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session read failed\", toError(error));\n\t\t}\n\t}\n\n\tprivate snapshot<TWrite extends FacadeWrite<TMessage>>(write: TWrite): TWrite {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(write);\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", \"Session writes must contain plain data\", toError(error));\n\t\t}\n\t}\n\n\tprivate async write(write: FacadeWrite<TMessage>): Promise<void> {\n\t\tconst phase = this.getPhase();\n\t\tif (phase === \"turn\") {\n\t\t\tthis.sessionWrites.enqueue(write);\n\t\t\treturn;\n\t\t}\n\t\tif (phase !== \"idle\") {\n\t\t\tthrow new AgentHarnessError(\"invalid_state\", `Cannot write session state during ${phase}`);\n\t\t}\n\t\ttry {\n\t\t\tawait this.sessionWrites.persistAfterPending(write);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AgentHarnessError && error.code === \"invalid_state\") throw error;\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session write failed\", toError(error));\n\t\t}\n\t}\n}\n"]}
@@ -11,11 +11,11 @@ import { AgentHarnessError, toError } from "./errors.js";
11
11
  export class HarnessSessionFacade {
12
12
  session;
13
13
  getPhase;
14
- pendingWrites;
15
- constructor(session, getPhase, pendingWrites) {
14
+ sessionWrites;
15
+ constructor(session, getPhase, sessionWrites) {
16
16
  this.session = session;
17
17
  this.getPhase = getPhase;
18
- this.pendingWrites = pendingWrites;
18
+ this.sessionWrites = sessionWrites;
19
19
  }
20
20
  async getMetadata() {
21
21
  return this.read(() => this.session.getMetadata());
@@ -42,15 +42,13 @@ export class HarnessSessionFacade {
42
42
  return this.read(() => this.session.getSessionName());
43
43
  }
44
44
  getPendingWrites() {
45
- return createImmutableSnapshot(this.pendingWrites);
45
+ return this.sessionWrites.snapshot();
46
46
  }
47
47
  async appendMessage(message) {
48
- const write = this.snapshot({ type: "message", message });
49
- await this.write(write, () => this.session.appendMessage(write.message));
48
+ await this.write(this.snapshot({ type: "message", message }));
50
49
  }
51
50
  async appendCustomEntry(customType, data) {
52
- const write = this.snapshot({ type: "custom", customType, data });
53
- await this.write(write, () => this.session.appendCustomEntry(write.customType, write.data));
51
+ await this.write(this.snapshot({ type: "custom", customType, data }));
54
52
  }
55
53
  async appendCustomMessage(input) {
56
54
  const write = this.snapshot({
@@ -60,25 +58,22 @@ export class HarnessSessionFacade {
60
58
  display: input.display,
61
59
  details: input.details,
62
60
  });
63
- await this.write(write, () => this.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details));
61
+ await this.write(write);
64
62
  }
65
63
  async appendLabel(targetId, label) {
66
64
  if (!(await this.getEntry(targetId))) {
67
65
  throw new AgentHarnessError("invalid_argument", `Entry ${targetId} not found`);
68
66
  }
69
- const write = this.snapshot({ type: "label", targetId, label });
70
- await this.write(write, () => this.session.appendLabel(write.targetId, write.label));
67
+ await this.write(this.snapshot({ type: "label", targetId, label }));
71
68
  }
72
69
  async appendSessionName(name) {
73
- const write = this.snapshot({ type: "session_info", name: name.trim() });
74
- await this.write(write, () => this.session.appendSessionName(write.name ?? ""));
70
+ await this.write(this.snapshot({ type: "session_info", name: name.trim() }));
75
71
  }
76
72
  async setLeafId(targetId) {
77
73
  if (targetId !== null && !(await this.getEntry(targetId))) {
78
74
  throw new AgentHarnessError("invalid_argument", `Entry ${targetId} not found`);
79
75
  }
80
- const write = this.snapshot({ type: "leaf", targetId });
81
- await this.write(write, () => this.session.moveTo(write.targetId));
76
+ await this.write(this.snapshot({ type: "leaf", targetId }));
82
77
  }
83
78
  async read(operation) {
84
79
  try {
@@ -96,19 +91,21 @@ export class HarnessSessionFacade {
96
91
  throw new AgentHarnessError("invalid_argument", "Session writes must contain plain data", toError(error));
97
92
  }
98
93
  }
99
- async write(write, persist) {
94
+ async write(write) {
100
95
  const phase = this.getPhase();
101
96
  if (phase === "turn") {
102
- this.pendingWrites.push(write);
97
+ this.sessionWrites.enqueue(write);
103
98
  return;
104
99
  }
105
100
  if (phase !== "idle") {
106
101
  throw new AgentHarnessError("invalid_state", `Cannot write session state during ${phase}`);
107
102
  }
108
103
  try {
109
- await persist();
104
+ await this.sessionWrites.persistAfterPending(write);
110
105
  }
111
106
  catch (error) {
107
+ if (error instanceof AgentHarnessError && error.code === "invalid_state")
108
+ throw error;
112
109
  throw new AgentHarnessError("session", "Session write failed", toError(error));
113
110
  }
114
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"harness-session.js","sourceRoot":"","sources":["../../src/harness/harness-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"../plain-data.ts\";\nimport { AgentHarnessError, toError } from \"./errors.ts\";\n\ntype FacadeWrite<TMessage> =\n\t| { readonly type: \"message\"; readonly message: TMessage }\n\t| { readonly type: \"custom\"; readonly customType: string; readonly data?: unknown }\n\t| {\n\t\t\treadonly type: \"custom_message\";\n\t\t\treadonly customType: string;\n\t\t\treadonly content: string | (TextContent | ImageContent)[];\n\t\t\treadonly details?: unknown;\n\t\t\treadonly display: boolean;\n\t }\n\t| { readonly type: \"label\"; readonly targetId: string; readonly label: string | undefined }\n\t| { readonly type: \"session_info\"; readonly name?: string }\n\t| { readonly type: \"leaf\"; readonly targetId: string | null };\n\ntype PendingWrite<TMessage> =\n\t| FacadeWrite<TMessage>\n\t| { readonly type: \"thinking_level_change\"; readonly thinkingLevel: string }\n\t| { readonly type: \"model_change\"; readonly provider: string; readonly modelId: string }\n\t| { readonly type: \"active_tools_change\"; readonly activeToolNames: string[] }\n\t| {\n\t\t\treadonly type: \"compaction\";\n\t\t\treadonly summary: string;\n\t\t\treadonly firstKeptEntryId: string;\n\t\t\treadonly tokensBefore: number;\n\t\t\treadonly details?: unknown;\n\t\t\treadonly fromHook?: boolean;\n\t }\n\t| {\n\t\t\treadonly type: \"branch_summary\";\n\t\t\treadonly fromId: string;\n\t\t\treadonly summary: string;\n\t\t\treadonly details?: unknown;\n\t\t\treadonly fromHook?: boolean;\n\t };\n\ninterface SessionPort<TMessage, TEntry, TContext, TMetadata> {\n\tgetMetadata(): Promise<TMetadata>;\n\tgetLeafId(): Promise<string | null>;\n\tgetEntry(id: string): Promise<TEntry | undefined>;\n\tgetEntries(): Promise<TEntry[]>;\n\tgetBranch(fromId?: string): Promise<TEntry[]>;\n\tbuildContext(): Promise<TContext>;\n\tgetLabel(id: string): Promise<string | undefined>;\n\tgetSessionName(): Promise<string | undefined>;\n\tappendMessage(message: TMessage): Promise<string>;\n\tappendCustomEntry(customType: string, data?: unknown): Promise<string>;\n\tappendCustomMessageEntry(\n\t\tcustomType: string,\n\t\tcontent: string | (TextContent | ImageContent)[],\n\t\tdisplay: boolean,\n\t\tdetails?: unknown,\n\t): Promise<string>;\n\tappendLabel(targetId: string, label: string | undefined): Promise<string>;\n\tappendSessionName(name: string): Promise<string>;\n\tmoveTo(entryId: string | null): Promise<string | undefined>;\n}\n\ninterface CustomMessageInput<T = unknown> {\n\treadonly customType: string;\n\treadonly content: string | readonly (TextContent | ImageContent)[];\n\treadonly display: boolean;\n\treadonly details?: T;\n}\n\n/**\n * Internal implementation of the public `HarnessSession` structural interface.\n *\n * This module intentionally imports no harness/session types. `AgentHarness` and the\n * raw session are in a legacy import-cycle component; importing their types here would\n * pull this new boundary into that component because the cycle ratchet treats static\n * type imports as architectural edges. Generic ports keep the implementation a leaf.\n */\nexport class HarnessSessionFacade<TMessage, TEntry, TContext, TMetadata> {\n\tprivate readonly session: SessionPort<TMessage, TEntry, TContext, TMetadata>;\n\tprivate readonly getPhase: () => string;\n\tprivate readonly pendingWrites: PendingWrite<TMessage>[];\n\n\tconstructor(\n\t\tsession: SessionPort<TMessage, TEntry, TContext, TMetadata>,\n\t\tgetPhase: () => string,\n\t\tpendingWrites: PendingWrite<TMessage>[],\n\t) {\n\t\tthis.session = session;\n\t\tthis.getPhase = getPhase;\n\t\tthis.pendingWrites = pendingWrites;\n\t}\n\n\tasync getMetadata(): Promise<Readonly<TMetadata>> {\n\t\treturn this.read(() => this.session.getMetadata());\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\treturn this.read(() => this.session.getLeafId());\n\t}\n\n\tasync getEntry(id: string): Promise<Readonly<TEntry> | undefined> {\n\t\treturn this.read(() => this.session.getEntry(id));\n\t}\n\n\tasync getEntries(): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getEntries());\n\t}\n\n\tasync getBranch(fromId?: string): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getBranch(fromId));\n\t}\n\n\tasync buildContext(): Promise<Readonly<TContext>> {\n\t\treturn this.read(() => this.session.buildContext());\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getLabel(id));\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getSessionName());\n\t}\n\n\tgetPendingWrites(): readonly PendingWrite<TMessage>[] {\n\t\treturn createImmutableSnapshot(this.pendingWrites);\n\t}\n\n\tasync appendMessage(message: TMessage): Promise<void> {\n\t\tconst write = this.snapshot({ type: \"message\", message });\n\t\tawait this.write(write, () => this.session.appendMessage(write.message));\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<void> {\n\t\tconst write = this.snapshot({ type: \"custom\", customType, data });\n\t\tawait this.write(write, () => this.session.appendCustomEntry(write.customType, write.data));\n\t}\n\n\tasync appendCustomMessage<T = unknown>(input: CustomMessageInput<T>): Promise<void> {\n\t\tconst write = this.snapshot({\n\t\t\ttype: \"custom_message\",\n\t\t\tcustomType: input.customType,\n\t\t\tcontent: typeof input.content === \"string\" ? input.content : [...input.content],\n\t\t\tdisplay: input.display,\n\t\t\tdetails: input.details,\n\t\t});\n\t\tawait this.write(write, () =>\n\t\t\tthis.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details),\n\t\t);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<void> {\n\t\tif (!(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tconst write = this.snapshot({ type: \"label\", targetId, label });\n\t\tawait this.write(write, () => this.session.appendLabel(write.targetId, write.label));\n\t}\n\n\tasync appendSessionName(name: string): Promise<void> {\n\t\tconst write = this.snapshot({ type: \"session_info\", name: name.trim() });\n\t\tawait this.write(write, () => this.session.appendSessionName(write.name ?? \"\"));\n\t}\n\n\tasync setLeafId(targetId: string | null): Promise<void> {\n\t\tif (targetId !== null && !(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tconst write = this.snapshot({ type: \"leaf\", targetId });\n\t\tawait this.write(write, () => this.session.moveTo(write.targetId));\n\t}\n\n\tprivate async read<T>(operation: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(await operation());\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session read failed\", toError(error));\n\t\t}\n\t}\n\n\tprivate snapshot<TWrite extends FacadeWrite<TMessage>>(write: TWrite): TWrite {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(write);\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", \"Session writes must contain plain data\", toError(error));\n\t\t}\n\t}\n\n\tprivate async write(write: FacadeWrite<TMessage>, persist: () => Promise<unknown>): Promise<void> {\n\t\tconst phase = this.getPhase();\n\t\tif (phase === \"turn\") {\n\t\t\tthis.pendingWrites.push(write);\n\t\t\treturn;\n\t\t}\n\t\tif (phase !== \"idle\") {\n\t\t\tthrow new AgentHarnessError(\"invalid_state\", `Cannot write session state during ${phase}`);\n\t\t}\n\t\ttry {\n\t\t\tawait persist();\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session write failed\", toError(error));\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"harness-session.js","sourceRoot":"","sources":["../../src/harness/harness-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"omk-ai\";\nimport { createImmutableSnapshot } from \"../plain-data.ts\";\nimport { AgentHarnessError, toError } from \"./errors.ts\";\nimport type { QueuedSessionWrite, SessionWriteCoordinator } from \"./session-write-coordinator.ts\";\n\ntype FacadeWrite<TMessage> =\n\t| { readonly type: \"message\"; readonly message: TMessage }\n\t| { readonly type: \"custom\"; readonly customType: string; readonly data?: unknown }\n\t| {\n\t\t\treadonly type: \"custom_message\";\n\t\t\treadonly customType: string;\n\t\t\treadonly content: string | (TextContent | ImageContent)[];\n\t\t\treadonly details?: unknown;\n\t\t\treadonly display: boolean;\n\t }\n\t| { readonly type: \"label\"; readonly targetId: string; readonly label: string | undefined }\n\t| { readonly type: \"session_info\"; readonly name?: string }\n\t| { readonly type: \"leaf\"; readonly targetId: string | null };\n\ninterface SessionPort<TEntry, TContext, TMetadata> {\n\tgetMetadata(): Promise<TMetadata>;\n\tgetLeafId(): Promise<string | null>;\n\tgetEntry(id: string): Promise<TEntry | undefined>;\n\tgetEntries(): Promise<TEntry[]>;\n\tgetBranch(fromId?: string): Promise<TEntry[]>;\n\tbuildContext(): Promise<TContext>;\n\tgetLabel(id: string): Promise<string | undefined>;\n\tgetSessionName(): Promise<string | undefined>;\n}\n\ninterface CustomMessageInput<T = unknown> {\n\treadonly customType: string;\n\treadonly content: string | readonly (TextContent | ImageContent)[];\n\treadonly display: boolean;\n\treadonly details?: T;\n}\n\n/**\n * Internal implementation of the public `HarnessSession` structural interface.\n *\n * This module intentionally imports no harness/session types. `AgentHarness` and the\n * raw session are in a legacy import-cycle component; importing their types here would\n * pull this new boundary into that component because the cycle ratchet treats static\n * type imports as architectural edges. Generic ports keep the implementation a leaf.\n */\nexport class HarnessSessionFacade<TMessage, TEntry, TContext, TMetadata> {\n\tprivate readonly session: SessionPort<TEntry, TContext, TMetadata>;\n\tprivate readonly getPhase: () => string;\n\tprivate readonly sessionWrites: SessionWriteCoordinator<TMessage>;\n\n\tconstructor(\n\t\tsession: SessionPort<TEntry, TContext, TMetadata>,\n\t\tgetPhase: () => string,\n\t\tsessionWrites: SessionWriteCoordinator<TMessage>,\n\t) {\n\t\tthis.session = session;\n\t\tthis.getPhase = getPhase;\n\t\tthis.sessionWrites = sessionWrites;\n\t}\n\n\tasync getMetadata(): Promise<Readonly<TMetadata>> {\n\t\treturn this.read(() => this.session.getMetadata());\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\treturn this.read(() => this.session.getLeafId());\n\t}\n\n\tasync getEntry(id: string): Promise<Readonly<TEntry> | undefined> {\n\t\treturn this.read(() => this.session.getEntry(id));\n\t}\n\n\tasync getEntries(): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getEntries());\n\t}\n\n\tasync getBranch(fromId?: string): Promise<readonly Readonly<TEntry>[]> {\n\t\treturn this.read(() => this.session.getBranch(fromId));\n\t}\n\n\tasync buildContext(): Promise<Readonly<TContext>> {\n\t\treturn this.read(() => this.session.buildContext());\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getLabel(id));\n\t}\n\n\tasync getSessionName(): Promise<string | undefined> {\n\t\treturn this.read(() => this.session.getSessionName());\n\t}\n\n\tgetPendingWrites(): readonly QueuedSessionWrite<TMessage>[] {\n\t\treturn this.sessionWrites.snapshot();\n\t}\n\n\tasync appendMessage(message: TMessage): Promise<void> {\n\t\tawait this.write(this.snapshot({ type: \"message\", message }));\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<void> {\n\t\tawait this.write(this.snapshot({ type: \"custom\", customType, data }));\n\t}\n\n\tasync appendCustomMessage<T = unknown>(input: CustomMessageInput<T>): Promise<void> {\n\t\tconst write = this.snapshot({\n\t\t\ttype: \"custom_message\",\n\t\t\tcustomType: input.customType,\n\t\t\tcontent: typeof input.content === \"string\" ? input.content : [...input.content],\n\t\t\tdisplay: input.display,\n\t\t\tdetails: input.details,\n\t\t});\n\t\tawait this.write(write);\n\t}\n\n\tasync appendLabel(targetId: string, label: string | undefined): Promise<void> {\n\t\tif (!(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tawait this.write(this.snapshot({ type: \"label\", targetId, label }));\n\t}\n\n\tasync appendSessionName(name: string): Promise<void> {\n\t\tawait this.write(this.snapshot({ type: \"session_info\", name: name.trim() }));\n\t}\n\n\tasync setLeafId(targetId: string | null): Promise<void> {\n\t\tif (targetId !== null && !(await this.getEntry(targetId))) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", `Entry ${targetId} not found`);\n\t\t}\n\t\tawait this.write(this.snapshot({ type: \"leaf\", targetId }));\n\t}\n\n\tprivate async read<T>(operation: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(await operation());\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session read failed\", toError(error));\n\t\t}\n\t}\n\n\tprivate snapshot<TWrite extends FacadeWrite<TMessage>>(write: TWrite): TWrite {\n\t\ttry {\n\t\t\treturn createImmutableSnapshot(write);\n\t\t} catch (error) {\n\t\t\tthrow new AgentHarnessError(\"invalid_argument\", \"Session writes must contain plain data\", toError(error));\n\t\t}\n\t}\n\n\tprivate async write(write: FacadeWrite<TMessage>): Promise<void> {\n\t\tconst phase = this.getPhase();\n\t\tif (phase === \"turn\") {\n\t\t\tthis.sessionWrites.enqueue(write);\n\t\t\treturn;\n\t\t}\n\t\tif (phase !== \"idle\") {\n\t\t\tthrow new AgentHarnessError(\"invalid_state\", `Cannot write session state during ${phase}`);\n\t\t}\n\t\ttry {\n\t\t\tawait this.sessionWrites.persistAfterPending(write);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AgentHarnessError && error.code === \"invalid_state\") throw error;\n\t\t\tthrow new AgentHarnessError(\"session\", \"Session write failed\", toError(error));\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Owns the single mutable copy of the harness lifecycle state.
3
+ *
4
+ * The controller is the only writer: it builds commands, runs them through
5
+ * the pure reducer, and performs the side effects the reducer is forbidden
6
+ * from touching — clock reads, ID allocation, abort-signal delivery, and
7
+ * settlement of the operation's `settled` promise.
8
+ *
9
+ * Lease discipline: every mutating method requires the `OperationLease`
10
+ * returned by `begin`. A lease that is not the current record fails closed,
11
+ * so a stale async continuation from a finished operation can never mutate
12
+ * the state of a newer one. `settle()` is the only path from `active` to
13
+ * `idle`, runs the caller's finalizer inside the `settling` barrier, and is
14
+ * guarded against double invocation at both controller and reducer level.
15
+ *
16
+ * The `settled` promise resolves with the recorded outcome and never
17
+ * rejects; finalizer failures surface only through the `settle()` rejection,
18
+ * so callers cannot strand an unhandled rejection by ignoring `lease.settled`.
19
+ */
20
+ import type { HarnessAttemptOutcome, HarnessAttemptRef, HarnessAttemptSummary, HarnessLifecycleCommand, HarnessLifecycleDependencies, HarnessLifecycleState, HarnessOperationKind, HarnessOperationOutcome, HarnessOperationRef } from "./operation-lifecycle-types.ts";
21
+ export interface OperationLease {
22
+ readonly operation: HarnessOperationRef;
23
+ readonly signal: AbortSignal;
24
+ /** Resolves once with the recorded outcome when the operation finishes settling. Never rejects. */
25
+ readonly settled: Promise<HarnessOperationOutcome>;
26
+ }
27
+ export interface AttemptLease {
28
+ readonly attempt: HarnessAttemptRef;
29
+ /** The owning operation's signal. Attempts never own a separate AbortController. */
30
+ readonly signal: AbortSignal;
31
+ }
32
+ export interface HarnessAbortCapture {
33
+ readonly target?: OperationLease;
34
+ readonly signalDelivered: boolean;
35
+ }
36
+ export declare class OperationLifecycleController {
37
+ private readonly deps;
38
+ private state;
39
+ private current?;
40
+ private readonly idleWaiters;
41
+ constructor(deps: HarnessLifecycleDependencies);
42
+ getSnapshot(): Readonly<HarnessLifecycleState>;
43
+ getCurrentOperation(): HarnessOperationRef | undefined;
44
+ begin(kind: HarnessOperationKind): OperationLease;
45
+ setStage(lease: OperationLease, stage: Extract<HarnessLifecycleCommand, {
46
+ type: "stage";
47
+ }>["stage"]): void;
48
+ beginAttempt(lease: OperationLease, reason: HarnessAttemptRef["reason"]): AttemptLease;
49
+ finishAttempt(lease: OperationLease, attempt: AttemptLease, outcome: HarnessAttemptOutcome): void;
50
+ /** Captures the current operation as the abort target; never targets operations started later. */
51
+ requestAbort(): HarnessAbortCapture;
52
+ /**
53
+ * Sole active -> idle path. Runs `finalize` inside the settling barrier, then
54
+ * releases state and resolves the lease. A finalizer failure still completes
55
+ * the state release and resolves `lease.settled`, then rejects this call.
56
+ */
57
+ settle(lease: OperationLease, outcome: HarnessOperationOutcome, finalize: () => Promise<void>): Promise<HarnessOperationOutcome>;
58
+ /** Bounded attempt summaries for the current operation; no transcripts. */
59
+ getAttemptSummaries(lease: OperationLease): readonly HarnessAttemptSummary[];
60
+ /** The recorded summary for one attempt of the current operation. */
61
+ getAttemptSummary(lease: OperationLease, attempt: AttemptLease): HarnessAttemptSummary;
62
+ waitForIdle(): Promise<void>;
63
+ private expectActive;
64
+ private expectCurrentLease;
65
+ private apply;
66
+ private asHarnessError;
67
+ }
68
+ //# sourceMappingURL=operation-lifecycle-controller.d.ts.map