@sema-agent/server 7.135.0 → 7.136.0

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 (185) hide show
  1. package/MIGRATION.md +64 -2
  2. package/USAGE.md +62 -25
  3. package/deploy/sema-up/README.md +1 -1
  4. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  5. package/deploy/sema-up/chart/values.yaml +1 -1
  6. package/dist/approval-card.d.ts +60 -4
  7. package/dist/approval-card.js +18 -1
  8. package/dist/approval-content-kind.d.ts +1 -0
  9. package/dist/approval-content-kind.js +1 -1
  10. package/dist/auth-bridge.d.ts +34 -5
  11. package/dist/auth-bridge.js +20 -3
  12. package/dist/boot/budget-tracing.d.ts +1 -2
  13. package/dist/boot/budget-tracing.js +2 -2
  14. package/dist/boot/org-memory.js +1 -1
  15. package/dist/boot/resolve-spec.js +10 -7
  16. package/dist/boot/runner-deps.js +4 -2
  17. package/dist/boot/stage-04-stores.d.ts +1 -1
  18. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  19. package/dist/boot/stage-06-runners.d.ts +1 -1
  20. package/dist/boot/stage-07-capability-layer.d.ts +1 -1
  21. package/dist/boot/stage-08-reapers.d.ts +1 -1
  22. package/dist/boot/stage-09-leader.d.ts +1 -1
  23. package/dist/boot/stage-10-http-server.d.ts +1 -1
  24. package/dist/boot/stores.js +2 -3
  25. package/dist/boot/web-admin.js +10 -17
  26. package/dist/brain.js +12 -3
  27. package/dist/budget.d.ts +3 -3
  28. package/dist/budget.js +4 -0
  29. package/dist/capabilities/mcp-probe.d.ts +5 -4
  30. package/dist/capability-facts.d.ts +3 -7
  31. package/dist/capability-facts.js +3 -5
  32. package/dist/center-credential.d.ts +18 -4
  33. package/dist/center-credential.js +18 -3
  34. package/dist/config-catalog.js +12 -10
  35. package/dist/config-d-migration.js +1 -1
  36. package/dist/config-invariants.d.ts +1 -1
  37. package/dist/config-invariants.js +8 -0
  38. package/dist/config-lkg.d.ts +0 -38
  39. package/dist/config-lkg.js +1 -83
  40. package/dist/config-types.d.ts +12 -4
  41. package/dist/config.js +26 -8
  42. package/dist/deployment-shape.d.ts +1 -1
  43. package/dist/elicitation.d.ts +8 -7
  44. package/dist/elicitation.js +9 -8
  45. package/dist/file-history-disclosure.d.ts +6 -4
  46. package/dist/file-history-disclosure.js +22 -6
  47. package/dist/fleet-client.d.ts +32 -1
  48. package/dist/fleet-client.js +24 -4
  49. package/dist/fleet-lease.d.ts +7 -3
  50. package/dist/fleet-lease.js +31 -23
  51. package/dist/hooks/hook-llm.js +14 -8
  52. package/dist/hooks/hook-runner.d.ts +4 -4
  53. package/dist/hooks/hook-runner.js +2 -2
  54. package/dist/http/checkpoint-ctx.d.ts +28 -9
  55. package/dist/http/checkpoint-ctx.js +5 -1
  56. package/dist/http/dispatch.js +9 -9
  57. package/dist/http/resume-ctx-read.d.ts +2 -16
  58. package/dist/http/resume-ctx-read.js +2 -3
  59. package/dist/http/routes/approvals-assistant.d.ts +0 -2
  60. package/dist/http/routes/approvals-assistant.js +47 -12
  61. package/dist/http/routes/runs.js +2 -2
  62. package/dist/http/routes/session-sync.js +31 -9
  63. package/dist/http/routes/sessions.js +18 -14
  64. package/dist/http/submit-ctx.js +1 -1
  65. package/dist/http/wire-types.d.ts +5 -3
  66. package/dist/index.d.ts +1 -0
  67. package/dist/index.js +1 -0
  68. package/dist/model-route-gap.d.ts +49 -4
  69. package/dist/model-route-gap.js +34 -1
  70. package/dist/object-store-buckets.d.ts +13 -0
  71. package/dist/object-store-buckets.js +3 -0
  72. package/dist/observability/fail-open.d.ts +6 -6
  73. package/dist/observability/fail-open.js +6 -6
  74. package/dist/observability/metrics.js +1 -1
  75. package/dist/observability/principal-context.d.ts +4 -1
  76. package/dist/observability/principal-context.js +6 -1
  77. package/dist/parked-decide.d.ts +4 -3
  78. package/dist/parked-decide.js +26 -7
  79. package/dist/peer-directory.d.ts +2 -1
  80. package/dist/peer-directory.js +5 -5
  81. package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
  82. package/dist/plugins/approval-ask-store-sql.js +3 -0
  83. package/dist/plugins/blob-backend.js +2 -1
  84. package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
  85. package/dist/plugins/checkpoint-index-columns.js +28 -0
  86. package/dist/plugins/checkpoint-store-sql.d.ts +27 -3
  87. package/dist/plugins/checkpoint-store-sql.js +58 -29
  88. package/dist/plugins/file-history-store-sql.d.ts +6 -0
  89. package/dist/plugins/file-history-store-sql.js +30 -23
  90. package/dist/plugins/local-checkpoint-store.js +4 -3
  91. package/dist/plugins/one-time-migrations.d.ts +21 -28
  92. package/dist/plugins/one-time-migrations.js +39 -123
  93. package/dist/plugins/remote-env-k8s.d.ts +18 -5
  94. package/dist/plugins/remote-env-k8s.js +24 -7
  95. package/dist/plugins/s3-answer.d.ts +15 -0
  96. package/dist/plugins/s3-answer.js +5 -0
  97. package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
  98. package/dist/plugins/snapshot-bucket-presence.js +35 -7
  99. package/dist/plugins/sql-errors.d.ts +39 -0
  100. package/dist/plugins/sql-errors.js +49 -1
  101. package/dist/plugins/store-fault.d.ts +4 -0
  102. package/dist/plugins/store-fault.js +8 -0
  103. package/dist/plugins/tidb-pool.js +1 -1
  104. package/dist/plugins/web-search.d.ts +1 -1
  105. package/dist/plugins/web-search.js +2 -2
  106. package/dist/request-declaration-refusal.d.ts +94 -0
  107. package/dist/request-declaration-refusal.js +78 -0
  108. package/dist/request-image-urls.d.ts +6 -0
  109. package/dist/request-image-urls.js +27 -0
  110. package/dist/request-key-closure.d.ts +5 -0
  111. package/dist/request-key-closure.js +4 -1
  112. package/dist/runtime-caps-resolver.js +1 -1
  113. package/dist/sandbox-pkg-source.d.ts +28 -1
  114. package/dist/sandbox-pkg-source.js +1 -2
  115. package/dist/sealed-key.d.ts +19 -0
  116. package/dist/sealed-key.js +47 -4
  117. package/dist/session-incarnation.d.ts +16 -5
  118. package/dist/session-incarnation.js +7 -4
  119. package/dist/session-row.d.ts +3 -17
  120. package/dist/session-row.js +4 -0
  121. package/dist/session-sync.d.ts +15 -3
  122. package/dist/session-sync.js +5 -2
  123. package/dist/task-a2a.d.ts +52 -81
  124. package/dist/task-a2a.js +219 -89
  125. package/dist/task-mcp.d.ts +6 -14
  126. package/dist/task-mcp.js +16 -51
  127. package/dist/tool-approval.d.ts +23 -10
  128. package/dist/tool-approval.js +51 -36
  129. package/dist/trace/core-keyset-guard.d.ts +2 -2
  130. package/dist/trace/exit-seat.js +37 -13
  131. package/dist/trace/redact.d.ts +28 -12
  132. package/dist/trace/redact.js +233 -94
  133. package/dist/trace/self-minted-wire.d.ts +267 -0
  134. package/dist/trace/self-minted-wire.js +79 -0
  135. package/dist/url-credential-refusal.d.ts +52 -0
  136. package/dist/url-credential-refusal.js +14 -0
  137. package/dist/url-refusal.d.ts +13 -4
  138. package/dist/url-refusal.js +8 -2
  139. package/dist/workspace-space.d.ts +27 -0
  140. package/dist/workspace-space.js +26 -0
  141. package/package.json +3 -2
  142. package/scripts/drain-check-local.mjs +2 -1
  143. package/scripts/drain-check.mysql.sql +3 -1
  144. package/scripts/drain-check.pg.sql +3 -1
  145. package/skills/schedule.md +1 -1
  146. package/wire-changes/server@7.104.0.json +134 -0
  147. package/wire-changes/server@7.105.0.json +53 -0
  148. package/wire-changes/server@7.106.0.json +475 -0
  149. package/wire-changes/server@7.107.0.json +64 -0
  150. package/wire-changes/server@7.108.0.json +153 -0
  151. package/wire-changes/server@7.109.0.json +46 -0
  152. package/wire-changes/server@7.110.0.json +90 -0
  153. package/wire-changes/server@7.111.0.json +86 -0
  154. package/wire-changes/server@7.112.0.json +60 -0
  155. package/wire-changes/server@7.113.0.json +26 -0
  156. package/wire-changes/server@7.114.0.json +26 -0
  157. package/wire-changes/server@7.115.0.json +97 -0
  158. package/wire-changes/server@7.116.0.json +218 -0
  159. package/wire-changes/server@7.116.1.json +26 -0
  160. package/wire-changes/server@7.117.0.json +65 -0
  161. package/wire-changes/server@7.118.0.json +129 -0
  162. package/wire-changes/server@7.119.0.json +65 -0
  163. package/wire-changes/server@7.120.0.json +89 -0
  164. package/wire-changes/server@7.121.0.json +219 -0
  165. package/wire-changes/server@7.122.0.json +54 -0
  166. package/wire-changes/server@7.123.0.json +26 -0
  167. package/wire-changes/server@7.124.0.json +336 -0
  168. package/wire-changes/server@7.124.1.json +26 -0
  169. package/wire-changes/server@7.125.0.json +111 -0
  170. package/wire-changes/server@7.126.0.json +285 -0
  171. package/wire-changes/server@7.127.0.json +77 -0
  172. package/wire-changes/server@7.127.1.json +26 -0
  173. package/wire-changes/server@7.127.2.json +53 -0
  174. package/wire-changes/server@7.128.0.json +26 -0
  175. package/wire-changes/server@7.129.0.json +106 -0
  176. package/wire-changes/server@7.129.1.json +26 -0
  177. package/wire-changes/server@7.130.0.json +26 -0
  178. package/wire-changes/server@7.131.0.json +118 -0
  179. package/wire-changes/server@7.132.0.json +226 -0
  180. package/wire-changes/server@7.133.0.json +1155 -0
  181. package/wire-changes/server@7.134.0.json +107 -0
  182. package/wire-changes/server@7.135.0.json +174 -0
  183. package/wire-changes/server@7.136.0.json +448 -0
  184. package/dist/url-credential.d.ts +0 -52
  185. package/dist/url-credential.js +0 -49
