maka-agent 0.2.0-dev.14.20260902 → 0.2.0-dev.16.20260903

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 (160) hide show
  1. package/dist/cli-core.js +0 -1
  2. package/dist/pi-transcript.js +48 -0
  3. package/dist/pi-tui-form-interaction.js +497 -0
  4. package/dist/pi-tui-pickers.js +256 -55
  5. package/dist/pi-tui-runner.js +215 -12
  6. package/dist/runtime-host-cli.js +17 -42
  7. package/dist/runtime-host-lifecycle-transaction.js +31 -13
  8. package/dist/runtime-host-managed-lifecycle-manager.js +1 -1
  9. package/dist/runtime-host-onboarding.js +3 -1
  10. package/dist/runtime-host-openrc-service.js +469 -0
  11. package/dist/runtime-host-run-command.js +6 -2
  12. package/dist/runtime-host-service-command.js +3 -3
  13. package/dist/runtime-host-service-management-command.js +43 -15
  14. package/dist/runtime-host-session-channel.js +3 -0
  15. package/dist/runtime-host-session-driver.js +21 -6
  16. package/dist/runtime-host-setup-command.js +30 -31
  17. package/dist/tui-context-refresh.js +103 -0
  18. package/dist/tui-copy-catalog.js +73 -0
  19. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  20. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  21. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  22. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  23. package/node_modules/@maka/core/dist/chat-model-choice.js +4 -0
  24. package/node_modules/@maka/core/dist/computer-use.js +27 -92
  25. package/node_modules/@maka/core/dist/interaction.js +577 -3
  26. package/node_modules/@maka/core/dist/llm-connections.js +4 -0
  27. package/node_modules/@maka/core/dist/local-memory.js +2 -2
  28. package/node_modules/@maka/core/dist/model-call-usage-projection.js +3 -0
  29. package/node_modules/@maka/core/dist/model-metadata.js +26 -7
  30. package/node_modules/@maka/core/dist/model-thinking.js +17 -0
  31. package/node_modules/@maka/core/dist/provider-registry.js +1 -0
  32. package/node_modules/@maka/core/dist/runtime-event.js +23 -1
  33. package/node_modules/@maka/core/dist/scheduled-task.js +4 -0
  34. package/node_modules/@maka/core/dist/session.js +95 -2
  35. package/node_modules/@maka/core/dist/text-sanitize.js +19 -0
  36. package/node_modules/@maka/core/dist/usage-ledger-merge.js +3 -0
  37. package/node_modules/@maka/core/dist/usage-record-schema.js +11 -5
  38. package/node_modules/@maka/core/dist/workhub-creation-intent.js +75 -9
  39. package/node_modules/@maka/core/dist/workhub-session-resolver.js +50 -0
  40. package/node_modules/@maka/core/package.json +2 -0
  41. package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -1
  42. package/node_modules/@maka/eval/dist/harness-executor.js +3 -2
  43. package/node_modules/@maka/eval/dist/install-preflight.js +39 -10
  44. package/node_modules/@maka/eval/dist/process-termination.js +83 -0
  45. package/node_modules/@maka/mcp/dist/sep-2243.js +3 -3
  46. package/node_modules/@maka/runtime/dist/agent-run.js +6 -3
  47. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +332 -118
  48. package/node_modules/@maka/runtime/dist/ai-sdk-compaction-contract.js +6 -1
  49. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +171 -341
  50. package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +3 -26
  51. package/node_modules/@maka/runtime/dist/bundled-skill-catalog.generated.js +1 -1
  52. package/node_modules/@maka/runtime/dist/computer-use-codec.js +1 -141
  53. package/node_modules/@maka/runtime/dist/computer-use-tools.js +23 -105
  54. package/node_modules/@maka/runtime/dist/context-budget-policy.js +20 -26
  55. package/node_modules/@maka/runtime/dist/context-budget.js +18 -20
  56. package/node_modules/@maka/runtime/dist/conversation-copy.js +23 -19
  57. package/node_modules/@maka/runtime/dist/cua-frame-state.js +1 -69
  58. package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +267 -0
  59. package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +6 -9
  60. package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +67 -23
  61. package/node_modules/@maka/runtime/dist/history-compact-summary-validation.js +11 -18
  62. package/node_modules/@maka/runtime/dist/history-compaction.js +66 -73
  63. package/node_modules/@maka/runtime/dist/interaction-authority.js +69 -0
  64. package/node_modules/@maka/runtime/dist/mcp-tools.js +13 -1
  65. package/node_modules/@maka/runtime/dist/memory-extraction.js +11 -11
  66. package/node_modules/@maka/runtime/dist/model-adapter.js +24 -6
  67. package/node_modules/@maka/runtime/dist/model-factory.js +14 -5
  68. package/node_modules/@maka/runtime/dist/oauth-provider-contracts.js +28 -1
  69. package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +7 -6
  70. package/node_modules/@maka/runtime/dist/opencode-session-header.js +29 -0
  71. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +11 -1
  72. package/node_modules/@maka/runtime/dist/runtime-kernel.js +12 -11
  73. package/node_modules/@maka/runtime/dist/runtime-read-model.js +51 -13
  74. package/node_modules/@maka/runtime/dist/scheduled-task-tools.js +3 -0
  75. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +26 -0
  76. package/node_modules/@maka/runtime/dist/session-manager.js +1 -0
  77. package/node_modules/@maka/runtime/dist/session-projection-helpers.js +16 -1
  78. package/node_modules/@maka/runtime/dist/stream-graph-projection.js +6 -0
  79. package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +5 -0
  80. package/node_modules/@maka/runtime/dist/stream-usage-fallback-fetch.js +74 -0
  81. package/node_modules/@maka/runtime/dist/subscription-credentials.js +51 -4
  82. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +47 -3
  83. package/node_modules/@maka/runtime/dist/system-prompt/main-session-prompt.js +20 -1
  84. package/node_modules/@maka/runtime/dist/test-connection.js +18 -11
  85. package/node_modules/@maka/runtime/dist/tool-runtime.js +183 -0
  86. package/node_modules/@maka/runtime/package.json +1 -0
  87. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +14 -0
  88. package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +84 -1
  89. package/node_modules/@maka/runtime-host/dist/client/host-profile.js +68 -51
  90. package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
  91. package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +8 -5
  92. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +342 -36
  93. package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +60 -4
  94. package/node_modules/@maka/runtime-host/dist/peer-mesh/index.js +3 -3
  95. package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +1 -1
  96. package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +48 -60
  97. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +847 -431
  98. package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +33 -74
  99. package/node_modules/@maka/runtime-host/dist/peer-mesh/store.js +82 -69
  100. package/node_modules/@maka/runtime-host/dist/peer-reachability/index.js +21 -0
  101. package/node_modules/@maka/runtime-host/dist/peer-reachability/model.js +202 -0
  102. package/node_modules/@maka/runtime-host/dist/peer-reachability/owner.js +119 -0
  103. package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +245 -0
  104. package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +4 -1
  105. package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +68 -0
  106. package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +15 -8
  107. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +7 -23
  108. package/node_modules/@maka/runtime-host/dist/protocol/index.js +47 -1
  109. package/node_modules/@maka/runtime-host/dist/protocol/oauth.js +25 -1
  110. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
  111. package/node_modules/@maka/runtime-host/dist/protocol/peer-mesh.js +14 -31
  112. package/node_modules/@maka/runtime-host/dist/protocol/scheduled-task.js +19 -6
  113. package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +36 -1
  114. package/node_modules/@maka/runtime-host/dist/protocol/session-revision.js +11 -2
  115. package/node_modules/@maka/runtime-host/dist/protocol/usage-pricing.js +16 -2
  116. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +51 -2
  117. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +35 -2
  118. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
  119. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +12 -10
  120. package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +163 -30
  121. package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +38 -14
  122. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +45 -22
  123. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +78 -20
  124. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +4 -2
  125. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +2 -1
  126. package/node_modules/@maka/runtime-host/dist/server/execution-service.js +49 -28
  127. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -6
  128. package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +130 -27
  129. package/node_modules/@maka/runtime-host/dist/server/interaction-projection.js +30 -1
  130. package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -4
  131. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +16 -3
  132. package/node_modules/@maka/runtime-host/dist/server/oauth-coordinator.js +66 -3
  133. package/node_modules/@maka/runtime-host/dist/server/oauth-execution-authority.js +20 -3
  134. package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +8 -21
  135. package/node_modules/@maka/runtime-host/dist/server/peer-mesh-authority.js +1 -1
  136. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +6 -3
  137. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +72 -12
  138. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +48 -18
  139. package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +43 -26
  140. package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +15 -1
  141. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +229 -4
  142. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +146 -8
  143. package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +32 -6
  144. package/node_modules/@maka/runtime-host/package.json +1 -0
  145. package/node_modules/@maka/storage/dist/agent-run-store.js +18 -5
  146. package/node_modules/@maka/storage/dist/codex-session-adapter.js +14 -0
  147. package/node_modules/@maka/storage/dist/execution-stores.js +5 -0
  148. package/node_modules/@maka/storage/dist/interaction-store.js +13 -1
  149. package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +9 -3
  150. package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +49 -19
  151. package/node_modules/@maka/storage/dist/runtime-policy/oauth-login-receipt-document.js +3 -1
  152. package/node_modules/@maka/storage/dist/runtime-policy/onboarding-transaction.js +13 -5
  153. package/node_modules/@maka/storage/dist/session-copy-cleanup.js +4 -2
  154. package/node_modules/@maka/storage/dist/session-store.js +39 -6
  155. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +16 -9
  156. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +30 -3
  157. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +117 -0
  158. package/node_modules/@maka/storage/dist/sqlite-usage-store.js +29 -6
  159. package/node_modules/@maka/storage/dist/usage-stores.js +3 -1
  160. package/package.json +1 -1
