@code-yeongyu/senpi 2026.10.26-1 → 2026.10.27-1

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 (179) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/bundle/chunks/{anthropic-messages-M235FJQV.js → anthropic-messages-ODSFJCXR.js} +1 -1
  3. package/dist/bundle/chunks/{args-FBNJOJSR.js → args-SWKJAYQD.js} +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-YQRTQ7H6.js → azure-openai-responses-REGXS4XO.js} +1 -1
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  6. package/dist/bundle/chunks/{chunk-DMFAVE5N.js → chunk-4QVORFQ2.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-ZXHLH4MW.js → chunk-5W3DTM2J.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-QYFOHFPS.js → chunk-6CJC6JY4.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-QREKAXKX.js → chunk-6GAMRAVM.js} +5 -5
  10. package/dist/bundle/chunks/{chunk-MSHRIZGT.js → chunk-7IZH2KQX.js} +3 -3
  11. package/dist/bundle/chunks/{chunk-PH3JDRB2.js → chunk-ARSENVDY.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-5KZH6EYQ.js → chunk-CNDPCYNT.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-DYWIFF2Q.js → chunk-CYOHFREV.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-3LPWILUZ.js → chunk-DNDZPKIQ.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-ZLIVZQDY.js → chunk-EQNUL2WB.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-LO5MYLGF.js → chunk-FQDARFHT.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-7KOUQSBX.js → chunk-GPPOPK76.js} +3 -3
  18. package/dist/bundle/chunks/{chunk-I7QWTWVZ.js → chunk-IHKHCWWG.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-MY4ZD3EW.js → chunk-IY4G44MN.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-OQ3WDMB3.js → chunk-JH7MVRNJ.js} +1 -1
  21. package/dist/bundle/chunks/chunk-KBSFTXAT.js +2 -0
  22. package/dist/bundle/chunks/{chunk-UW6M3EXY.js → chunk-KRO63T4Q.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-J5FCH7TI.js → chunk-KTDIIF2F.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-3WP5RFQR.js → chunk-NDNIMC5W.js} +1 -1
  25. package/dist/bundle/chunks/chunk-NXQFBAUI.js +2 -0
  26. package/dist/bundle/chunks/{chunk-CDQPA4FP.js → chunk-ODRGPC5S.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-5FQMNHXV.js → chunk-P5E4EVQF.js} +1 -1
  28. package/dist/bundle/chunks/{chunk-3A3XCLNH.js → chunk-PDG4XWO2.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-5LWUUXMH.js → chunk-PY4ZQHTN.js} +1 -1
  30. package/dist/bundle/chunks/chunk-Q2A2V6NT.js +5 -0
  31. package/dist/bundle/chunks/{chunk-SOXQ42VS.js → chunk-RQ5JTXUC.js} +1 -1
  32. package/dist/bundle/chunks/{chunk-JPGT64RV.js → chunk-TV3UI63P.js} +1 -1
  33. package/dist/bundle/chunks/{chunk-JFQUWEN7.js → chunk-TWHF52G6.js} +4 -4
  34. package/dist/bundle/chunks/{chunk-6U3TBFBA.js → chunk-UJPG4ROT.js} +1 -1
  35. package/dist/bundle/chunks/{chunk-UDM7VO6O.js → chunk-XM36IFP3.js} +1 -1
  36. package/dist/bundle/chunks/{chunk-XQ45JMGN.js → chunk-Z6BREXV3.js} +1 -1
  37. package/dist/bundle/chunks/chunk-ZAS4NBVK.js +410 -0
  38. package/dist/bundle/chunks/{chunk-ADQAPDPV.js → chunk-ZP6JJDYV.js} +1 -1
  39. package/dist/bundle/chunks/{chunk-EZP6AYW2.js → chunk-ZXIAVLJV.js} +88 -88
  40. package/dist/bundle/chunks/{cli-main-55EQA5PS.js → cli-main-36FRZ7ZB.js} +1 -1
  41. package/dist/bundle/chunks/client-CBDPMWZJ.js +2 -0
  42. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-ZJQGIIRC.js → cloudflare-workers-ai-system-one-WDHT7BBV.js} +1 -1
  43. package/dist/bundle/chunks/cursor-agent.js +1 -1
  44. package/dist/bundle/chunks/devin-agent.js +1 -1
  45. package/dist/bundle/chunks/{dispatch-C2IBP5VV.js → dispatch-W5PENPZM.js} +3 -3
  46. package/dist/bundle/chunks/{google-generative-ai-5G4RIQA6.js → google-generative-ai-FHYFXKLA.js} +1 -1
  47. package/dist/bundle/chunks/{google-vertex-ALD4P27O.js → google-vertex-5DWE5U5K.js} +1 -1
  48. package/dist/bundle/chunks/{help-fast-path-PEIKGCKZ.js → help-fast-path-FETLIF3W.js} +1 -1
  49. package/dist/bundle/chunks/{host-command-657VCQ34.js → host-command-XEB7SHT3.js} +1 -1
  50. package/dist/bundle/chunks/{host-session-command-OV5XDOJK.js → host-session-command-FFN52LXJ.js} +1 -1
  51. package/dist/bundle/chunks/{interactive-mode-MLXSYK2H.js → interactive-mode-5S7NNV2Z.js} +1 -1
  52. package/dist/bundle/chunks/{mistral-conversations-RAE4K2XY.js → mistral-conversations-I2PI7DDF.js} +1 -1
  53. package/dist/bundle/chunks/{multi-session-host-774MACJU.js → multi-session-host-E3OPE6HJ.js} +3 -3
  54. package/dist/bundle/chunks/{openai-codex-responses-3CW4R7TG.js → openai-codex-responses-WSEX6SH7.js} +1 -1
  55. package/dist/bundle/chunks/{openai-completions-5UIJXQUF.js → openai-completions-YCBHTBSS.js} +1 -1
  56. package/dist/bundle/chunks/{openai-responses-2BO44MRW.js → openai-responses-GXNSQF4A.js} +1 -1
  57. package/dist/bundle/chunks/{package-manager-cli-MZHVCX2S.js → package-manager-cli-3XO456VE.js} +1 -1
  58. package/dist/bundle/chunks/{pi-messages-RF6GER4R.js → pi-messages-O6GISIRW.js} +1 -1
  59. package/dist/bundle/chunks/protocol-GLNWYI5G.js +2 -0
  60. package/dist/bundle/chunks/{role-ETSFSDTS.js → role-SHIZQKK4.js} +1 -1
  61. package/dist/bundle/chunks/{rotation-stream-ZMQB4GPO.js → rotation-stream-2PCZWNKF.js} +1 -1
  62. package/dist/bundle/chunks/{rpc-mode-UJXOIZCW.js → rpc-mode-GLWKLTMA.js} +1 -1
  63. package/dist/bundle/chunks/{schedule-command-KA7LFFNK.js → schedule-command-B7RRSANU.js} +1 -1
  64. package/dist/bundle/chunks/{session-control-endpoint-MFWXUR7Z.js → session-control-endpoint-KLE2X4RR.js} +1 -1
  65. package/dist/bundle/chunks/{session-picker-BP33NRSL.js → session-picker-DTGBOXDR.js} +1 -1
  66. package/dist/bundle/chunks/session-worker.js +131 -131
  67. package/dist/bundle/chunks/{typesafe-system-one-GNNZ5O5U.js → typesafe-system-one-XGYJJVSE.js} +1 -1
  68. package/dist/bundle/cli.js +2 -2
  69. package/dist/bundle/index.js +1 -1
  70. package/dist/bundle/rpc-entry.js +1 -1
  71. package/dist/bundle/runtime-manifest.json +1 -1
  72. package/dist/core/agent-session.d.ts +11 -1
  73. package/dist/core/agent-session.js +19 -2
  74. package/dist/core/extensions/builtin/anthropic-subscription/account-context.d.ts +15 -0
  75. package/dist/core/extensions/builtin/anthropic-subscription/account-context.js +53 -0
  76. package/dist/core/extensions/builtin/anthropic-subscription/auth-attempt.d.ts +3 -0
  77. package/dist/core/extensions/builtin/anthropic-subscription/auth-attempt.js +26 -9
  78. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane-boundary.d.ts +16 -0
  79. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane-boundary.js +27 -0
  80. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts +2 -13
  81. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +3 -26
  82. package/dist/core/extensions/builtin/anthropic-subscription/errors.js +3 -2
  83. package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts +6 -1
  84. package/dist/core/extensions/builtin/anthropic-subscription/guidance.js +1 -1
  85. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +13 -2
  86. package/dist/core/extensions/builtin/compaction/index.js +7 -1
  87. package/dist/core/extensions/builtin/herdr/index.js +9 -5
  88. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +6 -0
  89. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +25 -0
  90. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +3 -3
  91. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +50 -19
  92. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +4 -3
  93. package/dist/core/extensions/builtin/mcp/auth/oauth.js +19 -12
  94. package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +1 -0
  95. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +1 -0
  96. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +4 -1
  97. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +33 -35
  98. package/dist/core/extensions/builtin/mcp/modern/request.js +6 -2
  99. package/dist/core/extensions/builtin/mcp/modern/transport.d.ts +2 -0
  100. package/dist/core/extensions/builtin/mcp/modern/transport.js +4 -2
  101. package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +2 -0
  102. package/dist/core/extensions/builtin/mcp/sdk.lazy.js +2 -0
  103. package/dist/core/extensions/builtin/mcp/transport-sdk.js +12 -1
  104. package/dist/core/extensions/builtin/terminal/extension-state.js +2 -1
  105. package/dist/core/extensions/builtin/vertex-model-garden/auth.d.ts +9 -0
  106. package/dist/core/extensions/builtin/vertex-model-garden/auth.js +17 -1
  107. package/dist/core/extensions/builtin/websearch/websearch/providers/google.d.ts +6 -0
  108. package/dist/core/extensions/builtin/websearch/websearch/providers/google.js +6 -1
  109. package/dist/core/extensions/types.d.ts +2 -0
  110. package/dist/core/lifecycle-children.d.ts +3 -1
  111. package/dist/core/lifecycle-children.js +79 -15
  112. package/dist/core/lifecycle-record.js +1 -24
  113. package/dist/core/lifecycle-session-binding.js +2 -1
  114. package/dist/core/lifecycle-wait-items.d.ts +17 -0
  115. package/dist/core/lifecycle-wait-items.js +72 -0
  116. package/dist/core/model-runtime.js +6 -1
  117. package/dist/core/provider-api-key-auth.d.ts +6 -1
  118. package/dist/core/provider-api-key-auth.js +11 -4
  119. package/dist/core/provider-composer.d.ts +8 -1
  120. package/dist/core/provider-composer.js +7 -3
  121. package/dist/core/sdk.js +4 -1
  122. package/dist/core/session-activity.js +2 -0
  123. package/dist/core/session-lifecycle-types.d.ts +20 -4
  124. package/dist/core/session-lifecycle.js +35 -4
  125. package/dist/core/structured-session-lane.d.ts +33 -0
  126. package/dist/core/structured-session-lane.js +62 -0
  127. package/dist/core/work-custody-record.d.ts +2 -1
  128. package/dist/core/work-custody-record.js +2 -0
  129. package/dist/core/work-obligations.d.ts +1 -1
  130. package/dist/core/work-obligations.js +8 -2
  131. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  132. package/dist/modes/interactive/interactive-mode.js +2 -1
  133. package/dist/modes/json-event.d.ts +79 -1
  134. package/dist/modes/json-event.js +79 -1
  135. package/dist/modes/rpc/compact-commit-dependencies.d.ts +13 -0
  136. package/dist/modes/rpc/compact-commit-dependencies.js +48 -0
  137. package/dist/modes/rpc/compact-content-index.d.ts +84 -0
  138. package/dist/modes/rpc/compact-content-index.js +243 -0
  139. package/dist/modes/rpc/compact-content-store.d.ts +10 -43
  140. package/dist/modes/rpc/compact-content-store.js +61 -163
  141. package/dist/modes/rpc/compact-index-pages.d.ts +12 -0
  142. package/dist/modes/rpc/compact-index-pages.js +31 -0
  143. package/dist/modes/rpc/compact-read-lease.d.ts +15 -0
  144. package/dist/modes/rpc/compact-read-lease.js +40 -0
  145. package/dist/modes/rpc/compact-session-state.d.ts +14 -63
  146. package/dist/modes/rpc/compact-session-state.js +69 -51
  147. package/dist/modes/rpc/compact-spool-dir.d.ts +4 -1
  148. package/dist/modes/rpc/compact-spool-dir.js +41 -8
  149. package/dist/modes/rpc/compact-spool-storage.d.ts +58 -0
  150. package/dist/modes/rpc/compact-spool-storage.js +195 -0
  151. package/dist/modes/rpc/compact-storage-io.d.ts +2 -1
  152. package/dist/modes/rpc/compact-storage-io.js +7 -1
  153. package/dist/modes/rpc/compact-text-roots.d.ts +22 -0
  154. package/dist/modes/rpc/compact-text-roots.js +138 -0
  155. package/dist/modes/rpc/compact-text-view.d.ts +22 -0
  156. package/dist/modes/rpc/compact-text-view.js +16 -0
  157. package/dist/modes/rpc/compact-transition.d.ts +41 -0
  158. package/dist/modes/rpc/compact-transition.js +12 -0
  159. package/dist/modes/rpc/connection-handler.d.ts +2 -0
  160. package/dist/modes/rpc/connection-handler.js +12 -6
  161. package/dist/modes/rpc/host-ensure-start.js +5 -3
  162. package/dist/modes/rpc/security-presentations.js +2 -1
  163. package/dist/modes/rpc/session-binding.js +10 -0
  164. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  165. package/dist/modes/rpc/session-event-writer.js +2 -0
  166. package/dist/modes/rpc/session-record-stream.d.ts +3 -3
  167. package/dist/modes/rpc/session-record-stream.js +3 -3
  168. package/dist/modes/rpc/session-structured-lanes.d.ts +12 -0
  169. package/dist/modes/rpc/session-structured-lanes.js +24 -0
  170. package/dist/modes/rpc/session-worker-client.js +6 -5
  171. package/dist/modes/rpc/structured-session-records.d.ts +12 -0
  172. package/dist/modes/rpc/structured-session-records.js +88 -0
  173. package/docs/extensions.md +7 -1
  174. package/docs/rpc.md +17 -2
  175. package/package.json +6 -6
  176. package/dist/bundle/chunks/chunk-GQJYNFMO.js +0 -2
  177. package/dist/bundle/chunks/chunk-VICDRG3J.js +0 -410
  178. package/dist/bundle/chunks/chunk-VL7O5PO4.js +0 -5
  179. package/dist/bundle/chunks/client-LOS6GSEW.js +0 -2