@@ -13,7 +13,7 @@ import { recordFailOpen } from "./observability/fail-open.js";
13
13
  import { ownerOfCheckpointScope } from "./security.js";
14
14
  import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS, noteLandedOnRow, noteRecordedReceipt } from "./approval-ask-machine.js";
15
15
  import { currentParkArm, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildReplayFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readProbeReason, readReadRootCandidate, readStoredCardEnvelope, readRuleEvidence, readAllowRuleShadowed, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, readAskClassPair, readClosedWordSeat, buildApprovalCompleteFrame, readRuleOfferList, } from "./approval-card.js";
16
- import { askRowCarriesOwner, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
16
+ import { askOwnerAdmits, askRowCarriesOwner, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
17
17
  import { APPROVAL_ENDING_CAUSES, SETTLE_OUTCOME, causeOfAskRow } from "./approval-deny-reasons.js";
18
18
  import { settleByPolicy } from "./approval-policy-settlement.js";
19
19
  import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
@@ -195,8 +195,11 @@ function mandatedFromSeat(seat) {
195
195
  return false;
196
196
  }
197
197
  }
198
+ function rowOrUnknown(read) {
199
+ return read.kind === "row" ? read.row : null;
200
+ }
198
201
  function askRowVisibleTo(row, principal) {
199
- return row !== null && (row.owner === null || row.owner === principal);
202
+ return row !== null && askOwnerAdmits(row.owner, principal);
200
203
  }
201
204
  function waitingLegsReached(entry, othersReached) {
202
205
  return (entry.adopted === true ? 0 : 1) + othersReached;
@@ -206,7 +209,7 @@ function withConsumerDisclosure(result, waitingLegs) {
206
209
  }
207
210
  function livePendingRowOf(e) {
208
211
  const listFrame = e.adopted === true ? e.cardFrame : (e.cardFrame ?? e.frame);
209
- const approvalCard = e.adopted === true ? e.cardFrame.approvalCard : e.frame.approvalCard;
212
+ const approvalCard = e.adopted === true ? e.cardFrame.card.approvalCard : e.frame.approvalCard;
210
213
  return {
211
214
  approvalId: e.approvalId,
212
215
  toolName: e.toolName,
@@ -2049,17 +2052,21 @@ export class ToolApprovalCoordinator {
2049
2052
  }
2050
2053
  const entry = this.liveEntryFor(id);
2051
2054
  if (entry?.adopted === true)
2052
- return this.respondAdopted(entry, id, principal, parsed, httpReq);
2053
- if (!entry || (entry.owner !== null && entry.owner !== principal)) {
2054
- return this.respondLate(id, principal, parsed, httpReq);
2055
- }
2056
- return this.answerRegistration(entry, entry.owner, id, principal, parsed, httpReq);
2055
+ return this.respondByRow(entry, id, principal, parsed, httpReq);
2056
+ if (entry !== undefined && askOwnerAdmits(entry.owner, principal))
2057
+ return this.answerRegistration(entry, entry.owner, id, principal, parsed, httpReq);
2058
+ if (entry !== undefined && this.registrationHasRow(entry))
2059
+ return this.respondByRow(entry, id, principal, parsed, httpReq);
2060
+ return this.respondLate(id, principal, parsed, httpReq);
2057
2061
  }
2058
- async respondAdopted(entry, id, principal, parsed, httpReq) {
2062
+ registrationHasRow(entry) {
2063
+ return this.askStore !== undefined && entry.askId !== undefined;
2064
+ }
2065
+ async respondByRow(entry, id, principal, parsed, httpReq) {
2059
2066
  const store = this.askStore;
2060
2067
  if (!store)
2061
2068
  return this.notPending();
2062
- const row = await this.askRowFor(store, id, "getAsk(respond-adopted)");
2069
+ const row = rowOrUnknown(await this.askRowFor(store, id, "getAsk(respond-by-row)"));
2063
2070
  if (!askRowVisibleTo(row, principal))
2064
2071
  return this.notPending(NOT_PENDING_UNKNOWN_CAUSE);
2065
2072
  if (row.state !== "STREAM_PENDING")
@@ -2067,7 +2074,7 @@ export class ToolApprovalCoordinator {
2067
2074
  return this.answerRegistration(entry, row.owner, id, principal, parsed, httpReq);
2068
2075
  }
2069
2076
  answerRegistration(entry, judgedOwner, id, principal, parsed, httpReq) {
2070
- const preRule = this.precheckEditedRuleBeforeDecision(entry, parsed);
2077
+ const preRule = this.precheckEditedRuleBeforeDecision(entry, judgedOwner, parsed);
2071
2078
  if (preRule !== undefined)
2072
2079
  return preRule;
2073
2080
  if (parsed.value !== "deny" && policyCannotAnswer(heldAskOf(entry.requiresRealApproval === true), parsed.decidedBy)) {
@@ -2076,17 +2083,17 @@ export class ToolApprovalCoordinator {
2076
2083
  const ruleEligibility = { eligible: true };
2077
2084
  const store = this.askStore;
2078
2085
  if (store && entry.askId !== undefined && entry.batchId !== undefined) {
2079
- return this.respondWithCas(store, entry.askId, entry.batchId, judgedOwner, entry, parsed, ruleEligibility).then((cas) => cas.lost ? this.dispatchRowTruth(cas.row, id, parsed, httpReq, principal) : this.afterLiveDecision(cas.result, entry, parsed, principal, ruleEligibility));
2086
+ return this.respondWithCas(store, entry.askId, entry.batchId, judgedOwner, entry, parsed, ruleEligibility).then((cas) => cas.lost ? this.dispatchRowTruth(cas.row, id, parsed, httpReq, principal) : this.afterLiveDecision(cas.result, entry, judgedOwner, parsed, principal, ruleEligibility));
2080
2087
  }
2081
- return this.afterLiveDecision(this.finishRespond(entry, parsed), entry, parsed, principal, ruleEligibility);
2088
+ return this.afterLiveDecision(this.finishRespond(entry, judgedOwner, parsed), entry, judgedOwner, parsed, principal, ruleEligibility);
2082
2089
  }
2083
- afterLiveDecision(decided, entry, parsed, principal, ruleEligibility) {
2084
- const settled = this.recordDeploymentListAnswer(decided, entry, parsed, principal, ruleEligibility);
2090
+ afterLiveDecision(decided, entry, judgedOwner, parsed, principal, ruleEligibility) {
2091
+ const settled = this.recordDeploymentListAnswer(decided, entry, judgedOwner, parsed, principal, ruleEligibility);
2085
2092
  if (parsed.persistRule === undefined)
2086
2093
  return settled;
2087
- return this.persistRuleAfterDecision(settled, entry, parsed.persistRule, parsed.updatedInput !== undefined, ruleEligibility);
2094
+ return this.persistRuleAfterDecision(settled, entry, judgedOwner, parsed.persistRule, parsed.updatedInput !== undefined, ruleEligibility);
2088
2095
  }
2089
- recordDeploymentListAnswer(decided, entry, parsed, principal, eligibility) {
2096
+ recordDeploymentListAnswer(decided, entry, judgedOwner, parsed, principal, eligibility) {
2090
2097
  const store = this.sessionGrants;
2091
2098
  const sessionId = entry.sessionId;
2092
2099
  if (parsed.value !== "allow_session" || !sessionId || store === undefined)
@@ -2101,9 +2108,9 @@ export class ToolApprovalCoordinator {
2101
2108
  return { ...result, body: { ...result.body, rememberApplied: false } };
2102
2109
  let rememberApplied = false;
2103
2110
  try {
2104
- await this.withStoreDeadline(store.grant(sessionId, entry.toolName, principal ?? entry.owner), "sessionGrants.grant(deployment-list)", DURABLE_CALL_TIMEOUT_MS);
2111
+ await this.withStoreDeadline(store.grant(sessionId, entry.toolName, principal ?? judgedOwner), "sessionGrants.grant(deployment-list)", DURABLE_CALL_TIMEOUT_MS);
2105
2112
  rememberApplied = true;
2106
- defaultLogger.info("approval_exemption_granted", { sessionId, toolName: entry.toolName, grantedBy: principal ?? entry.owner });
2113
+ defaultLogger.info("approval_exemption_granted", { sessionId, toolName: entry.toolName, grantedBy: principal ?? judgedOwner });
2107
2114
  }
2108
2115
  catch (err) {
2109
2116
  defaultLogger.warn("approval_exemption_grant_failed", { sessionId, toolName: entry.toolName, error: String(err).slice(0, 200) });
@@ -2111,7 +2118,7 @@ export class ToolApprovalCoordinator {
2111
2118
  return { ...result, body: { ...result.body, rememberApplied } };
2112
2119
  })();
2113
2120
  }
2114
- precheckEditedRuleBeforeDecision(entry, parsed) {
2121
+ precheckEditedRuleBeforeDecision(entry, judgedOwner, parsed) {
2115
2122
  const persistRule = parsed.persistRule;
2116
2123
  if (persistRule === undefined || persistRule.kind !== "text" || !persistRule.edited)
2117
2124
  return undefined;
@@ -2120,7 +2127,7 @@ export class ToolApprovalCoordinator {
2120
2127
  if (entry.governanceForced === true)
2121
2128
  return undefined;
2122
2129
  const command = entry.ruleLane?.command;
2123
- if (this.ruleConsent === undefined || command === undefined || entry.owner === null)
2130
+ if (this.ruleConsent === undefined || command === undefined || judgedOwner === null)
2124
2131
  return undefined;
2125
2132
  const verdict = precheckCardRuleText(persistRule.rule, command);
2126
2133
  if (verdict === undefined || verdict.ok)
@@ -2155,9 +2162,9 @@ export class ToolApprovalCoordinator {
2155
2162
  }
2156
2163
  readApproval(id, principal) {
2157
2164
  const entry = this.liveEntryFor(id);
2158
- if (entry?.adopted === true)
2159
- return this.readAdopted(entry, id, principal);
2160
- if (entry !== undefined && (entry.owner === null || entry.owner === principal)) {
2165
+ if (entry !== undefined && this.registrationHasRow(entry))
2166
+ return this.readByRow(entry, id, principal);
2167
+ if (entry !== undefined && askOwnerAdmits(entry.owner, principal)) {
2161
2168
  const body = { state: "pending", ...livePendingRowOf(entry) };
2162
2169
  return { status: 200, body };
2163
2170
  }
@@ -2166,11 +2173,18 @@ export class ToolApprovalCoordinator {
2166
2173
  return this.notPending();
2167
2174
  return this.readAskRowState(store, id, principal);
2168
2175
  }
2169
- async readAdopted(entry, id, principal) {
2176
+ async readByRow(entry, id, principal) {
2170
2177
  const store = this.askStore;
2171
2178
  if (!store)
2172
2179
  return this.notPending();
2173
- const row = await this.askRowFor(store, id, "getAsk(read-adopted)");
2180
+ const read = await this.askRowFor(store, id, "getAsk(read-by-row)");
2181
+ if (read.kind === "unreadable") {
2182
+ if (entry.adopted === true || !askOwnerAdmits(entry.owner, principal))
2183
+ return this.notPending(NOT_PENDING_UNKNOWN_CAUSE);
2184
+ const body = { state: "pending", ...livePendingRowOf(entry) };
2185
+ return { status: 200, body };
2186
+ }
2187
+ const row = rowOrUnknown(read);
2174
2188
  if (askRowVisibleTo(row, principal) && row.state === "STREAM_PENDING") {
2175
2189
  const body = { state: "pending", ...livePendingRowOf(entry) };
2176
2190
  return { status: 200, body };
@@ -2178,7 +2192,7 @@ export class ToolApprovalCoordinator {
2178
2192
  return this.readStateOfRow(row, principal);
2179
2193
  }
2180
2194
  async readAskRowState(store, id, principal) {
2181
- return this.readStateOfRow(await this.askRowFor(store, id, "getAsk(read)"), principal);
2195
+ return this.readStateOfRow(rowOrUnknown(await this.askRowFor(store, id, "getAsk(read)")), principal);
2182
2196
  }
2183
2197
  async readParkedGate(row) {
2184
2198
  const gate = this.parkedGate;
@@ -2310,15 +2324,16 @@ export class ToolApprovalCoordinator {
2310
2324
  return undefined;
2311
2325
  }
2312
2326
  async dispatchLateByRow(store, id, parsed, httpReq, principal) {
2313
- return this.dispatchRowTruth(await this.askRowFor(store, id, "getAsk(respond-late)"), id, parsed, httpReq, principal);
2327
+ return this.dispatchRowTruth(rowOrUnknown(await this.askRowFor(store, id, "getAsk(respond-late)")), id, parsed, httpReq, principal);
2314
2328
  }
2315
2329
  async askRowFor(store, id, label) {
2316
2330
  try {
2317
- return await this.withStoreDeadline(store.getAsk(id), label, DURABLE_CONVERGE_READ_TIMEOUT_MS);
2331
+ const row = await this.withStoreDeadline(store.getAsk(id), label, DURABLE_CONVERGE_READ_TIMEOUT_MS);
2332
+ return row === null ? { kind: "absent" } : { kind: "row", row };
2318
2333
  }
2319
2334
  catch (err) {
2320
2335
  this.noteStoreError(err, label);
2321
- return null;
2336
+ return { kind: "unreadable" };
2322
2337
  }
2323
2338
  }
2324
2339
  async dispatchRowTruth(row, id, parsed, httpReq, principal) {
@@ -2580,13 +2595,13 @@ export class ToolApprovalCoordinator {
2580
2595
  },
2581
2596
  };
2582
2597
  }
2583
- async persistRuleAfterDecision(settled, entry, persistRule, inputWasEdited, ruleEligibility) {
2598
+ async persistRuleAfterDecision(settled, entry, judgedOwner, persistRule, inputWasEdited, ruleEligibility) {
2584
2599
  const result = await settled;
2585
2600
  if (result.status !== 200)
2586
2601
  return result;
2587
2602
  const lane = this.ruleConsent;
2588
2603
  const material = entry.ruleLane;
2589
- const owner = entry.owner;
2604
+ const owner = judgedOwner;
2590
2605
  const withFlag = (rulePersisted, ruleRefusal, echo) => ({
2591
2606
  status: result.status,
2592
2607
  body: { ...result.body, rulePersisted, ...(ruleRefusal !== undefined ? { ruleRefusal } : {}), ...(echo ?? {}) },
@@ -2708,11 +2723,11 @@ export class ToolApprovalCoordinator {
2708
2723
  if (!converged) {
2709
2724
  return { lost: false, result: this.notPending() };
2710
2725
  }
2711
- return { lost: false, result: withConsumerDisclosure(this.finishRespond(entry, parsed, { updatedInputDelivered: false, noteRecorded: noteLanded }), waitingLegsReached(entry, othersReached)) };
2726
+ return { lost: false, result: withConsumerDisclosure(this.finishRespond(entry, owner, parsed, { updatedInputDelivered: false, noteRecorded: noteLanded }), waitingLegsReached(entry, othersReached)) };
2712
2727
  }
2713
2728
  const payloadIsMine = ambiguousConfirmedRow === undefined ||
2714
2729
  approvalPayloadFingerprint({ updatedInput: ambiguousConfirmedRow.updatedInput }) === approvalPayloadFingerprint({ updatedInput: parsed.updatedInput });
2715
- const result = this.finishRespond(entry, parsed, {
2730
+ const result = this.finishRespond(entry, owner, parsed, {
2716
2731
  noteRecorded: noteLanded,
2717
2732
  ...(winnerRow !== undefined ? { settleOutcome: outcomeOfDecidedRow(winnerRow), settleOutcomeIsThisRequest: ambiguousConfirmedRow === undefined } : {}),
2718
2733
  ...(entry.adopted === true ? { updatedInputDelivered: false } : ambiguousConfirmedRow !== undefined ? { updatedInputDelivered: payloadIsMine } : {}),
@@ -2720,7 +2735,7 @@ export class ToolApprovalCoordinator {
2720
2735
  const othersReached = winnerRow !== undefined ? this.notifyExternalDecision(askId, outcomeOfDecidedRow(winnerRow)).settled : 0;
2721
2736
  return { lost: false, result: withConsumerDisclosure(result, waitingLegsReached(entry, othersReached)) };
2722
2737
  }
2723
- finishRespond(entry, parsed, opts) {
2738
+ finishRespond(entry, judgedOwner, parsed, opts) {
2724
2739
  let rememberApplied;
2725
2740
  const home = standingAnswerHome(pendingEntryStandingAnswerFacts(entry));
2726
2741
  let grantable;
@@ -2745,7 +2760,7 @@ export class ToolApprovalCoordinator {
2745
2760
  if (oldest !== undefined)
2746
2761
  this.allowAllSessions.delete(oldest);
2747
2762
  }
2748
- this.allowAllSessions.add(sessionAllowKey(entry.owner, entry.sessionId, sessionAllowCategory(entry.toolName), entry.grantBinding));
2763
+ this.allowAllSessions.add(sessionAllowKey(judgedOwner, entry.sessionId, sessionAllowCategory(entry.toolName), entry.grantBinding));
2749
2764
  }
2750
2765
  const allowed = parsed.value !== "deny";
2751
2766
  const settleArgs = opts?.settleOutcome !== undefined ? settleArgsOfDecided(opts.settleOutcome) : { allowed, updatedInput: parsed.updatedInput, settledBy: liveSettledBy(parsed.decidedBy) };
@@ -218,8 +218,8 @@ type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected |
218
218
  type ParkArmKeys = keyof Extract<PendingAction, {
219
219
  kind: "tool_approval";
220
220
  }>;
221
- type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
222
- type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "family" | "pathTarget" | "hostTarget" | "targets" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy" | "boundGrantDigest" | "boundGrants" | "classifierNoRuling";
221
+ type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts | keyof import("../approval-card.js").SkillCardFacts;
222
+ type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "family" | "pathTarget" | "hostTarget" | "targets" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy";
223
223
  type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
224
224
  type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
225
225
  type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
@@ -3,7 +3,7 @@ import { CALLER_TEXT_CUT_MARK } from "../caller-text-echo.js";
3
3
  import { errorKindOf } from "../observability/err-text.js";
4
4
  import { recordFailOpen } from "../observability/fail-open.js";
5
5
  import { OVERSIZED_REDACTION_PLACEHOLDER } from "../observability/run-terminal.js";
6
- import { proposeRedactionSpans, REDACTION_TOKEN_RE_SRC } from "./redact.js";
6
+ import { proposeRedactionSpans, readingHeadEnd, REDACTION_TOKEN_RE_SRC } from "./redact.js";
7
7
  const EXIT_SEATS = {
8
8
  "trace-frame-text": { scope: "free-text" },
9
9
  "trace-frame-line": { scope: "free-text", maxChars: 300 },
@@ -38,8 +38,7 @@ function mergeSpans(spans) {
38
38
  return out;
39
39
  }
40
40
  const TOKEN_AT = new RegExp(REDACTION_TOKEN_RE_SRC, "y");
41
- function cutPlain(gap, room) {
42
- let cut = room;
41
+ function safeCutPoint(gap, cut) {
43
42
  const hi = gap.charCodeAt(cut - 1);
44
43
  if (cut > 0 && hi >= 0xd800 && hi <= 0xdbff && (gap.charCodeAt(cut) & 0xfc00) === 0xdc00)
45
44
  cut -= 1;
@@ -50,16 +49,19 @@ function cutPlain(gap, room) {
50
49
  if (m !== null && open + m[0].length > cut)
51
50
  cut = open;
52
51
  }
53
- return gap.slice(0, cut);
52
+ return cut;
54
53
  }
55
- function renderOnce(text, groups) {
54
+ const cutPlain = (gap, room) => gap.slice(0, safeCutPoint(gap, room));
55
+ function renderOnce(text, groups, until = Number.POSITIVE_INFINITY) {
56
56
  const parts = [];
57
57
  let at = 0;
58
58
  for (const g of groups) {
59
+ if (g.start >= until)
60
+ break;
59
61
  parts.push(text.slice(at, g.start), g.repl);
60
62
  at = g.end;
61
63
  }
62
- parts.push(text.slice(at));
64
+ parts.push(text.slice(at, until));
63
65
  return parts.join("");
64
66
  }
65
67
  function emitAs(form, text) {
@@ -79,12 +81,18 @@ function emitAs(form, text) {
79
81
  }
80
82
  }
81
83
  const layoutOf = (row) => (row.emit === undefined ? "multi-line" : "single-line");
82
- function boundRow(out, row) {
83
- if (row.maxChars === undefined || out.length <= row.maxChars)
84
+ const SCAN_WINDOW_FLOOR = 4096;
85
+ const SCAN_WINDOW_PER_BOUND = 8;
86
+ const scanWindowOf = (maxChars) => Math.max(SCAN_WINDOW_FLOOR, maxChars * SCAN_WINDOW_PER_BOUND);
87
+ function boundRow(out, row, cutUpstream) {
88
+ if (row.maxChars === undefined)
84
89
  return out;
85
90
  if (row.emit === undefined)
86
- return cutPlain(out, row.maxChars);
87
- return cutPlain(out, row.maxChars - CALLER_TEXT_CUT_MARK.length) + CALLER_TEXT_CUT_MARK;
91
+ return out.length <= row.maxChars ? out : cutPlain(out, row.maxChars);
92
+ if (out.length <= row.maxChars && !cutUpstream)
93
+ return out;
94
+ const room = row.maxChars - CALLER_TEXT_CUT_MARK.length;
95
+ return (out.length <= room ? out : cutPlain(out, room)) + CALLER_TEXT_CUT_MARK;
88
96
  }
89
97
  export function seatText(seat, text) {
90
98
  const row = SEAT_ROWS.get(seat);
@@ -92,11 +100,27 @@ export function seatText(seat, text) {
92
100
  throw new Error(`exit seat: ${JSON.stringify(seat)} is not a row of the seat table`);
93
101
  let out;
94
102
  try {
95
- if (row.emit !== undefined && emitAs(row.emit, text) === "")
103
+ const layout = layoutOf(row);
104
+ const scanWindow = row.maxChars === undefined ? undefined : scanWindowOf(row.maxChars);
105
+ let windowEnd;
106
+ let regionEnd;
107
+ if (scanWindow !== undefined && text.length > scanWindow) {
108
+ const half = scanWindow >>> 1;
109
+ const region = safeCutPoint(text, readingHeadEnd(text, layout, half));
110
+ const end = safeCutPoint(text, readingHeadEnd(text, layout, scanWindow - half, region));
111
+ if (end < text.length) {
112
+ windowEnd = end;
113
+ regionEnd = region;
114
+ }
115
+ }
116
+ const head = windowEnd === undefined ? text : text.slice(0, windowEnd);
117
+ if (row.emit !== undefined && emitAs(row.emit, head) === "")
96
118
  out = "";
97
119
  else {
98
- const rendered = renderOnce(text, mergeSpans(proposeRedactionSpans(text, row.scope, layoutOf(row))));
99
- out = boundRow(row.emit === undefined ? rendered : emitAs(row.emit, rendered), row);
120
+ const emitUntil = regionEnd;
121
+ const extent = windowEnd === undefined ? "whole" : "head";
122
+ const rendered = renderOnce(head, mergeSpans(proposeRedactionSpans(head, row.scope, layout, extent)), emitUntil);
123
+ out = boundRow(row.emit === undefined ? rendered : emitAs(row.emit, rendered), row, windowEnd !== undefined);
100
124
  }
101
125
  }
102
126
  catch (err) {
@@ -65,16 +65,6 @@ declare const DISPLAY_SAFE_URL_TOKEN = "\u00ABredacted:url\u00BB";
65
65
  /** [ref]:出网代理 URL 的回显尺(`outbound-dispatcher.ts` `displayProxyUrl`)形不干净时整只用**同一枚**占位 —— 单独一行导出,
66
66
  * 上面那行字面量保持 `const NAME = "…";` 形(`test/api-notice-text-freeze.test.ts` §6 的冻结扫描按这一形认替换 token)。 */
67
67
  export { DISPLAY_SAFE_URL_TOKEN };
68
- /**
69
- * 名表的**只读导出**([ref]:第四处消费 —— 受理口的 URL 凭据判官 `url-credential.ts`;文档同步钉按它对表)。
70
- * 表的属主仍是本模块的 {@link CREDENTIAL_NAME_ANCHORS};加一个名字只改那一处,本数组与下面的谓词随之现算。
71
- */
72
- export declare const CREDENTIAL_PARAM_NAMES: readonly string[];
73
- /**
74
- * 「这个**已解码**的参数名是不是凭据名」([ref] 第四处消费)。入参是收方读到的那一形(`URLSearchParams` 解过一次码的名字),
75
- * 判法与本模块 URL 参数臂同读 —— 同一只判定 {@link credentialNameAnchor}:整名在表里(ASCII 大小写不敏感,不做 Unicode 折叠)。
76
- */
77
- export declare function isCredentialParamName(decodedName: string): boolean;
78
68
  /**
79
69
  * 🔴 **取证相的重扫模式(测试座,[ref])** —— 生产恒 `"incremental"`;`"full"` 是**参照语义** = 「没有可复用的上一次执行」:
80
70
  * 每级五条取证臂整段重跑、URL 臂每段重扫,既不读足迹也不回放(池把每一级的脏区间当作「未知」交下去,与级 0 同一个读法)。
@@ -135,6 +125,32 @@ export declare function setRedactionObserver(fn: ((patternLabel: string, count:
135
125
  * · `single-line`:出口在读者看见之前把文本压成一行(出口座的日志字段行 / 标题行:空白控制字符被删或折成空格)⇒ ①②③④。
136
126
  */
137
127
  export type RedactionLayout = "multi-line" | "single-line";
128
+ /**
129
+ * 一段文本相对「它所在的那一整段」的**范围**(闭集两词;调用方对自己交来的文本的陈述,不是旋钮):
130
+ * · `whole`:这就是全部。所有「见收尾才认」的结构臂走到末尾仍没见收尾 ⇒ 这一起点不成 —— 判据 = 声明正则(`test/trace-redact.test.ts` 的真值表钉着扫描器与声明正则逐跨度等价);
131
+ * · `head`:这只是更长文本的头一段(出口座对有界行只扫一段头,见 `trace/exit-seat.ts` 的扫描窗)。**一条律:结构的判定走到这一段末尾还没走完 ⇒ 判定在更远处(未决)⇒ 从结构起点遮到这一段的末尾**
132
+ * (fail-closed:起点在、判定要看窗外,明文不许出门)。未决只有两种,同读一条臂:
133
+ * ① **收尾没见到** —— JSON 串值(要收尾引号)· URL authority(要 `@`;authority 一路走到末尾没有 `/` / 空白,里面有没有 `@` 都读成还在 userinfo 里)·
134
+ * SSH2 / PEM 栅栏(要收尾栅栏)· 带引号的强锚值(要收尾引号)· PuTTY 容器(终止符之后到末尾没见下一个版本头);
135
+ * ② **起头没读完**(起头本身不设长度上界,起点可以离末尾很远)—— PEM 起始栅栏行(标签之后到末尾一枚 `-----` 都没有,已读到的部分也还可能是合法栅栏行的前缀)· PuTTY 版本头(`<数字串>: ` 没写完)·
136
+ * 无头 .ppk 字段(`Private-Lines: <数字串>` 之后下一个体行的判定没走完)。
137
+ * PuTTY 容器臂的「无合格终止 ⇒ 吃到输入末尾」本来就是这一律在整段文本上的形,两种范围都吃到末尾;区别只在留痕 —— 整段读法上它是兜底(记 `server.redact.ppk-eat-to-end`),
138
+ * head 读法上终止可能在更远处,是未决(不记)。**未决一律不记 fail-open**:它是这一律的正常读数,不是回退;
139
+ * 由裸形 / 关键字 / 查询参数 / 环境变量赋值等**不靠收尾**的臂认出的凭据(值到字符类的自然右界)两种范围读法逐字相同。
140
+ */
141
+ export type RedactionExtent = "whole" | "head";
142
+ /**
143
+ * 🔴 **出口座扫描窗的单位 = 读法单元,不是原始码元**(修复验证腿 high ①):从 `from`(缺省 0)起数 `units` 个**读法单元**(各读法里最少的那一读 —— 删得最多的 —— 剩下的码元),
144
+ * 返回原文里的终点(含 `≥ units` 个读法单元的前缀,至多是最短那一个的两倍;文本不够长 ⇒ `text.length`)。格式字符 / 控制字符 / 转义写法的不可见字符不计数:它们在读法格里被删,
145
+ * 整段读法认得出的凭据(`sk-` + 18 位 + 五千个零宽空格 + 余下 12 位)靠它们之后的字节凑够最短长度 —— 窗若按原始码元计,填充一撑就把证据推到窗外,窗读法只见前缀而漏。
146
+ * `from`:出门区与余量**各自从自己的起点数**(窗 = 出门区〔从头数半窗〕+ 余量〔从出门区终点起再数半窗〕),不是各自从头数「第一个 ≥ units 的终点」—— 后者遇到不可见填充会按翻倍
147
+ * 跳到同一个终点,余量塌成 0,被窗尾切开的凭据在出门区里露出半截(修复验证 r3 high)。
148
+ * 取舍:不可见填充很大的文本,窗随之延伸(最坏延伸到整段 = 整段读法的代价),换来「窗里的读法单元数恒 ≥ `units`」这一个不变量 —— 窗里每一读的视图都不短于 `units`。
149
+ * 终点不落在转义写法中间:前缀末尾还没写完的转义写法({@link trailingPartialEscapeStart},在读法后文本上判,{@link shortestReading})不计数、窗延伸到它写完 —— 出门区终点与窗尾都因此落在转义的边上,余量从干净的边起数。
150
+ * 复杂度:**第一趟**若可见占比 ≥ 一半(少量换行 / 色码)按占比补足缺口,均匀的文本一两趟落在终点附近;补不足(或占比低)的趟一律至少翻倍 —— 总趟数 O(log 终点)、总功 O(终点),
151
+ * 不存在逐趟只前进几个码元的爬行(4095 个可见 + 4000 个控制字符曾因此平方,K=4000 约 0.2–0.3 s)。判据与读法格同源(同一批切除点收集器,零第二份「什么算不可见」)。
152
+ */
153
+ export declare function readingHeadEnd(text: string, layout: RedactionLayout, units: number, from?: number): number;
138
154
  export declare function redactSecrets(text: string, opts?: {
139
155
  observe?: boolean;
140
156
  json?: boolean;
@@ -195,7 +211,7 @@ export type RedactionSpan = {
195
211
  readonly repl: string;
196
212
  };
197
213
  export type { RedactionScope };
198
- export declare function proposeRedactionSpans(text: string, scope: RedactionScope, layout: RedactionLayout): readonly RedactionSpan[];
214
+ export declare function proposeRedactionSpans(text: string, scope: RedactionScope, layout: RedactionLayout, extent: RedactionExtent): readonly RedactionSpan[];
199
215
  /**
200
216
  * 🔴 环 + 深度预算(2026-07-25 补)。原实现是纯递归、**既不记已访问节点也不限深度**,而它的调用位置是
201
217
  * **最不可信的边界**:`toolStart`/`toolEnd` 把工具的 `args`/`output`/`structured` 直接喂进来。后果两条:
@@ -232,7 +248,7 @@ export declare function redactedPreview(value: unknown, cap?: number): string |
232
248
  *
233
249
  * 🔴 **[ref] 展示半场(7.90.0 / core 7.24.0):两条正交的律合成一次,名表的射程缩到不可约的那一半**。
234
250
  * 改前内部只有一条:`redactSecrets(new URL(raw).href)` —— 自由文本那条**名单制**的律,query 的值只在键名
235
- * 落在 `CREDENTIAL_NAME_WORDS`(或值本身像个密钥)时才遮,于是**没人列过的那个键名**就是它的盲点
251
+ * 落在凭据名表 `CREDENTIAL_PARAM_NAMES`(或值本身像个密钥)时才遮,于是**没人列过的那个键名**就是它的盲点
236
252
  * (`?sig=…` / `?auth=…` / 部署自定义的网关参数名)。现在前面先跑 core 的 `displayUrlOf`,它是**位置制**的:
237
253
  * · userinfo **落地**(不是遮成记号 —— 它从**解析**组合 `scheme://host + path + query`,userinfo 结构上
238
254
  * 进不了产物);凭据在 userinfo 这件事本身仍然响亮,只不过是在**声明关口**响