@@ -308,6 +308,10 @@ function interactiveOAuthConnectionSlugBase(providerType) {
308
308
  return 'codex-subscription';
309
309
  case 'xai-oauth':
310
310
  return 'xai-oauth';
311
+ // Shared with the local `gh` credential import so both routes to a Copilot
312
+ // account land on one Connection instead of two.
313
+ case 'github-copilot':
314
+ return 'github-copilot';
311
315
  }
312
316
  }
313
317
  /** Derive an unused OAuth Connection slug without moving allocation into a surface. */
@@ -17,6 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { redactSecrets } from './redaction.js';
20
+ import { truncateUtf16Safe } from './text-sanitize.js';
20
21
  export const LOCAL_MEMORY_MAX_BYTES = 128 * 1024;
21
22
  export const LOCAL_MEMORY_PROMPT_MAX_CHARS = 12_000;
22
23
  export const LOCAL_MEMORY_PROMPT_TRUNCATION_MARKER = '[本地记忆已按长度截断]';
@@ -69,8 +70,7 @@ export function buildLocalMemoryPromptBody(input, context = {}) {
69
70
  return undefined;
70
71
  if (body.length <= LOCAL_MEMORY_PROMPT_MAX_CHARS)
71
72
  return body;
72
- const truncated = body.slice(0, LOCAL_MEMORY_PROMPT_MAX_CHARS);
73
- const boundarySafe = /[\uD800-\uDBFF]$/.test(truncated) ? truncated.slice(0, -1) : truncated;
73
+ const boundarySafe = truncateUtf16Safe(body, LOCAL_MEMORY_PROMPT_MAX_CHARS);
74
74
  return `${boundarySafe.trimEnd()}\n\n${LOCAL_MEMORY_PROMPT_TRUNCATION_MARKER}`;
75
75
  }
