maka-agent 0.2.0-dev.23.20260906 → 0.2.0-dev.25.20260908

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 (215) hide show
  1. package/README.md +38 -19
  2. package/README.zh-CN.md +34 -19
  3. package/dist/acp/maka-acp-agent.js +2 -1
  4. package/dist/acp/session-configuration.js +143 -0
  5. package/dist/acp/session-registry.js +90 -4
  6. package/dist/cli-core.js +2 -0
  7. package/dist/pi-transcript-tools.js +6 -0
  8. package/dist/pi-transcript.js +5 -12
  9. package/dist/pi-tui-layout.js +56 -10
  10. package/dist/pi-tui-pickers.js +102 -1
  11. package/dist/pi-tui-runner.js +230 -46
  12. package/dist/pi-tui-todo.js +221 -0
  13. package/dist/pi-tui-transcript-viewer.js +134 -7
  14. package/dist/runtime-host-cli-context.js +224 -78
  15. package/dist/runtime-host-cli-installation.js +8 -2
  16. package/dist/runtime-host-cli.js +31 -1
  17. package/dist/runtime-host-handoff-surface.js +106 -0
  18. package/dist/runtime-host-installed-update-bootstrap.js +13 -0
  19. package/dist/runtime-host-installed-update-coordinator.js +33 -0
  20. package/dist/runtime-host-lifecycle-transaction.js +9 -1
  21. package/dist/runtime-host-local-handoff.js +48 -5
  22. package/dist/runtime-host-local-managed-activation.js +84 -0
  23. package/dist/runtime-host-local-source-retirement.js +18 -2
  24. package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
  25. package/dist/runtime-host-operator-retirement.js +50 -0
  26. package/dist/runtime-host-service-manager.js +3 -3
  27. package/dist/runtime-host-session-channel.js +13 -0
  28. package/dist/runtime-host-session-driver.js +46 -26
  29. package/dist/runtime-host-session-update.js +62 -0
  30. package/dist/runtime-host-tui-command.js +38 -73
  31. package/dist/runtime-host-tui-context.js +1 -0
  32. package/dist/runtime-host-tui-owner.js +226 -0
  33. package/dist/tui-copy-catalog.js +219 -6
  34. package/dist/tui-host-owner-copy.js +24 -0
  35. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  36. package/node_modules/@maka/core/dist/agent-run.js +1 -0
  37. package/node_modules/@maka/core/dist/attachments.js +3 -0
  38. package/node_modules/@maka/core/dist/canonical-json.js +27 -0
  39. package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
  40. package/node_modules/@maka/core/dist/capabilities.js +35 -0
  41. package/node_modules/@maka/core/dist/computer-use.js +0 -1
  42. package/node_modules/@maka/core/dist/display-redaction.js +18 -0
  43. package/node_modules/@maka/core/dist/llm-connections.js +13 -0
  44. package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
  45. package/node_modules/@maka/core/dist/model-failure.js +40 -0
  46. package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
  47. package/node_modules/@maka/core/dist/model-metadata.js +7 -3
  48. package/node_modules/@maka/core/dist/model-web-search.js +5 -4
  49. package/node_modules/@maka/core/dist/provider-registry.js +45 -4
  50. package/node_modules/@maka/core/dist/record-schema.js +9 -2
  51. package/node_modules/@maka/core/dist/redaction.js +35 -0
  52. package/node_modules/@maka/core/dist/run-composition.js +77 -2
  53. package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
  54. package/node_modules/@maka/core/dist/runtime-event.js +37 -6
  55. package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
  56. package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
  57. package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
  58. package/node_modules/@maka/core/dist/session.js +84 -56
  59. package/node_modules/@maka/core/dist/slash-command-catalog.js +3 -0
  60. package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
  61. package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
  62. package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
  63. package/node_modules/@maka/core/package.json +5 -0
  64. package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
  65. package/node_modules/@maka/mcp/dist/index.js +131 -1
  66. package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
  67. package/node_modules/@maka/runtime/dist/agent-run.js +351 -105
  68. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +29 -13
  69. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
  70. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +184 -183
  71. package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
  72. package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
  73. package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
  74. package/node_modules/@maka/runtime/dist/conversation-copy.js +158 -15
  75. package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
  76. package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
  77. package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
  78. package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
  79. package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
  80. package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +174 -0
  81. package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
  82. package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
  83. package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
  84. package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
  85. package/node_modules/@maka/runtime/dist/model-fetcher.js +49 -2
  86. package/node_modules/@maka/runtime/dist/model-history.js +0 -112
  87. package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
  88. package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
  89. package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
  90. package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
  91. package/node_modules/@maka/runtime/dist/plugin-runtime.js +20 -10
  92. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
  93. package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
  94. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +76 -109
  95. package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
  96. package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
  97. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +83 -55
  98. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +121 -159
  99. package/node_modules/@maka/runtime/dist/runtime-kernel.js +157 -112
  100. package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
  101. package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
  102. package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
  103. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
  104. package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
  105. package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
  106. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +21 -0
  107. package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
  108. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
  109. package/node_modules/@maka/runtime/dist/test-connection.js +16 -17
  110. package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
  111. package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
  112. package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
  113. package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
  114. package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
  115. package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
  116. package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
  117. package/node_modules/@maka/runtime/package.json +2 -0
  118. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
  119. package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
  120. package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
  121. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +179 -0
  122. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +275 -0
  123. package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
  124. package/node_modules/@maka/runtime-host/dist/client/index.js +2 -0
  125. package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
  126. package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
  127. package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +46 -5
  128. package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
  129. package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
  130. package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
  131. package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
  132. package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
  133. package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
  134. package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
  135. package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
  136. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +21 -12
  137. package/node_modules/@maka/runtime-host/dist/protocol/index.js +29 -1
  138. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
  139. package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
  140. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
  141. package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
  142. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
  143. package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
  144. package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
  145. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
  146. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +31 -2
  147. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
  148. package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
  149. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
  150. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
  151. package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
  152. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +144 -33
  153. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
  154. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +45 -16
  155. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
  156. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +96 -17
  157. package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
  158. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
  159. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
  160. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +47 -22
  161. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +76 -6
  162. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  163. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
  164. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +325 -73
  165. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
  166. package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
  167. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +50 -7
  168. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
  169. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
  170. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +12 -55
  171. package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
  172. package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
  173. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
  174. package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
  175. package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
  176. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +20 -2
  177. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +13 -4
  178. package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -0
  179. package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
  180. package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
  181. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -5
  182. package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -198
  183. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
  184. package/node_modules/@maka/storage/dist/execution-stores.js +9 -7
  185. package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
  186. package/node_modules/@maka/storage/dist/git-exec.js +61 -0
  187. package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
  188. package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
  189. package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
  190. package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
  191. package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
  192. package/node_modules/@maka/storage/dist/project-catalog.js +2 -23
  193. package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
  194. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  195. package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
  196. package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
  197. package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
  198. package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
  199. package/node_modules/@maka/storage/dist/session-bundle-policy.js +26 -5
  200. package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
  201. package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
  202. package/node_modules/@maka/storage/dist/session-repository.js +767 -0
  203. package/node_modules/@maka/storage/dist/session-store.js +25 -99
  204. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
  205. package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
  206. package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +7 -1
  207. package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
  208. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +130 -27
  209. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
  210. package/node_modules/@maka/storage/dist/storage-id.js +28 -0
  211. package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +138 -0
  212. package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
  213. package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
  214. package/node_modules/@maka/storage/package.json +2 -0
  215. package/package.json +1 -1
