@sema-agent/server 7.141.0 → 7.142.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.
- package/MIGRATION.md +215 -0
- package/USAGE.md +67 -12
- package/dist/approval-ask-audit-store.d.ts +54 -38
- package/dist/approval-ask-audit-store.js +58 -39
- package/dist/approval-card.d.ts +8 -8
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +33 -1
- package/dist/approval-reconciler.js +43 -40
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +16 -0
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +26 -23
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/memory-consolidation.js +2 -1
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.js +4 -3
- package/dist/boot/resolve-spec.js +11 -2
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.js +16 -13
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +3 -2
- package/dist/boot/stage-04-stores.js +3 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.js +8 -6
- package/dist/boot/stage-06-runners.d.ts +1 -1
- package/dist/boot/stage-06-runners.js +2 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -1
- package/dist/boot/stage-07-capability-layer.js +2 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -1
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +5 -4
- package/dist/boot/stores.js +3 -2
- package/dist/boot/web-admin.js +17 -17
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capability-facts.d.ts +24 -0
- package/dist/capability-facts.js +23 -0
- package/dist/config-catalog.js +1 -0
- package/dist/config-types.d.ts +15 -4
- package/dist/config.d.ts +6 -0
- package/dist/config.js +17 -3
- package/dist/execution-lane-caps.d.ts +2 -1
- package/dist/fleet/fleet-bus.d.ts +6 -3
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +30 -1
- package/dist/http/dispatch.js +4 -2
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.js +5 -9
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +5 -2
- package/dist/http/routes/memory-policy.js +26 -6
- package/dist/http/routes/runs.js +3 -3
- package/dist/http/routes/session-memory-sync.d.ts +63 -0
- package/dist/http/routes/session-memory-sync.js +157 -0
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/tasks.js +7 -5
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +9 -3
- package/dist/http/send.d.ts +5 -0
- package/dist/http/send.js +9 -2
- package/dist/http/server.d.ts +14 -8
- package/dist/http/server.js +2 -4
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-sync-client.d.ts +21 -21
- package/dist/memory-sync-client.js +50 -42
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +7 -1
- package/dist/memory-sync.js +6 -3
- package/dist/observability/fail-open.d.ts +27 -10
- package/dist/observability/fail-open.js +20 -7
- package/dist/observability/run-terminal.d.ts +92 -34
- package/dist/observability/run-terminal.js +167 -162
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +48 -7
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +9 -8
- package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
- package/dist/plugins/approval-ask-store-sql.js +49 -19
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/local-session-store.d.ts +36 -7
- package/dist/plugins/local-session-store.js +76 -32
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +11 -1
- package/dist/plugins/one-time-migrations.d.ts +27 -1
- package/dist/plugins/one-time-migrations.js +57 -3
- package/dist/plugins/remote-env-e2b.d.ts +23 -4
- package/dist/plugins/remote-env-e2b.js +111 -28
- package/dist/plugins/remote-env-k8s.d.ts +31 -5
- package/dist/plugins/remote-env-k8s.js +86 -19
- package/dist/plugins/remote-env-local-docker.d.ts +15 -3
- package/dist/plugins/remote-env-local-docker.js +40 -12
- package/dist/plugins/run-store-sql.d.ts +9 -1
- package/dist/plugins/run-store-sql.js +9 -1
- package/dist/plugins/store-backend.d.ts +10 -7
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +26 -1
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +221 -28
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/run-local.js +2 -8
- package/dist/runs.js +1 -2
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +9 -0
- package/dist/security.js +8 -0
- package/dist/session-error-wire.d.ts +24 -0
- package/dist/session-error-wire.js +7 -0
- package/dist/task-settings.d.ts +10 -2
- package/dist/task-settings.js +15 -2
- package/dist/terminal.d.ts +39 -19
- package/dist/terminal.js +65 -22
- package/dist/tool-approval.d.ts +4 -3
- package/dist/tool-approval.js +12 -14
- package/dist/trace/core-keyset-guard.d.ts +123 -6
- package/dist/trace/engine-notice-wire.d.ts +8 -2
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +2 -1
- package/dist/trace/ledger-sink.d.ts +8 -4
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -0
- package/dist/trace/self-minted-wire.d.ts +251 -2
- package/dist/trace/self-minted-wire.js +82 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/web-admin-client/apply-effective.js +21 -1
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/types.d.ts +4 -0
- package/package.json +1 -1
- package/wire-changes/server@7.142.0.json +639 -0
|
@@ -3,7 +3,7 @@ import { redactSecrets } from "../trace/redact.js";
|
|
|
3
3
|
import { isProviderEchoShape } from "@sema-agent/core";
|
|
4
4
|
import { isModelTextLedgerEventType, isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
5
5
|
import { isUnsuccessfulRunStatus } from "../plugins/store-contracts.js";
|
|
6
|
-
import { mapPauseSeats, PAUSE_SEATS, sealResultForExit } from "../terminal.js";
|
|
6
|
+
import { mapPauseSeats, PAUSE_SEATS, sealResultForExit, sealStoredResult } from "../terminal.js";
|
|
7
7
|
import { errorText } from "./err-text.js";
|
|
8
8
|
import { recordFailOpen } from "./fail-open.js";
|
|
9
9
|
import { toolEndMediaAsDescriptors } from "../trace/tool-output-media.js";
|
|
@@ -128,43 +128,138 @@ function shapeEchoMembers(node, table, seat) {
|
|
|
128
128
|
}
|
|
129
129
|
return out ?? node;
|
|
130
130
|
}
|
|
131
|
-
function shapeEchoRows(rows, table, seat) {
|
|
132
|
-
if (!Array.isArray(rows))
|
|
133
|
-
return rows;
|
|
134
|
-
const next = rows.map((row) => shapeEchoMembers(row, table, seat));
|
|
135
|
-
return next.every((x, i) => x === rows[i]) ? rows : next;
|
|
136
|
-
}
|
|
137
131
|
const MAX_EDITED_FILES = 1000;
|
|
138
|
-
const
|
|
139
|
-
terminal: "redacted",
|
|
140
|
-
remoteEnvFailures: "redacted",
|
|
141
|
-
editedFiles: "redacted",
|
|
142
|
-
responseModel: "provider-echo",
|
|
143
|
-
apiFailure: "provider-echo",
|
|
144
|
-
strandedHumanAnswers: "provider-echo",
|
|
132
|
+
export const RESULT_TEXT_LEAVES = {
|
|
145
133
|
taskId: "verbatim",
|
|
146
134
|
runId: "verbatim",
|
|
147
135
|
sessionId: "verbatim",
|
|
148
136
|
model: "verbatim",
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
137
|
+
"terminal.code": "outward-code",
|
|
138
|
+
"terminal.message": "error-text",
|
|
139
|
+
"terminal.reason": "error-text",
|
|
140
|
+
"terminal.detail": "terminal-detail",
|
|
141
|
+
"terminal.checkpointId": "verbatim",
|
|
142
|
+
"terminal.requestedByCallId": "provider-join-key",
|
|
143
|
+
"terminal.nestedPause.checkpointId": "verbatim",
|
|
144
|
+
"terminal.nestedPause.requestedByCallId": "provider-join-key",
|
|
145
|
+
result: "model-text",
|
|
146
|
+
salvagedOutput: "model-text",
|
|
147
|
+
"remoteEnvFailures.[].message": "error-text",
|
|
148
|
+
"rewindNotes.[].message": "error-text",
|
|
149
|
+
"editedFiles.[].path": "model-text",
|
|
150
|
+
responseModel: "provider-echo",
|
|
151
|
+
apiFailure: "api-failure",
|
|
152
|
+
"strandedHumanAnswers.[]": "stranded-answer",
|
|
153
|
+
"degraded.from": "verbatim",
|
|
154
|
+
"degraded.to": "verbatim",
|
|
155
|
+
"degraded.chain.[]": "verbatim",
|
|
156
|
+
"effectiveReasoning.endpoint": "verbatim",
|
|
157
|
+
"resultEntryIds.[]": "verbatim",
|
|
158
|
+
"pendingApproval.askIds.[]": "verbatim",
|
|
159
|
+
"effectiveReadDenyPatterns.[].pattern": "verbatim",
|
|
160
|
+
"effectiveMemoryScopes.writeScope": "verbatim",
|
|
161
|
+
"effectiveMemoryScopes.scopes.[].scope": "verbatim",
|
|
162
|
+
"effectiveMemoryScopes.projectKey.scope": "verbatim",
|
|
163
|
+
"effectiveMemoryScopes.materializedResidue.[]": "verbatim",
|
|
155
164
|
structuredOutput: "verbatim",
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
stats: "stats-tree",
|
|
166
|
+
"verification.findings.[]": "model-text",
|
|
167
|
+
"verification.evidence": "model-text",
|
|
168
|
+
"verification.verifierPause.sessionId": "verbatim",
|
|
169
|
+
"attempts.[].model": "verbatim",
|
|
170
|
+
"attempts.[].errorCode": "outward-code",
|
|
171
|
+
"attempts.[].gateError": "error-text",
|
|
172
|
+
"attempts.[].diagnostics": "model-text",
|
|
173
|
+
"cancelContext.lastEventKind": "verbatim",
|
|
174
|
+
"cancelContext.lastBrainStatus.phase": "verbatim",
|
|
175
|
+
"cancelContext.lastBrainStatus.errClass": "verbatim",
|
|
167
176
|
};
|
|
177
|
+
const DROP_LEAF = Symbol("drop-leaf");
|
|
178
|
+
function textLeafAct(treatment) {
|
|
179
|
+
switch (treatment) {
|
|
180
|
+
case "verbatim":
|
|
181
|
+
case "provider-join-key":
|
|
182
|
+
case "outward-code":
|
|
183
|
+
return undefined;
|
|
184
|
+
case "model-text":
|
|
185
|
+
return (v) => (typeof v === "string" ? redactModelText(v) : v);
|
|
186
|
+
case "error-text":
|
|
187
|
+
return (v) => (typeof v === "string" ? redactErrorMessage(v) : v);
|
|
188
|
+
case "provider-echo":
|
|
189
|
+
return (v, _holder, seat) => (admitProviderEcho(v, seat) ? v : DROP_LEAF);
|
|
190
|
+
case "stats-tree":
|
|
191
|
+
return (v) => redactStats(v);
|
|
192
|
+
case "terminal-detail":
|
|
193
|
+
return (v, holder) => shapeTerminalDetail(v, holder);
|
|
194
|
+
case "api-failure":
|
|
195
|
+
return (v) => shapeEchoMembers(v, API_FAILURE_TREATMENT, "apiFailure");
|
|
196
|
+
case "stranded-answer":
|
|
197
|
+
return (v) => shapeEchoMembers(v, STRANDED_ANSWER_TREATMENT, "strandedHumanAnswers[]");
|
|
198
|
+
default: {
|
|
199
|
+
const unhandled = treatment;
|
|
200
|
+
throw new Error(`unhandled text-leaf treatment: ${String(unhandled)}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const ACTIVE_TEXT_LEAVES = Object.entries(RESULT_TEXT_LEAVES).flatMap(([seat, treatment]) => {
|
|
205
|
+
const act = textLeafAct(treatment);
|
|
206
|
+
return act === undefined ? [] : [{ seat, segments: seat.split("."), act }];
|
|
207
|
+
});
|
|
208
|
+
function mapTextLeaf(node, segments, depth, holder, act, seat) {
|
|
209
|
+
if (depth === segments.length)
|
|
210
|
+
return act(node, holder, seat);
|
|
211
|
+
if (node === null || typeof node !== "object")
|
|
212
|
+
return node;
|
|
213
|
+
const segment = segments[depth];
|
|
214
|
+
if (segment === "[]") {
|
|
215
|
+
if (!Array.isArray(node))
|
|
216
|
+
return node;
|
|
217
|
+
let changed = false;
|
|
218
|
+
const next = [];
|
|
219
|
+
for (const el of node) {
|
|
220
|
+
const n = el === undefined ? el : mapTextLeaf(el, segments, depth + 1, node, act, seat);
|
|
221
|
+
if (n === DROP_LEAF) {
|
|
222
|
+
changed = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (!Object.is(n, el))
|
|
226
|
+
changed = true;
|
|
227
|
+
next.push(n);
|
|
228
|
+
}
|
|
229
|
+
return changed ? next : node;
|
|
230
|
+
}
|
|
231
|
+
if (Array.isArray(node))
|
|
232
|
+
return node;
|
|
233
|
+
const obj = node;
|
|
234
|
+
let out;
|
|
235
|
+
for (const key of segment === "*" ? Object.keys(obj) : Object.hasOwn(obj, segment) ? [segment] : []) {
|
|
236
|
+
const child = obj[key];
|
|
237
|
+
if (child === undefined)
|
|
238
|
+
continue;
|
|
239
|
+
const n = mapTextLeaf(child, segments, depth + 1, obj, act, seat);
|
|
240
|
+
if (n === DROP_LEAF)
|
|
241
|
+
delete (out ??= { ...obj })[key];
|
|
242
|
+
else if (!Object.is(n, child))
|
|
243
|
+
(out ??= { ...obj })[key] = n;
|
|
244
|
+
}
|
|
245
|
+
return out ?? node;
|
|
246
|
+
}
|
|
247
|
+
function applyResultTextLeaves(result) {
|
|
248
|
+
let out = result;
|
|
249
|
+
for (const row of ACTIVE_TEXT_LEAVES)
|
|
250
|
+
out = mapTextLeaf(out, row.segments, 0, undefined, row.act, row.seat);
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
function shapeTerminalDetail(detail, terminal) {
|
|
254
|
+
const kind = terminal !== null && typeof terminal === "object" ? terminal.kind : undefined;
|
|
255
|
+
if (kind === "failed")
|
|
256
|
+
return shapeArmedDetail(detail, TERMINAL_FAILURE_DETAIL_TREATMENT);
|
|
257
|
+
if (kind === "completed")
|
|
258
|
+
return shapeArmedDetail(detail, TERMINAL_COMPLETED_DETAIL_TREATMENT);
|
|
259
|
+
if (kind === "blocked")
|
|
260
|
+
return shapeTerminalBlockedDetail(detail);
|
|
261
|
+
return shapeArmedDetail(detail, {});
|
|
262
|
+
}
|
|
168
263
|
const API_FAILURE_TREATMENT = {
|
|
169
264
|
status: "verbatim",
|
|
170
265
|
requestId: "provider-echo",
|
|
@@ -209,12 +304,15 @@ function redactStringLeaves(node, depth = 0) {
|
|
|
209
304
|
}
|
|
210
305
|
const OFF_ARM_MEMBER_TREATMENT = "free-text-tree";
|
|
211
306
|
const redactDetailMember = (v) => redactStringLeaves(v, 1);
|
|
212
|
-
|
|
307
|
+
const TERMINAL_COMPLETED_DETAIL_TREATMENT = {
|
|
308
|
+
finish_reason_unknown: { kind: "verbatim", finishReasonUnknown: "provider-echo" },
|
|
309
|
+
};
|
|
310
|
+
function shapeArmedDetail(detail, arms) {
|
|
213
311
|
if (detail === null || typeof detail !== "object" || Array.isArray(detail))
|
|
214
312
|
return detail;
|
|
215
313
|
const d = detail;
|
|
216
314
|
const kind = d.kind;
|
|
217
|
-
const arm = typeof kind === "string" && Object.hasOwn(
|
|
315
|
+
const arm = typeof kind === "string" && Object.hasOwn(arms, kind) ? arms[kind] : {};
|
|
218
316
|
let out;
|
|
219
317
|
for (const key of Object.keys(d)) {
|
|
220
318
|
const v = d[key];
|
|
@@ -237,7 +335,7 @@ function shapeTerminalFailureDetail(detail) {
|
|
|
237
335
|
}
|
|
238
336
|
default: {
|
|
239
337
|
const unhandled = treatment;
|
|
240
|
-
throw new Error(`unhandled
|
|
338
|
+
throw new Error(`unhandled detail-member treatment: ${String(unhandled)}`);
|
|
241
339
|
}
|
|
242
340
|
}
|
|
243
341
|
}
|
|
@@ -282,153 +380,60 @@ function shapeTerminalBlockedDetail(detail) {
|
|
|
282
380
|
}
|
|
283
381
|
return out ?? detail;
|
|
284
382
|
}
|
|
285
|
-
const PROVIDER_ECHO_SHAPERS = {
|
|
286
|
-
responseModel: (v) => (admitProviderEcho(v, "responseModel") ? v : undefined),
|
|
287
|
-
apiFailure: (v) => shapeEchoMembers(v, API_FAILURE_TREATMENT, "apiFailure"),
|
|
288
|
-
strandedHumanAnswers: (v) => shapeEchoRows(v, STRANDED_ANSWER_TREATMENT, "strandedHumanAnswers[]"),
|
|
289
|
-
};
|
|
290
|
-
{
|
|
291
|
-
const judged = Object.entries(RESULT_KEY_TREATMENT).filter(([, t]) => t === "provider-echo").map(([k]) => k).sort();
|
|
292
|
-
const wired = Object.keys(PROVIDER_ECHO_SHAPERS).sort();
|
|
293
|
-
if (judged.join(",") !== wired.join(","))
|
|
294
|
-
throw new Error(`run-terminal: provider-echo classification [${judged.join(",")}] ≠ shaper table [${wired.join(",")}]`);
|
|
295
|
-
}
|
|
296
383
|
const LEGACY_RESULT_LEAVES = ["errorMessage", "blockedReason", "checkpointGate"];
|
|
297
384
|
export const TREATED_RESULT_LEAVES = [
|
|
298
385
|
...new Set([
|
|
299
|
-
...
|
|
386
|
+
...ACTIVE_TEXT_LEAVES.map((row) => row.segments[0]),
|
|
300
387
|
...LEGACY_RESULT_LEAVES,
|
|
301
388
|
...PAUSE_SEATS.map((seat) => seat.path[0]),
|
|
389
|
+
"editedFiles",
|
|
302
390
|
]),
|
|
303
391
|
];
|
|
304
392
|
export function redactTerminalResult(result) {
|
|
305
393
|
if (result === undefined || result === null)
|
|
306
394
|
return result;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
if (r.checkpointGate !== undefined) {
|
|
319
|
-
const legacyGate = redactCheckpointGate(r.checkpointGate);
|
|
320
|
-
if (legacyGate !== r.checkpointGate) {
|
|
321
|
-
out.checkpointGate = legacyGate;
|
|
322
|
-
changed = true;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (r.terminal !== null && typeof r.terminal === "object") {
|
|
326
|
-
const t = r.terminal;
|
|
327
|
-
let nt;
|
|
328
|
-
if (t.kind === "failed" && typeof t.message === "string")
|
|
329
|
-
nt = { ...t, message: redactErrorMessage(t.message) };
|
|
330
|
-
else if (t.kind === "blocked" && typeof t.reason === "string")
|
|
331
|
-
nt = { ...t, reason: redactErrorMessage(t.reason) };
|
|
332
|
-
if (t.kind === "blocked") {
|
|
333
|
-
const cur = nt ?? t;
|
|
334
|
-
const detail = cur.detail;
|
|
335
|
-
const shaped = shapeTerminalBlockedDetail(detail);
|
|
336
|
-
if (shaped !== detail)
|
|
337
|
-
nt = { ...cur, detail: shaped };
|
|
338
|
-
}
|
|
339
|
-
const base = nt ?? t;
|
|
340
|
-
if (t.kind === "failed") {
|
|
341
|
-
const cur = nt ?? base;
|
|
342
|
-
const detail = cur.detail;
|
|
343
|
-
const shaped = shapeTerminalFailureDetail(detail);
|
|
344
|
-
if (shaped !== detail)
|
|
345
|
-
nt = { ...cur, detail: shaped };
|
|
346
|
-
}
|
|
347
|
-
if (nt !== undefined) {
|
|
348
|
-
out.terminal = nt;
|
|
349
|
-
changed = true;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const gated = mapPauseSeats(out, (seat, keys) => {
|
|
395
|
+
return redactLegacyPlaneLeaves(redactPauseSeatGates(applyResultTextLeaves(capEditedFiles(result))));
|
|
396
|
+
}
|
|
397
|
+
function capEditedFiles(result) {
|
|
398
|
+
const files = result.editedFiles;
|
|
399
|
+
if (!Array.isArray(files) || files.length <= MAX_EDITED_FILES)
|
|
400
|
+
return result;
|
|
401
|
+
return { ...result, editedFiles: files.slice(0, MAX_EDITED_FILES) };
|
|
402
|
+
}
|
|
403
|
+
function redactPauseSeatGates(result) {
|
|
404
|
+
return mapPauseSeats(result, (seat, keys) => {
|
|
353
405
|
if (!Object.hasOwn(seat, keys.gate))
|
|
354
406
|
return seat;
|
|
355
407
|
const g = seat[keys.gate];
|
|
356
408
|
const ng = redactCheckpointGate(g);
|
|
357
409
|
return ng === g ? seat : { ...seat, [keys.gate]: ng };
|
|
358
410
|
});
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (r.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
changed = true;
|
|
368
|
-
}
|
|
369
|
-
if (r.editedFiles !== undefined) {
|
|
370
|
-
out.editedFiles = Array.isArray(r.editedFiles)
|
|
371
|
-
? r.editedFiles
|
|
372
|
-
.slice(0, MAX_EDITED_FILES)
|
|
373
|
-
.map((f) => {
|
|
374
|
-
if (f === null || typeof f !== "object")
|
|
375
|
-
return f;
|
|
376
|
-
const path = f.path;
|
|
377
|
-
return typeof path === "string" ? { ...f, path: redactSecrets(path) } : f;
|
|
378
|
-
})
|
|
379
|
-
: r.editedFiles;
|
|
380
|
-
changed = true;
|
|
381
|
-
}
|
|
382
|
-
if (typeof r.result === "string") {
|
|
383
|
-
const next = redactModelText(r.result);
|
|
384
|
-
if (next !== r.result) {
|
|
385
|
-
out.result = next;
|
|
386
|
-
changed = true;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (typeof r.salvagedOutput === "string") {
|
|
390
|
-
const next = redactModelText(r.salvagedOutput);
|
|
391
|
-
if (next !== r.salvagedOutput) {
|
|
392
|
-
out.salvagedOutput = next;
|
|
393
|
-
changed = true;
|
|
394
|
-
}
|
|
411
|
+
}
|
|
412
|
+
function redactLegacyPlaneLeaves(result) {
|
|
413
|
+
const r = result;
|
|
414
|
+
let out;
|
|
415
|
+
if (typeof r.errorMessage === "string") {
|
|
416
|
+
const next = redactErrorMessage(r.errorMessage);
|
|
417
|
+
if (next !== r.errorMessage)
|
|
418
|
+
(out ??= { ...result }).errorMessage = next;
|
|
395
419
|
}
|
|
396
|
-
if (r.
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return n;
|
|
401
|
-
const message = n.message;
|
|
402
|
-
return typeof message === "string" ? { ...n, message: redactErrorMessage(message) } : n;
|
|
403
|
-
})
|
|
404
|
-
: r.rewindNotes;
|
|
405
|
-
changed = true;
|
|
406
|
-
}
|
|
407
|
-
if (r.stats !== undefined) {
|
|
408
|
-
const next = redactStats(r.stats);
|
|
409
|
-
if (next !== r.stats) {
|
|
410
|
-
out.stats = next;
|
|
411
|
-
changed = true;
|
|
412
|
-
}
|
|
420
|
+
if (typeof r.blockedReason === "string") {
|
|
421
|
+
const next = redactErrorMessage(r.blockedReason);
|
|
422
|
+
if (next !== r.blockedReason)
|
|
423
|
+
(out ??= { ...result }).blockedReason = next;
|
|
413
424
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
continue;
|
|
419
|
-
const next = shape(v);
|
|
420
|
-
if (next === v)
|
|
421
|
-
continue;
|
|
422
|
-
if (next === undefined)
|
|
423
|
-
delete out[key];
|
|
424
|
-
else
|
|
425
|
-
out[key] = next;
|
|
426
|
-
changed = true;
|
|
425
|
+
if (r.checkpointGate !== undefined) {
|
|
426
|
+
const next = redactCheckpointGate(r.checkpointGate);
|
|
427
|
+
if (next !== r.checkpointGate)
|
|
428
|
+
(out ??= { ...result }).checkpointGate = next;
|
|
427
429
|
}
|
|
428
|
-
return
|
|
430
|
+
return out === undefined ? result : out;
|
|
431
|
+
}
|
|
432
|
+
export function sealRunRowResult(result) {
|
|
433
|
+
return result === null ? null : redactTerminalResult(sealResultForExit(result));
|
|
429
434
|
}
|
|
430
|
-
export function
|
|
431
|
-
return result === null ? null : redactTerminalResult(
|
|
435
|
+
export function sealStoredRunRowResult(result, seat) {
|
|
436
|
+
return result === null ? null : redactTerminalResult(sealStoredResult(result, seat));
|
|
432
437
|
}
|
|
433
438
|
export function redactLedgerEventData(type, data) {
|
|
434
439
|
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
@@ -897,7 +902,7 @@ export function memoRedactedRead(seat, raw, redact) {
|
|
|
897
902
|
export function memoRedactTerminalResult(seat, raw) {
|
|
898
903
|
if (raw === undefined || raw === null)
|
|
899
904
|
return raw;
|
|
900
|
-
const result =
|
|
905
|
+
const result = sealStoredResult(raw, seat);
|
|
901
906
|
const r = result;
|
|
902
907
|
if (TREATED_RESULT_LEAVES.every((k) => r[k] === undefined))
|
|
903
908
|
return result;
|
|
@@ -126,6 +126,10 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
126
126
|
private replayRejectedCount;
|
|
127
127
|
/** 重放期被丢弃的坏行数(撕尾不计:那是本格式存在的理由,由 {@link corruptQuarantinePath} 单独说)。 */
|
|
128
128
|
private corruptLineCount;
|
|
129
|
+
/** 本次启动重放到的 v1 信封记录数(随 M34;{@link upgradeJournalEnvelope} 的读数)。 */
|
|
130
|
+
private journalV1Records;
|
|
131
|
+
/** 盘上还有 v1 信封记录(随 M34):重放到 v1 记录时置位,任何一次压实(整本重写成 v2)清位 —— 已被压实过的账本不再为升级重写第二次。 */
|
|
132
|
+
private journalV1OnDisk;
|
|
129
133
|
/** 本次启动隔离出去的原件路径(无坏字节 ⇒ `undefined`)。 */
|
|
130
134
|
readonly corruptQuarantinePath: string | undefined;
|
|
131
135
|
/**
|
|
@@ -179,8 +183,8 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
179
183
|
* 🔴 [ref] —— **出域的一次决议不落账本**。作用域的属主维(`owner`,判据 {@link askRowCarriesOwner})不在记录上:行此刻不带着门判过的那位属主
|
|
180
184
|
* ⇒ 内核上这一笔必输、零写(读钟也在判定之后,不开账本窗)⇒ 没有要重放的东西,直调内核拿它的输者答案(与留存两动词「空的一次不落」同一条先例)。
|
|
181
185
|
* 判定、落账、内核调用都在同一个同步前缀里,其间行不可能换属主。于是**落了账的每一条决议都在域内**,重放按行自己的属主喂属主维
|
|
182
|
-
* ({@link FileApprovalAskStore.replayOp})——
|
|
183
|
-
*
|
|
186
|
+
* ({@link FileApprovalAskStore.replayOp})—— 决议记录的形一个字节不变(没有属主格):升级前写下的决议记录与升级后写下的同读,零迁移、零双读
|
|
187
|
+
* (整本账的信封号另算 —— 见顶注「信封版本」)。
|
|
184
188
|
*/
|
|
185
189
|
decideAsk(askId: string, batchId: string, owner: string | null, decision: DecideAskInput): Promise<DecideResult>;
|
|
186
190
|
claimTerminal(askId: string, batchId: string, intent: AskTerminalClaimIntent, opts?: AskTerminalClaimOpts): Promise<AskTerminalClaimOutcome>;
|
|
@@ -208,14 +212,20 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
208
212
|
corruptLines: number;
|
|
209
213
|
records: number;
|
|
210
214
|
};
|
|
211
|
-
/**
|
|
212
|
-
*
|
|
215
|
+
/**
|
|
216
|
+
* 问行成因两列的 local 腿(一次性迁移 M34,窗口见 `plugins/one-time-migrations.ts` 登记表;`LocalBackend.runOneTimeMigrations` 调):本次启动重放到的 v1 信封记录
|
|
217
|
+
* 已由读侧半场按意义译进内存({@link translateJournalV1Record}),这里**一次压实**把整本账重写成 v2 快照(压实 = 既有整本原子重写)。返回 v1 记录数;没有 ⇒ 零写、0。
|
|
218
|
+
* 本次启动里账本已被别的压实整本重写过(撕尾 / 坏行收拢、M32 的车道回填)⇒ 盘上已无 v1,不再重写,读数照报。
|
|
219
|
+
*/
|
|
220
|
+
upgradeJournalEnvelope(): number;
|
|
213
221
|
/**
|
|
214
222
|
* 执行放置切换 ④b 的 local 腿(一次性迁移登记表 M32;`LocalBackend.runOneTimeMigrations` 在任何读者之前调):升级前写下的问(日志 `ensureAsk` 记录行 /
|
|
215
223
|
* 压实快照行没有 `executionLane`)记成本部署车道表的唯一项,然后**一次压实**把整本账重写成带车道的快照(压实 = 既有整本原子重写)。
|
|
216
224
|
* 没有缺车道的行 ⇒ 零写。车道表不是一元而还有缺车道的行 ⇒ 抛与 SQL 同一只拒(`backfill_ambiguous`,由调用方铸),一个字节没写。
|
|
217
225
|
*/
|
|
218
226
|
backfillExecutionLanes(lanes: readonly ExecutionLane[], refuse: (pending: number) => never): number;
|
|
227
|
+
/** 释放追加 fd(`LocalBackend.close` → 优雅重启重开)。不包 try/catch 的理由同
|
|
228
|
+
* {@link FileApprovalNonceStore.dispose}:core 的 `AppendLog.close()` 自己就是全函数。 */
|
|
219
229
|
dispose(): void;
|
|
220
230
|
}
|
|
221
231
|
export {};
|
|
@@ -30,6 +30,29 @@ const JOURNAL_OP_WORDS = new Set(JOURNAL_OPS);
|
|
|
30
30
|
export const JOURNAL_OP_TABLE_GATE = true;
|
|
31
31
|
const JOURNAL_V1_CLAIM_INTENTS = ["expire", "cancel"];
|
|
32
32
|
const JOURNAL_V1_CLAIM_INTENT_WORDS = new Set(JOURNAL_V1_CLAIM_INTENTS);
|
|
33
|
+
const JOURNAL_ENVELOPE = 2;
|
|
34
|
+
const JOURNAL_V1_ENVELOPE = 1;
|
|
35
|
+
const V1_ENDING_STATES = new Set(["VOID", "DENIED"]);
|
|
36
|
+
const V1_TRIGGER_STATES = new Set(["PARKING", "PARKED"]);
|
|
37
|
+
function translateJournalV1Record(raw, refuse) {
|
|
38
|
+
const rec = raw;
|
|
39
|
+
if (rec.k === "ask" && rec.row !== null && typeof rec.row === "object") {
|
|
40
|
+
const { deniedReason, ...row } = rec.row;
|
|
41
|
+
const cause = typeof deniedReason === "string" ? deniedReason : null;
|
|
42
|
+
if (cause !== null && row.state === "STREAM_PENDING") {
|
|
43
|
+
refuse(`an envelope-1 snapshot row in STREAM_PENDING carries the single cause field ${JSON.stringify(cause)} — no released server wrote that shape and it has no two-column reading`);
|
|
44
|
+
}
|
|
45
|
+
return { ...raw, row: { ...row, endingCause: V1_ENDING_STATES.has(row.state) ? cause : null, parkTrigger: V1_TRIGGER_STATES.has(row.state) ? cause : null } };
|
|
46
|
+
}
|
|
47
|
+
if (rec.k === "op" && (rec.op === "transitionAsk" || rec.op === "resolveProvisional") && rec.patch !== null && typeof rec.patch === "object" && "deniedReason" in rec.patch) {
|
|
48
|
+
const { deniedReason, ...patch } = rec.patch;
|
|
49
|
+
if (!V1_ENDING_STATES.has(rec.to) || (typeof deniedReason !== "string" && deniedReason !== null)) {
|
|
50
|
+
refuse(`an envelope-1 ${String(rec.op)} record sets the single cause field on a transition to ${JSON.stringify(rec.to)} with ${JSON.stringify(deniedReason)} — no released server wrote that shape and it has no two-column reading`);
|
|
51
|
+
}
|
|
52
|
+
return { ...raw, patch: { ...patch, endingCause: deniedReason } };
|
|
53
|
+
}
|
|
54
|
+
return raw;
|
|
55
|
+
}
|
|
33
56
|
const ASK_JOURNAL_SHAPE = {
|
|
34
57
|
what: "approval-ask journal record",
|
|
35
58
|
read: (record) => record,
|
|
@@ -57,6 +80,8 @@ export class FileApprovalAskStore {
|
|
|
57
80
|
pinnedNow = null;
|
|
58
81
|
replayRejectedCount = 0;
|
|
59
82
|
corruptLineCount = 0;
|
|
83
|
+
journalV1Records = 0;
|
|
84
|
+
journalV1OnDisk = false;
|
|
60
85
|
corruptQuarantinePath;
|
|
61
86
|
constructor(root) {
|
|
62
87
|
const dir = join(root, APPROVAL_ASK_DIR);
|
|
@@ -95,7 +120,7 @@ export class FileApprovalAskStore {
|
|
|
95
120
|
}
|
|
96
121
|
write(call, run) {
|
|
97
122
|
const t = Date.now();
|
|
98
|
-
const record = { v:
|
|
123
|
+
const record = { v: JOURNAL_ENVELOPE, k: "op", t, ...call };
|
|
99
124
|
const encoded = encodeStoreJsonPayload(`${call.op} call`, record);
|
|
100
125
|
this.logFor().append(encoded, true);
|
|
101
126
|
this.records += 1;
|
|
@@ -125,22 +150,29 @@ export class FileApprovalAskStore {
|
|
|
125
150
|
}
|
|
126
151
|
compact(snapshot = this.core.exportRows()) {
|
|
127
152
|
const lines = [
|
|
128
|
-
...snapshot.batches.map((row) => `${JSON.stringify({ v:
|
|
129
|
-
...snapshot.asks.map((row) => `${JSON.stringify({ v:
|
|
153
|
+
...snapshot.batches.map((row) => `${JSON.stringify({ v: JOURNAL_ENVELOPE, k: "batch", row })}\n`),
|
|
154
|
+
...snapshot.asks.map((row) => `${JSON.stringify({ v: JOURNAL_ENVELOPE, k: "ask", row })}\n`),
|
|
130
155
|
];
|
|
131
156
|
this.log?.closeForSwap();
|
|
132
157
|
atomicWriteFile(this.tmpDir, this.path, lines.join(""));
|
|
158
|
+
this.journalV1OnDisk = false;
|
|
133
159
|
this.records = lines.length;
|
|
134
160
|
}
|
|
135
|
-
replayRecord(
|
|
136
|
-
const
|
|
161
|
+
replayRecord(disk) {
|
|
162
|
+
const head = disk;
|
|
137
163
|
const refuse = (why) => {
|
|
138
164
|
throw new StoreBootRefusalError(`FileApprovalAskStore: approval ask journal ${this.path} carries a record this build cannot replay — ${why} ` +
|
|
139
|
-
`(v=${JSON.stringify(
|
|
165
|
+
`(v=${JSON.stringify(head.v)} k=${JSON.stringify(head.k)} op=${JSON.stringify(head.op)}). ` +
|
|
140
166
|
"Replaying only the records it understands would resurrect decided asks or drop a human's approval. " +
|
|
141
167
|
"Run the server version that wrote this journal, or move the file aside (every approval on it is abandoned) to continue.");
|
|
142
168
|
};
|
|
143
|
-
if (
|
|
169
|
+
if (head.v === JOURNAL_V1_ENVELOPE) {
|
|
170
|
+
this.journalV1Records += 1;
|
|
171
|
+
this.journalV1OnDisk = true;
|
|
172
|
+
}
|
|
173
|
+
const raw = head.v === JOURNAL_V1_ENVELOPE ? translateJournalV1Record(disk, refuse) : disk;
|
|
174
|
+
const probe = raw;
|
|
175
|
+
if (probe.v !== JOURNAL_ENVELOPE && probe.v !== JOURNAL_V1_ENVELOPE)
|
|
144
176
|
refuse("unknown envelope version");
|
|
145
177
|
if (probe.k === "ask" || probe.k === "batch") {
|
|
146
178
|
const row = probe.row;
|
|
@@ -296,6 +328,15 @@ export class FileApprovalAskStore {
|
|
|
296
328
|
replayStats() {
|
|
297
329
|
return { rejected: this.replayRejectedCount, corruptLines: this.corruptLineCount, records: this.records };
|
|
298
330
|
}
|
|
331
|
+
upgradeJournalEnvelope() {
|
|
332
|
+
const upgraded = this.journalV1Records;
|
|
333
|
+
if (upgraded === 0)
|
|
334
|
+
return 0;
|
|
335
|
+
if (this.journalV1OnDisk)
|
|
336
|
+
this.compact();
|
|
337
|
+
this.journalV1Records = 0;
|
|
338
|
+
return upgraded;
|
|
339
|
+
}
|
|
299
340
|
backfillExecutionLanes(lanes, refuse) {
|
|
300
341
|
const pending = this.core.exportRows().asks.filter((r) => !isExecutionLane(String(r.executionLane ?? ""))).length;
|
|
301
342
|
if (pending === 0)
|
|
@@ -101,7 +101,8 @@ export declare class InMemoryApprovalAskStore implements ApprovalAskStore {
|
|
|
101
101
|
* 单调性论证)。
|
|
102
102
|
*/
|
|
103
103
|
claimTerminal(askId: string, batchId: string, intent: AskTerminalClaimIntent, opts?: AskTerminalClaimOpts): Promise<AskTerminalClaimOutcome>;
|
|
104
|
-
/** `cause`(可选)= 转 park 的触发成因,赢下 PARKING 的同一步落进 `
|
|
104
|
+
/** `cause`(可选)= 转 park 的触发成因,赢下 PARKING 的同一步落进 `parkTrigger`(SQL twin 同一条 UPDATE 的镜像;接口上的 `expireAsk` 不带它,只有 `claimTerminal(expire, { cause })` 带)——
|
|
105
|
+
* `parkTrigger` 的唯一写点:此后的转移(补丁里没有这一键)不再碰它。 */
|
|
105
106
|
expireAsk(askId: string, batchId: string, cause?: AskTerminalClaimOpts["cause"]): Promise<ExpireResult>;
|
|
106
107
|
bindBatch(batchId: string, askId: string, gate: BindGateInput): Promise<BindResult>;
|
|
107
108
|
deferReconcile(askId: string, expectedState: AskState, expectedRev: number, nowMs: number): Promise<boolean>;
|
|
@@ -11,8 +11,8 @@ function applyPatch(row, patch) {
|
|
|
11
11
|
row.decisionNote = patch.decisionNote;
|
|
12
12
|
if (patch.decidedAtMs !== undefined)
|
|
13
13
|
row.decidedAtMs = patch.decidedAtMs;
|
|
14
|
-
if (patch.
|
|
15
|
-
row.
|
|
14
|
+
if (patch.endingCause !== undefined)
|
|
15
|
+
row.endingCause = patch.endingCause;
|
|
16
16
|
if (patch.gateToken !== undefined)
|
|
17
17
|
row.gateToken = patch.gateToken;
|
|
18
18
|
if (patch.gateBoundCallId !== undefined)
|
|
@@ -99,7 +99,8 @@ export class InMemoryApprovalAskStore {
|
|
|
99
99
|
decisionNote: null,
|
|
100
100
|
settledBy: null,
|
|
101
101
|
decidedAtMs: null,
|
|
102
|
-
|
|
102
|
+
endingCause: null,
|
|
103
|
+
parkTrigger: null,
|
|
103
104
|
gateToken: null,
|
|
104
105
|
gateBoundCallId: null,
|
|
105
106
|
gateBoundInputHash: null,
|
|
@@ -181,7 +182,7 @@ export class InMemoryApprovalAskStore {
|
|
|
181
182
|
const scoped = this.asks.get(askId);
|
|
182
183
|
if (scoped !== undefined && scoped.batchId !== batchId)
|
|
183
184
|
return { claimed: false, current: { state: "absent" } };
|
|
184
|
-
const won = await this.transitionAsk(askId, "STREAM_PENDING", "VOID", { updatedAtMs: this.now(), ...(opts?.cause !== undefined ? {
|
|
185
|
+
const won = await this.transitionAsk(askId, "STREAM_PENDING", "VOID", { updatedAtMs: this.now(), ...(opts?.cause !== undefined ? { endingCause: opts.cause } : {}) });
|
|
185
186
|
if (won)
|
|
186
187
|
return { claimed: true, voidedSiblings: [] };
|
|
187
188
|
break;
|
|
@@ -222,7 +223,7 @@ export class InMemoryApprovalAskStore {
|
|
|
222
223
|
return { won: false, voidedSiblings: [] };
|
|
223
224
|
ask.state = "PARKING";
|
|
224
225
|
if (cause !== undefined)
|
|
225
|
-
ask.
|
|
226
|
+
ask.parkTrigger = cause;
|
|
226
227
|
ask.rev += 1;
|
|
227
228
|
ask.updatedAtMs = now;
|
|
228
229
|
if (batch.state === "OPEN") {
|
|
@@ -234,7 +235,7 @@ export class InMemoryApprovalAskStore {
|
|
|
234
235
|
for (const [id, r] of this.asks) {
|
|
235
236
|
if (r.batchId === batchId && r.state === "STREAM_PENDING" && id !== askId) {
|
|
236
237
|
r.state = "VOID";
|
|
237
|
-
r.
|
|
238
|
+
r.endingCause = APPROVAL_ENDING_CAUSES.BATCH_SIBLING_WITHDRAWN;
|
|
238
239
|
r.rev += 1;
|
|
239
240
|
r.updatedAtMs = now;
|
|
240
241
|
voided.push(id);
|
|
@@ -267,7 +268,7 @@ export class InMemoryApprovalAskStore {
|
|
|
267
268
|
for (const [id, r] of this.asks) {
|
|
268
269
|
if (r.batchId === batchId && r.state === "PARKING" && id !== askId) {
|
|
269
270
|
r.state = "VOID";
|
|
270
|
-
r.
|
|
271
|
+
r.endingCause = APPROVAL_ENDING_CAUSES.BATCH_BOUND_ELSEWHERE;
|
|
271
272
|
r.rev += 1;
|
|
272
273
|
r.updatedAtMs = now;
|
|
273
274
|
voidedSiblings.push(id);
|
|
@@ -295,7 +296,7 @@ export class InMemoryApprovalAskStore {
|
|
|
295
296
|
for (const [id, r] of this.asks) {
|
|
296
297
|
if (r.batchId === batchId && r.state === "STREAM_PENDING") {
|
|
297
298
|
r.state = "VOID";
|
|
298
|
-
r.
|
|
299
|
+
r.endingCause = APPROVAL_ENDING_CAUSES.BATCH_ABORTED;
|
|
299
300
|
r.rev += 1;
|
|
300
301
|
r.updatedAtMs = now;
|
|
301
302
|
voided.push(id);
|