@code-yeongyu/senpi 2026.11.2-1 → 2026.11.2-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/bundle/chunks/{chunk-AMCDEC5A.js → chunk-B2LRAXQU.js} +2 -2
  3. package/dist/bundle/chunks/{chunk-D22YF34L.js → chunk-CQ3G57HE.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-ZUVGFXCS.js → chunk-FVYWSBX3.js} +3 -3
  5. package/dist/bundle/chunks/{chunk-665UPAKG.js → chunk-H3BTMIH5.js} +6 -6
  6. package/dist/bundle/chunks/{chunk-KEJWNCLN.js → chunk-HUSQY3ID.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-IBV4OVXQ.js → chunk-N6OHYIJR.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-XZR4J6HM.js → chunk-PP52OWBB.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-XVQISKHL.js → chunk-QV2IEA3B.js} +4 -4
  10. package/dist/bundle/chunks/{chunk-2V6BR24J.js → chunk-VEWJSPOZ.js} +1 -1
  11. package/dist/bundle/chunks/{cli-main-REFQSP6S.js → cli-main-6ZI4QO2Z.js} +1 -1
  12. package/dist/bundle/chunks/{host-command-Y3ZT6YLD.js → host-command-O3ALG2O3.js} +1 -1
  13. package/dist/bundle/chunks/{host-lifecycle-VW73K4WA.js → host-lifecycle-SGTVODHB.js} +1 -1
  14. package/dist/bundle/chunks/host-lifecycle.js +5 -5
  15. package/dist/bundle/chunks/{host-session-command-YDEUZBTX.js → host-session-command-4WRR5JSB.js} +1 -1
  16. package/dist/bundle/chunks/{interactive-mode-UZXPNJ4V.js → interactive-mode-TF7R35GK.js} +1 -1
  17. package/dist/bundle/chunks/{main-O5G75MYS.js → main-XLUYSOXP.js} +1 -1
  18. package/dist/bundle/chunks/{multi-session-host-AZKJYA2X.js → multi-session-host-CADYC2BS.js} +4 -3
  19. package/dist/bundle/chunks/{package-manager-cli-2YDUGJNR.js → package-manager-cli-I62KMIWK.js} +1 -1
  20. package/dist/bundle/chunks/{rpc-mode-KZD5HZNI.js → rpc-mode-T5GVMUBQ.js} +1 -1
  21. package/dist/bundle/chunks/{schedule-command-7LSJAINZ.js → schedule-command-YXGRN5CM.js} +1 -1
  22. package/dist/bundle/chunks/{session-control-endpoint-BG2KQ6MJ.js → session-control-endpoint-PREVS5UO.js} +1 -1
  23. package/dist/bundle/chunks/session-worker.js +63 -62
  24. package/dist/bundle/cli.js +2 -2
  25. package/dist/bundle/host-supervisor.js +5 -5
  26. package/dist/bundle/index.js +1 -1
  27. package/dist/bundle/rpc-entry.js +1 -1
  28. package/dist/bundle/runtime-manifest.json +1 -1
  29. package/dist/core/session-recovery-identity.d.ts +12 -1
  30. package/dist/modes/rpc/compact-call-fold.d.ts +3 -1
  31. package/dist/modes/rpc/compact-call-fold.js +17 -6
  32. package/dist/modes/rpc/compact-call-identity.d.ts +13 -0
  33. package/dist/modes/rpc/compact-call-identity.js +30 -0
  34. package/dist/modes/rpc/compact-call-model.d.ts +14 -0
  35. package/dist/modes/rpc/compact-call-model.js +4 -0
  36. package/dist/modes/rpc/compact-call-table.d.ts +9 -0
  37. package/dist/modes/rpc/compact-call-table.js +20 -0
  38. package/dist/modes/rpc/compact-catalog-settled.d.ts +5 -0
  39. package/dist/modes/rpc/compact-catalog-settled.js +9 -2
  40. package/dist/modes/rpc/compact-lane-current.d.ts +2 -0
  41. package/dist/modes/rpc/compact-lane-current.js +4 -0
  42. package/dist/modes/rpc/compact-message-identity.d.ts +74 -0
  43. package/dist/modes/rpc/compact-message-identity.js +101 -0
  44. package/dist/modes/rpc/compact-session-calls.d.ts +5 -4
  45. package/dist/modes/rpc/compact-session-calls.js +13 -8
  46. package/dist/modes/rpc/compact-session-state.d.ts +2 -0
  47. package/dist/modes/rpc/compact-session-state.js +4 -0
  48. package/dist/modes/rpc/compact-settled-messages.d.ts +12 -4
  49. package/dist/modes/rpc/compact-settled-messages.js +18 -10
  50. package/dist/modes/rpc/connection-handler.js +1 -1
  51. package/dist/modes/rpc/connection-question-bridge.d.ts +2 -1
  52. package/dist/modes/rpc/connection-question-bridge.js +3 -1
  53. package/dist/modes/rpc/host-lifecycle-proxy.d.ts +9 -0
  54. package/dist/modes/rpc/host-lifecycle-proxy.js +29 -7
  55. package/dist/modes/rpc/rpc-question-types.d.ts +2 -0
  56. package/dist/modes/rpc/session-acceptance-compact.d.ts +17 -7
  57. package/dist/modes/rpc/session-acceptance-compact.js +20 -8
  58. package/dist/modes/rpc/session-acceptance-lane.d.ts +5 -0
  59. package/dist/modes/rpc/session-acceptance-lane.js +12 -11
  60. package/dist/modes/rpc/session-event-writer-destinations.d.ts +2 -1
  61. package/dist/modes/rpc/session-event-writer-destinations.js +24 -9
  62. package/dist/utils/version-check.js +9 -3
  63. package/docs/engine-socket/fixtures/admission-routes/i-live-identity.json +716 -0
  64. package/docs/engine-socket/fixtures/admission-routes/index.json +3 -1
  65. package/docs/engine-socket/fixtures/admission-routes/j-live-call-identity.json +1020 -0
  66. package/docs/rpc.md +66 -0
  67. package/package.json +6 -6
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
3
- import"./chunks/chunk-QQPZQZAV.js";import{main}from"./chunks/chunk-665UPAKG.js";import"./chunks/chunk-2BTB7AWD.js";import"./chunks/chunk-KEJWNCLN.js";import"./chunks/chunk-AMCDEC5A.js";import"./chunks/chunk-5TG7AOLR.js";import"./chunks/chunk-VRKA3LIT.js";import"./chunks/chunk-C5G5PONA.js";import"./chunks/chunk-OG2JODU6.js";import"./chunks/chunk-HECKLNOO.js";import{configureHttpDispatcher}from"./chunks/chunk-FEQDJBDL.js";import"./chunks/chunk-B255QWLT.js";import"./chunks/chunk-ARNSNIED.js";import"./chunks/chunk-L5OFGHDA.js";import"./chunks/chunk-UURGXV5B.js";import"./chunks/chunk-NEOL3GWO.js";import"./chunks/chunk-J2OBN7TI.js";import"./chunks/chunk-Y4MPEIY5.js";import"./chunks/chunk-ZJSGYBBD.js";import"./chunks/chunk-FOMJ73UU.js";import"./chunks/chunk-VICVLJNU.js";import"./chunks/chunk-PIAY636U.js";import"./chunks/chunk-LGO25HPL.js";import"./chunks/chunk-Y3TMJV3J.js";import"./chunks/chunk-ETCYVVVO.js";import"./chunks/chunk-7ICGN62G.js";import"./chunks/chunk-IBV4OVXQ.js";import"./chunks/chunk-VOSHKGXG.js";import"./chunks/chunk-WEOFT5KT.js";import"./chunks/chunk-QMRJYMNH.js";import"./chunks/chunk-7DWTKE4Z.js";import"./chunks/chunk-ZUVGFXCS.js";import"./chunks/chunk-6LDUM5CL.js";import"./chunks/chunk-FNBEICAA.js";import"./chunks/chunk-SGS76663.js";import"./chunks/chunk-H4QOEVS5.js";import"./chunks/chunk-3CKVTGWG.js";import"./chunks/chunk-PE3PNVKT.js";import"./chunks/chunk-5UVTDRIP.js";import"./chunks/chunk-ZNPH5BWS.js";import"./chunks/chunk-6VWIMHMH.js";import"./chunks/chunk-RQDLTDNO.js";import"./chunks/chunk-KLAX467B.js";import"./chunks/chunk-SAZ6E74O.js";import"./chunks/chunk-F7GBSQRV.js";import"./chunks/chunk-GZVCRV7I.js";import"./chunks/chunk-KUPQO7MC.js";import"./chunks/chunk-U3VOXCGP.js";import"./chunks/chunk-SOMFLLPD.js";import"./chunks/chunk-PZSDJIXD.js";import"./chunks/chunk-26UBFF6N.js";import"./chunks/chunk-MTQQ2VQH.js";import"./chunks/chunk-WEGTCUO3.js";import"./chunks/chunk-GDPWQJDQ.js";import"./chunks/chunk-E6UCZ7E5.js";import"./chunks/chunk-UYO5Z35O.js";import"./chunks/chunk-H52J72VA.js";import"./chunks/chunk-HB35RBP4.js";import"./chunks/chunk-7X72IACK.js";import"./chunks/chunk-YWD54RHU.js";import"./chunks/chunk-C5XX36EB.js";import"./chunks/chunk-IYL5E4QQ.js";import"./chunks/chunk-OEFCY46F.js";import"./chunks/chunk-KRNXUTZJ.js";import"./chunks/chunk-AC72EWI5.js";import"./chunks/chunk-RQFD6LG2.js";import"./chunks/chunk-RPAEB2Z5.js";import"./chunks/chunk-D2ROBVZ4.js";import"./chunks/chunk-CLZLV65G.js";import"./chunks/chunk-YU4AYH4C.js";import"./chunks/chunk-N4TSAHRL.js";import"./chunks/chunk-LNAZ7XEU.js";import"./chunks/chunk-DLAY7CIU.js";import"./chunks/chunk-KKVVSE6C.js";import"./chunks/chunk-COH5F7QV.js";import"./chunks/chunk-RFB5CBW7.js";import"./chunks/chunk-Y7QPP7BG.js";import"./chunks/chunk-4WWORAQH.js";import"./chunks/chunk-GVSBCJFI.js";import"./chunks/chunk-QTS2XGPQ.js";import"./chunks/chunk-TG4E7Z7W.js";import"./chunks/chunk-32R442XT.js";import"./chunks/chunk-WHPDEDCC.js";import"./chunks/chunk-7UC4W2T2.js";import"./chunks/chunk-HK46JMSC.js";import"./chunks/chunk-WBTKUCRH.js";import"./chunks/chunk-WDTJ6JKE.js";import"./chunks/chunk-5ROMKDLK.js";import"./chunks/chunk-3W47LTXQ.js";import"./chunks/chunk-3BJFH3CZ.js";import"./chunks/chunk-3SASTB6Y.js";import"./chunks/chunk-5CSZPC55.js";import"./chunks/chunk-PJ34QVL5.js";import"./chunks/chunk-BMX2U22F.js";import"./chunks/chunk-YU2P3CAR.js";import"./chunks/chunk-XZR4J6HM.js";import"./chunks/chunk-D22YF34L.js";import"./chunks/chunk-ZOFZRJSL.js";import"./chunks/chunk-ZFIC2DZV.js";import"./chunks/chunk-6WISUXVI.js";import"./chunks/chunk-XVRRNJQD.js";import"./chunks/chunk-42XRJQM6.js";import"./chunks/chunk-2SOVDETB.js";import"./chunks/chunk-UKSFF47T.js";import"./chunks/chunk-OM7LH7AP.js";import"./chunks/chunk-QEESHRTS.js";import{APP_NAME,scrubBrandFromEnvironment}from"./chunks/chunk-ISZ4EWIS.js";import"./chunks/chunk-ATQFN5HI.js";import"./chunks/chunk-EYSV6A7S.js";import"./chunks/chunk-MIENF2YK.js";import"./chunks/chunk-OXNWERO4.js";import"./chunks/chunk-QN2BHP3M.js";import"./chunks/chunk-AOTHPTMU.js";import"./chunks/chunk-O5RYI3IW.js";import"./chunks/chunk-IKOFCODM.js";process.title=`${APP_NAME}-rpc`;scrubBrandFromEnvironment();process.env.PI_CODING_AGENT="true";process.env.AI_AGENT=APP_NAME;process.emitWarning=(()=>{});configureHttpDispatcher();main(["--mode","rpc",...process.argv.slice(2)]);
3
+ import"./chunks/chunk-QQPZQZAV.js";import{main}from"./chunks/chunk-H3BTMIH5.js";import"./chunks/chunk-2BTB7AWD.js";import"./chunks/chunk-HUSQY3ID.js";import"./chunks/chunk-B2LRAXQU.js";import"./chunks/chunk-5TG7AOLR.js";import"./chunks/chunk-VRKA3LIT.js";import"./chunks/chunk-C5G5PONA.js";import"./chunks/chunk-OG2JODU6.js";import"./chunks/chunk-HECKLNOO.js";import{configureHttpDispatcher}from"./chunks/chunk-FEQDJBDL.js";import"./chunks/chunk-B255QWLT.js";import"./chunks/chunk-ARNSNIED.js";import"./chunks/chunk-L5OFGHDA.js";import"./chunks/chunk-UURGXV5B.js";import"./chunks/chunk-NEOL3GWO.js";import"./chunks/chunk-J2OBN7TI.js";import"./chunks/chunk-Y4MPEIY5.js";import"./chunks/chunk-ZJSGYBBD.js";import"./chunks/chunk-FOMJ73UU.js";import"./chunks/chunk-VICVLJNU.js";import"./chunks/chunk-PIAY636U.js";import"./chunks/chunk-LGO25HPL.js";import"./chunks/chunk-Y3TMJV3J.js";import"./chunks/chunk-ETCYVVVO.js";import"./chunks/chunk-7ICGN62G.js";import"./chunks/chunk-N6OHYIJR.js";import"./chunks/chunk-VOSHKGXG.js";import"./chunks/chunk-WEOFT5KT.js";import"./chunks/chunk-QMRJYMNH.js";import"./chunks/chunk-7DWTKE4Z.js";import"./chunks/chunk-FVYWSBX3.js";import"./chunks/chunk-6LDUM5CL.js";import"./chunks/chunk-FNBEICAA.js";import"./chunks/chunk-SGS76663.js";import"./chunks/chunk-H4QOEVS5.js";import"./chunks/chunk-3CKVTGWG.js";import"./chunks/chunk-PE3PNVKT.js";import"./chunks/chunk-5UVTDRIP.js";import"./chunks/chunk-ZNPH5BWS.js";import"./chunks/chunk-6VWIMHMH.js";import"./chunks/chunk-RQDLTDNO.js";import"./chunks/chunk-KLAX467B.js";import"./chunks/chunk-SAZ6E74O.js";import"./chunks/chunk-F7GBSQRV.js";import"./chunks/chunk-GZVCRV7I.js";import"./chunks/chunk-KUPQO7MC.js";import"./chunks/chunk-U3VOXCGP.js";import"./chunks/chunk-SOMFLLPD.js";import"./chunks/chunk-PZSDJIXD.js";import"./chunks/chunk-26UBFF6N.js";import"./chunks/chunk-MTQQ2VQH.js";import"./chunks/chunk-WEGTCUO3.js";import"./chunks/chunk-GDPWQJDQ.js";import"./chunks/chunk-E6UCZ7E5.js";import"./chunks/chunk-UYO5Z35O.js";import"./chunks/chunk-H52J72VA.js";import"./chunks/chunk-HB35RBP4.js";import"./chunks/chunk-7X72IACK.js";import"./chunks/chunk-YWD54RHU.js";import"./chunks/chunk-C5XX36EB.js";import"./chunks/chunk-IYL5E4QQ.js";import"./chunks/chunk-OEFCY46F.js";import"./chunks/chunk-KRNXUTZJ.js";import"./chunks/chunk-AC72EWI5.js";import"./chunks/chunk-RQFD6LG2.js";import"./chunks/chunk-RPAEB2Z5.js";import"./chunks/chunk-D2ROBVZ4.js";import"./chunks/chunk-CLZLV65G.js";import"./chunks/chunk-YU4AYH4C.js";import"./chunks/chunk-N4TSAHRL.js";import"./chunks/chunk-LNAZ7XEU.js";import"./chunks/chunk-DLAY7CIU.js";import"./chunks/chunk-KKVVSE6C.js";import"./chunks/chunk-COH5F7QV.js";import"./chunks/chunk-RFB5CBW7.js";import"./chunks/chunk-Y7QPP7BG.js";import"./chunks/chunk-4WWORAQH.js";import"./chunks/chunk-GVSBCJFI.js";import"./chunks/chunk-QTS2XGPQ.js";import"./chunks/chunk-TG4E7Z7W.js";import"./chunks/chunk-32R442XT.js";import"./chunks/chunk-WHPDEDCC.js";import"./chunks/chunk-7UC4W2T2.js";import"./chunks/chunk-HK46JMSC.js";import"./chunks/chunk-WBTKUCRH.js";import"./chunks/chunk-WDTJ6JKE.js";import"./chunks/chunk-5ROMKDLK.js";import"./chunks/chunk-3W47LTXQ.js";import"./chunks/chunk-3BJFH3CZ.js";import"./chunks/chunk-3SASTB6Y.js";import"./chunks/chunk-5CSZPC55.js";import"./chunks/chunk-PJ34QVL5.js";import"./chunks/chunk-BMX2U22F.js";import"./chunks/chunk-YU2P3CAR.js";import"./chunks/chunk-PP52OWBB.js";import"./chunks/chunk-CQ3G57HE.js";import"./chunks/chunk-ZOFZRJSL.js";import"./chunks/chunk-ZFIC2DZV.js";import"./chunks/chunk-6WISUXVI.js";import"./chunks/chunk-XVRRNJQD.js";import"./chunks/chunk-42XRJQM6.js";import"./chunks/chunk-2SOVDETB.js";import"./chunks/chunk-UKSFF47T.js";import"./chunks/chunk-OM7LH7AP.js";import"./chunks/chunk-QEESHRTS.js";import{APP_NAME,scrubBrandFromEnvironment}from"./chunks/chunk-ISZ4EWIS.js";import"./chunks/chunk-ATQFN5HI.js";import"./chunks/chunk-EYSV6A7S.js";import"./chunks/chunk-MIENF2YK.js";import"./chunks/chunk-OXNWERO4.js";import"./chunks/chunk-QN2BHP3M.js";import"./chunks/chunk-AOTHPTMU.js";import"./chunks/chunk-O5RYI3IW.js";import"./chunks/chunk-IKOFCODM.js";process.title=`${APP_NAME}-rpc`;scrubBrandFromEnvironment();process.env.PI_CODING_AGENT="true";process.env.AI_AGENT=APP_NAME;process.emitWarning=(()=>{});configureHttpDispatcher();main(["--mode","rpc",...process.argv.slice(2)]);
@@ -1 +1 @@
1
- {"buildId":"29c0244c28dfb182","externals":["@earendil-works/chord","@earendil-works/pi-pty","@silvia-odwyer/photon-node","bufferutil","canvas","kerberos","supports-color","utf-8-validate"]}
1
+ {"buildId":"b333b4398db12fde","externals":["@earendil-works/chord","@earendil-works/pi-pty","@silvia-odwyer/photon-node","bufferutil","canvas","kerberos","supports-color","utf-8-validate"]}
@@ -1,4 +1,15 @@
1
- /** The recovery identity of one session record; never a property on the record, so no byte changes (R3.C1.2). */
1
+ /**
2
+ * The recovery identity of one session record. It is never a property on the record a session produces, so every
3
+ * legacy record, and every record with the compact-updates flag off, keeps its bytes (R3.C1.2). One exception
4
+ * (RULING-120, senpi#5331): with the flag on, the RPC host stamps the copy it sends a `compact_events` peer of a live
5
+ * `message_end` (`messageId`, `terminalSequence`), `entry_appended` (`owningMessageId`) and `transcript_write_failed`
6
+ * (`messageId`) with the ids the settled store recorded for that message, so that peer can join the live record to its
7
+ * settled `message-<terminalSequence>` and `join-<n>` rows across a reconnect or recovery cut. The ids are opaque,
8
+ * stable for the message's lifetime and never reused in the session; nothing else carries them. The same copy of a live
9
+ * call record (`toolcall_*` updates, `tool_execution_*`, `tool_hook_status`) carries the `runtimeItemKey` of the call
10
+ * the host changed with it, or `callUnknown: true` when it changed none (RULING-123, senpi#5346): that key comes from
11
+ * the calls component (`compact-call-model.ts`), not from this carrier.
12
+ */
2
13
  export interface RecoveryCarrier {
3
14
  readonly messageId: string;
4
15
  /** Host-issued nonce, internal to the producer/lane handshake; never serialized with a public record. */
@@ -1,11 +1,13 @@
1
1
  import type { ActiveBlock } from "./compact-active-block.ts";
2
- import type { CallProjection, CallScope } from "./compact-call-model.ts";
2
+ import { type CallIdentity, type CallProjection, type CallScope } from "./compact-call-model.ts";
3
3
  import type { CallStep, CompactSessionCalls } from "./compact-session-calls.ts";
4
4
  import type { SeededMessage } from "./compact-seeded-messages.ts";
5
5
  import type { CompactTransition } from "./compact-transition.ts";
6
6
  export interface CallFold {
7
7
  readonly changed: boolean;
8
8
  readonly projection?: CallProjection;
9
+ /** An execution or hook record's call (RULING-123): the one it changed, or `callUnknown` when it changed none. */
10
+ readonly identity?: CallIdentity;
9
11
  }
10
12
  type SeededBlockTransition = Extract<CompactTransition, {
11
13
  type: "seeded_block";
@@ -1,3 +1,5 @@
1
+ import { CALL_NAMED_RECORDS } from "./compact-call-identity.js";
2
+ import { CALL_UNKNOWN, callIdentity, } from "./compact-call-model.js";
1
3
  const field = (record, key) => Reflect.get(record, key);
2
4
  const text = (record, key) => String(field(record, key) ?? "");
3
5
  const ownedRaw = (block) => block ? { kind: "owned", root: block.root, tailRoot: block.tailRoot } : null;
@@ -47,10 +49,12 @@ export function foldCallRecord(calls, record, scope, sequence) {
47
49
  const step = { sequence, scope };
48
50
  const before = calls.table.counters.replacements;
49
51
  let projection;
50
- switch (field(record, "type")) {
52
+ let call;
53
+ const type = field(record, "type");
54
+ switch (type) {
51
55
  case "tool_execution_start": {
52
56
  const parentToolCallId = field(record, "parentToolCallId");
53
- calls.executionStart(step, {
57
+ call = calls.executionStart(step, {
54
58
  toolCallId: text(record, "toolCallId"),
55
59
  toolName: text(record, "toolName"),
56
60
  args: field(record, "args") ?? null,
@@ -59,13 +63,13 @@ export function foldCallRecord(calls, record, scope, sequence) {
59
63
  break;
60
64
  }
61
65
  case "tool_execution_update":
62
- calls.executionUpdate(step, text(record, "toolCallId"), field(record, "args") ?? null, field(record, "partialResult") ?? null);
66
+ call = calls.executionUpdate(step, text(record, "toolCallId"), field(record, "args") ?? null, field(record, "partialResult") ?? null);
63
67
  break;
64
68
  case "tool_execution_end":
65
- calls.executionEnd(step, text(record, "toolCallId"), field(record, "result") ?? null, field(record, "isError") === true);
69
+ call = calls.executionEnd(step, text(record, "toolCallId"), field(record, "result") ?? null, field(record, "isError") === true);
66
70
  break;
67
71
  case "tool_hook_status":
68
- calls.hook(step, text(record, "toolCallId"), record);
72
+ call = calls.hook(step, text(record, "toolCallId"), record);
69
73
  break;
70
74
  case "turn_end":
71
75
  calls.turnEnd(step);
@@ -80,6 +84,13 @@ export function foldCallRecord(calls, record, scope, sequence) {
80
84
  break;
81
85
  }
82
86
  }
83
- return { changed: calls.table.counters.replacements !== before, ...(projection ? { projection } : {}) };
87
+ const named = CALL_NAMED_RECORDS.has(String(type));
88
+ // C3.4: a call sharing its toolCallId inside its message was picked by order, which a stamp never vouches for.
89
+ const identity = call && calls.table.sharesIdInMessage(call) ? CALL_UNKNOWN : callIdentity(call);
90
+ return {
91
+ changed: calls.table.counters.replacements !== before,
92
+ ...(projection ? { projection } : {}),
93
+ ...(named ? { identity } : {}),
94
+ };
84
95
  }
85
96
  //# sourceMappingURL=compact-call-fold.js.map
@@ -0,0 +1,13 @@
1
+ import { type CallIdentity } from "./compact-call-model.ts";
2
+ /** RULING-123 (#5346): the execution and hook records whose compact copy names the call they belong to. */
3
+ export declare const CALL_NAMED_RECORDS: ReadonlySet<string>;
4
+ /** The content index of the block a `toolcall_*` update streams; undefined for every other record. */
5
+ export declare function toolcallIndex(record: object): number | undefined;
6
+ /**
7
+ * What a compact peer's copy of `record` says when the lane holds no identity for it. A live call record the host
8
+ * changed no call with (ignored as unknown or late, refused at the protocol depth limit, or arriving while the compact
9
+ * state is unavailable) says `callUnknown` rather than omitting the key (RULING-123 (7), RULING-122's fail-safe rule);
10
+ * every other record says nothing.
11
+ */
12
+ export declare function unvouchedCall(record: object): CallIdentity | undefined;
13
+ //# sourceMappingURL=compact-call-identity.d.ts.map
@@ -0,0 +1,30 @@
1
+ import { CALL_UNKNOWN } from "./compact-call-model.js";
2
+ /** RULING-123 (#5346): the execution and hook records whose compact copy names the call they belong to. */
3
+ export const CALL_NAMED_RECORDS = new Set([
4
+ "tool_execution_start",
5
+ "tool_execution_update",
6
+ "tool_execution_end",
7
+ "tool_hook_status",
8
+ ]);
9
+ const streamEvent = (record) => Object(Reflect.get(record, "assistantMessageEvent"));
10
+ /** A `toolcall_start`, `toolcall_delta` or `toolcall_end` update: it streams one call's block of its message. */
11
+ function streamsCall(record) {
12
+ const kind = Reflect.get(streamEvent(record), "type");
13
+ return Reflect.get(record, "type") === "message_update" && typeof kind === "string" && kind.startsWith("toolcall_");
14
+ }
15
+ /** The content index of the block a `toolcall_*` update streams; undefined for every other record. */
16
+ export function toolcallIndex(record) {
17
+ const index = Reflect.get(streamEvent(record), "contentIndex");
18
+ return streamsCall(record) && typeof index === "number" ? index : undefined;
19
+ }
20
+ /**
21
+ * What a compact peer's copy of `record` says when the lane holds no identity for it. A live call record the host
22
+ * changed no call with (ignored as unknown or late, refused at the protocol depth limit, or arriving while the compact
23
+ * state is unavailable) says `callUnknown` rather than omitting the key (RULING-123 (7), RULING-122's fail-safe rule);
24
+ * every other record says nothing.
25
+ */
26
+ export function unvouchedCall(record) {
27
+ const named = CALL_NAMED_RECORDS.has(String(Reflect.get(record, "type")));
28
+ return named || streamsCall(record) ? CALL_UNKNOWN : undefined;
29
+ }
30
+ //# sourceMappingURL=compact-call-identity.js.map
@@ -110,6 +110,20 @@ export declare const CALL_RECORDS: ReadonlySet<string>;
110
110
  /** Records whose only lane effect is a call transition: ignored, they take no sequence (RULING-13 Q9). */
111
111
  export declare const CALL_ONLY_RECORDS: ReadonlySet<string>;
112
112
  export declare function runtimeItemKey(scope: CallScope, creationSequence: number, k: number): string;
113
+ /**
114
+ * RULING-123 (#5346): the call a compact peer's copy of a live call record names. `runtimeItemKey` is the key
115
+ * `runtimeItemKey` above minted when the outstanding call the record changed was created, the key that call's snapshot
116
+ * `calls` entry and its settled projection carry: opaque, fixed for the call's lifetime, and never reused in the session
117
+ * (it embeds the epoch and the creation sequence, so it alone is unique, even when providers reuse a `toolCallId`).
118
+ * `callUnknown`: the host changed no call with this record, so it cannot vouch for one (RULING-122's fail-safe rule).
119
+ */
120
+ export type CallIdentity = {
121
+ readonly runtimeItemKey: string;
122
+ } | {
123
+ readonly callUnknown: true;
124
+ };
125
+ export declare const CALL_UNKNOWN: CallIdentity;
126
+ export declare function callIdentity(call: OutstandingCall | undefined): CallIdentity;
113
127
  /** Fixed-width tree ordinal so the root orders calls by creation, independent of the epoch string. */
114
128
  export declare function callOrder(creationSequence: number, k: number): string;
115
129
  export declare function callLeaves(call: OutstandingCall): readonly LeafId[];
@@ -19,6 +19,10 @@ export const CALL_ONLY_RECORDS = new Set([
19
19
  export function runtimeItemKey(scope, creationSequence, k) {
20
20
  return `${scope.sessionEpoch}:${creationSequence}.${k}`;
21
21
  }
22
+ export const CALL_UNKNOWN = Object.freeze({ callUnknown: true });
23
+ export function callIdentity(call) {
24
+ return call ? { runtimeItemKey: call.runtimeItemKey } : CALL_UNKNOWN;
25
+ }
22
26
  /** Fixed-width tree ordinal so the root orders calls by creation, independent of the epoch string. */
23
27
  export function callOrder(creationSequence, k) {
24
28
  return `${String(creationSequence).padStart(16, "0")}.${String(k).padStart(4, "0")}`;
@@ -20,6 +20,7 @@ export declare class CompactCallTable {
20
20
  private readonly byMessage;
21
21
  private readonly byId;
22
22
  private readonly children;
23
+ private readonly shared;
23
24
  private slotBytes;
24
25
  private headerBytes;
25
26
  readonly counters: {
@@ -41,6 +42,12 @@ export declare class CompactCallTable {
41
42
  * a call still executing, each execution end closes, and each carrier settles, the call it belongs to.
42
43
  */
43
44
  oldestById(toolCallId: string, turnId: string | null, open: (call: OutstandingCall) => boolean): OutstandingCall | undefined;
45
+ /**
46
+ * RULING-123, C3.4: whether another call of `call`'s own message took the same toolCallId. An execution or hook
47
+ * record names only the toolCallId, so it cannot say which of those calls it is about. The mark lasts while any call
48
+ * of that message and id is live, so a later record is not attributed by elimination either.
49
+ */
50
+ sharesIdInMessage(call: OutstandingCall): boolean;
44
51
  ofTurn(turnId: string): Iterable<OutstandingCall>;
45
52
  all(): Iterable<OutstandingCall>;
46
53
  childrenOf(parentKey: string): readonly OutstandingCall[];
@@ -64,6 +71,8 @@ export declare class CompactCallTable {
64
71
  release(ref: ValueRef | undefined): void;
65
72
  entriesOf(ref: ValueRef | undefined, valid: () => void): AsyncGenerator<readonly [string, OutstandingCall], any, any>;
66
73
  private index;
74
+ /** The live calls of one message under one toolCallId. */
75
+ private inMessage;
67
76
  private unindex;
68
77
  }
69
78
  /** The creation ordinal is fixed by the key (RULING-13 Q1): `epoch:creationSequence.k`. */
@@ -3,6 +3,7 @@ import { CompactComponentRoots } from "./compact-component-roots.js";
3
3
  import { CompactValues } from "./compact-values.js";
4
4
  const turnKey = (turnId, toolCallId) => `${turnId}\u0000${toolCallId}`;
5
5
  const messageKey = (messageId, contentIndex) => `${messageId}#${contentIndex}`;
6
+ const sharedKey = (messageId, toolCallId) => `${messageId}\u0000${toolCallId}`;
6
7
  /**
7
8
  * The resident side of the calls component (S06 design section 2): the live entries, four indexes, the owned slot
8
9
  * values, and the persistent root every change republishes through the shared component roots.
@@ -15,6 +16,7 @@ export class CompactCallTable {
15
16
  this.byMessage = new Map();
16
17
  this.byId = new Map();
17
18
  this.children = new Map();
19
+ this.shared = new Set();
18
20
  this.slotBytes = 0;
19
21
  this.headerBytes = 0;
20
22
  this.counters = { replacements: 0, progressReplacements: 0, unknown: 0, late: 0, collisions: 0, reused: 0 };
@@ -60,6 +62,15 @@ export class CompactCallTable {
60
62
  }
61
63
  return oldest?.call;
62
64
  }
65
+ /**
66
+ * RULING-123, C3.4: whether another call of `call`'s own message took the same toolCallId. An execution or hook
67
+ * record names only the toolCallId, so it cannot say which of those calls it is about. The mark lasts while any call
68
+ * of that message and id is live, so a later record is not attributed by elimination either.
69
+ */
70
+ sharesIdInMessage(call) {
71
+ const { owningMessageId, toolCallId } = call;
72
+ return owningMessageId !== null && toolCallId !== null && this.shared.has(sharedKey(owningMessageId, toolCallId));
73
+ }
63
74
  *ofTurn(turnId) {
64
75
  for (const { call } of this.entries.values())
65
76
  if (call.turnId === turnId)
@@ -117,6 +128,9 @@ export class CompactCallTable {
117
128
  }
118
129
  this.headerBytes -= headerBytes(entry.call);
119
130
  this.entries.delete(key);
131
+ const { owningMessageId, toolCallId } = entry.call;
132
+ if (owningMessageId !== null && toolCallId && !this.inMessage(owningMessageId, toolCallId).length)
133
+ this.shared.delete(sharedKey(owningMessageId, toolCallId));
120
134
  this.root = this.roots.set(this.root, entry.order, undefined);
121
135
  this.counters.replacements++;
122
136
  }
@@ -165,6 +179,8 @@ export class CompactCallTable {
165
179
  const keys = this.byId.get(call.toolCallId) ?? [];
166
180
  if (!keys.includes(call.runtimeItemKey))
167
181
  this.byId.set(call.toolCallId, [...keys, call.runtimeItemKey]);
182
+ if (call.owningMessageId !== null && this.inMessage(call.owningMessageId, call.toolCallId).length > 1)
183
+ this.shared.add(sharedKey(call.owningMessageId, call.toolCallId));
168
184
  }
169
185
  if (call.owningMessageId !== null && call.contentIndex !== null)
170
186
  this.byMessage.set(messageKey(call.owningMessageId, call.contentIndex), call.runtimeItemKey);
@@ -174,6 +190,10 @@ export class CompactCallTable {
174
190
  this.children.set(call.parentKey, siblings);
175
191
  }
176
192
  }
193
+ /** The live calls of one message under one toolCallId. */
194
+ inMessage(messageId, toolCallId) {
195
+ return (this.byId.get(toolCallId) ?? []).filter((key) => this.get(key)?.owningMessageId === messageId);
196
+ }
177
197
  unindex(call) {
178
198
  if (call.toolCallId) {
179
199
  const slot = turnKey(call.turnId, call.toolCallId);
@@ -2,6 +2,11 @@ import { type CatalogReader } from "./compact-catalog-steps.ts";
2
2
  import type { CatalogComponent } from "./compact-recovery-catalog.ts";
3
3
  import type { SettledReader } from "./compact-settled-messages.ts";
4
4
  import type { FrozenAcceptedRoot } from "./session-acceptance-lane.ts";
5
+ /**
6
+ * The settled member of the terminal a message ended at: `message-<terminalSequence>`. A compact peer's live
7
+ * `message_end` carries that `terminalSequence` and the row's `messageId` (RULING-120, #5331).
8
+ */
9
+ export declare const settledMember: (terminalSequence: number) => import("./compact-recovery-ids.ts").BoundedId;
5
10
  /**
6
11
  * The settled family (R4.B2, S05, RULING-13 Q2): one `message-<terminal sequence>` member per terminal still pending
7
12
  * or failed in the cut, with its status, error, entry join, terminal payload and the call projection it retired, paged
@@ -4,6 +4,13 @@ import { textComponent } from "./compact-catalog-transcript.js";
4
4
  import { emptyComponent, stepsComponent } from "./compact-catalog-writer.js";
5
5
  const SETTLED = familyId("settled");
6
6
  const IDENTITY = familyId("identity");
7
+ /**
8
+ * The settled member of the terminal a message ended at: `message-<terminalSequence>`. A compact peer's live
9
+ * `message_end` carries that `terminalSequence` and the row's `messageId` (RULING-120, #5331).
10
+ */
11
+ export const settledMember = (terminalSequence) => memberId("message", terminalSequence);
12
+ /** The identity member of this epoch's n-th entry join: `join-<n>`, whose `messageId` an `entry_appended` names. */
13
+ const joinMember = (ordinal) => memberId("join", ordinal);
7
14
  /** A terminal as stored: its row, its payload (terminal record and correlation) spliced in, its paged call walked. */
8
15
  function terminalSteps(settled, row, valid) {
9
16
  const { kind: _kind, payload, call, ...fields } = row;
@@ -25,7 +32,7 @@ export async function* settledComponents(reader, settled) {
25
32
  if (member.kind !== "terminal")
26
33
  continue;
27
34
  const steps = terminalSteps(settled, member.row, reader.valid);
28
- yield* stepsComponent(reader.store, SETTLED, memberId("message", member.row.terminalSequence), steps);
35
+ yield* stepsComponent(reader.store, SETTLED, settledMember(member.row.terminalSequence), steps);
29
36
  count += 1;
30
37
  }
31
38
  if (count === 0)
@@ -45,7 +52,7 @@ export async function* identityComponents(reader, root, settled) {
45
52
  if (member.kind !== "owner")
46
53
  continue;
47
54
  const join = JSON.stringify({ messageId: member.messageId, entryId: member.entryId });
48
- yield* textComponent(reader.store, IDENTITY, memberId("join", member.ordinal), join);
55
+ yield* textComponent(reader.store, IDENTITY, joinMember(member.ordinal), join);
49
56
  }
50
57
  }
51
58
  //# sourceMappingURL=compact-catalog-settled.js.map
@@ -40,6 +40,8 @@ export declare class CompactLaneCurrent {
40
40
  foldComponent(record: object, sequence: number): void;
41
41
  /** An execution, hook or boundary record for the calls component (S06); undefined once the state is unavailable. */
42
42
  foldCall(record: object, sequence: number, turnId: string | null): CallFold | undefined;
43
+ /** The call whose block a `toolcall_*` update streams (RULING-123); undefined once the state is unavailable. */
44
+ callOwning(messageId: string, contentIndex: number): import("./compact-call-model.ts").OutstandingCall | undefined;
43
45
  /**
44
46
  * The cold work of one accepted transcript transition: an owned append makes its message appended, and the flow a
45
47
  * real producer committed (edit, context-edit removal, compaction, branch) maps through this epoch's settled joins.
@@ -74,6 +74,10 @@ export class CompactLaneCurrent {
74
74
  return undefined;
75
75
  }
76
76
  }
77
+ /** The call whose block a `toolcall_*` update streams (RULING-123); undefined once the state is unavailable. */
78
+ callOwning(messageId, contentIndex) {
79
+ return this.state?.callOwning(messageId, contentIndex);
80
+ }
77
81
  /**
78
82
  * The cold work of one accepted transcript transition: an owned append makes its message appended, and the flow a
79
83
  * real producer committed (edit, context-edit removal, compaction, branch) maps through this epoch's settled joins.
@@ -0,0 +1,74 @@
1
+ import type { TranscriptTransition } from "../../core/session-transcript-journal.ts";
2
+ import type { CallIdentity } from "./compact-call-model.ts";
3
+ /**
4
+ * An accepted end's settled ids: the message's `messageId` and the end's accepted sequence. A pending or failed terminal
5
+ * pages them as the `message-<terminalSequence>` member; a joined message writes no terminal row and pages only its
6
+ * `join-<n>` member's `messageId`.
7
+ */
8
+ export interface TerminalIdentity {
9
+ readonly messageId: string;
10
+ readonly terminalSequence: number;
11
+ }
12
+ /**
13
+ * RULING-120 (#5331): the ids the settled store records for one message, which a compact peer's own copy of the live
14
+ * record carries too: an accepted end's terminal ids, a failed write's `messageId`, an entry join's `owningMessageId`
15
+ * (the `messageId` of its `join-<n>` member). RULING-123 (#5346): a live call record's call, by its snapshot key.
16
+ */
17
+ export type LiveIdentity = TerminalIdentity | {
18
+ readonly messageId: string;
19
+ } | {
20
+ readonly owningMessageId: string;
21
+ } | {
22
+ readonly ownerUnknown: true;
23
+ } | CallIdentity;
24
+ /** An end the settled store accepted: whether it joined the message to its durable entry, and the ids it recorded. */
25
+ export interface SettledEnd {
26
+ readonly joined: boolean;
27
+ readonly identity: TerminalIdentity;
28
+ }
29
+ /**
30
+ * How many joined entries may wait for their `entry_appended` at once. Each normally arrives within a batch or two of
31
+ * its append, so this only bounds entries whose `entry_appended` the lane never folds; the oldest is dropped first.
32
+ */
33
+ export declare const MAX_PENDING_JOINS = 1024;
34
+ /**
35
+ * RULING-122 (#5337): how many unvouched entry ids the epoch remembers, so their `entry_appended` says `ownerUnknown`
36
+ * instead of a false not-owned: an owned append whose join was dropped at `MAX_PENDING_JOINS`, or that the settled
37
+ * store never joined (its transcript unavailable, its join refused). Past this the epoch overflows and can no longer
38
+ * vouch for any entry it has no join for.
39
+ */
40
+ export declare const MAX_EVICTED_JOINS = 4096;
41
+ /**
42
+ * One lane epoch's view of those ids for delivery: each joined entry's owning message, captured when its append is
43
+ * applied and kept by entry id until that entry's `entry_appended` takes it (RULING-121, #5337), so transcript batches
44
+ * applied in between cannot drop it; and the ids of the record it accepted last, answered only while that record, at
45
+ * that sequence, is the one being delivered.
46
+ */
47
+ export declare class MessageIdentities {
48
+ private readonly joins;
49
+ private readonly unvouched;
50
+ private overflowed;
51
+ private evictions;
52
+ private overflows;
53
+ private held;
54
+ /** The epoch ended: nothing it joined or held can name a record delivered later. */
55
+ reset(): void;
56
+ /** Joins dropped at the bound and evicted ids dropped from the ring, over the lane's life (never reset). */
57
+ status(): {
58
+ ownerJoinsEvicted: number;
59
+ ownerJoinsOverflowed: number;
60
+ };
61
+ /**
62
+ * `owner`: the message the settled store joined this append to, the `messageId` of its `join-<n>` member; undefined
63
+ * when it recorded no join, which for an owned append (it carries `owningMessageId`) leaves the entry unvouched.
64
+ */
65
+ joined(transition: TranscriptTransition, owner: string | undefined): void;
66
+ private unvouch;
67
+ /** Every acceptance starts with nothing held, so a record the lane refuses never inherits its predecessor's ids. */
68
+ clear(): void;
69
+ /** Holds the ids the settled store recorded for this record, or for an `entry_appended`, its entry's join (taken). */
70
+ hold(type: string, sequence: number, record: object, recorded: LiveIdentity | undefined): void;
71
+ of(type: unknown, sequence: number): LiveIdentity | undefined;
72
+ private takeEntryOwner;
73
+ }
74
+ //# sourceMappingURL=compact-message-identity.d.ts.map
@@ -0,0 +1,101 @@
1
+ /**
2
+ * How many joined entries may wait for their `entry_appended` at once. Each normally arrives within a batch or two of
3
+ * its append, so this only bounds entries whose `entry_appended` the lane never folds; the oldest is dropped first.
4
+ */
5
+ export const MAX_PENDING_JOINS = 1024;
6
+ /**
7
+ * RULING-122 (#5337): how many unvouched entry ids the epoch remembers, so their `entry_appended` says `ownerUnknown`
8
+ * instead of a false not-owned: an owned append whose join was dropped at `MAX_PENDING_JOINS`, or that the settled
9
+ * store never joined (its transcript unavailable, its join refused). Past this the epoch overflows and can no longer
10
+ * vouch for any entry it has no join for.
11
+ */
12
+ export const MAX_EVICTED_JOINS = 4096;
13
+ const OWNER_UNKNOWN = Object.freeze({ ownerUnknown: true });
14
+ /**
15
+ * One lane epoch's view of those ids for delivery: each joined entry's owning message, captured when its append is
16
+ * applied and kept by entry id until that entry's `entry_appended` takes it (RULING-121, #5337), so transcript batches
17
+ * applied in between cannot drop it; and the ids of the record it accepted last, answered only while that record, at
18
+ * that sequence, is the one being delivered.
19
+ */
20
+ export class MessageIdentities {
21
+ constructor() {
22
+ this.joins = new Map();
23
+ this.unvouched = new Set();
24
+ this.overflowed = false;
25
+ this.evictions = 0;
26
+ this.overflows = 0;
27
+ }
28
+ /** The epoch ended: nothing it joined or held can name a record delivered later. */
29
+ reset() {
30
+ this.joins.clear();
31
+ this.unvouched.clear();
32
+ this.overflowed = false;
33
+ this.held = undefined;
34
+ }
35
+ /** Joins dropped at the bound and evicted ids dropped from the ring, over the lane's life (never reset). */
36
+ status() {
37
+ return { ownerJoinsEvicted: this.evictions, ownerJoinsOverflowed: this.overflows };
38
+ }
39
+ /**
40
+ * `owner`: the message the settled store joined this append to, the `messageId` of its `join-<n>` member; undefined
41
+ * when it recorded no join, which for an owned append (it carries `owningMessageId`) leaves the entry unvouched.
42
+ */
43
+ joined(transition, owner) {
44
+ if (transition.kind !== "append")
45
+ return;
46
+ if (owner === undefined) {
47
+ if (transition.owningMessageId !== undefined)
48
+ this.unvouch(transition.id);
49
+ return;
50
+ }
51
+ this.joins.set(transition.id, owner);
52
+ if (this.joins.size <= MAX_PENDING_JOINS)
53
+ return;
54
+ const [oldest] = this.joins.keys();
55
+ if (oldest === undefined)
56
+ return;
57
+ this.joins.delete(oldest);
58
+ this.evictions += 1;
59
+ this.unvouch(oldest);
60
+ }
61
+ unvouch(entryId) {
62
+ this.unvouched.add(entryId);
63
+ if (this.unvouched.size <= MAX_EVICTED_JOINS)
64
+ return;
65
+ const [forgotten] = this.unvouched.keys();
66
+ if (forgotten !== undefined)
67
+ this.unvouched.delete(forgotten);
68
+ this.overflows += 1;
69
+ if (this.overflowed)
70
+ return;
71
+ this.overflowed = true;
72
+ process.stderr.write(`senpi rpc acceptance: unvouched-owner ring overflowed (${MAX_EVICTED_JOINS}; evicted or never-joined owned entries); unmatched entries carry ownerUnknown until the epoch ends\n`);
73
+ }
74
+ /** Every acceptance starts with nothing held, so a record the lane refuses never inherits its predecessor's ids. */
75
+ clear() {
76
+ this.held = undefined;
77
+ }
78
+ /** Holds the ids the settled store recorded for this record, or for an `entry_appended`, its entry's join (taken). */
79
+ hold(type, sequence, record, recorded) {
80
+ const identity = recorded ?? (type === "entry_appended" ? this.takeEntryOwner(record) : undefined);
81
+ if (identity)
82
+ this.held = { type, sequence, identity };
83
+ }
84
+ of(type, sequence) {
85
+ const held = this.held;
86
+ return held && held.type === type && held.sequence === sequence ? held.identity : undefined;
87
+ }
88
+ takeEntryOwner(record) {
89
+ const entryId = Reflect.get(Object(Reflect.get(record, "entry")), "id");
90
+ if (typeof entryId !== "string")
91
+ return undefined;
92
+ const owningMessageId = this.joins.get(entryId);
93
+ this.joins.delete(entryId);
94
+ if (owningMessageId !== undefined)
95
+ return { owningMessageId };
96
+ // RULING-122: an entry whose join may have been dropped is never reported as not message-owned.
97
+ const unknown = this.unvouched.delete(entryId) || this.overflowed;
98
+ return unknown ? OWNER_UNKNOWN : undefined;
99
+ }
100
+ }
101
+ //# sourceMappingURL=compact-message-identity.js.map
@@ -40,11 +40,12 @@ export declare class CompactSessionCalls {
40
40
  requestedName(step: CallStep, messageId: string, contentIndex: number, name: string): void;
41
41
  /** `toolcall_end` and each terminal block: the announcement's final id, name and input; the call owns the raw. */
42
42
  announce(step: CallStep, block: AnnouncedBlock, raw: CallRaw | null): void;
43
- executionStart(step: CallStep, start: ExecutionStart): void;
44
- executionUpdate(step: CallStep, toolCallId: string, args: unknown, partialResult: unknown): void;
45
- hook(step: CallStep, toolCallId: string, record: unknown): void;
43
+ /** Each per-id execution record returns the call it changed (RULING-123), undefined when it changed none. */
44
+ executionStart(step: CallStep, start: ExecutionStart): OutstandingCall | undefined;
45
+ executionUpdate(step: CallStep, toolCallId: string, args: unknown, partialResult: unknown): OutstandingCall | undefined;
46
+ hook(step: CallStep, toolCallId: string, record: unknown): OutstandingCall | undefined;
46
47
  /** An end closes the oldest call still open under its id (senpi#4278); with none open it may replace an abandoned one. */
47
- executionEnd(step: CallStep, toolCallId: string, result: unknown, isError: boolean): void;
48
+ executionEnd(step: CallStep, toolCallId: string, result: unknown, isError: boolean): OutstandingCall | undefined;
48
49
  /**
49
50
  * The carrier settled (RULING-13 Q2): the oldest call under its id still awaiting a carrier, own turn first (the
50
51
  * same order the ends closed them in, senpi#4278), leaves with its completed descendants as one projection.