@@ -19,6 +19,22 @@
19
19
  import { encodeProtocolMessage, } from '../protocol/index.js';
20
20
  const MAX_QUEUED_FRAMES = 64;
21
21
  const MAX_QUEUED_BYTES = 2 * 1024 * 1024;
22
+ function frameLane(frame) {
23
+ if ('operation' in frame) {
24
+ if (frame.operation === 'subscription.close')
25
+ return 'barrier';
26
+ if (frame.operation === 'session.transcript.page' ||
27
+ frame.operation === 'artifact.query' ||
28
+ frame.operation === 'runtime.resource.query')
29
+ return 'bulk';
30
+ return 'control';
31
+ }
32
+ if (frame.kind === 'subscription.closed')
33
+ return 'barrier';
34
+ if (frame.kind === 'subscription.runtime_resource_pty_data')
35
+ return 'pty';
36
+ return frame.kind.startsWith('subscription.') ? 'state' : 'control';
37
+ }
22
38
  export class RuntimeHostOutboundQueueError extends Error {
23
39
  code;
24
40
  constructor(code) {
@@ -37,6 +53,8 @@ export class BoundedSerialOutboundWriter {
37
53
  #writing = false;
38
54
  #drainTask;
39
55
  #closed = false;
56
+ #controlBurst = 0;
57
+ #dataLane = 0;
40
58
  constructor(transport, onFailure) {
41
59
  this.#transport = transport;
42
60
  this.#onFailure = onFailure;
@@ -65,7 +83,7 @@ export class BoundedSerialOutboundWriter {
65
83
  throw failure;
66
84
  }
67
85
  const flushed = new Promise((resolve, reject) => {
68
- this.#queue.push({ message, resolve, reject });
86
+ this.#queue.push({ message, lane: frameLane(frame), resolve, reject });
69
87
  this.#queuedBytes += message.byteLength;
70
88
  if (!this.#writing) {
71
89
  this.#writing = true;
@@ -88,7 +106,7 @@ export class BoundedSerialOutboundWriter {
88
106
  async #drain() {
89
107
  try {
90
108
  while (!this.#closed) {
91
- const queued = this.#queue[0];
109
+ const queued = this.#nextFrame();
92
110
  if (!queued)
93
111
  return;
94
112
  try {
@@ -100,7 +118,7 @@ export class BoundedSerialOutboundWriter {
100
118
  }
101
119
  if (this.#closed)
102
120
  return;
103
- this.#queue.shift();
121
+ this.#queue.splice(this.#queue.indexOf(queued), 1);
104
122
  this.#queuedBytes -= queued.message.byteLength;
105
123
  queued.resolve();
106
124
  }
@@ -118,6 +136,30 @@ export class BoundedSerialOutboundWriter {
118
136
  this.close(error);
119
137
  this.#onFailure();
120
138
  }
139
+ #nextFrame() {
140
+ // Close acknowledgements are fences: no lower-priority subscription
141
+ // frames may arrive after a Client has retired their correlation ID.
142
+ const barrier = this.#queue.findIndex((frame) => frame.lane === 'barrier');
143
+ if (barrier === 0)
144
+ return this.#queue[0];
145
+ const eligible = barrier < 0 ? this.#queue : this.#queue.slice(0, barrier);
146
+ const control = eligible.find((frame) => frame.lane === 'control');
147
+ if (control && this.#controlBurst < 8) {
148
+ this.#controlBurst += 1;
149
+ return control;
150
+ }
151
+ const lanes = ['state', 'bulk', 'pty'];
152
+ for (let offset = 0; offset < lanes.length; offset += 1) {
153
+ const lane = (this.#dataLane + offset) % lanes.length;
154
+ const frame = eligible.find((candidate) => candidate.lane === lanes[lane]);
155
+ if (!frame)
156
+ continue;
157
+ this.#dataLane = (lane + 1) % lanes.length;
158
+ this.#controlBurst = 0;
159
+ return frame;
160
+ }
161
+ return control;
162
+ }
121
163
  }
122
164
  function asError(error) {
123
165
  return error instanceof Error ? error : new Error(String(error));
@@ -25,10 +25,14 @@ import { executionBoundaryDisplayMode, } from '@maka/core/sandbox-boundary';
25
25
  import { DEFAULT_SESSION_NAME, normalizeUserSessionName } from '@maka/core/session-name';
26
26
  import { isSessionStartModeLabel as isExecutionSemanticLabel, sessionStartModeSpec, } from '@maka/core/session-start-mode';
27
27
  import { isWorkHubCoordinationSessionId, isWorkHubCoordinationSessionTarget, } from '@maka/core/session';
28
- import { isSessionNotFoundError, SessionMetadataConflictError, SessionMetadataVersionConflictError, SessionReadMarkerMessageNotFoundError, } from '@maka/storage/execution-stores';
28
+ import { isSessionNotFoundError, SessionMetadataConflictError, SessionMetadataVersionConflictError, } from '@maka/storage/execution-stores';
29
+ import { isVisibleSessionMessage } from '@maka/storage/session-message-projection';
29
30
  import { SessionConfigurationRevisionConflictError, SessionConfigurationTransitionError, } from '@maka/runtime/session-manager';
30
31
  import { decodeSessionCatalogProjection, decodeSharedSessionCatalogProjection, SESSION_CATALOG_LIVE_RUN_STATE_SCHEMA_VERSION, SESSION_CATALOG_LABEL_MAX_BYTES, SESSION_CATALOG_LABEL_MAX_ITEMS, SESSION_CATALOG_MODEL_MAX_BYTES, SESSION_CATALOG_PAGE_MAX_ITEMS, SESSION_CATALOG_RESULT_MAX_BYTES, SESSION_CATALOG_RUNNING_TURN_MAX_ITEMS, projectSessionTurnLandmarkForWire, SESSION_TURN_QUERY_RESULT_MAX_BYTES, projectSessionTurnContributionForWire, } from '../protocol/index.js';
31
32
  import { WorkspaceResolutionError } from './workspace-resolver.js';
33
+ /** One page of the backwards scan a read marker walks to find the newest visible message. */
34
+ const SESSION_READ_MARKER_TAIL_MAX_MESSAGES = 64;
35
+ const SESSION_READ_MARKER_TAIL_MAX_BYTES = 256 * 1024;
32
36
  export class SessionOperationFailure extends Error {
33
37
  code;
34
38
  constructor(code, message) {
@@ -130,6 +134,7 @@ export class HostSessionCatalogCoordinator {
130
134
  'session.turns.query': (input) => this.#queryTurns(input),
131
135
  };
132
136
  #stores;
137
+ #turnIndex;
133
138
  #runtimePolicy;
134
139
  #manager;
135
140
  #admission;
@@ -139,6 +144,7 @@ export class HostSessionCatalogCoordinator {
139
144
  #sessionAccessAuthority;
140
145
  constructor(options) {
141
146
  this.#stores = options.stores;
147
+ this.#turnIndex = options.turnIndex;
142
148
  this.#runtimePolicy = options.runtimePolicy;
143
149
  this.#manager = options.manager;
144
150
  this.#admission = options.admission;
@@ -297,7 +303,7 @@ export class HostSessionCatalogCoordinator {
297
303
  let maxContributions = input.maxContributions;
298
304
  let throughSequence = input.throughSequence;
299
305
  while (true) {
300
- const page = await this.#stores.readTurnContributionsSnapshot(input.sessionId, throughSequence, input.position, maxContributions);
306
+ const page = await this.#turnIndex.readDurableTurnContributions(input.sessionId, throughSequence, input.position, maxContributions);
301
307
  throughSequence = page.throughSequence;
302
308
  const result = {
303
309
  sessionId: input.sessionId,
@@ -323,7 +329,7 @@ export class HostSessionCatalogCoordinator {
323
329
  }
324
330
  async #queryTurnLandmarks(input) {
325
331
  try {
326
- const snapshot = await this.#stores.readTurnLandmarksSnapshot(input.sessionId, input.maxLandmarks);
332
+ const snapshot = await this.#turnIndex.readDurableTurnLandmarks(input.sessionId, input.maxLandmarks);
327
333
  return {
328
334
  ok: true,
329
335
  result: {
@@ -533,7 +539,7 @@ export class HostSessionCatalogCoordinator {
533
539
  if (isWorkHubCoordinationSessionTarget(current.header)) {
534
540
  return readMarkerFailure('operation_conflict', 'WorkHub Coordination Session read state requires WorkHub authority');
535
541
  }
536
- await this.#stores.markSessionReadThroughMessage(input.sessionId, input.readThroughMessageId);
542
+ await this.#clearUnreadAtTranscriptTail(current, input.readThroughMessageId);
537
543
  await this.#continuity.refreshCanonical(input.sessionId, lease);
538
544
  return {
539
545
  ok: true,
@@ -543,9 +549,6 @@ export class HostSessionCatalogCoordinator {
543
549
  catch (error) {
544
550
  if (isNotFound(error))
545
551
  return readMarkerFailure('not_found', 'Session does not exist');
546
- if (error instanceof SessionReadMarkerMessageNotFoundError) {
547
- return readMarkerFailure('invalid_request', error.message);
548
- }
549
552
  if (error instanceof SessionMetadataVersionConflictError) {
550
553
  return readMarkerFailure('operation_conflict', 'Session changed while advancing the read marker');
551
554
  }
@@ -554,6 +557,46 @@ export class HostSessionCatalogCoordinator {
554
557
  }
555
558
  });
556
559
  }
560
+ /**
561
+ * A Session is read once the client has caught up with the ledger's newest
562
+ * visible message. `hasUnread` is the only thing the marker decides and every
563
+ * Turn raises it again, so a client still behind the tail changes nothing.
564
+ */
565
+ async #clearUnreadAtTranscriptTail(record, readThroughMessageId) {
566
+ const latest = await this.#newestVisibleMessage(record.header.id);
567
+ if (latest?.id !== readThroughMessageId)
568
+ return;
569
+ if (record.header.lastReadMessageId === readThroughMessageId && !record.header.hasUnread) {
570
+ return;
571
+ }
572
+ await this.#stores.updateHeaderVersioned(record.header.id, { lastReadMessageId: readThroughMessageId, hasUnread: false }, record.revision);
573
+ }
574
+ /**
575
+ * The ledger's newest message a client can actually see. A Turn that ends on
576
+ * tool traffic can put more hidden records at the tail than one page holds,
577
+ * so the scan pages past them instead of reading the Session as never caught
578
+ * up and leaving it unread for good.
579
+ */
580
+ async #newestVisibleMessage(sessionId) {
581
+ let throughSequence;
582
+ let position;
583
+ while (true) {
584
+ const page = await this.#turnIndex.readDurableRecords(sessionId, {
585
+ direction: 'older',
586
+ maxMessages: SESSION_READ_MARKER_TAIL_MAX_MESSAGES,
587
+ maxStoredBytes: SESSION_READ_MARKER_TAIL_MAX_BYTES,
588
+ ...(throughSequence === undefined ? {} : { throughSequence }),
589
+ ...(position === undefined ? {} : { position }),
590
+ });
591
+ const visible = page.records.find(({ message }) => isVisibleSessionMessage(message));
592
+ if (visible)
593
+ return visible.message;
594
+ if (page.nextPosition === null)
595
+ return undefined;
596
+ throughSequence = page.throughSequence;
597
+ position = page.nextPosition;
598
+ }
599
+ }
557
600
  async #committedUpdate(sessionId, lease) {
558
601
  await this.#continuity.refreshCanonical(sessionId, lease);
559
602
  return {
@@ -43,6 +43,21 @@ export class SessionContinuityCoordinator {
43
43
  onPublicationFailure;
44
44
  onCatalogChanged;
45
45
  handlers = {
46
+ 'subscription.pty_interest.set': async (input, context) => {
47
+ const subscriber = this.#ownedSubscriber(context.connectionId, input.subscriptionId);
48
+ if (!subscriber || !this.#canObserve(subscriber, subscriber.sessionId)) {
49
+ return {
50
+ ok: false,
51
+ error: { code: 'not_found', message: 'Session subscription was not found' },
52
+ };
53
+ }
54
+ subscriber.ptyInterests = new Set(input.refs);
55
+ // An already writing frame may finish. Everything else belongs to the
56
+ // current visible set; reacquiring uses a fresh terminal snapshot.
57
+ subscriber.ptyQueue = subscriber.ptyQueue.filter((entry, index) => (index === 0 && subscriber.ptyPumping) || subscriber.ptyInterests.has(entry.frame.ref));
58
+ subscriber.ptyQueuedBytes = subscriber.ptyQueue.reduce((bytes, entry) => bytes + entry.encodedBytes, 0);
59
+ return { ok: true, result: { subscriptionId: input.subscriptionId } };
60
+ },
46
61
  'subscription.open': async (input, context) => {
47
62
  const result = await this.#open(context, input);
48
63
  return result.ok
@@ -267,35 +282,36 @@ export class SessionContinuityCoordinator {
267
282
  this.#scheduleSessionDomainChanges(sessionId, changes);
268
283
  }
269
284
  }
270
- /** Publish live PTY bytes on the same ordered Session subscription as its durable projection. */
285
+ /** PTY congestion never consumes Session sequence numbers or its queue budget. */
271
286
  async enqueueRuntimeResourcePtyData(event) {
272
- if (this.#closed ||
273
- Buffer.byteLength(event.data, 'utf8') > SESSION_RUNTIME_RESOURCE_PTY_DATA_MAX_BYTES) {
287
+ if (this.#closed)
274
288
  return;
275
- }
276
289
  try {
277
- await this.sessionAdmission.enqueueDetached(event.sessionId, () => {
278
- if (this.#closed)
279
- return;
280
- const state = this.#sessions.get(event.sessionId);
281
- if (!state)
282
- return;
283
- for (const subscriber of state.subscribers.values()) {
284
- const frame = {
285
- kind: 'subscription.runtime_resource_pty_data',
286
- hostEpoch: this.#hostEpoch,
287
- subscriptionId: subscriber.subscriptionId,
288
- sequence: subscriber.nextSequence,
289
- sessionId: event.sessionId,
290
- ref: event.ref,
291
- ptySequence: event.sequence,
292
- data: event.data,
293
- };
294
- if (Buffer.byteLength(JSON.stringify(frame), 'utf8') <= SESSION_SUBSCRIPTION_FRAME_MAX_BYTES) {
295
- this.#enqueue(subscriber, frame);
296
- }
290
+ const state = this.#sessions.get(event.sessionId);
291
+ if (!state)
292
+ return;
293
+ for (const subscriber of state.subscribers.values()) {
294
+ if (!this.#canObserve(subscriber, event.sessionId)) {
295
+ this.#closeSubscriber(subscriber, 'access_revoked');
296
+ continue;
297
297
  }
298
- });
298
+ if (!subscriber.ptyInterests.has(event.ref))
299
+ continue;
300
+ const frame = {
301
+ kind: 'subscription.runtime_resource_pty_data',
302
+ hostEpoch: this.#hostEpoch,
303
+ subscriptionId: subscriber.subscriptionId,
304
+ sessionId: event.sessionId,
305
+ ref: event.ref,
306
+ ptySequence: event.sequence,
307
+ ...(Buffer.byteLength(event.data, 'utf8') > SESSION_RUNTIME_RESOURCE_PTY_DATA_MAX_BYTES
308
+ ? { data: '', reset: true }
309
+ : { data: event.data }),
310
+ };
311
+ if (Buffer.byteLength(JSON.stringify(frame), 'utf8') <= SESSION_SUBSCRIPTION_FRAME_MAX_BYTES) {
312
+ this.#enqueuePty(subscriber, frame);
313
+ }
314
+ }
299
315
  }
300
316
  catch (error) {
301
317
  this.onPublicationFailure(error);
@@ -705,6 +721,10 @@ export class SessionContinuityCoordinator {
705
721
  nextSequence: 1,
706
722
  lastFlushedSequence: 0,
707
723
  queue: [],
724
+ ptyQueue: [],
725
+ ptyInterests: new Set(),
726
+ ptyQueuedBytes: 0,
727
+ ptyPumping: false,
708
728
  queuedBytes: 0,
709
729
  pumping: false,
710
730
  terminalQueued: false,
@@ -1073,6 +1093,7 @@ export class SessionContinuityCoordinator {
1073
1093
  return;
1074
1094
  subscriber.activated = true;
1075
1095
  this.#pump(subscriber);
1096
+ this.#pumpPty(subscriber);
1076
1097
  }
1077
1098
  #abortSubscription(connectionId, subscriptionId) {
1078
1099
  const subscriber = this.#ownedSubscriber(connectionId, subscriptionId);
@@ -1164,6 +1185,45 @@ export class SessionContinuityCoordinator {
1164
1185
  #evictSlowSubscriber(subscriber) {
1165
1186
  this.#closeSubscriber(subscriber, 'slow_consumer');
1166
1187
  }
1188
+ #enqueuePty(subscriber, frame) {
1189
+ if (subscriber.phase !== 'open' || subscriber.terminalQueued)
1190
+ return;
1191
+ let encodedBytes = encodeProtocolMessage(frame).byteLength;
1192
+ if (subscriber.ptyQueue.length >= 8 || subscriber.ptyQueuedBytes + encodedBytes > 128 * 1024) {
1193
+ // Reset is session-wide for terminal consumers, so one marker covers
1194
+ // every omitted resource without an unbounded per-resource dirty set.
1195
+ const inFlight = subscriber.ptyPumping ? subscriber.ptyQueue[0] : undefined;
1196
+ subscriber.ptyQueue = inFlight ? [inFlight] : [];
1197
+ subscriber.ptyQueuedBytes = inFlight?.encodedBytes ?? 0;
1198
+ frame = { ...frame, data: '', reset: true };
1199
+ encodedBytes = encodeProtocolMessage(frame).byteLength;
1200
+ }
1201
+ subscriber.ptyQueue.push({ frame, encodedBytes });
1202
+ subscriber.ptyQueuedBytes += encodedBytes;
1203
+ this.#pumpPty(subscriber);
1204
+ }
1205
+ #pumpPty(subscriber) {
1206
+ if (subscriber.ptyPumping || !subscriber.activated || subscriber.phase !== 'open')
1207
+ return;
1208
+ const queued = subscriber.ptyQueue[0];
1209
+ if (!queued)
1210
+ return;
1211
+ subscriber.ptyPumping = true;
1212
+ void Promise.resolve()
1213
+ .then(() => {
1214
+ if (subscriber.phase !== 'open')
1215
+ return;
1216
+ return subscriber.sink.send(queued.frame);
1217
+ })
1218
+ .then(() => {
1219
+ subscriber.ptyPumping = false;
1220
+ if (subscriber.ptyQueue[0] === queued) {
1221
+ subscriber.ptyQueue.shift();
1222
+ subscriber.ptyQueuedBytes -= queued.encodedBytes;
1223
+ }
1224
+ this.#pumpPty(subscriber);
1225
+ }, () => this.#removeSubscriber(subscriber));
1226
+ }
1167
1227
  #closeSubscriber(subscriber, reason) {
1168
1228
  if (subscriber.phase !== 'open')
1169
1229
  return;
@@ -1330,6 +1390,8 @@ export class SessionContinuityCoordinator {
1330
1390
  subscriber.phase = 'closed';
1331
1391
  subscriber.queue = [];
1332
1392
  subscriber.queuedBytes = 0;
1393
+ subscriber.ptyQueue = [];
1394
+ subscriber.ptyQueuedBytes = 0;
1333
1395
  const state = this.#sessions.get(subscriber.sessionId);
1334
1396
  const removed = state?.subscribers.delete(subscriber.subscriptionId);
1335
1397
  this.#subscriptions.delete(subscriber.subscriptionId);
@@ -439,8 +439,11 @@ export class HostSessionRetirementCoordinator {
439
439
  throw new SessionRetirementBusyError(`Session ${sessionId} has a live derived effect`);
440
440
  }
441
441
  const header = requireFamilyRecord(family, sessionId).header;
442
- if (!header.subagentParent && (await this.#graph.hasLiveSessionState(sessionId))) {
443
- throw new SessionRetirementBusyError(`Session ${sessionId} has a live Agent Graph`);
442
+ if (!header.subagentParent) {
443
+ const graph = await this.#graph.readRetirementDisposition(sessionId);
444
+ if (graph.kind === 'busy') {
445
+ throw new SessionRetirementBusyError(`Session ${sessionId} has an Agent Graph that is ${graph.status}`);
446
+ }
444
447
  }
445
448
  if (!header.subagentParent && this.#graphWake.hasLiveSessionState(sessionId)) {
446
449
  throw new SessionRetirementBusyError(`Session ${sessionId} has an active Agent Graph supervisor wake`);
@@ -407,10 +407,8 @@ export class HostSessionRevisionCoordinator {
407
407
  // An empty copy carries no source state, including no in-progress Todo.
408
408
  copyCurrent: kind === 'branch' && slice.beforeTs === undefined && input.sourceTurnId !== undefined,
409
409
  });
410
- if (copiedMessages.length > 0) {
411
- await this.#stores.sessionStore.appendMessages(input.targetSessionId, [...copiedMessages]);
412
- }
413
- await this.#stores.sessionStore.appendMessage(input.targetSessionId, conversationCopyStartNote(kind, input, createInput));
410
+ // `cloneConversationRuntimeLedger` already wrote the copy's own spine, and
411
+ // the copy reads back off that: nothing here writes a second transcript.
414
412
  await this.#stores.sessionStore.updateHeader(input.targetSessionId, {
415
413
  conversationCopy: {
416
414
  ...createInput.conversationCopy,
@@ -596,21 +594,13 @@ export class HostSessionRevisionCoordinator {
596
594
  }, header.id);
597
595
  await this.#stores.sessionStore.discardStableConversationCopy(header.id, copy.requestFingerprint);
598
596
  }
597
+ /**
598
+ * A revision copy that admitted a turn of its own. The admission ledger is
599
+ * the whole answer: a copy clones the source's history but never its
600
+ * admissions, so every row it holds was admitted on this session.
601
+ */
599
602
  async #hasAdmittedRevisionTurn(sessionId) {
600
- if ((await this.#stores.agentRunStore.listRootTurnAdmissionsForRecovery(sessionId)).length > 0) {
601
- return true;
602
- }
603
- const messages = await this.#stores.sessionStore.readMessagesForRecovery(sessionId);
604
- let boundary = -1;
605
- for (let index = 0; index < messages.length; index += 1) {
606
- const message = messages[index];
607
- if (message.type === 'system_note' &&
608
- message.kind === 'session_start' &&
609
- isRevisionStartData(message.data)) {
610
- boundary = index;
611
- }
612
- }
613
- return boundary >= 0 && messages.slice(boundary + 1).some((message) => message.type === 'user');
603
+ return ((await this.#stores.agentRunStore.listRootTurnAdmissionsForRecovery(sessionId)).length > 0);
614
604
  }
615
605
  async #hasCommittedConversationCopyDependent(sessionId) {
616
606
  return (await this.#stores.sessionStore.listHeaders()).some((header) => header.conversationCopy?.state === 'committed' &&
@@ -638,49 +628,12 @@ function conversationCopyFingerprint(kind, input) {
638
628
  ];
639
629
  return `sha256:${createHash('sha256').update(JSON.stringify(identity)).digest('hex')}`;
640
630
  }
641
- function conversationCopyStartNote(kind, input, createInput) {
642
- const base = {
643
- type: 'system_note',
644
- id: randomUUID(),
645
- ts: Date.now(),
646
- kind: 'session_start',
647
- };
648
- if (kind !== 'revision') {
649
- // Empty copies record provenance without a branch turn.
650
- return {
651
- ...base,
652
- data: {
653
- parentSessionId: input.sourceSessionId,
654
- ...(input.sourceTurnId === undefined ? {} : { branchOfTurnId: input.sourceTurnId }),
655
- },
656
- };
657
- }
658
- if (input.sourceTurnId === undefined) {
659
- throw new Error('Session revision copy requires a turn boundary');
660
- }
661
- return {
662
- ...base,
663
- data: {
664
- revisionRootSessionId: createInput.revisionRootSessionId,
665
- revisionParentSessionId: input.sourceSessionId,
666
- revisionOfTurnId: input.sourceTurnId,
667
- revisionIndex: createInput.revisionIndex,
668
- revisionState: 'preparing',
669
- },
670
- };
671
- }
672
631
  function conversationCopySemanticKind(kind, input) {
673
632
  return kind === 'branch' && input.intent === 'side_conversation' ? input.intent : kind;
674
633
  }
675
634
  function persistedConversationCopyKind(kind) {
676
635
  return kind === 'revision' ? 'revision' : 'branch';
677
636
  }
678
- function isRevisionStartData(value) {
679
- return (!!value &&
680
- typeof value === 'object' &&
681
- !Array.isArray(value) &&
682
- 'revisionRootSessionId' in value);
683
- }
684
637
  function collectArchivedToolResultPlaceholders(events, messages, copyTurnIds, operationalEvents) {
685
638
  const retainedTurnIds = new Set(copyTurnIds);
686
639
  const archives = new Map();
@@ -689,6 +642,8 @@ function collectArchivedToolResultPlaceholders(events, messages, copyTurnIds, op
689
642
  return true;
690
643
  if (!isArchivedToolResultPlaceholder(value))
691
644
  return false;
645
+ if (value.rewriteVersion === 2)
646
+ return true;
692
647
  addDescriptor(value);
693
648
  return true;
694
649
  };
@@ -731,6 +686,8 @@ function collectArchivedToolResultPlaceholders(events, messages, copyTurnIds, op
731
686
  continue;
732
687
  }
733
688
  if (message.content.kind === 'archived_tool_result') {
689
+ if (message.content.rewriteVersion === 2 && message.content.resourceRef)
690
+ continue;
734
691
  if (!message.content.artifactId && !message.content.bodySha256)
735
692
  continue;
736
693
  if (!message.content.artifactId || !message.content.bodySha256)
@@ -16,26 +16,11 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- const CONTEXT_GARBAGE_BATCH_BLOBS = 64;
20
- async function retireContextSession(contextOffload, sessionId) {
21
- await contextOffload.retireSession(sessionId);
22
- for (;;) {
23
- const collected = await contextOffload.collectGarbage({
24
- olderThan: Number.MAX_SAFE_INTEGER,
25
- maxBlobs: CONTEXT_GARBAGE_BATCH_BLOBS,
26
- maxBytes: Number.MAX_SAFE_INTEGER,
27
- });
28
- if (!collected.hasMore)
29
- return;
30
- }
31
- }
32
19
  export async function purgeSessionSidecars(authority, sessionId) {
33
20
  const outcomes = await Promise.allSettled([
34
21
  authority.artifacts.purgeSessionArtifacts(sessionId),
35
22
  authority.sessionTodo.purgeSessionState(sessionId),
36
- ...(authority.contextOffload
37
- ? [retireContextSession(authority.contextOffload, sessionId)]
38
- : []),
23
+ ...(authority.contextOffload ? [authority.contextOffload.retireSession(sessionId)] : []),
39
24
  authority.purgeOperationalState(sessionId),
40
25
  ]);
41
26
  const failures = outcomes.flatMap((outcome) => outcome.status === 'rejected' ? [outcome.reason] : []);
@@ -63,7 +63,6 @@ export async function createSessionTranscriptBootstrap(input) {
63
63
  });
64
64
  const bootstrap = {
65
65
  throughSequence: input.throughSequence,
66
- durableCoverage: projection === 'shared' ? 'projected' : 'complete',
67
66
  overlayMessageCount: overlayMessages.length,
68
67
  durable: pageFromSelection(state, 'durable', 'older', rangeEdges.selected, input.throughSequence, rangeEdges.rangeBoundarySequence, rangeEdges.protectedTurnSequence),
69
68
  overlay: pageFromSelection(state, 'overlay', 'older', selectedOverlay),