@sema-agent/server 7.50.0 → 7.52.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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +83 -20
- package/dist/adoption/plan.d.ts +4 -4
- package/dist/adoption/plan.js +11 -0
- package/dist/approval-card.d.ts +26 -2
- package/dist/approval-card.js +9 -0
- package/dist/approval-hmac.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +2 -2
- package/dist/approval-reconciler.js +1 -1
- package/dist/bench/l8/artifact.d.ts +1 -1
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/live-deps.d.ts +1 -1
- package/dist/bench/s1/oracle.d.ts +1 -1
- package/dist/bench/s1/row.d.ts +1 -1
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/budget-tracing.js +2 -5
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +1 -1
- package/dist/boot/device-lane.d.ts +79 -0
- package/dist/boot/device-lane.js +63 -0
- package/dist/boot/engine-lease.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +48 -2
- package/dist/boot/execution-env.js +57 -5
- package/dist/boot/governance-seams.d.ts +2 -2
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +6 -6
- package/dist/boot/org-memory.d.ts +1 -1
- package/dist/boot/parked-revive-gate.d.ts +3 -3
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/reapers.js +17 -4
- package/dist/boot/resolve-spec.d.ts +15 -0
- package/dist/boot/resolve-spec.js +40 -15
- package/dist/boot/retention-lane.d.ts +2 -2
- package/dist/boot/runner-deps.d.ts +17 -7
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/runtime-caps.d.ts +23 -0
- package/dist/boot/runtime-caps.js +24 -2
- package/dist/boot/session-faces.d.ts +6 -1
- package/dist/boot/session-faces.js +17 -3
- package/dist/boot/shutdown.d.ts +8 -0
- package/dist/boot/shutdown.js +3 -1
- package/dist/boot/stores.js +40 -1
- package/dist/boot/task-list-lane.d.ts +2 -2
- package/dist/brain.js +51 -31
- package/dist/budget.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/collab-workflows.d.ts +1 -1
- package/dist/capabilities/hands-lane.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +3 -3
- package/dist/capabilities/skills.d.ts +1 -1
- package/dist/config-center/hot-keys-registry.js +5 -0
- package/dist/config-center/http-client.d.ts +2 -2
- package/dist/config-center/read-face.d.ts +1 -1
- package/dist/config-center/restart-signal.d.ts +1 -1
- package/dist/config-center/types.d.ts +10 -3
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +18 -0
- package/dist/config-types.d.ts +90 -18
- package/dist/config.js +91 -19
- package/dist/deployment-governance.d.ts +1 -1
- package/dist/device-enrollment.d.ts +125 -0
- package/dist/device-enrollment.js +156 -0
- package/dist/device-store.d.ts +385 -0
- package/dist/device-store.js +407 -0
- package/dist/device-ws-hub.d.ts +182 -0
- package/dist/device-ws-hub.js +1012 -0
- package/dist/device-ws-protocol.d.ts +429 -0
- package/dist/device-ws-protocol.js +464 -0
- package/dist/env-facts.d.ts +5 -2
- package/dist/env-facts.js +1 -0
- package/dist/execution-lane-caps.d.ts +152 -0
- package/dist/execution-lane-caps.js +166 -0
- package/dist/fleet/fleet-bus.d.ts +1 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/fleet-terminal-window.d.ts +1 -1
- package/dist/http/active-run-conflict.d.ts +2 -2
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/route-ctx.d.ts +24 -4
- package/dist/http/routes/admin-memory-optout.d.ts +48 -0
- package/dist/http/routes/admin-memory-optout.js +75 -0
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +21 -21
- package/dist/http/routes/capabilities.js +27 -11
- package/dist/http/routes/diagnostics.d.ts +1 -1
- package/dist/http/routes/leader.d.ts +1 -1
- package/dist/http/routes/memory-bundle.d.ts +1 -1
- package/dist/http/routes/memory-bundle.js +3 -1
- package/dist/http/routes/memory-compliance.d.ts +4 -4
- package/dist/http/routes/memory-origin.d.ts +4 -4
- package/dist/http/routes/memory-origin.js +3 -1
- package/dist/http/routes/memory-policy.js +6 -2
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/rules.d.ts +5 -2
- package/dist/http/routes/rules.js +84 -5
- package/dist/http/routes/run-memory-capture.d.ts +34 -0
- package/dist/http/routes/run-memory-capture.js +97 -0
- package/dist/http/routes/runs.d.ts +1 -0
- package/dist/http/routes/runs.js +212 -6
- package/dist/http/routes/session-sync.d.ts +4 -6
- package/dist/http/routes/session-sync.js +60 -47
- package/dist/http/routes/sessions.js +61 -188
- package/dist/http/routes/shared-memory.d.ts +1 -1
- package/dist/http/routes/tasks.js +1 -1
- package/dist/http/routes/workflows.js +19 -0
- package/dist/http/server.d.ts +49 -13
- package/dist/http/server.js +109 -13
- package/dist/http/verify-rounds.d.ts +1 -1
- package/dist/http/wire-types.d.ts +41 -8
- package/dist/leader/endpoint.d.ts +1 -1
- package/dist/leader/leader.d.ts +1 -1
- package/dist/leader/repair-oracle.d.ts +2 -2
- package/dist/leader/repair-oracle.js +1 -1
- package/dist/leader/wire.js +4 -2
- package/dist/main.js +23 -9
- package/dist/memory-bundle-engine.d.ts +1 -1
- package/dist/memory-operator-faces.d.ts +5 -5
- package/dist/memory-origin-fence.d.ts +50 -0
- package/dist/memory-origin-fence.js +15 -0
- package/dist/memory-posture.d.ts +13 -1
- package/dist/memory-posture.js +1 -0
- package/dist/memory-scope.d.ts +6 -1
- package/dist/memory-scope.js +3 -2
- package/dist/model-select.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +17 -5
- package/dist/observability/fail-open.js +17 -5
- package/dist/orchestration/hardened-vm-runner.d.ts +9 -2
- package/dist/orchestration/hardened-vm-runner.js +11 -1
- package/dist/orchestration/hardened-vm-worker-runner.js +2 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +2 -2
- package/dist/plugins/background-shell-support.d.ts +2 -2
- package/dist/plugins/blob-backend.d.ts +7 -2
- package/dist/plugins/blob-backend.js +22 -18
- package/dist/plugins/caching-session-store.d.ts +7 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +2 -2
- package/dist/plugins/device-store-sql.d.ts +130 -0
- package/dist/plugins/device-store-sql.js +574 -0
- package/dist/plugins/file-history-store-sql.d.ts +170 -0
- package/dist/plugins/file-history-store-sql.js +827 -0
- package/dist/plugins/file-run-store.d.ts +44 -5
- package/dist/plugins/file-run-store.js +85 -13
- package/dist/plugins/fork-routing-session-store.d.ts +1 -1
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/leader-run-store-sql.d.ts +1 -1
- package/dist/plugins/local-session-store.d.ts +9 -1
- package/dist/plugins/memory-embedder-fingerprint.d.ts +1 -1
- package/dist/plugins/memory-optout-grant-store-sql.d.ts +139 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +157 -0
- package/dist/plugins/memory-origin-law.d.ts +2 -2
- package/dist/plugins/memory-run-store.d.ts +4 -3
- package/dist/plugins/memory-run-store.js +2 -1
- package/dist/plugins/permission-rule-store-file.d.ts +16 -6
- package/dist/plugins/permission-rule-store-file.js +77 -13
- package/dist/plugins/permission-rule-store-sql.d.ts +156 -8
- package/dist/plugins/permission-rule-store-sql.js +123 -21
- package/dist/plugins/pg-pool.js +59 -0
- package/dist/plugins/pg-safe-json.d.ts +1 -1
- package/dist/plugins/remote-env-device.d.ts +271 -0
- package/dist/plugins/remote-env-device.js +727 -0
- package/dist/plugins/remote-env-k8s.d.ts +1 -1
- package/dist/plugins/remote-scratchpad.js +1 -1
- package/dist/plugins/retention-lane-store-sql.d.ts +3 -3
- package/dist/plugins/retention-store-sql.d.ts +7 -7
- package/dist/plugins/retention-store-sql.js +6 -0
- package/dist/plugins/run-store-sql.d.ts +9 -3
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/send-user-file.d.ts +1 -1
- package/dist/plugins/session-placement.d.ts +1 -1
- package/dist/plugins/sql-errors.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +68 -29
- package/dist/plugins/store-backend.js +78 -15
- package/dist/plugins/store-contracts.d.ts +5 -3
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/tidb-pool.js +59 -0
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/prompts-domain-validate.js +1 -1
- package/dist/resource-suspend.d.ts +1 -1
- package/dist/resource-window.d.ts +3 -3
- package/dist/rules-consent.d.ts +231 -9
- package/dist/rules-consent.js +411 -13
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +15 -1
- package/dist/runs.js +19 -0
- package/dist/runtime-caps-resolver.d.ts +36 -1
- package/dist/runtime-caps-resolver.js +25 -0
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/security.d.ts +5 -5
- package/dist/security.js +1 -1
- package/dist/session-sync.d.ts +52 -37
- package/dist/session-sync.js +19 -16
- package/dist/shared-memory-scope-authorizer.d.ts +1 -1
- package/dist/sighup-idle.d.ts +1 -1
- package/dist/spec-fields.d.ts +22 -1
- package/dist/spec-fields.js +11 -0
- package/dist/store-live-probe.d.ts +2 -2
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-cwd.d.ts +25 -0
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-settings.d.ts +3 -3
- package/dist/task-settings.js +3 -1
- package/dist/task-workflow.d.ts +1 -1
- package/dist/text-bidi.d.ts +2 -2
- package/dist/tool-approval.d.ts +191 -9
- package/dist/tool-approval.js +248 -29
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/engine-notice-wire.d.ts +6 -4
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/ledger-events.d.ts +1 -1
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/project.d.ts +3 -2
- package/dist/trace/project.js +1 -0
- package/dist/turn-activity.d.ts +1 -1
- package/dist/wall-clock-jump-guard.d.ts +3 -3
- package/package.json +2 -2
- package/dist/http/tar.d.ts +0 -17
- package/dist/http/tar.js +0 -53
- package/dist/http/workspace-content.d.ts +0 -11
- package/dist/http/workspace-content.js +0 -20
- package/dist/plugins/file-snapshot-store-sql.d.ts +0 -170
- package/dist/plugins/file-snapshot-store-sql.js +0 -213
package/dist/tool-approval.js
CHANGED
|
@@ -6,7 +6,7 @@ import { hasBidiControls } from "./text-bidi.js";
|
|
|
6
6
|
import { createLogger } from "./observability/logger.js";
|
|
7
7
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
8
8
|
import { deriveAskId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
9
|
-
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readProbeCause, readRuleEvidence, } from "./approval-card.js";
|
|
9
|
+
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readProbeCause, readRuleEvidence, readRuleOffersAbsence, } from "./approval-card.js";
|
|
10
10
|
import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
|
|
11
11
|
const defaultLogger = createLogger();
|
|
12
12
|
export const APPROVAL_GATE_KINDS = ["human", "irreversible_ask"];
|
|
@@ -16,6 +16,12 @@ export function isApprovalGateKind(k) {
|
|
|
16
16
|
export const APPROVAL_GATE_KINDS_SQL_IN = `(${APPROVAL_GATE_KINDS.map((k) => `'${k}'`).join(",")})`;
|
|
17
17
|
const MAX_APPROVAL_ARGS_BYTES = 16384;
|
|
18
18
|
const DEFAULT_APPROVAL_TTL_MS = 5 * 60_000;
|
|
19
|
+
const DEFAULT_UNREACHED_ASK_TTL_MS = 60 * 60_000;
|
|
20
|
+
const SUSPENDED_POLL_MAX_PER_TICK = 64;
|
|
21
|
+
const SUSPENDED_POLL_BUDGET_MS = 5_000;
|
|
22
|
+
const EDITED_DECISION_MARK_CAP_MS = 10_000;
|
|
23
|
+
const ABANDONED_EDIT_TOMBSTONE_TTL_MS = 10 * 60_000;
|
|
24
|
+
const ABANDONED_EDIT_TOMBSTONE_MAX = 256;
|
|
19
25
|
const DEFAULT_WINDOW_MARGIN_MS = 10_000;
|
|
20
26
|
const MAX_ALLOW_SESSIONS = 4096;
|
|
21
27
|
const DEFAULT_ADMIT_MAX_PER_TASK = 32;
|
|
@@ -35,6 +41,7 @@ function durableRuleMaterial(material) {
|
|
|
35
41
|
}
|
|
36
42
|
return { ruleCommand: material.command, ruleScopeRoot: material.scopeRoot ?? null };
|
|
37
43
|
}
|
|
44
|
+
export const ASK_DECISION_CONSUMER_ABSENT = "absent";
|
|
38
45
|
const DURABLE_CONVERGE_BACKOFF_MS = [50, 100, 200, 400, 800, 1600];
|
|
39
46
|
const DURABLE_CONVERGE_READ_TIMEOUT_MS = 2_000;
|
|
40
47
|
const DURABLE_CALL_TIMEOUT_MS = 5_000;
|
|
@@ -203,12 +210,16 @@ export class ToolApprovalCoordinator {
|
|
|
203
210
|
allowAllSessions = new Set();
|
|
204
211
|
streams = new Map();
|
|
205
212
|
ttlMs;
|
|
213
|
+
unreachedTtlMs;
|
|
206
214
|
askStore;
|
|
207
215
|
windowMarginMs;
|
|
208
216
|
admitMaxPerTask;
|
|
209
217
|
admitMaxPerOwner;
|
|
210
218
|
admitByTask = new Map();
|
|
211
219
|
admitByOwner = new Map();
|
|
220
|
+
suspendedPollOffset = 0;
|
|
221
|
+
editedDecisionsInFlight = new Map();
|
|
222
|
+
abandonedEditedDecisions = new Map();
|
|
212
223
|
storeErrorWarned = false;
|
|
213
224
|
storeErrorTally = 0;
|
|
214
225
|
governanceAskMarks;
|
|
@@ -225,6 +236,7 @@ export class ToolApprovalCoordinator {
|
|
|
225
236
|
this.ruleScopeRootFor = opts?.ruleScopeRootFor;
|
|
226
237
|
this.governanceAskMarks = opts?.governanceAskMarks;
|
|
227
238
|
this.ttlMs = opts?.ttlMs ?? DEFAULT_APPROVAL_TTL_MS;
|
|
239
|
+
this.unreachedTtlMs = opts?.unreachedTtlMs ?? DEFAULT_UNREACHED_ASK_TTL_MS;
|
|
228
240
|
this.askStore = opts?.askStore;
|
|
229
241
|
this.windowMarginMs = opts?.windowMarginMs ?? DEFAULT_WINDOW_MARGIN_MS;
|
|
230
242
|
this.admitMaxPerTask = opts?.admitMaxPerTask ?? DEFAULT_ADMIT_MAX_PER_TASK;
|
|
@@ -427,6 +439,12 @@ export class ToolApprovalCoordinator {
|
|
|
427
439
|
const row = await this.withStoreDeadline(this.askStore.getAsk(askId), "getAsk(expire-error-recovery)", DURABLE_CONVERGE_READ_TIMEOUT_MS);
|
|
428
440
|
if (row === null || row.state !== "DECIDED")
|
|
429
441
|
return undefined;
|
|
442
|
+
if (replayTerminalAskRow(row) === true && this.abandonedEditedDecisionPending(askId)) {
|
|
443
|
+
this.guardRowDerivedApprove(askId, true, "expire-error-recovery");
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
if (this.editedDecisionInFlight(askId))
|
|
447
|
+
return undefined;
|
|
430
448
|
const replayed = replayTerminalAskRow(row);
|
|
431
449
|
return typeof replayed === "boolean" ? replayed : undefined;
|
|
432
450
|
}
|
|
@@ -435,6 +453,15 @@ export class ToolApprovalCoordinator {
|
|
|
435
453
|
return undefined;
|
|
436
454
|
}
|
|
437
455
|
}
|
|
456
|
+
hasOtherSuspendedOwner(askId, self) {
|
|
457
|
+
if (askId === undefined)
|
|
458
|
+
return false;
|
|
459
|
+
for (const e of this.pendingByAskId.get(askId) ?? []) {
|
|
460
|
+
if (e !== self && e.suspended === true && e.settledOutcome === undefined)
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
438
465
|
settleSameAskIdParkRoute(askId) {
|
|
439
466
|
const entries = this.pendingByAskId.get(askId);
|
|
440
467
|
if (!entries)
|
|
@@ -506,6 +533,10 @@ export class ToolApprovalCoordinator {
|
|
|
506
533
|
const entries = this.pendingByAskId.get(askId);
|
|
507
534
|
if (!entries)
|
|
508
535
|
return { settled: 0 };
|
|
536
|
+
if (allowed && updatedInput === undefined) {
|
|
537
|
+
if (this.guardRowDerivedApprove(askId, true, "notify-external-decision") === "unavailable")
|
|
538
|
+
return { settled: 0 };
|
|
539
|
+
}
|
|
509
540
|
let settled = 0;
|
|
510
541
|
for (const entry of [...entries]) {
|
|
511
542
|
entry.settle(allowed, allowed ? "allowed" : "denied", updatedInput);
|
|
@@ -572,33 +603,49 @@ export class ToolApprovalCoordinator {
|
|
|
572
603
|
const ctx = this.als.getStore();
|
|
573
604
|
if (!ctx)
|
|
574
605
|
return this.unattendedOutcome();
|
|
575
|
-
return this.askBroadcast({
|
|
606
|
+
return this.askBroadcast({
|
|
607
|
+
taskId: ctx.taskId,
|
|
608
|
+
owner: ctx.owner,
|
|
609
|
+
...(ctx.sessionId !== undefined ? { sessionId: ctx.sessionId } : {}),
|
|
610
|
+
legKey: ctx.legKey ?? "",
|
|
611
|
+
...(ctx.legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic: ctx.legDeadlineMonotonic } : {}),
|
|
612
|
+
}, [ctx], req, signal);
|
|
576
613
|
};
|
|
577
614
|
boundAsk = (identity) => {
|
|
578
615
|
return (req, signal) => {
|
|
579
616
|
const set = this.streams.get(this.streamKey(identity.owner, identity.sessionId, identity.taskId));
|
|
580
617
|
const live = set && set.size > 0 ? [...set] : [];
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
618
|
+
return this.askBroadcast({
|
|
619
|
+
taskId: identity.taskId,
|
|
620
|
+
owner: identity.owner,
|
|
621
|
+
...(identity.sessionId !== undefined ? { sessionId: identity.sessionId } : {}),
|
|
622
|
+
legKey: identity.legKey ?? "",
|
|
623
|
+
...(identity.legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic: identity.legDeadlineMonotonic } : {}),
|
|
624
|
+
}, live, req, signal);
|
|
584
625
|
};
|
|
585
626
|
};
|
|
586
627
|
async askBroadcast(origin, ctxs, req, signal) {
|
|
587
628
|
if (signal?.aborted)
|
|
588
629
|
return false;
|
|
589
630
|
const aliveCtxs = ctxs.filter((c) => !c.abortSignal?.aborted);
|
|
590
|
-
|
|
591
|
-
|
|
631
|
+
const reachability = aliveCtxs.length === 0 ? "unreached" : "reachable";
|
|
632
|
+
if (reachability === "unreached") {
|
|
633
|
+
const unattended = this.unattendedOutcome();
|
|
634
|
+
if (unattended === false)
|
|
635
|
+
return unattended;
|
|
636
|
+
if (!this.askStore)
|
|
637
|
+
return unattended;
|
|
638
|
+
}
|
|
592
639
|
const primary = aliveCtxs[0];
|
|
593
|
-
const governanceForced = (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(
|
|
640
|
+
const governanceForced = (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(origin.owner, origin.sessionId, origin.taskId))).isMarked(req.toolCallId);
|
|
594
641
|
if (!governanceForced &&
|
|
595
642
|
req.requiresRealApproval !== true &&
|
|
596
|
-
|
|
597
|
-
this.allowAllSessions.has(sessionAllowKey(
|
|
643
|
+
origin.sessionId &&
|
|
644
|
+
this.allowAllSessions.has(sessionAllowKey(origin.owner, origin.sessionId, sessionAllowCategory(req.toolName))) &&
|
|
598
645
|
(await this.sessionGrantUncontestedByRow(origin, req))) {
|
|
599
646
|
return true;
|
|
600
647
|
}
|
|
601
|
-
const releaseAdmission = this.admit(origin.taskId,
|
|
648
|
+
const releaseAdmission = this.admit(origin.taskId, origin.owner);
|
|
602
649
|
if (!releaseAdmission) {
|
|
603
650
|
defaultLogger.warn("approval admission gate: too many pending asks — routing to park", {
|
|
604
651
|
taskId: origin.taskId,
|
|
@@ -612,7 +659,7 @@ export class ToolApprovalCoordinator {
|
|
|
612
659
|
const child = isChildAsk(req);
|
|
613
660
|
const bounded = boundArgs(req.args);
|
|
614
661
|
let effectiveGovernanceForced = governanceForced;
|
|
615
|
-
let ruleLaneMaterial = this.buildRuleLaneMaterial(req,
|
|
662
|
+
let ruleLaneMaterial = this.buildRuleLaneMaterial(req, origin.owner, governanceForced, origin.sessionId);
|
|
616
663
|
const frame = {
|
|
617
664
|
type: "tool_approval",
|
|
618
665
|
approvalId: id,
|
|
@@ -641,12 +688,14 @@ export class ToolApprovalCoordinator {
|
|
|
641
688
|
? { persistedRuleShadowed: redactSecrets(req.persistedRuleShadowed).slice(0, MAX_RULE_TEXT_CHARS) }
|
|
642
689
|
: {}),
|
|
643
690
|
...(ruleLaneMaterial !== undefined ? { ruleOffers: ruleLaneMaterial.offers } : {}),
|
|
691
|
+
...((a) => (a !== undefined ? { ruleOffersAbsence: a } : {}))(readRuleOffersAbsence(req)),
|
|
644
692
|
...((c) => (c !== undefined ? { probeCause: c } : {}))(readProbeCause(req)),
|
|
645
693
|
...((c) => (c !== undefined ? { ruleEvidence: c } : {}))(readRuleEvidence(req)),
|
|
646
694
|
...(bounded.omitted ? { argsOmitted: true } : { args: bounded.args }),
|
|
647
695
|
...(bounded.hasBidi || req.hasBidiControls === true ? { inputHasBidi: true } : {}),
|
|
648
696
|
};
|
|
649
|
-
const
|
|
697
|
+
const windowBudgetMs = reachability === "unreached" ? this.unreachedTtlMs : this.ttlMs;
|
|
698
|
+
const effectiveWindowMs = effectiveAskWindowMs(windowBudgetMs, this.windowMarginMs, origin.legDeadlineMonotonic, performance.now());
|
|
650
699
|
const expiresAtMs = Date.now() + effectiveWindowMs;
|
|
651
700
|
let askId;
|
|
652
701
|
let batchId;
|
|
@@ -676,8 +725,8 @@ export class ToolApprovalCoordinator {
|
|
|
676
725
|
askId,
|
|
677
726
|
taskId: origin.taskId,
|
|
678
727
|
sourceTaskId,
|
|
679
|
-
sessionId:
|
|
680
|
-
owner:
|
|
728
|
+
sessionId: origin.sessionId ?? origin.taskId,
|
|
729
|
+
owner: origin.owner,
|
|
681
730
|
batchId,
|
|
682
731
|
toolCallId: req.toolCallId,
|
|
683
732
|
legKey,
|
|
@@ -695,13 +744,14 @@ export class ToolApprovalCoordinator {
|
|
|
695
744
|
});
|
|
696
745
|
if (existingOrCreated.row.state !== "STREAM_PENDING") {
|
|
697
746
|
releaseAdmission();
|
|
698
|
-
const replayed = replayTerminalAskRow(existingOrCreated.row);
|
|
747
|
+
const replayed = this.guardRowDerivedApprove(askId, replayTerminalAskRow(existingOrCreated.row), "ensure-ask-idempotent-replay");
|
|
699
748
|
if (replayed === true)
|
|
700
749
|
recordRowDerivedApproveReplay("ensure-ask-idempotent-replay");
|
|
701
750
|
return replayed === "unavailable" ? this.unattendedOutcome() : replayed;
|
|
702
751
|
}
|
|
703
752
|
const persistedEnvelope = ApprovalCardEnvelopeSchema.safeParse(existingOrCreated.row.cardJson);
|
|
704
|
-
const
|
|
753
|
+
const maxMintableWindowMs = Math.max(effectiveWindowMs, this.unreachedTtlMs);
|
|
754
|
+
const sanePersistedDeadline = Number.isFinite(existingOrCreated.row.expiresAtMs) && existingOrCreated.row.expiresAtMs <= Date.now() + maxMintableWindowMs + DURABLE_CALL_TIMEOUT_MS;
|
|
705
755
|
if (!persistedEnvelope.success || !sanePersistedDeadline) {
|
|
706
756
|
this.noteStoreError(new Error(`persisted approval row is unusable (envelopeOk=${persistedEnvelope.success}, deadlineOk=${sanePersistedDeadline}, ` +
|
|
707
757
|
`origin=${existingOrCreated.inserted ? "row-this-call-just-inserted" : "row-hit-idempotently"})`), "ensureAsk(persisted-row-unusable)");
|
|
@@ -772,6 +822,10 @@ export class ToolApprovalCoordinator {
|
|
|
772
822
|
abandonedEnsure = true;
|
|
773
823
|
if (lateEnsuredAskId !== undefined)
|
|
774
824
|
voidAbandonedRow(lateEnsuredAskId);
|
|
825
|
+
if (reachability === "unreached") {
|
|
826
|
+
releaseAdmission();
|
|
827
|
+
return this.unattendedOutcome();
|
|
828
|
+
}
|
|
775
829
|
}
|
|
776
830
|
}
|
|
777
831
|
}
|
|
@@ -818,7 +872,7 @@ export class ToolApprovalCoordinator {
|
|
|
818
872
|
sig.removeEventListener("abort", fn);
|
|
819
873
|
this.pending.delete(id);
|
|
820
874
|
if (windowRouteUnavailable && askId !== undefined && batchId !== undefined && this.unattendedOutcome() !== false) {
|
|
821
|
-
this.recordParkTombstone(wireApprovalId, { askId, batchId, owner:
|
|
875
|
+
this.recordParkTombstone(wireApprovalId, { askId, batchId, owner: origin.owner, parkedAtMs: Date.now() });
|
|
822
876
|
parkRouted = true;
|
|
823
877
|
}
|
|
824
878
|
if (askId !== undefined) {
|
|
@@ -851,11 +905,11 @@ export class ToolApprovalCoordinator {
|
|
|
851
905
|
settle(false, "expired");
|
|
852
906
|
return;
|
|
853
907
|
}
|
|
854
|
-
if (row.state === "STREAM_PENDING") {
|
|
908
|
+
if (row.state === "STREAM_PENDING" || this.editedDecisionInFlight(pinnedAskId)) {
|
|
855
909
|
await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
|
|
856
910
|
continue;
|
|
857
911
|
}
|
|
858
|
-
const outcome = replayTerminalAskRow(row);
|
|
912
|
+
const outcome = this.guardRowDerivedApprove(pinnedAskId, replayTerminalAskRow(row), `converge:${where}`);
|
|
859
913
|
if (outcome === "unavailable") {
|
|
860
914
|
windowRouteUnavailable = true;
|
|
861
915
|
settle(false, "expired");
|
|
@@ -874,8 +928,15 @@ export class ToolApprovalCoordinator {
|
|
|
874
928
|
await new Promise((r) => setTimeout(r, DURABLE_CONVERGE_BACKOFF_MS[attempt]).unref?.());
|
|
875
929
|
}
|
|
876
930
|
}
|
|
877
|
-
if (!done)
|
|
878
|
-
|
|
931
|
+
if (!done) {
|
|
932
|
+
if (this.editedDecisionInFlight(pinnedAskId)) {
|
|
933
|
+
windowRouteUnavailable = true;
|
|
934
|
+
settle(false, "expired");
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
settle(false, "expired");
|
|
938
|
+
}
|
|
939
|
+
}
|
|
879
940
|
};
|
|
880
941
|
const settleParkRouteIfUnsettled = (hostSettledBy) => {
|
|
881
942
|
if (done)
|
|
@@ -937,6 +998,10 @@ export class ToolApprovalCoordinator {
|
|
|
937
998
|
settle(false, "expired");
|
|
938
999
|
return;
|
|
939
1000
|
}
|
|
1001
|
+
if (this.hasOtherSuspendedOwner(askId, selfEntry)) {
|
|
1002
|
+
settle(false, "expired");
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
940
1005
|
try {
|
|
941
1006
|
const won = await this.withStoreDeadline(this.askStore.transitionAsk(askId, "STREAM_PENDING", "VOID", { updatedAtMs: Date.now() }), "transitionAsk(cancel)", DURABLE_CALL_TIMEOUT_MS, (lateWon) => {
|
|
942
1007
|
if (!lateWon)
|
|
@@ -991,11 +1056,11 @@ export class ToolApprovalCoordinator {
|
|
|
991
1056
|
settle,
|
|
992
1057
|
forceParkNow,
|
|
993
1058
|
settleParkRoute: () => settleParkRouteIfUnsettled(),
|
|
994
|
-
owner:
|
|
995
|
-
ctxTaskId: primary.taskId,
|
|
996
|
-
|
|
997
|
-
...(child ? { originTaskId: req.sourceTaskId } : { targetCtxs: new Set(aliveCtxs), onTargetGone }),
|
|
998
|
-
...(
|
|
1059
|
+
owner: origin.owner,
|
|
1060
|
+
...(primary ? { ctxTaskId: primary.taskId, originCtx: primary } : {}),
|
|
1061
|
+
...(reachability === "unreached" ? { suspended: true } : {}),
|
|
1062
|
+
...(child ? { originTaskId: req.sourceTaskId } : primary ? { targetCtxs: new Set(aliveCtxs), onTargetGone } : {}),
|
|
1063
|
+
...(origin.sessionId ? { sessionId: origin.sessionId } : {}),
|
|
999
1064
|
toolName: req.toolName,
|
|
1000
1065
|
registeredAtMs: Date.now(),
|
|
1001
1066
|
expiresAtMs: persistedExpiresAtMs,
|
|
@@ -1045,7 +1110,7 @@ export class ToolApprovalCoordinator {
|
|
|
1045
1110
|
const openByCtx = new Map(aliveCtxs.map((c) => [c, emitOne(c)]));
|
|
1046
1111
|
const emitAllP = (async () => {
|
|
1047
1112
|
const results = await Promise.all(openByCtx.values());
|
|
1048
|
-
const anySucceeded = results.some((r) => r.legacy || r.card);
|
|
1113
|
+
const anySucceeded = reachability === "unreached" || this.hasOtherSuspendedOwner(askId, selfEntry) || results.some((r) => r.legacy || r.card);
|
|
1049
1114
|
if (!anySucceeded && !done) {
|
|
1050
1115
|
if (this.askStore && askId !== undefined && batchId !== undefined) {
|
|
1051
1116
|
try {
|
|
@@ -1580,14 +1645,168 @@ export class ToolApprovalCoordinator {
|
|
|
1580
1645
|
...(e.sessionId !== undefined ? { sessionId: e.sessionId } : {}),
|
|
1581
1646
|
...(e.requiresRealApproval ? { requiresRealApproval: true } : {}),
|
|
1582
1647
|
...(e.governanceForced ? { governanceForced: true } : {}),
|
|
1583
|
-
...(e.originTaskId !== undefined ? { fromSubagent: true } : {}),
|
|
1648
|
+
...(e.originTaskId !== undefined ? { fromSubagent: true, originTaskId: e.originTaskId } : {}),
|
|
1584
1649
|
});
|
|
1585
1650
|
}
|
|
1586
1651
|
return rows;
|
|
1587
1652
|
}
|
|
1653
|
+
async pollSuspendedDecisions() {
|
|
1654
|
+
const store = this.askStore;
|
|
1655
|
+
if (!store)
|
|
1656
|
+
return { polled: 0, settled: 0, backlog: 0, truncated: false };
|
|
1657
|
+
const candidates = [];
|
|
1658
|
+
const seen = new Set();
|
|
1659
|
+
for (const entry of this.pending.values()) {
|
|
1660
|
+
if (entry.suspended === true && entry.askId !== undefined && entry.settledOutcome === undefined && !seen.has(entry.askId)) {
|
|
1661
|
+
seen.add(entry.askId);
|
|
1662
|
+
candidates.push(entry.askId);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
const start = candidates.length > 0 ? this.suspendedPollOffset % candidates.length : 0;
|
|
1666
|
+
const ordered = [...candidates.slice(start), ...candidates.slice(0, start)];
|
|
1667
|
+
const deadlineMs = Date.now() + SUSPENDED_POLL_BUDGET_MS;
|
|
1668
|
+
let settled = 0;
|
|
1669
|
+
let polled = 0;
|
|
1670
|
+
let truncated = false;
|
|
1671
|
+
for (const askId of ordered) {
|
|
1672
|
+
if (polled >= SUSPENDED_POLL_MAX_PER_TICK || Date.now() >= deadlineMs) {
|
|
1673
|
+
truncated = true;
|
|
1674
|
+
break;
|
|
1675
|
+
}
|
|
1676
|
+
polled++;
|
|
1677
|
+
this.suspendedPollOffset = start + polled;
|
|
1678
|
+
let row;
|
|
1679
|
+
try {
|
|
1680
|
+
row = await this.withStoreDeadline(store.getAsk(askId), "getAsk(poll-suspended)", DURABLE_CONVERGE_READ_TIMEOUT_MS);
|
|
1681
|
+
}
|
|
1682
|
+
catch (err) {
|
|
1683
|
+
this.noteStoreError(err, "getAsk(poll-suspended)");
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
if (row === null || row.state === "STREAM_PENDING")
|
|
1687
|
+
continue;
|
|
1688
|
+
if (this.editedDecisionInFlight(askId))
|
|
1689
|
+
continue;
|
|
1690
|
+
const outcome = replayTerminalAskRow(row);
|
|
1691
|
+
if (outcome === "unavailable") {
|
|
1692
|
+
const before = this.pendingByAskId.get(askId)?.size ?? 0;
|
|
1693
|
+
this.settleSameAskIdParkRoute(askId);
|
|
1694
|
+
settled += before;
|
|
1695
|
+
continue;
|
|
1696
|
+
}
|
|
1697
|
+
if (typeof outcome !== "boolean")
|
|
1698
|
+
continue;
|
|
1699
|
+
const delivered = this.notifyExternalDecision(askId, outcome).settled;
|
|
1700
|
+
if (outcome && delivered > 0)
|
|
1701
|
+
recordRowDerivedApproveReplay("poll-suspended");
|
|
1702
|
+
settled += delivered;
|
|
1703
|
+
}
|
|
1704
|
+
const backlog = Math.max(0, candidates.length - polled);
|
|
1705
|
+
if (truncated) {
|
|
1706
|
+
defaultLogger.warn("approval_suspended_poll_truncated", {
|
|
1707
|
+
polled,
|
|
1708
|
+
settled,
|
|
1709
|
+
backlog,
|
|
1710
|
+
maxPerTick: SUSPENDED_POLL_MAX_PER_TICK,
|
|
1711
|
+
budgetMs: SUSPENDED_POLL_BUDGET_MS,
|
|
1712
|
+
resumeFromOffset: this.suspendedPollOffset,
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
return { polled, settled, backlog, truncated };
|
|
1716
|
+
}
|
|
1717
|
+
liveConsumerCount(askId) {
|
|
1718
|
+
let n = 0;
|
|
1719
|
+
for (const e of this.pendingByAskId.get(askId) ?? [])
|
|
1720
|
+
if (e.settledOutcome === undefined)
|
|
1721
|
+
n++;
|
|
1722
|
+
return n;
|
|
1723
|
+
}
|
|
1724
|
+
markEditedDecisionInFlight(askId) {
|
|
1725
|
+
this.editedDecisionsInFlight.set(askId, (this.editedDecisionsInFlight.get(askId) ?? 0) + 1);
|
|
1726
|
+
let released = false;
|
|
1727
|
+
const drop = () => {
|
|
1728
|
+
if (released)
|
|
1729
|
+
return;
|
|
1730
|
+
released = true;
|
|
1731
|
+
clearTimeout(watchdog);
|
|
1732
|
+
const n = (this.editedDecisionsInFlight.get(askId) ?? 1) - 1;
|
|
1733
|
+
if (n <= 0)
|
|
1734
|
+
this.editedDecisionsInFlight.delete(askId);
|
|
1735
|
+
else
|
|
1736
|
+
this.editedDecisionsInFlight.set(askId, n);
|
|
1737
|
+
};
|
|
1738
|
+
const watchdog = setTimeout(() => {
|
|
1739
|
+
if (released)
|
|
1740
|
+
return;
|
|
1741
|
+
defaultLogger.warn("approval_edited_decision_mark_expired", { askId, capMs: EDITED_DECISION_MARK_CAP_MS });
|
|
1742
|
+
this.abandonedEditedDecisions.set(askId, Date.now() + ABANDONED_EDIT_TOMBSTONE_TTL_MS);
|
|
1743
|
+
if (this.abandonedEditedDecisions.size > ABANDONED_EDIT_TOMBSTONE_MAX) {
|
|
1744
|
+
const oldest = this.abandonedEditedDecisions.keys().next();
|
|
1745
|
+
if (!oldest.done)
|
|
1746
|
+
this.dropAbandonedEditTombstone(oldest.value, "capacity");
|
|
1747
|
+
}
|
|
1748
|
+
drop();
|
|
1749
|
+
}, EDITED_DECISION_MARK_CAP_MS);
|
|
1750
|
+
watchdog.unref?.();
|
|
1751
|
+
return drop;
|
|
1752
|
+
}
|
|
1753
|
+
editedDecisionInFlightCount(askId) {
|
|
1754
|
+
return this.editedDecisionsInFlight.get(askId) ?? 0;
|
|
1755
|
+
}
|
|
1756
|
+
editedDecisionInFlight(askId) {
|
|
1757
|
+
return (this.editedDecisionsInFlight.get(askId) ?? 0) > 0;
|
|
1758
|
+
}
|
|
1759
|
+
abandonedEditedDecisionPending(askId) {
|
|
1760
|
+
const until = this.abandonedEditedDecisions.get(askId);
|
|
1761
|
+
if (until === undefined)
|
|
1762
|
+
return false;
|
|
1763
|
+
if (Date.now() > until) {
|
|
1764
|
+
this.dropAbandonedEditTombstone(askId, "ttl");
|
|
1765
|
+
return true;
|
|
1766
|
+
}
|
|
1767
|
+
return true;
|
|
1768
|
+
}
|
|
1769
|
+
guardRowDerivedApprove(askId, outcome, where) {
|
|
1770
|
+
if (outcome !== true || askId === undefined)
|
|
1771
|
+
return outcome;
|
|
1772
|
+
if (!this.abandonedEditedDecisionPending(askId))
|
|
1773
|
+
return outcome;
|
|
1774
|
+
defaultLogger.warn("approval_row_derived_approve_parked_after_abandoned_edit", { askId, where });
|
|
1775
|
+
this.settleSameAskIdParkRoute(askId);
|
|
1776
|
+
return "unavailable";
|
|
1777
|
+
}
|
|
1778
|
+
dropAbandonedEditTombstone(askId, why) {
|
|
1779
|
+
const stillWaiting = this.liveConsumerCount(askId);
|
|
1780
|
+
if (stillWaiting > 0) {
|
|
1781
|
+
defaultLogger.warn("approval_abandoned_edit_tombstone_dropped_parking_consumers", { askId, why, parked: stillWaiting });
|
|
1782
|
+
this.settleSameAskIdParkRoute(askId);
|
|
1783
|
+
}
|
|
1784
|
+
this.abandonedEditedDecisions.delete(askId);
|
|
1785
|
+
}
|
|
1786
|
+
expireEditedDecisionMarkForTest(askId) {
|
|
1787
|
+
this.abandonedEditedDecisions.set(askId, Date.now() + ABANDONED_EDIT_TOMBSTONE_TTL_MS);
|
|
1788
|
+
this.editedDecisionsInFlight.delete(askId);
|
|
1789
|
+
}
|
|
1790
|
+
forceAbandonedEditTombstoneExpiryForTest(askId) {
|
|
1791
|
+
if (!this.abandonedEditedDecisions.has(askId))
|
|
1792
|
+
return;
|
|
1793
|
+
this.abandonedEditedDecisions.set(askId, Date.now() - 1);
|
|
1794
|
+
this.abandonedEditedDecisionPending(askId);
|
|
1795
|
+
}
|
|
1796
|
+
setAbandonedEditTombstoneDeadlineForTest(askId, atMs) {
|
|
1797
|
+
if (this.abandonedEditedDecisions.has(askId))
|
|
1798
|
+
this.abandonedEditedDecisions.set(askId, atMs);
|
|
1799
|
+
}
|
|
1588
1800
|
pendingCount() {
|
|
1589
1801
|
return this.pending.size;
|
|
1590
1802
|
}
|
|
1803
|
+
suspendedCount() {
|
|
1804
|
+
let n = 0;
|
|
1805
|
+
for (const e of this.pending.values())
|
|
1806
|
+
if (e.suspended === true)
|
|
1807
|
+
n++;
|
|
1808
|
+
return n;
|
|
1809
|
+
}
|
|
1591
1810
|
sessionAllowedCount() {
|
|
1592
1811
|
return this.allowAllSessions.size;
|
|
1593
1812
|
}
|
|
@@ -26,7 +26,7 @@ type BgNotifExcluded = "kind" | "sessionScoped" | "owner" | "scope" | "descripti
|
|
|
26
26
|
type _GuardBgNotif = AssertAllKeysHandled<Exclude<keyof BackgroundChildEvent, BgNotifProjected | BgNotifExcluded>>;
|
|
27
27
|
type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
|
|
28
28
|
type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
|
|
29
|
-
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval";
|
|
29
|
+
type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval";
|
|
30
30
|
type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "probeReason" | "hasBidiControls" | "previewWithheld";
|
|
31
31
|
type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
|
|
32
32
|
type TaskEventHandled = "text_delta" | "reasoning_delta" | "tool_start" | "tool_end" | "turn_end" | "compacted" | "diagnostics" | "message_committed" | "status" | "task_notification" | "task_progress" | "steering_injected" | "workspace_changed" | "done" | "context_usage" | "compaction_outcome" | "human_input" | "wiring_manifest" | "text_end";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 上 wire 的通告码 —— **显式闭集常量表**(设计稿
|
|
2
|
+
* 上 wire 的通告码 —— **显式闭集常量表**(设计稿 ①,起步三码;今天的码数以本常量自身为准——
|
|
3
|
+
* 在散文里抄数字必陈腐,codex 5.65 增量批 R1-[low] 实逮:上一句曾写「今天九码」而表里是十条。
|
|
4
|
+
* 码数的**受门**真源=契约文档附录 D.3 的小节标题,⑥b 双向门盯着)。
|
|
3
5
|
*
|
|
4
6
|
* 起步的那六码同族:都是「这条 session 的记忆姿态」的披露,resume 之后仍然相关 ⇒ 也进 durable 账本
|
|
5
7
|
* (cli 断连补看走既有 events 重放腿,[4634](ii))。
|
|
@@ -14,7 +16,7 @@
|
|
|
14
16
|
* 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
|
|
15
17
|
* 刚立,立完就在下一次加码时自己咬住了)。
|
|
16
18
|
*/
|
|
17
|
-
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained"];
|
|
19
|
+
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted"];
|
|
18
20
|
export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
|
|
19
21
|
/** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
|
|
20
22
|
export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
|
|
@@ -80,13 +82,13 @@ export type EngineNoticeWireSink = (row: EngineNoticeWireRow) => void;
|
|
|
80
82
|
*
|
|
81
83
|
* 🔴 跨 session 隔离靠的是 **Map 键的精确串相等**,不是任何「最近活跃」回落 —— 缺席就是缺席(G5)。
|
|
82
84
|
*
|
|
83
|
-
* 🔴 **同 session 的跨代隔离靠 legId「最新代独占」**(#310 codex
|
|
85
|
+
* 🔴 **同 session 的跨代隔离靠 legId「最新代独占」**(#310 codex 交叉复审 R1-[high],验真后修)。
|
|
84
86
|
* 病(真窗,本仓每一处 in-flight 登记都为它写过 identity-guard):run 行在**流体内**就已 setTerminal /
|
|
85
87
|
* setSuspended,而本腿的注销在 `finally` —— 中间那一窗里,同一条 session 上的**下一条腿**(快速续跑 /
|
|
86
88
|
* 紧接着的新提交)已经注册好了自己的口。若按「会话内全量扇出」投,新腿的一条通告会**同时**落进:
|
|
87
89
|
* · 旧腿那条已 end 的 SSE(顶多记一次 fail-open,无害),以及
|
|
88
90
|
* · **旧 taskId 的账本** —— 一条不属于那条 run 的、终态之后的行;resume 复用同 taskId 时还会与新腿
|
|
89
|
-
* 抢同一个 `(task_id, seq)`
|
|
91
|
+
* 抢同一个 `(task_id, seq)` 域。这不是跨租户外泄,是**同租户跨 run 的账本污染**,同样必须关死。
|
|
90
92
|
* 药:每次 `registerLeg` 铸一个单调 legId,`route` **只投最新代**;旧代的口留在表里(它自己的注销仍
|
|
91
93
|
* 按身份精确删,不会误删新代),但一帧都收不到。方向 = fail-closed(宁可旧腿少一帧,不可新腿的事实
|
|
92
94
|
* 写进旧腿的账)。
|
|
@@ -11,6 +11,8 @@ export const ENGINE_NOTICE_WIRE_CODES = [
|
|
|
11
11
|
"steering.parked_input_blocked",
|
|
12
12
|
"task.user_steer_undrained",
|
|
13
13
|
"task.user_followup_undrained",
|
|
14
|
+
"memory.capture_opted_out",
|
|
15
|
+
"memory.capture_optout_unpersisted",
|
|
14
16
|
];
|
|
15
17
|
const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
|
|
16
18
|
export function isEngineNoticeWireCode(code) {
|
|
@@ -40,7 +40,7 @@ export interface LedgerSink {
|
|
|
40
40
|
* 与 {@link onEvent} 的 `case "status"` 共用**同一个**白名单 builder(`brainStatusEventData`),差别只有
|
|
41
41
|
* 一处:本门**同步入链** —— 在回调进入的那一刻就占住序号槽,而不是先 `await flush()` 再入链。
|
|
42
42
|
*
|
|
43
|
-
* 🔴 **为什么必须同步入链**(codex
|
|
43
|
+
* 🔴 **为什么必须同步入链**(codex 交叉复审 R2 [medium],红先复现后修):`onForwardEvent` 是同步入链的,
|
|
44
44
|
* 而 core 的第五席与 forward sink 都是**同步回调**、可能同一拍相邻到达。走 `onEvent` 的话那一次
|
|
45
45
|
* `await flush()` 会让**后到**的 forward 帧抢到**更小**的序号 ⇒ durable 时间线倒挂(消费端读到「子代
|
|
46
46
|
* 进度」排在触发它的那次重连之前)。钉在 `test/issue190-status-wire-disclosure.test.ts` §4⑤b。
|
|
@@ -71,7 +71,7 @@ export interface LedgerSink {
|
|
|
71
71
|
}): Promise<void>;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
|
-
* **单写串行链**的可复用形(#310 codex
|
|
74
|
+
* **单写串行链**的可复用形(#310 codex 交叉复审 R1-[high],验真后修)—— `seq` 的**分配发生在链步内**,
|
|
75
75
|
* 于是「分配序 == 发起序 == 提交序」。
|
|
76
76
|
*
|
|
77
77
|
* 病(实测形,`LedgerSink` 内部早有这条纪律的内联版、其顶注 [1.211 codex H2] 逐字记着它):裸写口
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The event log (src/runs.ts) is a flat per-task seq stream: reasoning / text / tool_start / tool_end /
|
|
5
5
|
* turn_end / compacted / done / suspended / failed. sema-registry renders an ORDERED `blocks[]` transcript
|
|
6
6
|
* (CC-style thinking↔tool interleave), grouped into turns. This is pure presentation/projection = the
|
|
7
|
-
* SERVICE layer (core [R57]
|
|
7
|
+
* SERVICE layer (core [R57] cross-review: do NOT push this shape into core — different consumer, different shape).
|
|
8
8
|
*
|
|
9
9
|
* Per core [R57] corrections: there is no `suspended` event (suspend = the run row's status); per-turn tokens ride
|
|
10
10
|
* on `turn_end.usage` (core additive). As of core 1.121.0 (design/99 §E1) `tool_end` ALSO carries the model-facing
|
|
@@ -18,7 +18,7 @@ import type { RunEvent, RunRecord } from "../plugins/store-contracts.js";
|
|
|
18
18
|
import type { ModelUsageDelta, ModelUsageTracker, PromptManifestRecord } from "../budget.js";
|
|
19
19
|
import type { LedgerEventType } from "./ledger-events.js";
|
|
20
20
|
/** 账本写口的统一签名 —— `type` 是**闭集**(`ledger-events.ts`):新型必须先登记,否则调用点编译红
|
|
21
|
-
* (codex
|
|
21
|
+
* (codex 交叉复审 F2:别名门 `ap("x")` 与动态首参让纯词法门结构性抓不全)。 */
|
|
22
22
|
type AppendFn = (type: LedgerEventType, data: unknown) => Promise<void>;
|
|
23
23
|
type GetEventsFn = (taskId: string, afterSeq: number) => Promise<RunEvent[]>;
|
|
24
24
|
/** E8 (shell-host): drain the per-task model-usage accumulator into an append-only `model_usage` DELTA event.
|
|
@@ -402,6 +402,7 @@ export declare function brainStatusEventData(ev: {
|
|
|
402
402
|
maxRetries?: number;
|
|
403
403
|
retryInMs?: number;
|
|
404
404
|
errClass?: string;
|
|
405
|
+
retryAtMs?: number;
|
|
405
406
|
eventId?: string;
|
|
406
407
|
parentToolCallId?: string;
|
|
407
408
|
}): Record<string, unknown>;
|
package/dist/trace/project.js
CHANGED
|
@@ -295,6 +295,7 @@ export function brainStatusEventData(ev) {
|
|
|
295
295
|
...(num(ev.attempt) ? { attempt: ev.attempt } : {}),
|
|
296
296
|
...(num(ev.maxRetries) ? { maxRetries: ev.maxRetries } : {}),
|
|
297
297
|
...(num(ev.retryInMs) ? { retryInMs: ev.retryInMs } : {}),
|
|
298
|
+
...(num(ev.retryAtMs) ? { retryAtMs: ev.retryAtMs } : {}),
|
|
298
299
|
...(typeof ev.errClass === "string" && ev.errClass.length > 0 ? { errClass: ev.errClass } : {}),
|
|
299
300
|
...identityFields(ev),
|
|
300
301
|
};
|
package/dist/turn-activity.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* GET /v1/runs/:id 读面的判别材料 `msSinceLastActivity` 的唯一数据源。
|
|
4
4
|
*
|
|
5
5
|
* 【为什么需要它】run 行的 `updatedAt` 由 liveness heartbeat(独立 `setInterval`,runs.ts)推进——
|
|
6
|
-
* 它只证明 **server 进程**活着;turn 卡死在某个 await 上时心跳照走([3771]
|
|
6
|
+
* 它只证明 **server 进程**活着;turn 卡死在某个 await 上时心跳照走([3771] 推理缺陷的公开更正)。
|
|
7
7
|
* 「turn 真在推进」的证据 = ledger sink 的 durable append(text/tool/status/park/done 全族都过那一条
|
|
8
8
|
* 单写串行链)。cli 的 running 形保守臂([3779] C3)缺的就是这一维:没有它,「真忙的后台 run」与
|
|
9
9
|
* 「断连僵局」在 wire 上不可分辨,只能猜。
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* 本模块改判**宽限**:检出一次前跳后,按**单调钟**静默 `grace = slaMs` 毫秒不做判定。
|
|
19
19
|
*
|
|
20
|
-
* 🔴 **宽限量是一个完整 SLA,不是 `min(J, S)`**(codex
|
|
20
|
+
* 🔴 **宽限量是一个完整 SLA,不是 `min(J, S)`**(codex 交叉复审 R1 [high] 三,验真后修 —— 第一版正是
|
|
21
21
|
* `min(J, S)`,而那条「J < S 时宽限恰好补平损失」的证明**是错的**)。错在哪:宽限只是**推迟**判定,
|
|
22
22
|
* 它消不掉墙钟从此**永久领先** J 毫秒这件事。反例(逐字复算):S=45s,gate 在真实 t=0 铸出(deadline =
|
|
23
23
|
* W0+45s),真实 t=1s 时前跳 J=10s。`min(J,S)`=10s ⇒ 宽限到真实 t=11s;此后墙钟恒为 `W0+t+10`,
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* t_x + S(墙钟与单调钟从跳变后起同速),宽限只可能把它再往后推,绝不会提前 ✔
|
|
30
30
|
* 即**任一审批席都拿得到不少于一个完整 SLA 的真实时间**;单次阶跃下判定最多被推迟一个 S。
|
|
31
31
|
*
|
|
32
|
-
* 🔴 **「有界且一次性自清」只对单次阶跃成立**(codex
|
|
32
|
+
* 🔴 **「有界且一次性自清」只对单次阶跃成立**(codex 交叉复审 R2 [high] 五,验真后修 —— 第一版把这句话
|
|
33
33
|
* 写成了无条件的,那是错的):每次前跳都把窗延到「此刻 + 一个 S」,所以**只要每个 S 内再跳一次**,窗就
|
|
34
34
|
* 永远续下去,deny 腿被静默关停 —— 而它是一条 **fail-closed** 腿,永久不跑比跑早了更坏。
|
|
35
35
|
* 收口 = 一串连续跳变的**累计宽限总上限**(缺省 `2 × S`:一个 S 覆盖有证明的单次阶跃,第二个 S 容一串
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* ## 方向与响亮度(#157 安全轴)
|
|
41
41
|
* 宽限只会**推迟**一个 fail-closed 动作(deny),绝不会放行任何东西 —— 方向安全。但它必须**响亮**:
|
|
42
42
|
* 检出即 warn(带跳变量与宽限量),宽限期内每拍 info。绝对兜底仍在:`checkpoint.terminal_at_ms`
|
|
43
|
-
* (put
|
|
43
|
+
* (put 时写死的绝对上限)由另一条 reap 腿执行,不经本守卫 ⇒ 悬挂不可能无限。
|
|
44
44
|
*
|
|
45
45
|
* 后拨(墙钟相对单调钟**倒退**)不触发宽限:它只会让 deadline 显得更远 ⇒ 判定被推迟,已经是安全方向。
|
|
46
46
|
* 仍然 warn —— 一次没人知道的钟倒退是运维事实,不是可以吞掉的噪声。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.52.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sema-agent/core": "
|
|
57
|
+
"@sema-agent/core": "7.0.0",
|
|
58
58
|
"@sema-agent/settings-schema": "1.0.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|