76
76
  export function appendManualLocalMemoryEntryDraft(currentDraft, input) {
@@ -106,6 +106,7 @@ export function projectModelCallUsageSummary(attempts, query, now) {
106
106
  total: 0,
107
107
  };
108
108
  let totalCostUsd = 0;
109
+ let totalDurationMs = 0;
109
110
  let cacheHitRequests = 0;
110
111
  let cacheCreateRequests = 0;
111
112
  let errorRequests = 0;
@@ -119,6 +120,7 @@ export function projectModelCallUsageSummary(attempts, query, now) {
119
120
  totals.reasoning += t.reasoning;
120
121
  totals.total += t.total;
121
122
  totalCostUsd += pricedCost(attempt);
123
+ totalDurationMs += attempt.latencyMs;
122
124
  if (t.cacheRead > 0)
123
125
  cacheHitRequests += 1;
124
126
  if (t.cacheWrite > 0)
@@ -130,6 +132,7 @@ export function projectModelCallUsageSummary(attempts, query, now) {
130
132
  range,
131
133
  totalRequests: rows.length,
132
134
  totalCostUsd,
135
+ totalDurationMs,
133
136
  totalTokens: totals,
134
137
  cacheHitRequests,
135
138
  cacheCreateRequests,
@@ -215,11 +215,18 @@ const VOLCENGINE_CODING_PLAN_MODEL_METADATA = {
215
215
  'kimi-k2.6': planModel('Kimi-K2.6', true, 256_000, 32_000),
216
216
  'kimi-k2.7-code': planModel('Kimi-K2.7-Code', true, 256_000, 32_000),
217
217
  };
218
+ // Hand-maintained mirror of the official Agent Plan personal plan page
219
+ // (docs.volcengine.com/docs/82379/2366394) and its model release/retirement
220
+ // announcements (82379/2578669, 82379/2578673): the gateway has no
221
+ // model-list endpoint its plan key can reach and models.dev has no snapshot.
222
+ // The page's table lists windows as "1024k"/"128k"; transcribe those
223
+ // literals as 1_024_000/128_000 (its prose "1M" is the same figure rounded).
224
+ // Re-check the page before editing an entry here.
218
225
  const VOLCENGINE_AGENT_PLAN_MODEL_METADATA = {
219
226
  'ark-code-latest': agentPlanModel('Ark Code Latest', 256_000, 32_000, { vision: true }),
220
- // Agent Plan currently exposes GLM-5.3 in its ark-code-latest routing catalog.
221
- // The underlying model's published limits are 1M context and 128K output.
222
- 'glm-5.3': agentPlanModel('GLM-5.3', 1_000_000, 131_072),
227
+ // The plan page lists "glm-5.3 (glm-latest)": thinking is on by default and
228
+ // cannot be turned off.
229
+ 'glm-5.3': agentPlanModel('GLM-5.3', 1_024_000, 128_000),
223
230
  'doubao-seed-2.0-mini': agentPlanModel('Doubao Seed 2.0 Mini', 256_000, 128_000, {
224
231
  vision: true,
225
232
  }),
@@ -239,11 +246,23 @@ const VOLCENGINE_AGENT_PLAN_MODEL_METADATA = {
239
246
  'doubao-seed-2.0-pro': agentPlanModel('Doubao Seed 2.0 Pro', 256_000, 128_000, {
240
247
  lifecycle: 'deprecated',
241
248
  }),
242
- 'minimax-m2.7': agentPlanModel('MiniMax-M2.7', 200_000, 128_000),
243
- 'minimax-m3': agentPlanModel('MiniMax-M3', 512_000, 128_000, { vision: true }),
244
- 'glm-5.2': agentPlanModel('GLM-5.2', 1_024_000, 128_000),
249
+ 'minimax-m2.7': agentPlanModel('MiniMax-M2.7', 200_000, 128_000, {
250
+ // Upstream retirement notice of 2026-08-04; the gateway routes to minimax-m3.
251
+ lifecycle: 'deprecated',
252
+ }),
253
+ 'minimax-m3': agentPlanModel('MiniMax-M3', 1_024_000, 128_000, { vision: true }),
254
+ 'glm-5.2': agentPlanModel('GLM-5.2', 1_024_000, 128_000, {
255
+ // Upstream service ended 2026-08-31; the gateway routes to glm-5.3.
256
+ lifecycle: 'deprecated',
257
+ }),
258
+ // Official alias of glm-5.3 (the plan page lists "glm-5.3 (glm-latest)").
245
259
  'glm-latest': agentPlanModel('GLM Latest', 1_024_000, 128_000),
246
- 'kimi-k2.6': agentPlanModel('Kimi-K2.6', 256_000, 32_000, { vision: true }),
260
+ 'glm-5.3-flash': agentPlanModel('GLM-5.3-Flash', 1_024_000, 128_000, { vision: true }),
261
+ 'kimi-k2.6': agentPlanModel('Kimi-K2.6', 256_000, 32_000, {
262
+ // Upstream service ended 2026-08-18; migrate to kimi-k2.7-code or kimi-k3.
263
+ lifecycle: 'deprecated',
264
+ vision: true,
265
+ }),
247
266
  'kimi-k2.7-code': agentPlanModel('Kimi-K2.7-Code', 256_000, 32_000, { vision: true }),
248
267
  'deepseek-v4-pro': agentPlanModel('DeepSeek-V4-Pro', 1_024_000, 384_000),
249
268
  'kimi-k3': agentPlanModel('Kimi-K3', 1_024_000, 128_000, { vision: true }),
@@ -143,6 +143,23 @@ export function pruneRelayModelProfiles(table, enabledModelIds) {
143
143
  export function relayModelProfile(connection, modelId) {
144
144
  return normalizeRelayModelProfile(connection.relayModelProfiles?.[modelId]);
145
145
  }
146
+ /**
147
+ * The context window the USER declared for a model — the Maka window: the
148
+ * proactive compaction target, and nothing else. Exactly two sources count as
149
+ * a declaration: a model-facts pin (`factOverriddenFields` includes
150
+ * `contextWindow`, narrowest of window/input limit) and a relay model profile.
151
+ * A provider's `/models` report and generated metadata describe the model and
152
+ * are shown as a hint; they never become a threshold on their own. This is the
153
+ * single owner of that rule for runtime and UI (#4559).
154
+ */
155
+ export function declaredContextWindow(connection, modelId) {
156
+ const model = connection.models?.find((candidate) => candidate.id === modelId);
157
+ if (model?.factOverriddenFields?.includes('contextWindow')) {
158
+ const values = [model.contextWindow, model.inputLimit].filter((value) => typeof value === 'number' && Number.isFinite(value) && value > 0);
159
+ return values.length > 0 ? Math.min(...values) : undefined;
160
+ }
161
+ return relayModelProfile(connection, modelId)?.contextWindow;
162
+ }
146
163
  /**
147
164
  * Mirrors @ai-sdk/openai@4.0.42 priority-processing detection. The UI and
148
165
  * runtime share this gate so a saved Fast declaration always reaches the wire.
@@ -171,6 +171,7 @@ const volcengineCodingPlanModelIds = [
171
171
  const volcengineAgentPlanModelIds = [
172
172
  'ark-code-latest',
173
173
  'glm-5.3',
174
+ 'glm-5.3-flash',
174
175
  'doubao-seed-2.0-mini',
175
176
  'doubao-seed-2.0-lite',
176
177
  'deepseek-v4-flash',
@@ -32,7 +32,7 @@
32
32
  * projection, or ledger logic lives here. Those arrive in later nodes.
33
33
  */
34
34
  import { isMessageContent, normalizeMessageContent, } from './events.js';
35
- import { INTERACTION_ID_MAX_BYTES, INTERACTION_TOOL_NAME_MAX_BYTES } from './interaction.js';
35
+ import { INTERACTION_ID_MAX_BYTES, INTERACTION_TOOL_NAME_MAX_BYTES, decodeInteractionRequest, } from './interaction.js';
36
36
  import { decodeTurnOrigin } from './turn-origin.js';
37
37
  import { defineObjectShape, hasExactShape, isFiniteNumber, isOptionalMember, isOptionalString, isRecord, isStringArray, } from './record-schema.js';
38
38
  import { isPermissionDecisionFields, isPermissionRequestPayload, isUserQuestionRequest, } from './interaction-record-schema.js';
@@ -183,6 +183,8 @@ const RUNTIME_ACTIONS_SHAPE = defineObjectShape()([], [
183
183
  'permissionClosureAccepted',
184
184
  'userQuestionRequest',
185
185
  'userQuestionAnswerAccepted',
186
+ 'formRequest',
187
+ 'formAnswerAccepted',
186
188
  'transferToAgent',
187
189
  'endInvocation',
188
190
  'tokenUsage',
@@ -415,6 +417,9 @@ function isRuntimeEventActions(value) {
415
417
  (value.userQuestionRequest === undefined || isUserQuestionRequest(value.userQuestionRequest)) &&
416
418
  (value.userQuestionAnswerAccepted === undefined ||
417
419
  isRuntimeEventAnswerAcceptedIdentity(value.userQuestionAnswerAccepted)) &&
420
+ (value.formRequest === undefined || isRuntimeEventFormRequest(value.formRequest)) &&
421
+ (value.formAnswerAccepted === undefined ||
422
+ isRuntimeEventAnswerAcceptedIdentity(value.formAnswerAccepted)) &&
418
423
  isOptionalString(value.transferToAgent) &&
419
424
  (value.endInvocation === undefined || typeof value.endInvocation === 'boolean') &&
420
425
  (value.tokenUsage === undefined || isRuntimeTokenUsage(value.tokenUsage)) &&
@@ -428,6 +433,23 @@ function isRuntimeEventActions(value) {
428
433
  (value.managedMutationTerminal === undefined ||
429
434
  isRuntimeManagedMutationTerminal(value.managedMutationTerminal)));
430
435
  }
436
+ function isRuntimeEventFormRequest(value) {
437
+ if (!isRecord(value))
438
+ return false;
439
+ const { requestId, ...request } = value;
440
+ if (typeof requestId !== 'string' ||
441
+ requestId.length === 0 ||
442
+ UTF8.encode(requestId).byteLength > INTERACTION_ID_MAX_BYTES) {
443
+ return false;
444
+ }
445
+ try {
446
+ decodeInteractionRequest({ kind: 'form', ...request });
447
+ return true;
448
+ }
449
+ catch {
450
+ return false;
451
+ }
452
+ }
431
453
  function isRuntimeManagedMutationTerminal(value) {
432
454
  return (isRecord(value) &&
433
455
  hasExactShape(value, RUNTIME_MANAGED_MUTATION_TERMINAL_SHAPE) &&
@@ -399,6 +399,9 @@ function normalizeExecution(value) {
399
399
  return fail('agent_run requires execution template');
400
400
  if (typeof value.cwd !== 'string' || !value.cwd.trim())
401
401
  return fail('execution.cwd is required');
402
+ if (typeof value.llmConnectionId !== 'string' || !value.llmConnectionId.trim()) {
403
+ return fail('execution.llmConnectionId is required');
404
+ }
402
405
  if (typeof value.llmConnectionSlug !== 'string' || !value.llmConnectionSlug.trim()) {
403
406
  return fail('execution.llmConnectionSlug is required');
404
407
  }
@@ -428,6 +431,7 @@ function normalizeExecution(value) {
428
431
  value: {
429
432
  cwd: value.cwd.trim(),
430
433
  ...(projectId === undefined ? {} : { projectId }),
434
+ llmConnectionId: value.llmConnectionId.trim(),
431
435
  llmConnectionSlug: value.llmConnectionSlug.trim(),
432
436
  model: value.model.trim(),
433
437
  ...(value.thinkingLevel === undefined ? {} : { thinkingLevel: value.thinkingLevel }),
@@ -101,7 +101,7 @@ const SUBAGENT_SESSION_RUNTIME_SHAPE = defineObjectShape()([
101
101
  */
102
102
  const RETIRED_SUBAGENT_RUNTIME_KEYS = ['permissionCeiling'];
103
103
  const SUBAGENT_SESSION_SPAWN_IDENTITY_SHAPE = defineObjectShape()(['schemaVersion', 'requestFingerprint', 'initialTurnId', 'initialRunId'], []);
104
- const SESSION_CONVERSATION_COPY_SHAPE = defineObjectShape()(['kind', 'sourceSessionId', 'sourceTurnId', 'requestFingerprint', 'state'], ['intent']);
104
+ const SESSION_CONVERSATION_COPY_SHAPE = defineObjectShape()(['kind', 'sourceSessionId', 'requestFingerprint', 'state'], ['sourceTurnId', 'intent']);
105
105
  const SESSION_LINEAGE_ID_MAX_CHARS = 512;
106
106
  const SESSION_LINEAGE_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
107
107
  const SUBAGENT_RUNTIME_NAME_MAX_CHARS = 512;
@@ -185,7 +185,9 @@ export function isSessionConversationCopy(value) {
185
185
  (value.intent === undefined ||
186
186
  (value.kind === 'branch' && value.intent === 'side_conversation')) &&
187
187
  isSessionLineageId(value.sourceSessionId) &&
188
- isSessionLineageId(value.sourceTurnId) &&
188
+ (value.sourceTurnId === undefined
189
+ ? value.kind === 'branch' && value.intent === 'side_conversation'
190
+ : isSessionLineageId(value.sourceTurnId)) &&
189
191
  typeof value.requestFingerprint === 'string' &&
190
192
  /^sha256:[0-9a-f]{64}$/.test(value.requestFingerprint) &&
191
193
  (value.state === 'preparing' || value.state === 'committed'));
@@ -287,6 +289,11 @@ export function userFacingText(message) {
287
289
  const USER_VISIBLE_SESSION_SYSTEM_NOTES = new Set([
288
290
  'context_compacted',
289
291
  'context_compaction_failed_open',
292
+ 'context_provider_dropping',
293
+ 'context_window_suggestion',
294
+ 'context_window_overrun',
295
+ 'context_reported_window_exceeded',
296
+ 'context_overflow_after_compaction',
290
297
  'step_limit',
291
298
  ]);
292
299
  /** Closed policy for system notes that are part of the user-visible transcript. */
@@ -295,6 +302,15 @@ export function isUserVisibleSessionSystemNote(kind) {
295
302
  }
296
303
  export const WORKHUB_COORDINATION_RECORD_SCHEMA_VERSION = 1;
297
304
  export const WORKHUB_COORDINATION_REPLACEMENT_SCHEMA_VERSION = 2;
305
+ export const WORKHUB_COORDINATION_STOP_SCHEMA_VERSION = 3;
306
+ function isWorkHubDelegationStopResolution(outcome, targetTurnId) {
307
+ const hasTargetTurnId = typeof targetTurnId === 'string' && targetTurnId.length > 0;
308
+ if (outcome === 'stop_delivered' || outcome === 'not_owned')
309
+ return hasTargetTurnId;
310
+ if (outcome === 'cancelled_pending')
311
+ return targetTurnId === undefined;
312
+ return outcome === 'already_terminal' && (targetTurnId === undefined || hasTargetTurnId);
313
+ }
298
314
  const USER_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 'text'], [
299
315
  'displayText',
300
316
  'attachments',
@@ -426,6 +442,38 @@ const WORKHUB_DELEGATION_REPLACEMENT_ABORTED_MESSAGE_SHAPE = defineObjectShape()
426
442
  'targetSessionId',
427
443
  'reason',
428
444
  ], []);
445
+ const WORKHUB_DELEGATION_STOP_REQUESTED_MESSAGE_SHAPE = defineObjectShape()([
446
+ 'type',
447
+ 'id',
448
+ 'turnId',
449
+ 'ts',
450
+ 'schemaVersion',
451
+ 'kind',
452
+ 'actionId',
453
+ 'actionFingerprint',
454
+ 'coordinationTurnId',
455
+ 'stopsActionId',
456
+ 'stopsDelegationId',
457
+ 'targetSessionId',
458
+ 'targetMessageId',
459
+ 'targetSessionName',
460
+ 'userText',
461
+ ], []);
462
+ const WORKHUB_DELEGATION_STOP_RESOLVED_MESSAGE_SHAPE = defineObjectShape()([
463
+ 'type',
464
+ 'id',
465
+ 'turnId',
466
+ 'ts',
467
+ 'schemaVersion',
468
+ 'kind',
469
+ 'actionId',
470
+ 'actionFingerprint',
471
+ 'coordinationTurnId',
472
+ 'stopsActionId',
473
+ 'stopsDelegationId',
474
+ 'targetSessionId',
475
+ 'outcome',
476
+ ], ['targetTurnId']);
429
477
  const WORKHUB_DELEGATION_CREATE_SHAPE = defineObjectShape()(['title', 'workspace'], []);
430
478
  const WORKHUB_DELEGATION_PROJECT_WORKSPACE_SHAPE = defineObjectShape()(['kind', 'projectId'], []);
431
479
  const WORKHUB_DELEGATION_HOST_PATH_WORKSPACE_SHAPE = defineObjectShape()(['kind', 'path'], []);
@@ -439,6 +487,11 @@ const SYSTEM_NOTE_KINDS = new Set([
439
487
  'model_change',
440
488
  'context_compacted',
441
489
  'context_compaction_failed_open',
490
+ 'context_provider_dropping',
491
+ 'context_window_suggestion',
492
+ 'context_window_overrun',
493
+ 'context_reported_window_exceeded',
494
+ 'context_overflow_after_compaction',
442
495
  'step_limit',
443
496
  'error',
444
497
  'abort',
@@ -562,6 +615,37 @@ function decodeMessage(value, decodeToolResultContent) {
562
615
  throw new Error('Invalid stored message schema');
563
616
  }
564
617
  function isWorkHubCoordinationMessage(message) {
618
+ if (message.kind === 'delegation_stop_requested') {
619
+ return (hasMessageEnvelope(message, true) &&
620
+ hasExactShape(message, WORKHUB_DELEGATION_STOP_REQUESTED_MESSAGE_SHAPE) &&
621
+ message.schemaVersion === WORKHUB_COORDINATION_STOP_SCHEMA_VERSION &&
622
+ isWorkHubActionIdentity(message) &&
623
+ typeof message.stopsActionId === 'string' &&
624
+ message.stopsActionId.length > 0 &&
625
+ typeof message.stopsDelegationId === 'string' &&
626
+ message.stopsDelegationId.length > 0 &&
627
+ typeof message.targetSessionId === 'string' &&
628
+ message.targetSessionId.length > 0 &&
629
+ typeof message.targetMessageId === 'string' &&
630
+ message.targetMessageId.length > 0 &&
631
+ typeof message.targetSessionName === 'string' &&
632
+ message.targetSessionName.trim().length > 0 &&
633
+ typeof message.userText === 'string' &&
634
+ message.userText.trim().length > 0);
635
+ }
636
+ if (message.kind === 'delegation_stop_resolved') {
637
+ return (hasMessageEnvelope(message, true) &&
638
+ hasExactShape(message, WORKHUB_DELEGATION_STOP_RESOLVED_MESSAGE_SHAPE) &&
639
+ message.schemaVersion === WORKHUB_COORDINATION_STOP_SCHEMA_VERSION &&
640
+ isWorkHubActionIdentity(message) &&
641
+ typeof message.stopsActionId === 'string' &&
642
+ message.stopsActionId.length > 0 &&
643
+ typeof message.stopsDelegationId === 'string' &&
644
+ message.stopsDelegationId.length > 0 &&
645
+ typeof message.targetSessionId === 'string' &&
646
+ message.targetSessionId.length > 0 &&
647
+ isWorkHubDelegationStopResolution(message.outcome, message.targetTurnId));
648
+ }
565
649
  if (message.kind === 'delegation_replacement_aborted') {
566
650
  return (hasMessageEnvelope(message, true) &&
567
651
  hasExactShape(message, WORKHUB_DELEGATION_REPLACEMENT_ABORTED_MESSAGE_SHAPE) &&
@@ -642,6 +726,15 @@ function isWorkHubCoordinationMessage(message) {
642
726
  typeof message.replacesDelegationId === 'string' &&
643
727
  message.replacesDelegationId.length > 0)));
644
728
  }
729
+ function isWorkHubActionIdentity(message) {
730
+ return (typeof message.actionId === 'string' &&
731
+ message.actionId.length > 0 &&
732
+ typeof message.actionFingerprint === 'string' &&
733
+ /^sha256:[a-f0-9]{64}$/u.test(message.actionFingerprint) &&
734
+ typeof message.coordinationTurnId === 'string' &&
735
+ message.coordinationTurnId.length > 0 &&
736
+ message.turnId === message.coordinationTurnId);
737
+ }
645
738
  function isWorkHubDelegationCreateSpec(value) {
646
739
  if (!isRecord(value) ||
647
740
  !hasExactShape(value, WORKHUB_DELEGATION_CREATE_SHAPE) ||
@@ -96,3 +96,22 @@ export function sanitizeUnicodeText(text, opts) {
96
96
  return cleaned;
97
97
  return points.slice(0, opts.maxCodePoints).join('') + suffix;
98
98
  }
99
+ /**
100
+ * Truncate to at most `maxUnits` UTF-16 code units without ending on an
101
+ * unpaired high surrogate: when the cut lands inside a surrogate pair, the
102
+ * dangling high half is dropped with the rest of the clipped tail, so the
103
+ * result survives a UTF-8 round trip (durable storage, model requests)
104
+ * instead of decoding as U+FFFD.
105
+ *
106
+ * Budgets here are code *units*, not code points — callers clip against
107
+ * provider/storage limits measured in UTF-16 lengths. Marker/suffix policy
108
+ * stays with the caller, per the boundary note above.
109
+ */
110
+ export function truncateUtf16Safe(text, maxUnits) {
111
+ if (maxUnits <= 0)
112
+ return '';
113
+ if (text.length <= maxUnits)
114
+ return text;
115
+ const kept = text.slice(0, maxUnits);
116
+ return /[\uD800-\uDBFF]$/.test(kept) ? kept.slice(0, -1) : kept;
117
+ }
@@ -72,6 +72,9 @@ export function mergeUsageSummary(legacy, canonical, query, now) {
72
72
  range: projected.range,
73
73
  totalRequests: legacy.totalRequests + projected.totalRequests,
74
74
  totalCostUsd: legacy.totalCostUsd + projected.totalCostUsd,
75
+ // The projection always measures the attempts it counted, and every legacy
76
+ // summary carries the same field.
77
+ totalDurationMs: legacy.totalDurationMs + projected.totalDurationMs,
75
78
  totalTokens: {
76
79
  input: legacy.totalTokens.input + projected.totalTokens.input,
77
80
  output: legacy.totalTokens.output + projected.totalTokens.output,
@@ -54,7 +54,6 @@ const CURRENT_CONTEXT_BUDGET_SHAPE = defineObjectShape()([
54
54
  'droppedEvents',
55
55
  ], [
56
56
  'policyName',
57
- 'maxHistoryEstimatedTokens',
58
57
  'prunedToolResults',
59
58
  'prunedToolResultEstimatedTokensBefore',
60
59
  'prunedToolResultEstimatedTokensAfter',
@@ -75,6 +74,7 @@ const CURRENT_CONTEXT_BUDGET_SHAPE = defineObjectShape()([
75
74
  * produce them through ContextBudgetDiagnostic.
76
75
  */
77
76
  const RETIRED_CONTEXT_BUDGET_KEYS = [
77
+ 'maxHistoryEstimatedTokens',
78
78
  'maxHistoryTurns',
79
79
  'semanticCompactEnabled',
80
80
  'semanticCompactMode',
@@ -213,14 +213,20 @@ const CONTEXT_NUMBERS = [
213
213
  'activeEstimatedTokensSaved',
214
214
  ];
215
215
  const CONTEXT_REASON_COUNTS = ['archivePlaceholderReasonCounts'];
216
- const LAST_REQUEST_ANCHOR_SHAPE = defineObjectShape()(['inputTokens', 'payloadChars'], []);
216
+ const LAST_REQUEST_ANCHOR_SHAPE = defineObjectShape()(['inputTokens'], ['outputTokens', 'modelId', 'connectionId']);
217
+ const RETIRED_LAST_REQUEST_ANCHOR_KEYS = ['payloadChars'];
218
+ const LAST_REQUEST_ANCHOR_DECODE_SHAPE = {
219
+ required: LAST_REQUEST_ANCHOR_SHAPE.required,
220
+ allowed: new Set([...LAST_REQUEST_ANCHOR_SHAPE.allowed, ...RETIRED_LAST_REQUEST_ANCHOR_KEYS]),
221
+ };
217
222
  export function isLastRequestAnchor(value) {
218
223
  return (isRecord(value) &&
219
- hasExactShape(value, LAST_REQUEST_ANCHOR_SHAPE) &&
224
+ hasExactShape(value, LAST_REQUEST_ANCHOR_DECODE_SHAPE) &&
220
225
  isFiniteNumber(value.inputTokens) &&
221
- isFiniteNumber(value.payloadChars) &&
222
226
  value.inputTokens > 0 &&
223
- value.payloadChars > 0);
227
+ (value.outputTokens === undefined ||
228
+ (isFiniteNumber(value.outputTokens) && value.outputTokens >= 0)) &&
229
+ (value.payloadChars === undefined || isFiniteNumber(value.payloadChars)));
224
230
  }
225
231
  export function isTokenUsageFields(value) {
226
232
  if (!isRecord(value))
@@ -65,6 +65,9 @@ const TERMINAL_CHINESE_WITHDRAWAL = /(?:^|[\r\n,。;!?—–-]|然后|
65
65
  const CREATION_REQUEST_PREFIX = /^(?:(?:please|kindly|(?:can|could|would)\s+you|i\s+(?:want|need|would\s+like)\s+to|we\s+need\s+to|let['’]s)|(?:请|帮我|请帮我|麻烦(?:你)?|我想|我要|我们需要))?\s*$/iu;
66
66
  const NAMED_CREATION_TITLE_INTRODUCER = /\b(?:new|brand[- ]new)\s+(?:session|work|task)[\s,,::-]+(?:(?:called|named|titled)|with\s+(?:the\s+)?title)\s+|(?:新的?|全新的?)?\s*(?:Session|会话|工作|任务)[\s,,::-]*(?:叫做?|名叫|名为|命名为|标题为|名称为|名字为)\s*/iu;
67
67
  const LEADING_CORRECTION_SEPARATOR = /^[\s,.;:!?,。;:!?—–-]+/u;
68
+ const DIRECT_STOP_REQUEST = /^\s*(?:(?:please|kindly)\s+)?(?:stop|cancel|terminate|halt)\s+(?:(?:the|this)\s+)?(?:(?:session|work|task|job)\s+)?(.+?)\s*[.!。!]?\s*$/iu;
69
+ const DIRECT_CHINESE_STOP_REQUEST = /^\s*(?:(?:请|请帮我|帮我|麻烦你?)\s*)?(?:停止|取消|终止|中止)\s*(?:(?:这个|该)?(?:会话|工作|任务)\s*)?(.+?)\s*[。!]?\s*$/iu;
70
+ const UNSAFE_STOP_TARGET = /^(?:it|this|that|one|everything|all|current|session|work|task|job|(?:this|that|current)\s+(?:session|work|task|job)|它|这个|那个|全部|当前|会话|工作|任务|(?:这个|那个|当前)(?:会话|工作|任务))$/iu;
68
71
  /**
69
72
  * Whether trusted user text affirmatively asks WorkHub to create a new Session.
70
73
  *
@@ -202,6 +205,8 @@ export function readWorkHubRequestIntent(value) {
202
205
  : { kind: 'unusable' };
203
206
  const correctionCue = hasWorkHubCorrectionCue(source);
204
207
  const existingTarget = affirmativeWorkHubExistingCorrectionTarget(source);
208
+ const stopCue = directWorkHubStopCue(source, literalMask.malformed);
209
+ const stopTarget = stopCue ? directWorkHubStopTarget(source, false) : undefined;
205
210
  const actions = allMatches(masked, EXECUTION_ACTION);
206
211
  const execution = literalMask.malformed || naming.kind === 'unusable' || hasDominatingDeliberation(masked)
207
212
  ? 'non_executable'
@@ -217,6 +222,11 @@ export function readWorkHubRequestIntent(value) {
217
222
  cue: correctionCue,
218
223
  ...(existingTarget ? { existingTarget } : {}),
219
224
  },
225
+ stop: {
226
+ cue: stopCue,
227
+ imperative: Boolean(stopTarget),
228
+ ...(stopTarget ? { target: stopTarget } : {}),
229
+ },
220
230
  };
221
231
  }
222
232
  /** Whether a parsed correction names exactly this Session. */
@@ -270,11 +280,20 @@ function affirmativeWorkHubExistingCorrectionTarget(value) {
270
280
  }
271
281
  return lastTarget;
272
282
  }
273
- function correctionTargetMatchesSession(target, sessionName) {
274
- const normalizedTarget = normalizeCorrectionIdentity(target);
283
+ /**
284
+ * The one rule for reading a Session name out of a trusted reference.
285
+ *
286
+ * It is deliberately action-agnostic: it reports where the name matched and
287
+ * what text was left over, and says nothing about whether that leftover is
288
+ * acceptable. Each action's policy owns that question, because the answer
289
+ * genuinely differs — a stop reference may carry only punctuation after the
290
+ * name, while a correction may carry a further instruction.
291
+ */
292
+ export function matchWorkHubSessionName(reference, sessionName) {
293
+ const normalizedTarget = normalizeCorrectionIdentity(reference);
275
294
  const normalizedName = normalizeCorrectionIdentity(sessionName);
276
295
  if (!normalizedName)
277
- return false;
296
+ return { kind: 'none' };
278
297
  const quotedNames = [
279
298
  `"${normalizedName}"`,
280
299
  `“${normalizedName}”`,
@@ -285,15 +304,32 @@ function correctionTargetMatchesSession(target, sessionName) {
285
304
  .sort((left, right) => right.length - left.length)
286
305
  .find((candidate) => normalizedTarget.startsWith(candidate) &&
287
306
  !/[\p{L}\p{N}]/u.test(normalizedTarget[candidate.length] ?? ''));
288
- if (!matchedName)
289
- return false;
307
+ if (!matchedName) {
308
+ // A name whose own trailing punctuation the reference dropped still names
309
+ // it, but nothing may follow: there is no boundary left to trust.
310
+ return /[.!。!]$/u.test(normalizedName) &&
311
+ normalizedTarget === normalizedName.replace(/[.!。!]+$/u, '').trim()
312
+ ? { kind: 'elided_name_punctuation' }
313
+ : { kind: 'none' };
314
+ }
290
315
  if (matchedName === normalizedName && hasUnsafeUnquotedHardClauseBoundary(sessionName)) {
291
- return false;
316
+ return { kind: 'none' };
292
317
  }
293
- if (hasUnquotedTerminalWithdrawal(target)) {
318
+ return { kind: 'named', remainder: normalizedTarget.slice(matchedName.length).trim() };
319
+ }
320
+ /**
321
+ * The correction policy's tail rule. A correction may name its target and then
322
+ * say what to do with it, but a withdrawal anywhere in the reference retracts
323
+ * the whole thing.
324
+ *
325
+ * It takes a match rather than a Session name so that a caller which already
326
+ * resolved candidates through the shared Session Resolver applies exactly this
327
+ * rule to exactly that recall, instead of matching names a second time.
328
+ */
329
+ export function workHubCorrectionAdmitsReference(reference, match) {
330
+ if (match.kind !== 'named' || hasUnquotedTerminalWithdrawal(reference))
294
331
  return false;
295
- }
296
- const remainder = normalizedTarget.slice(matchedName.length).trim();
332
+ const { remainder } = match;
297
333
  if (!remainder || /^(?:instead\s*)?[.!?。!?]?$/iu.test(remainder))
298
334
  return true;
299
335
  const supplemental = remainder.match(/^[,;,;]\s*(.+)$/u)?.[1]?.trim();
@@ -302,6 +338,36 @@ function correctionTargetMatchesSession(target, sessionName) {
302
338
  !/[\r\n,.!?;,。!?;—–]|\b(?:and|then|but|however|actually)\b|(?:然后|随后|但|不过|其实)/iu.test(supplementalBody) &&
303
339
  readWorkHubRequestIntent(supplementalBody).execution === 'imperative');
304
340
  }
341
+ function correctionTargetMatchesSession(target, sessionName) {
342
+ return workHubCorrectionAdmitsReference(target, matchWorkHubSessionName(target, sessionName));
343
+ }
344
+ function directWorkHubStopTarget(value, malformedLiteral) {
345
+ if (malformedLiteral || /[??]\s*$/u.test(value))
346
+ return undefined;
347
+ const match = DIRECT_STOP_REQUEST.exec(value) ?? DIRECT_CHINESE_STOP_REQUEST.exec(value);
348
+ const rawTarget = match?.[1]?.trim();
349
+ if (!rawTarget)
350
+ return undefined;
351
+ const target = stripMatchingStopQuotes(rawTarget.replace(/[.!。!]+\s*$/u, '').trim());
352
+ if (!target || UNSAFE_STOP_TARGET.test(target))
353
+ return undefined;
354
+ return target;
355
+ }
356
+ function directWorkHubStopCue(value, malformedLiteral) {
357
+ if (malformedLiteral || /[??]\s*$/u.test(value))
358
+ return false;
359
+ return Boolean(DIRECT_STOP_REQUEST.test(value) || DIRECT_CHINESE_STOP_REQUEST.test(value));
360
+ }
361
+ function stripMatchingStopQuotes(value) {
362
+ const pairs = new Map([
363
+ ['"', '"'],
364
+ ["'", "'"],
365
+ ['“', '”'],
366
+ ['‘', '’'],
367
+ ]);
368
+ const closer = pairs.get(value[0] ?? '');
369
+ return closer && value.endsWith(closer) ? value.slice(1, -1).trim() : value;
370
+ }
305
371
  function normalizeCorrectionIdentity(value) {
306
372
  return value.normalize('NFKC').toLocaleLowerCase().replace(/\s+/gu, ' ').trim();
307
373
  }
@@ -0,0 +1,50 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { matchWorkHubSessionName, } from './workhub-creation-intent.js';
20
+ /**
21
+ * The temporary deterministic baseline: a reference resolves only when it names
22
+ * one visible Session exactly. Exact display names are conservative retrieval
23
+ * evidence, not the long-term product boundary, and this implementation exists
24
+ * to keep the port real while a ranked resolver is built behind it.
25
+ *
26
+ * It can be removed once every target-bearing WorkHub action resolves through
27
+ * this port, the replacement resolver passes the common routing evaluation, and
28
+ * its rollout retains a tested rollback path.
29
+ */
30
+ export function createExactNameSessionResolver() {
31
+ return {
32
+ resolve({ reference, sessions }) {
33
+ const candidates = [];
34
+ for (const session of sessions) {
35
+ const match = matchWorkHubSessionName(reference.text, session.sessionName);
36
+ if (match.kind === 'none')
37
+ continue;
38
+ candidates.push({ ref: session.ref, evidence: match });
39
+ }
40
+ const [first, ...rest] = candidates;
41
+ if (!first)
42
+ return { kind: 'none' };
43
+ // Exact naming has no score to separate equals by, so more than one match
44
+ // is ambiguity rather than a ranking a policy could safely act on.
45
+ if (rest.length > 0)
46
+ return { kind: 'ambiguous', candidates };
47
+ return { kind: 'ranked', candidates: [first] };
48
+ },
49
+ };
50
+ }
@@ -77,6 +77,7 @@
77
77
  "./daily-review": "./dist/daily-review.js",
78
78
  "./work-board": "./dist/work-board.js",
79
79
  "./workhub-creation-intent": "./dist/workhub-creation-intent.js",
80
+ "./workhub-session-resolver": "./dist/workhub-session-resolver.js",
80
81
  "./deep-research": "./dist/deep-research.js",
81
82
  "./session-start-mode": "./dist/session-start-mode.js",
82
83
  "./long-term-memory": "./dist/long-term-memory.js",
@@ -140,6 +141,7 @@
140
141
  "./task-submission-readiness": "./dist/task-submission-readiness.js",
141
142
  "./terminal-input": "./dist/terminal-input.js",
142
143
  "./terminal-mouse-input": "./dist/terminal-mouse-input.js",
144
+ "./text-sanitize": "./dist/text-sanitize.js",
143
145
  "./tool-activity-args": "./dist/tool-activity-args.js",
144
146
  "./tool-quiet-preview": "./dist/tool-quiet-preview.js",
145
147
  "./tool-result-record-schema": "./dist/tool-result-record-schema.js",
@@ -28,6 +28,7 @@ import { decodePreverifiedToolchain, isExternalProfile, } from './toolchain-veri
28
28
  import { isInferenceAdmissionEvent } from './provider-admission.js';
29
29
  import { DEEPSEEK_V4_FLASH_COST, deriveMetering, } from './provider-metering.js';
30
30
  import { removeEvalWebTools } from './provider-web-tool-surface.js';
31
+ import { terminateProcess } from './process-termination.js';
31
32
  import { takeRelayResultToken, writeRelayResult } from './relay-result-frame.js';
32
33
  const resultToken = takeRelayResultToken();
33
34
  // The profile directory this wrapper writes. `dsh` has no environment variable
@@ -277,7 +278,7 @@ let stopped = false;
277
278
  const stop = (signal) => {
278
279
  stopped = true;
279
280
  removeCredential();
280
- child?.kill(signal);
281
+ void terminateProcess(child, signal);
281
282
  };
282
283
  const terminate = () => stop('SIGTERM');
283
284
  const interrupt = () => stop('SIGINT');