@@ -7,7 +7,7 @@
7
7
  * reads the same records from either.
8
8
  */
9
9
  import type { AgentAbortSource } from "../../core/agent-abort-provenance.ts";
10
- import type { AgentSession } from "../../core/agent-session.ts";
11
- /** Subscribes `output` to the session's records; returns the unsubscribe. */
12
- export declare function subscribeSessionRecords(session: AgentSession, output: (record: object) => void, onAbortSource?: (source: AgentAbortSource) => void): () => void;
10
+ import type { AgentSession, AgentSessionEventListener } from "../../core/agent-session.ts";
11
+ /** Subscribes `output` to the session's records (from `source`, by default `session.subscribe`); returns the unsubscribe. */
12
+ export declare function subscribeSessionRecords(session: AgentSession, output: (record: object) => void, onAbortSource?: (source: AgentAbortSource) => void, source?: (listener: AgentSessionEventListener) => () => void): () => void;
13
13
  //# sourceMappingURL=session-record-stream.d.ts.map
@@ -1,10 +1,10 @@
1
1
  import { clientFacingIdentity } from "./client-admission-record.js";
2
2
  import { ClientAdmissions } from "./client-admissions.js";
3
3
  import { ClientMessageEvents } from "./client-message-events.js";
4
- /** Subscribes `output` to the session's records; returns the unsubscribe. */
5
- export function subscribeSessionRecords(session, output, onAbortSource = () => { }) {
4
+ /** Subscribes `output` to the session's records (from `source`, by default `session.subscribe`); returns the unsubscribe. */
5
+ export function subscribeSessionRecords(session, output, onAbortSource = () => { }, source = (listener) => session.subscribe(listener)) {
6
6
  const correlatedEvents = new ClientMessageEvents(output, () => ClientAdmissions.forSession(session).hasIdentities);
7
- return session.subscribe((event) => {
7
+ return source((event) => {
8
8
  if (event.type === "session_lifecycle") {
9
9
  // The connection adds the routing handle; classic stdio has no envelope.
10
10
  const { sessionId: _sessionId, ...record } = event;
@@ -0,0 +1,12 @@
1
+ type StructuredReceiver = (record: object) => void;
2
+ /**
3
+ * Per-session receivers of the internal structured lane (senpi#4272): the host-side owner of seeded records and
4
+ * block controls (S03/S04) registers here. Legacy sockets, stdout and the replay snapshot never read it.
5
+ */
6
+ export declare class SessionStructuredLanes {
7
+ private readonly receivers;
8
+ observe(sessionId: string, receiver: StructuredReceiver): () => void;
9
+ accept(sessionId: string, record: object): void;
10
+ }
11
+ export {};
12
+ //# sourceMappingURL=session-structured-lanes.d.ts.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Per-session receivers of the internal structured lane (senpi#4272): the host-side owner of seeded records and
3
+ * block controls (S03/S04) registers here. Legacy sockets, stdout and the replay snapshot never read it.
4
+ */
5
+ export class SessionStructuredLanes {
6
+ constructor() {
7
+ this.receivers = new Map();
8
+ }
9
+ observe(sessionId, receiver) {
10
+ const receivers = this.receivers.get(sessionId) ?? new Set();
11
+ receivers.add(receiver);
12
+ this.receivers.set(sessionId, receivers);
13
+ return () => {
14
+ receivers.delete(receiver);
15
+ if (receivers.size === 0 && this.receivers.get(sessionId) === receivers)
16
+ this.receivers.delete(sessionId);
17
+ };
18
+ }
19
+ accept(sessionId, record) {
20
+ for (const receiver of [...(this.receivers.get(sessionId) ?? [])])
21
+ receiver(record);
22
+ }
23
+ }
24
+ //# sourceMappingURL=session-structured-lanes.js.map
@@ -5,6 +5,7 @@ import { ownProcessStartedAtMs } from "../../core/extensions/builtin/terminal/pr
5
5
  import { isSecurityNoticeState } from "../../core/security-notices.js";
6
6
  import { createWebViewBroker } from "../../core/webview/webview-broker.js";
7
7
  import { roleConnection } from "./connection-roles.js";
8
+ import { answeredUiRequestId } from "./extension-ui-response.js";
8
9
  import { subscriptionLimits } from "../../core/subscription-limits/limit-store.js";
9
10
  import { SessionWorkerRequests } from "./session-worker-requests.js";
10
11
  import { acknowledge, acknowledgeGrant } from "./session-worker-signals.js";
@@ -125,11 +126,11 @@ export class SessionWorkerClient {
125
126
  if (writer.handleMcpForm(command))
126
127
  return;
127
128
  const connection = writer.currentConnection();
128
- if ("type" in command &&
129
- command.type === "extension_ui_response" &&
130
- "id" in command &&
131
- typeof command.id === "string" &&
132
- writer.acceptWorkerGrantResponse(sessionId, command.id, connection) === false)
129
+ const grantAnswer = "type" in command && command.type === "extension_ui_response"
130
+ ? answeredUiRequestId(command)
131
+ : undefined;
132
+ if (grantAnswer !== undefined &&
133
+ writer.acceptWorkerGrantResponse(sessionId, grantAnswer, connection) === false)
133
134
  return;
134
135
  await this.request({
135
136
  type: "command",
@@ -0,0 +1,12 @@
1
+ import type { AgentSession } from "../../core/agent-session.ts";
2
+ /**
3
+ * The engine-socket compact-updates rollout flag (`<brand>_ENGINE_SOCKET_COMPACT_UPDATES`), explicit-true only.
4
+ * Off, no session holds a structured subscription and every destination keeps its existing bytes.
5
+ */
6
+ export declare function structuredLaneEnabled(): boolean;
7
+ /**
8
+ * The session's internal structured lane, correlated like its legacy records: seeded `toJsonEvent` projections of
9
+ * every session event plus the three block controls, in session order. Never a legacy destination.
10
+ */
11
+ export declare function subscribeStructuredSessionRecords(session: AgentSession, output: (record: object) => void): () => void;
12
+ //# sourceMappingURL=structured-session-records.d.ts.map
@@ -0,0 +1,88 @@
1
+ import { getAssistantCapture } from "@earendil-works/pi-ai";
2
+ import { envValue } from "../../core/brand.js";
3
+ import { toJsonEvent } from "../json-event.js";
4
+ import { subscribeSessionRecords } from "./session-record-stream.js";
5
+ /**
6
+ * The engine-socket compact-updates rollout flag (`<brand>_ENGINE_SOCKET_COMPACT_UPDATES`), explicit-true only.
7
+ * Off, no session holds a structured subscription and every destination keeps its existing bytes.
8
+ */
9
+ export function structuredLaneEnabled() {
10
+ const value = envValue("ENGINE_SOCKET_COMPACT_UPDATES");
11
+ return value === "1" || value === "true";
12
+ }
13
+ const SEEDS = { blockSeeds: 1 };
14
+ /**
15
+ * Admits a block's records only after its own seed or insert reached this subscriber, so a subscriber that attached
16
+ * mid-message gets no seed, delta or control for that message, never a wrong one. A started block whose next record
17
+ * has no capture, or that has no accepted end when the message ends, is invalid: its later records are dropped and
18
+ * the assistant `message_end` names it in `invalidatedBlocks`, so a reader rebuilds it from the terminal message.
19
+ * A terminal that rewrote blocks from `rewrittenFrom` on invalidates every block at or above that index, and a
20
+ * `message_end` with no interpretable terminal capture (cancel, idle timeout) invalidates every block of the message.
21
+ * Blocks are addressed by the capture's own index (`capture.contentIndex ?? event.contentIndex`), never a drifted public one.
22
+ */
23
+ class SeededBlockGate {
24
+ accept(record) {
25
+ if (record.type === "message_block_state" || record.type === "message_block_metadata")
26
+ return this.control(record, record.type === "message_block_state" ? record.phase : undefined);
27
+ if (record.type === "message_tool_arguments_reset")
28
+ return this.control(record);
29
+ if (record.type === "message_start" && record.message.role === "assistant") {
30
+ const seeded = toJsonEvent(record, SEEDS);
31
+ this.blocks = seeded && new Map();
32
+ return seeded;
33
+ }
34
+ if (record.type === "message_end" && record.message.role === "assistant")
35
+ return this.end(record);
36
+ if (record.type !== "message_update")
37
+ return toJsonEvent(record, SEEDS);
38
+ if (!this.blocks)
39
+ return undefined;
40
+ const seeded = toJsonEvent(record, SEEDS);
41
+ const update = record.assistantMessageEvent;
42
+ if (!("contentIndex" in update))
43
+ return seeded;
44
+ const index = getAssistantCapture(record)?.contentIndex ?? update.contentIndex;
45
+ const starts = update.type === "text_start" || update.type === "thinking_start" || update.type === "toolcall_start";
46
+ if (!starts && this.blocks.get(index) !== "open")
47
+ return undefined;
48
+ const ends = update.type === "text_end" || update.type === "thinking_end" || update.type === "toolcall_end";
49
+ this.blocks.set(index, seeded ? (ends ? "ended" : "open") : "invalid");
50
+ return seeded;
51
+ }
52
+ control(record, phase) {
53
+ const state = this.blocks?.get(record.contentIndex);
54
+ if (!this.blocks || state === "invalid")
55
+ return undefined;
56
+ if (phase === "insert")
57
+ this.blocks.set(record.contentIndex, "open");
58
+ else if (state === undefined || (phase === "end" && state !== "open"))
59
+ return undefined;
60
+ else if (phase === "end")
61
+ this.blocks.set(record.contentIndex, "ended");
62
+ return record;
63
+ }
64
+ end(record) {
65
+ const capture = getAssistantCapture(record);
66
+ const rewrittenFrom = capture?.message === undefined ? 0 : (capture.rewrittenFrom ?? Number.POSITIVE_INFINITY);
67
+ const invalidatedBlocks = [...(this.blocks ?? [])].flatMap(([index, state]) => state === "ended" && index < rewrittenFrom ? [] : [index]);
68
+ this.blocks = undefined;
69
+ const seeded = toJsonEvent(record, SEEDS) ?? record;
70
+ return invalidatedBlocks.length === 0
71
+ ? seeded
72
+ : { ...seeded, invalidatedBlocks: invalidatedBlocks.sort((a, b) => a - b) };
73
+ }
74
+ }
75
+ /**
76
+ * The session's internal structured lane, correlated like its legacy records: seeded `toJsonEvent` projections of
77
+ * every session event plus the three block controls, in session order. Never a legacy destination.
78
+ */
79
+ export function subscribeStructuredSessionRecords(session, output) {
80
+ const gate = new SeededBlockGate();
81
+ return subscribeSessionRecords(session, output, undefined, (listener) => session.subscribeStructured((record) => {
82
+ const accepted = gate.accept(record);
83
+ // Correlation reads only `type`, `role` and outer fields, which seeded records and controls keep.
84
+ if (accepted)
85
+ listener(accepted);
86
+ }));
87
+ }
88
+ //# sourceMappingURL=structured-session-records.js.map
@@ -2170,7 +2170,13 @@ session-notice identity. Restart redelivers unconsumed queued messages once, but
2170
2170
  never replays a consumed result or reruns an interrupted tool.
2171
2171
 
2172
2172
  `pi.lifecycle.reportChild({ taskId, childSessionId, runEpoch, generation, state,
2173
- waitingOn?, terminal? })` persists runner-owned child state. Stale epoch/generation
2173
+ label?, since?, waitingOn?, terminal? })` persists runner-owned child state. `label`
2174
+ is a producer-authored name and `since` is the child's own state-entry time (ISO);
2175
+ an unparseable `since` is dropped. `waitingOn` takes the `SessionLifecycle.waitingOn`
2176
+ shape described in [RPC session lifecycle](rpc.md#session-lifecycle), named lists
2177
+ included; its counts stay authoritative and the child's human waits are not kept.
2178
+ A nonterminal report appears in the parent's `waitingOn.childReports` as
2179
+ `{ taskId, childSessionId, runEpoch, state, label?, since? }`. Stale epoch/generation
2174
2180
  pairs are ignored; a higher `runEpoch` clears the old terminal status. Terminal
2175
2181
  values are `completed`, `error`, `lost`, `interrupted`, and `cancelled`. Child UI
2176
2182
  requests remain automatically answered by their runners, not routed to a person.
package/docs/rpc.md CHANGED
@@ -145,8 +145,23 @@ The shared `SessionLifecycle` type is exported by `@code-yeongyu/senpi`:
145
145
  - `seq`, `epoch`, `epochOrigin` (`human` or `automatic`), `humanInputRevision`,
146
146
  `selfSettleEligible`, `restoring`, `persisted`, `runPaused`, and `since` (ISO time).
147
147
  - `role` (`root` or `child`), `rootSessionId`, and optional `parentSessionId` and `taskId`.
148
- - Optional `waitingOn`: `{ children, background, human }`. Background entries carry
149
- `source`, `activeCount`, and optional `retryAtMs`. Human entries carry `kind`,
148
+ - Optional `waitingOn`: `{ children, background, human, childSources?, childReports?, childReportsOmitted? }`.
149
+ `children` and each source's `activeCount` are the authoritative counts; every named list below is
150
+ supplemental and may be incomplete, so never derive a count from a list's length. `children` is the
151
+ larger of the nonterminal child reports and the summed `childSources` counts.
152
+ Background entries carry `source`, `activeCount`, optional `items`, optional `omittedItems`, and
153
+ optional `retryAtMs`. `childSources` entries carry `source`, `activeCount`, optional `items`, and
154
+ optional `omittedItems`. Each item is `{ id, label?, startedAt? }` (`startedAt` is ISO time), taken
155
+ from the producer's `wake_source_state` snapshot: its `items`, or the legacy `monitors` list when
156
+ `items` is missing or null. Terminal monitors are named by their stable `mon_` id. Each list is a
157
+ snapshot: entries whose `id` is not a non-blank string are dropped, duplicates collapse to one entry
158
+ per `id`, the list is sorted by `id`, and at most 32 entries are kept. `omittedItems` is the
159
+ producer's own omitted count plus any valid items cut at 32; it is not `activeCount` minus the list
160
+ length. Invalid detail drops only the detail, never the count.
161
+ `childReports` lists nonterminal reports from `reportChild` as
162
+ `{ taskId, childSessionId, runEpoch, state, label?, since? }`, excluding reports about the session
163
+ itself, sorted by `taskId` and capped at 32; `childReportsOmitted` counts the reports cut by that cap.
164
+ Human entries carry `kind`,
150
165
  `originSessionId`, `requestId`, `revision`, `since`, and optional child identity/label.
151
166
  Human kinds are `question`, `approval`, `goal_blocked`, `goal_paused`, `stalled`,
152
167
  and `interrupted_run`; attention remains visible even when a hold wins precedence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi",
3
- "version": "2026.10.26-1",
3
+ "version": "2026.10.27-1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -71,9 +71,9 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@earendil-works/chord": "0.99.1",
74
- "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.26-1",
75
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.26-1",
76
- "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.26-1",
74
+ "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.27-1",
75
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.27-1",
76
+ "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.27-1",
77
77
  "@silvia-odwyer/photon-node": "0.3.4",
78
78
  "ajv": "8.20.0",
79
79
  "chalk": "6.0.0",
@@ -93,8 +93,8 @@
93
93
  "yaml": "2.9.1",
94
94
  "@anthropic-ai/claude-agent-sdk": "0.3.306",
95
95
  "@anthropic-ai/sdk": "0.127.0",
96
- "@code-yeongyu/senpi-codemode": "2026.10.26-1",
97
- "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.26-1",
96
+ "@code-yeongyu/senpi-codemode": "2026.10.27-1",
97
+ "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.27-1",
98
98
  "@modelcontextprotocol/sdk": "1.32.1",
99
99
  "@mozilla/readability": "0.6.0",
100
100
  "linkedom": "0.18.13",
@@ -1,2 +0,0 @@
1
- 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 = () => {}; } }
2
- var captures=new WeakMap,readers=new WeakMap,writers=new WeakMap,states=new WeakMap,demanded=new WeakSet,warned=new WeakSet,NO_CHANGE=Object.freeze({}),subscribers=0,capturedAny=!1;function subscribeAssistantCapture(){subscribers++;let held=!0;return()=>{held&&subscribers--,held=!1}}function setAssistantReader(owner,reader){readers.set(owner,reader)}function setAssistantProgressWriter(owner,writer){writers.set(owner,writer)}function enableAssistantCapture(owner){states.has(owner)||states.set(owner,void 0)}function activeState(owner){let state=states.get(owner);if(state!==void 0||!states.has(owner))return state??void 0;let decided=subscribers>0||demanded.has(owner)?{positions:new Map,metadata:new Map,insertCopies:new Map,failedBlocks:new Set}:null;return capturedAny||=decided!==null,states.set(owner,decided),decided??void 0}function dropCapture(owner,error,state,contentIndex){contentIndex!==void 0&&state?.failedBlocks.add(contentIndex),!warned.has(owner)&&(warned.add(owner),console.warn("[assistant-capture] could not build an internal capture record; that block's records are dropped",error))}function pushAssistantProgress(owner,progress){let writer=writers.get(owner);if(!writer)return;if(Object.isFrozen(progress)){writer(progress);return}let state=activeState(owner);if(!(!state||state.failedBlocks.has(progress.contentIndex)))try{writer(freezeProgress(state,progress))}catch(error){dropCapture(owner,error,state,progress.contentIndex)}}function freezeProgress(state,progress){if(progress.type==="message_tool_arguments_reset"&&state.positions.set(progress.contentIndex,{offset:progress.rawArgumentPrefix.length,parsedThrough:progress.parsedThrough}),progress.type!=="message_block_state")return Object.freeze({...progress});let previous=state.insertCopies.get(progress.contentIndex),current=publicBlock(progress.block),block=previous!==void 0&&sameValue(current,previous)?previous:freezeCapture(current);return progress.phase==="insert"?state.insertCopies.set(progress.contentIndex,block):state.insertCopies.delete(progress.contentIndex),Object.freeze({...progress,block})}function pushAssistantSignature(owner,contentIndex,block,signature){!signature||signature===(block.type==="text"?block.textSignature:block.thinkingSignature)||pushAssistantProgress(owner,{type:"message_block_metadata",contentIndex,...block.type==="text"?{textSignature:signature}:{thinkingSignature:signature}})}function pushAssistantCompleteBlock(owner,contentIndex,block){for(let phase of["insert","end"])pushAssistantProgress(owner,{type:"message_block_state",contentIndex,phase,block})}function assistantRecords(source){demanded.add(source);let read=readers.get(source);return{[Symbol.asyncIterator]:()=>read?.(entry=>({value:"event"in entry?entry.event:entry.progress,done:!1}))??source[Symbol.asyncIterator]()}}function assistantEvents(source,onProgress){let read=readers.get(source);return{[Symbol.asyncIterator]:()=>read?.(entry=>{if("event"in entry)return{value:entry.event,done:!1};onProgress(entry.progress)})??source[Symbol.asyncIterator]()}}function getAssistantCapture(event){return captures.get(event)}function isAssistantProgress(event){return event.type==="message_block_state"||event.type==="message_block_metadata"||event.type==="message_tool_arguments_reset"}function freezeCapture(value){let freeze=part=>{if(!(part===null||typeof part!="object"||Object.isFrozen(part))){for(let child of Object.values(part))freeze(child);Object.freeze(part)}},copy=structuredClone(value);return freeze(copy),copy}function publicBlock(block){return Object.fromEntries({text:["type","text","audience","textSignature"],thinking:["type","thinking","thinkingSignature","redacted","startedAt","endedAt"],toolCall:["type","id","name","arguments","incomplete","errorMessage","thoughtSignature","namespace"],providerNative:["type","subtype","raw"]}[block.type].filter(key=>key in block).map(key=>[key,Reflect.get(block,key)]))}function sameValue(left,right){if(left===right)return!0;if(!isPlainData(left)||!isPlainData(right)||Array.isArray(left)!==Array.isArray(right))return!1;let keys=Object.keys(left);return keys.length===Object.keys(right).length&&keys.every(key=>Object.hasOwn(right,key)&&sameValue(Reflect.get(left,key),Reflect.get(right,key)))}function isPlainData(value){if(typeof value!="object"||value===null)return!1;let prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===Array.prototype||prototype===null}function dropAssistantCapture(event){capturedAny&&captures.delete(event)}function carryAssistantCapture(source,target,terminal){let capture=captures.get(source);if(!capture)return target;try{captures.set(target,terminal?Object.freeze({...capture,message:freezeCapture({...terminal,content:terminal.content.map(publicBlock)})}):capture)}catch(error){dropCapture(source,error)}return target}function captureAssistantEvent(owner,event,hint={}){let state=activeState(owner);if(!state||captures.has(event))return;let contentIndex="contentIndex"in event?hint.contentIndex??event.contentIndex:void 0;if(!(contentIndex!==void 0&&state.failedBlocks.has(contentIndex)))try{captures.set(event,buildCapture(state,event,hint))}catch(error){dropCapture(owner,error,state,contentIndex)}}function buildCapture(state,event,hint){let message=event.type==="done"?event.message:event.type==="error"?event.error:event.partial,usage=usageCopy(state,message.usage);if(!("contentIndex"in event))return Object.freeze({usage,metadata:changedMetadata(state,message),message:freezeCapture({...message,content:message.content.map(publicBlock)})});let index=hint.contentIndex??event.contentIndex,own=index===event.contentIndex?{}:{contentIndex:index};if("delta"in event){let position=state.positions.get(index)??{offset:0,parsedThrough:0},offset=hint.offset??position.offset;return position.offset=offset+event.delta.length,event.type==="toolcall_delta"&&event.delta.length>0&&(position.parsedThrough=hint.parsedThrough??Math.max(position.parsedThrough,position.offset)),state.positions.set(index,position),Object.freeze({usage,offset,...own,...event.type==="toolcall_delta"?{parsedThrough:position.parsedThrough}:{}})}let metadata=changedMetadata(state,message),block=freezeCapture(publicBlock(event.type==="toolcall_end"?event.toolCall:message.content[index]));if(event.type!=="text_start"&&event.type!=="thinking_start"&&event.type!=="toolcall_start")return Object.freeze({usage,metadata,block,...own});let rawArgumentPrefix=hint.rawArgumentPrefix??"",coveredCodeUnits=block.type==="text"?block.text.length:block.type==="thinking"?block.thinking.length:rawArgumentPrefix.length,parsedThrough=hint.parsedThrough??0;return state.positions.set(index,{offset:hint.offset??coveredCodeUnits,parsedThrough}),Object.freeze({usage,metadata,block,coveredCodeUnits,...own,...block.type==="toolCall"?{rawArgumentPrefix,parsedThrough}:{}})}function usageCopy(state,usage){if(state.usage!==void 0&&sameValue(usage,state.usage))return state.usage;let copy=Object.freeze({...usage,cost:Object.freeze({...usage.cost}),...usage.contextUsage?{contextUsage:Object.freeze({...usage.contextUsage})}:{}});return state.usage=copy,copy}function changedMetadata(state,message){let changed={};for(let key of Object.keys(message)){if(key==="content"||key==="usage")continue;let value=Reflect.get(message,key);(!state.metadata.has(key)||state.metadata.get(key)!==value)&&(changed[key]=value)}for(let key of state.metadata.keys())key in message||(changed[key]=null);let frozen=Object.keys(changed).length===0?NO_CHANGE:freezeCapture(changed);for(let key of Object.keys(changed))key in message?state.metadata.set(key,Reflect.get(message,key)):state.metadata.delete(key);return frozen}var FifoQueue=class{constructor(){this.incoming=[],this.outgoing=[]}get length(){return this.incoming.length+this.outgoing.length}enqueue(value){this.incoming.push(value)}prepend(value){this.outgoing.push(value)}dequeue(){if(this.outgoing.length===0)for(;this.incoming.length>0;)this.outgoing.push(this.incoming.pop());return this.outgoing.pop()}},EventStream=class{#queue;#queueHead;#failed;#error;#localWorkDepth;constructor(isComplete,extractResult){this.#queue=[],this.#queueHead=0,this.waiting=new FifoQueue,this.done=!1,this.#failed=!1,this.#localWorkDepth=0,this.isComplete=isComplete,this.extractResult=extractResult,this.finalResultPromise=new Promise((resolve,reject)=>{this.resolveFinalResult=resolve,this.rejectFinalResult=reject}),this.finalResultPromise.catch(()=>{})}get queue(){return this.#queue.slice(this.#queueHead).flatMap(entry=>"event"in entry?[entry.event]:[])}#enqueue(event){this.#queue.push(event)}#dequeue(){let event=this.#queue[this.#queueHead];if(event===void 0)throw new Error("EventStream queue underflow");return this.#queueHead++,this.#queueHead>1024&&this.#queueHead*2>=this.#queue.length&&(this.#queue=this.#queue.slice(this.#queueHead),this.#queueHead=0),event}push(event){this.done||(this.isComplete(event)&&(this.done=!0,this.resolveFinalResult(this.extractResult(event))),this.#deliver({event}))}enqueueProgress(progress){this.done||this.#deliver({progress})}#deliver(entry){let waiter=this.waiting.dequeue();waiter?waiter.resolve({value:entry,done:!1}):this.#enqueue(entry)}end(result){for(this.done=!0,result!==void 0&&this.resolveFinalResult(result);this.waiting.length>0;){let waiter=this.waiting.dequeue();waiter&&waiter.resolve({value:void 0,done:!0})}}fail(error){if(!this.done)for(this.done=!0,this.#failed=!0,this.#error=error,this.rejectFinalResult(error);this.waiting.length>0;){let waiter=this.waiting.dequeue();waiter&&waiter.reject(error)}}[Symbol.asyncIterator](){return this.mapIterator(entry=>"event"in entry?{value:entry.event,done:!1}:void 0)}mapIterator(project){return{next:()=>{for(;this.#queueHead<this.#queue.length;){let result=project(this.#dequeue());if(result)return Promise.resolve(result)}return this.#failed?Promise.reject(this.#error):this.done?Promise.resolve({value:void 0,done:!0}):new Promise((resolve,reject)=>{let waiter={reject,resolve:next=>{let result=next.done?{value:void 0,done:!0}:project(next.value);result?resolve(result):this.waiting.prepend(waiter)}};this.waiting.enqueue(waiter)})}}}result(){return this.finalResultPromise}async trackLocalWork(work){this.#localWorkDepth++;try{return await work}finally{this.#localWorkDepth--}}hasPendingLocalWork(){return this.#localWorkDepth>0}},AssistantMessageEventStream=class _AssistantMessageEventStream extends EventStream{constructor(options={}){super(event=>event.type==="done"||event.type==="error",event=>{if(event.type==="done")return event.message;if(event.type==="error")return event.error;throw new Error("Unexpected event type for final result")}),options.captureBlocks&&enableAssistantCapture(this),setAssistantProgressWriter(this,progress=>this.enqueueProgress(progress)),new.target.prototype[Symbol.asyncIterator]===_AssistantMessageEventStream.prototype[Symbol.asyncIterator]&&setAssistantReader(this,project=>this.mapIterator(project))}push(event,hint){this.done||(captureAssistantEvent(this,event,hint),super.push(event))}[Symbol.asyncIterator](){return this.mapIterator(entry=>{if("event"in entry)return dropAssistantCapture(entry.event),{value:entry.event,done:!1}})}};function createAssistantMessageEventStream(){return new AssistantMessageEventStream}function formatThrownValue(value){return value instanceof Error?value.message||value.name:typeof value=="string"?value:String(value)}function extractDiagnosticError(error){if(!(error instanceof Error))return{name:"ThrownValue",message:formatThrownValue(error)};let code=error.code;return{name:error.name||void 0,message:error.message||error.name,stack:error.stack,code:typeof code=="string"||typeof code=="number"?code:void 0}}function createAssistantMessageDiagnostic(type,error,details){return{type,timestamp:Date.now(),error:extractDiagnosticError(error),details}}function appendAssistantMessageDiagnostic(message,diagnostic){message.diagnostics=[...message.diagnostics??[],diagnostic]}export{subscribeAssistantCapture,setAssistantReader,enableAssistantCapture,pushAssistantProgress,pushAssistantSignature,pushAssistantCompleteBlock,assistantRecords,assistantEvents,getAssistantCapture,isAssistantProgress,carryAssistantCapture,EventStream,AssistantMessageEventStream,createAssistantMessageEventStream,formatThrownValue,extractDiagnosticError,createAssistantMessageDiagnostic,appendAssistantMessageDiagnostic};