@sema-agent/server 7.142.0 → 7.144.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 +608 -0
- package/USAGE.md +63 -36
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +7 -5
- package/dist/approval-ask-audit-store.js +12 -6
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +155 -39
- package/dist/approval-card.js +111 -31
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +8 -8
- package/dist/approval-reconciler.js +3 -3
- package/dist/audit.js +4 -2
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +3 -3
- package/dist/boot/reapers.js +5 -4
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +8 -8
- package/dist/boot/runner-deps.d.ts +14 -6
- package/dist/boot/runner-deps.js +6 -17
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +18 -2
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +3 -5
- package/dist/boot/stage-05-execution-env.d.ts +5 -2
- package/dist/boot/stage-06-runners.d.ts +5 -2
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +6 -2
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +4 -1
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +7 -7
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +31 -19
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +23 -17
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +8 -8
- package/dist/capability-facts.js +4 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +1 -1
- package/dist/config-d-migration.d.ts +2 -0
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +5 -5
- package/dist/config.d.ts +4 -3
- package/dist/config.js +1 -1
- package/dist/delegated-session-owner.d.ts +35 -151
- package/dist/delegated-session-owner.js +1 -83
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/entry-mode-clamp-notice.d.ts +1 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/execution-placement.d.ts +7 -4
- package/dist/fleet/fleet-bus.d.ts +28 -52
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/classic-stations.d.ts +3 -1
- package/dist/hooks/classic-stations.js +2 -0
- package/dist/hooks/hook-runner.js +28 -7
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +7 -3
- package/dist/http/refusal-body.js +1 -0
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +82 -53
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +143 -31
- package/dist/http/routes/capabilities.js +11 -2
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +1 -1
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +9 -8
- package/dist/http/routes/session-memory-sync.d.ts +34 -6
- package/dist/http/routes/session-memory-sync.js +27 -10
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +27 -49
- package/dist/http/server.d.ts +13 -7
- package/dist/http/server.js +2 -2
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lane-home-probe.d.ts +15 -0
- package/dist/lane-home-probe.js +4 -0
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.js +14 -3
- package/dist/memory-sync.d.ts +20 -4
- package/dist/memory-sync.js +23 -4
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +5 -9
- package/dist/observability/corrupt-read-seat.js +1 -2
- package/dist/observability/fail-open.d.ts +30 -10
- package/dist/observability/fail-open.js +27 -10
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +6 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.d.ts +6 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +122 -0
- package/dist/peer-inbound-face.js +123 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.js +18 -6
- package/dist/plugins/approval-ask-store-memory.js +8 -6
- package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
- package/dist/plugins/approval-ask-store-sql.js +30 -11
- package/dist/plugins/caching-session-store.d.ts +0 -3
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +45 -38
- package/dist/plugins/checkpoint-store-sql.js +108 -45
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +29 -0
- package/dist/plugins/file-run-store.js +97 -8
- package/dist/plugins/fork-routing-session-store.d.ts +8 -1
- package/dist/plugins/fork-routing-session-store.js +6 -0
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +6 -8
- package/dist/plugins/local-checkpoint-store.js +5 -4
- package/dist/plugins/local-session-store.d.ts +69 -30
- package/dist/plugins/local-session-store.js +133 -57
- package/dist/plugins/local-session-window.d.ts +4 -2
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +44 -14
- package/dist/plugins/one-time-migrations.js +118 -15
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/pg-session-storage.d.ts +1 -9
- package/dist/plugins/pg-session-storage.js +2 -19
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-host.d.ts +8 -4
- package/dist/plugins/remote-env-host.js +7 -9
- package/dist/plugins/remote-env-k8s.d.ts +37 -24
- package/dist/plugins/remote-env-k8s.js +50 -17
- package/dist/plugins/remote-shell.js +12 -2
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/schema-consistency.d.ts +5 -1
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-ownership.d.ts +9 -0
- package/dist/plugins/session-ownership.js +6 -0
- package/dist/plugins/session-store.d.ts +13 -16
- package/dist/plugins/session-store.js +9 -10
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/sql-json-column.d.ts +24 -2
- package/dist/plugins/sql-json-column.js +38 -3
- package/dist/plugins/store-backend.d.ts +23 -28
- package/dist/plugins/store-backend.js +12 -3
- package/dist/plugins/store-contracts.d.ts +29 -5
- package/dist/plugins/store-contracts.js +4 -1
- package/dist/plugins/task-attachment-store.d.ts +2 -2
- package/dist/plugins/task-attachment-store.js +37 -52
- package/dist/plugins/tidb-session-store.d.ts +2 -14
- package/dist/plugins/tidb-session-store.js +2 -20
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +1 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +11 -6
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +10 -7
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/security.d.ts +0 -4
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +34 -23
- package/dist/session-error-wire.js +8 -3
- package/dist/session-incarnation.d.ts +3 -3
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +97 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +11 -8
- package/dist/task-settings.js +31 -11
- package/dist/tool-approval.d.ts +55 -35
- package/dist/tool-approval.js +112 -74
- package/dist/trace/core-keyset-guard.d.ts +12 -9
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +7 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/ledger-sink.js +12 -4
- package/dist/trace/model-segment.d.ts +74 -25
- package/dist/trace/model-segment.js +141 -32
- package/dist/trace/project.d.ts +15 -6
- package/dist/trace/project.js +12 -4
- package/dist/trace/redact.d.ts +72 -9
- package/dist/trace/redact.js +1509 -471
- package/dist/trace/self-minted-wire.d.ts +598 -47
- package/dist/trace/self-minted-wire.js +190 -20
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +90 -10
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.143.0.json +1167 -0
- package/wire-changes/server@7.144.0.json +291 -0
- package/dist/session-records-skipped-notice.d.ts +0 -21
- package/dist/session-records-skipped-notice.js +0 -27
package/dist/approval-card.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, TOOL_APPROVAL_CARDS, BOUND_GRANT_ABSENCES, DENIAL_LIMIT_BOUNDS, MAX_RULE_TEXT_CHARS, PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS, READ_ROOT_CANDIDATE_DIR_MAX, RESUME_STARTUP_CAUSES, RULE_OFFERS_ABSENCES, RULE_OFFER_KINDS, STEER_REPLAY_FACETS, isAskClass, readRuleOffers, isAskOrigin, isAutoModeFallbackCause, isMintableProbeReason, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
2
|
+
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, TOOL_APPROVAL_CARDS, BOUND_GRANT_ABSENCES, DENIAL_LIMIT_BOUNDS, MAX_RULE_TEXT_CHARS, PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS, READ_ROOT_CANDIDATE_DIR_MAX, RESUME_STARTUP_CAUSES, RULE_OFFERS_ABSENCES, RULE_OFFER_KINDS, STEER_REPLAY_FACETS, HOOK_QUESTION_CAUSES, isAskClass, readRuleOffers, isAskOrigin, isAutoModeFallbackCause, isMintableProbeReason, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
3
3
|
import { redactSecrets } from "./trace/redact.js";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
5
|
import { readApprovalCard } from "./approval-content-kind.js";
|
|
6
|
+
import { seatText } from "./trace/exit-seat.js";
|
|
6
7
|
import { capText } from "./caller-text-echo.js";
|
|
7
8
|
import { grantBindingArm, grantBindingOf } from "./standing-answer-grant.js";
|
|
9
|
+
import { pgHasUnstorable } from "./plugins/pg-safe-json.js";
|
|
8
10
|
import { retireParkArmWords, retireStoredCardWords } from "./retired-card-words.js";
|
|
9
11
|
export const MAX_AGENT_NAME = 200;
|
|
10
12
|
const MAX_IDENT = 200;
|
|
@@ -332,6 +334,45 @@ export function readAskClassPair(carrier) {
|
|
|
332
334
|
recordFailOpen("server.approval-card.closed-word-out-of-set", isAskClass(askClass) ? `classes=${outOfSetDetail(classes)}` : `askClass=${outOfSetDetail(askClass)}`);
|
|
333
335
|
return undefined;
|
|
334
336
|
}
|
|
337
|
+
export function readAskReasons(carrier) {
|
|
338
|
+
if (typeof carrier !== "object" || carrier === null || !("askReasons" in carrier))
|
|
339
|
+
return undefined;
|
|
340
|
+
const raw = carrier.askReasons;
|
|
341
|
+
if (raw === undefined)
|
|
342
|
+
return undefined;
|
|
343
|
+
const out = [];
|
|
344
|
+
if (Array.isArray(raw) && raw.length > 0) {
|
|
345
|
+
for (const e of raw) {
|
|
346
|
+
const entry = readAskReasonEntry(e);
|
|
347
|
+
if (entry === undefined)
|
|
348
|
+
break;
|
|
349
|
+
out.push(entry);
|
|
350
|
+
}
|
|
351
|
+
if (out.length === raw.length)
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
recordFailOpen("server.approval-card.closed-word-out-of-set", `askReasons=${outOfSetDetail(raw)}`);
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
function readAskReasonEntry(e) {
|
|
358
|
+
if (typeof e !== "object" || e === null)
|
|
359
|
+
return undefined;
|
|
360
|
+
const r = e;
|
|
361
|
+
const words = r.words === undefined ? undefined : typeof r.words === "string" ? capText(seatText("trace-frame-text", r.words), MAX_MESSAGE) : null;
|
|
362
|
+
if (words === null)
|
|
363
|
+
return undefined;
|
|
364
|
+
if (r.by === "hook") {
|
|
365
|
+
if (!HOOK_QUESTION_CAUSES.includes(r.cause))
|
|
366
|
+
return undefined;
|
|
367
|
+
const registration = r.registration === undefined ? undefined : typeof r.registration === "string" ? capText(seatText("trace-frame-text", r.registration), MAX_IDENT) : null;
|
|
368
|
+
if (registration === null)
|
|
369
|
+
return undefined;
|
|
370
|
+
return { by: "hook", cause: r.cause, ...(registration !== undefined ? { registration } : {}), ...(words !== undefined ? { words } : {}) };
|
|
371
|
+
}
|
|
372
|
+
if (!isAskOrigin(r.by) || r.by === "hook" || words === undefined)
|
|
373
|
+
return undefined;
|
|
374
|
+
return { by: r.by, words };
|
|
375
|
+
}
|
|
335
376
|
export function parkRowFacts(stored) {
|
|
336
377
|
if (typeof stored !== "object" || stored === null || !("kind" in stored) || stored.kind !== "tool_approval")
|
|
337
378
|
return {};
|
|
@@ -340,6 +381,7 @@ export function parkRowFacts(stored) {
|
|
|
340
381
|
const mandate = readClosedWordSeat(pendingAction, "mandate");
|
|
341
382
|
const origin = readClosedWordSeat(pendingAction, "origin");
|
|
342
383
|
const askClassPair = readAskClassPair(pendingAction);
|
|
384
|
+
const askReasons = readAskReasons(pendingAction);
|
|
343
385
|
return {
|
|
344
386
|
...(readParkRowTrueSeat(pendingAction, "requiresRealApproval") ? { requiresRealApproval: true } : {}),
|
|
345
387
|
...(readParkRowTrueSeat(pendingAction, "mandated") ? { mandated: true } : {}),
|
|
@@ -347,6 +389,7 @@ export function parkRowFacts(stored) {
|
|
|
347
389
|
...(origin !== undefined ? { origin } : {}),
|
|
348
390
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
349
391
|
...(askClassPair ?? {}),
|
|
392
|
+
...(askReasons !== undefined ? { askReasons } : {}),
|
|
350
393
|
...grantBindingArm(grantBindingOf(pendingAction)),
|
|
351
394
|
};
|
|
352
395
|
}
|
|
@@ -356,12 +399,6 @@ export function skillCardFacts(summary) {
|
|
|
356
399
|
...(summary.classifierNoRuling !== undefined ? { classifierNoRuling: summary.classifierNoRuling } : {}),
|
|
357
400
|
};
|
|
358
401
|
}
|
|
359
|
-
export function parkRowCarriesSkillCardFacts(stored) {
|
|
360
|
-
if (typeof stored !== "object" || stored === null || !("kind" in stored) || stored.kind !== "tool_approval")
|
|
361
|
-
return false;
|
|
362
|
-
const digest = "boundGrantDigest" in stored ? stored.boundGrantDigest : undefined;
|
|
363
|
-
return typeof digest === "string" || ("classifierNoRuling" in stored && stored.classifierNoRuling !== undefined);
|
|
364
|
-
}
|
|
365
402
|
export const DenialLimitFallbackSchema = z
|
|
366
403
|
.object({
|
|
367
404
|
consecutive: z.number().int().nonnegative(),
|
|
@@ -416,8 +453,16 @@ export const ApprovalCardSchema = z
|
|
|
416
453
|
askClass: z.custom((v) => isAskClass(v)).optional(),
|
|
417
454
|
classes: z.array(z.custom((v) => isAskClass(v))).min(1).optional(),
|
|
418
455
|
fromSubagent: z.literal(true).optional(),
|
|
419
|
-
|
|
456
|
+
sourceSessionId: z.string().max(MAX_IDENT).optional(),
|
|
457
|
+
agentId: z.string().max(MAX_IDENT).optional(),
|
|
420
458
|
sourceAgentName: z.string().max(MAX_AGENT_NAME).optional(),
|
|
459
|
+
askReasons: z
|
|
460
|
+
.array(z.union([
|
|
461
|
+
z.object({ by: z.literal("hook"), registration: z.string().max(MAX_IDENT).optional(), cause: z.enum(HOOK_QUESTION_CAUSES), words: z.string().max(MAX_MESSAGE).optional() }).strict(),
|
|
462
|
+
z.object({ by: z.custom((v) => isAskOrigin(v) && v !== "hook"), words: z.string().max(MAX_MESSAGE) }).strict(),
|
|
463
|
+
]))
|
|
464
|
+
.min(1)
|
|
465
|
+
.optional(),
|
|
421
466
|
delegation: z
|
|
422
467
|
.object({
|
|
423
468
|
parentToolCallId: z.string().max(MAX_IDENT),
|
|
@@ -439,6 +484,7 @@ export const ApprovalCardEnvelopeSchema = z
|
|
|
439
484
|
schemaVersion: z.literal(1),
|
|
440
485
|
approvalId: z.string(),
|
|
441
486
|
card: ApprovalCardSchema,
|
|
487
|
+
liveFacts: z.unknown().optional(),
|
|
442
488
|
})
|
|
443
489
|
.strict();
|
|
444
490
|
export const APPROVAL_CARD_SCHEMA_VERSION = 1;
|
|
@@ -459,8 +505,10 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
459
505
|
const denialLimitFallback = readDenialLimitFallback(req);
|
|
460
506
|
const askClassPair = readAskClassPair(source);
|
|
461
507
|
const toolCallId = capText(source.toolCallId, MAX_IDENT);
|
|
462
|
-
const
|
|
508
|
+
const sourceSessionId = capText(source.sourceSessionId, MAX_IDENT);
|
|
509
|
+
const agentId = capText(source.agentId, MAX_IDENT);
|
|
463
510
|
const sourceAgentName = capText(source.sourceAgentName, MAX_AGENT_NAME);
|
|
511
|
+
const askReasons = readAskReasons(req);
|
|
464
512
|
const allowRuleShadowed = readAllowRuleShadowed(req);
|
|
465
513
|
const approvalCard = readApprovalCard(source.approvalCard);
|
|
466
514
|
return {
|
|
@@ -485,8 +533,10 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
485
533
|
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
486
534
|
...(askClassPair !== undefined ? { askClass: askClassPair.askClass, classes: askClassPair.classes } : {}),
|
|
487
535
|
...(source.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
488
|
-
...(
|
|
536
|
+
...(sourceSessionId !== undefined ? { sourceSessionId } : {}),
|
|
537
|
+
...(agentId !== undefined ? { agentId } : {}),
|
|
489
538
|
...(sourceAgentName !== undefined ? { sourceAgentName } : {}),
|
|
539
|
+
...(askReasons !== undefined ? { askReasons } : {}),
|
|
490
540
|
...(source.delegation
|
|
491
541
|
? {
|
|
492
542
|
delegation: {
|
|
@@ -498,15 +548,20 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
498
548
|
: {}),
|
|
499
549
|
};
|
|
500
550
|
}
|
|
501
|
-
export function buildApprovalCardEnvelope(approvalId, card) {
|
|
502
|
-
|
|
551
|
+
export function buildApprovalCardEnvelope(approvalId, card, liveFacts) {
|
|
552
|
+
const seat = pickApprovalRequestLiveFacts(liveFacts);
|
|
553
|
+
return { schemaVersion: APPROVAL_CARD_SCHEMA_VERSION, approvalId, card, ...(pgHasUnstorable(seat) ? {} : { liveFacts: seat }) };
|
|
503
554
|
}
|
|
504
555
|
export function readStoredCardEnvelope(cardJson) {
|
|
505
|
-
const stored = retireStoredCardWords(cardJson);
|
|
556
|
+
const stored = retireStoredCardWords(renameRetiredSourceKey(cardJson));
|
|
506
557
|
if (stored.seats.length > 0)
|
|
507
558
|
noteRetiredCardWordRead("card_json", stored.seats);
|
|
508
559
|
const parsed = ApprovalCardEnvelopeSchema.safeParse(stored.value);
|
|
509
|
-
|
|
560
|
+
if (!parsed.success)
|
|
561
|
+
return undefined;
|
|
562
|
+
const { liveFacts: seat, ...envelope } = parsed.data;
|
|
563
|
+
const liveFacts = seat === undefined ? undefined : ApprovalRequestLiveFactsSchema.safeParse(seat);
|
|
564
|
+
return liveFacts?.success === true ? { ...envelope, liveFacts: liveFacts.data } : envelope;
|
|
510
565
|
}
|
|
511
566
|
export function currentParkArm(pendingAction) {
|
|
512
567
|
const arm = retireParkArmWords(pendingAction);
|
|
@@ -514,6 +569,16 @@ export function currentParkArm(pendingAction) {
|
|
|
514
569
|
noteRetiredCardWordRead("parked row", arm.seats);
|
|
515
570
|
return arm.value;
|
|
516
571
|
}
|
|
572
|
+
function renameRetiredSourceKey(envelope) {
|
|
573
|
+
if (envelope === null || typeof envelope !== "object" || Array.isArray(envelope))
|
|
574
|
+
return envelope;
|
|
575
|
+
const card = envelope.card;
|
|
576
|
+
if (card === null || typeof card !== "object" || Array.isArray(card) || !Object.hasOwn(card, "sourceTaskId") || Object.hasOwn(card, "sourceSessionId"))
|
|
577
|
+
return envelope;
|
|
578
|
+
const { sourceTaskId, ...rest } = card;
|
|
579
|
+
recordFailOpen("server.approval-card.retired-card-key-read", "card_json: sourceTaskId");
|
|
580
|
+
return { ...envelope, card: { ...rest, sourceSessionId: sourceTaskId } };
|
|
581
|
+
}
|
|
517
582
|
function noteRetiredCardWordRead(carrier, seats) {
|
|
518
583
|
recordFailOpen("server.approval-card.retired-card-word-read", `${carrier}: ${seats.join(" / ")}`);
|
|
519
584
|
}
|
|
@@ -529,7 +594,7 @@ export function buildApprovalRequestFrame(input) {
|
|
|
529
594
|
expiresInMs: Math.max(0, input.expiresAtMs - input.nowMs),
|
|
530
595
|
serverNowMs: input.nowMs,
|
|
531
596
|
approvalId: input.approvalId,
|
|
532
|
-
...pickApprovalRequestLiveFacts(input.live),
|
|
597
|
+
...(input.live !== undefined ? pickApprovalRequestLiveFacts(input.live) : { liveFactsUnknown: true }),
|
|
533
598
|
};
|
|
534
599
|
}
|
|
535
600
|
export const APPROVAL_REQUEST_LIVE_FACT_KEYS = [
|
|
@@ -554,6 +619,26 @@ function pickApprovalRequestLiveFacts(src) {
|
|
|
554
619
|
}
|
|
555
620
|
return out;
|
|
556
621
|
}
|
|
622
|
+
export function adoptStoredLiveFacts(frame, seat) {
|
|
623
|
+
for (const key of APPROVAL_REQUEST_LIVE_FACT_KEYS)
|
|
624
|
+
delete frame[key];
|
|
625
|
+
Object.assign(frame, pickApprovalRequestLiveFacts(seat));
|
|
626
|
+
}
|
|
627
|
+
const ApprovalRequestLiveFactsSchema = z
|
|
628
|
+
.object({
|
|
629
|
+
mandated: z.literal(true).optional(),
|
|
630
|
+
mandate: z.custom((v) => isPersistedRuleMandate(v)).optional(),
|
|
631
|
+
origin: z.string().min(1).optional(),
|
|
632
|
+
boundGrantDigest: z.string().optional(),
|
|
633
|
+
boundGrants: z.object({ skill: z.string().nullable(), allowedTools: z.array(z.string()).readonly(), disallowedTools: z.array(z.string()).readonly() }).strict().optional(),
|
|
634
|
+
classifierNoRuling: z.custom((v) => isAutoModeFallbackCause(v)).optional(),
|
|
635
|
+
boundGrantAbsence: z.enum(BOUND_GRANT_ABSENCES).optional(),
|
|
636
|
+
approvalCard: z.enum(TOOL_APPROVAL_CARDS).optional(),
|
|
637
|
+
readRootCandidate: z.object({ dir: z.string().min(1).max(READ_ROOT_CANDIDATE_DIR_MAX), clearsThisAsk: z.literal(true), covers: z.literal("exact").optional() }).strict().optional(),
|
|
638
|
+
probeReason: z.string().min(1).optional(),
|
|
639
|
+
requestedBy: z.object({ kind: z.literal("extension"), name: z.string() }).strict().optional(),
|
|
640
|
+
})
|
|
641
|
+
.strict();
|
|
557
642
|
export function buildApprovalCompleteFrame(input) {
|
|
558
643
|
return {
|
|
559
644
|
type: "approval_complete",
|
|
@@ -571,25 +656,20 @@ export function buildRevokeFrame(batchId, askIds, reason, serverNowMs, originTas
|
|
|
571
656
|
return { type: "approval_revoke", schemaVersion: 1, batchId, askIds: [...askIds], reason, serverNowMs, taskId: originTaskId };
|
|
572
657
|
}
|
|
573
658
|
export function buildReplayFrame(row, nowMs) {
|
|
574
|
-
const
|
|
575
|
-
if (
|
|
659
|
+
const stored = readStoredCardEnvelope(row.cardJson);
|
|
660
|
+
if (stored === undefined)
|
|
576
661
|
return undefined;
|
|
577
|
-
return {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
expiresInMs: Math.max(0, row.expiresAtMs - nowMs),
|
|
586
|
-
serverNowMs: nowMs,
|
|
587
|
-
approvalId: row.askId,
|
|
588
|
-
};
|
|
662
|
+
return buildApprovalRequestFrame({ askId: row.askId, taskId: row.taskId, approvalId: row.askId, card: stored.card, expiresAtMs: row.expiresAtMs, nowMs, live: stored.liveFacts });
|
|
663
|
+
}
|
|
664
|
+
export function askDemandsAPerson(legRequest, recordCard) {
|
|
665
|
+
return legRequest?.requiresRealApproval === true || recordCard?.risk.requiresRealApproval === true;
|
|
666
|
+
}
|
|
667
|
+
const UNREADABLE_CARD_DEMANDS_A_PERSON = { risk: { requiresRealApproval: true } };
|
|
668
|
+
export function storedPersonDemandRecord(cardJson) {
|
|
669
|
+
return readStoredCardEnvelope(cardJson)?.card ?? UNREADABLE_CARD_DEMANDS_A_PERSON;
|
|
589
670
|
}
|
|
590
671
|
export function storedCardDemandsAPerson(cardJson) {
|
|
591
|
-
|
|
592
|
-
return parsed === undefined || parsed.card.risk.requiresRealApproval;
|
|
672
|
+
return askDemandsAPerson(undefined, storedPersonDemandRecord(cardJson));
|
|
593
673
|
}
|
|
594
674
|
export function buildApprovalPreamble(rows, nowMs, replayMax) {
|
|
595
675
|
const frames = [];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **运维审批队列 `pending[]` 的行族闭集** —— 全仓唯一的定义处([ref])。
|
|
3
|
+
*
|
|
4
|
+
* ## 规则(一句)
|
|
5
|
+
* `GET /v1/approvals` 的 `pending[]`(与 `/v1/approvals/stream` 的 `pending` / `resolved` 帧)= **决断口是 `POST /v1/approvals/:id/decide` 的全部行**;
|
|
6
|
+
* 每一行自报它属于哪一族(`kind`,本闭集一词),消费方**先按 `kind` 分支**,再读那一族的键。数组名仍是决断口的判据(`livePending[]` 的口是
|
|
7
|
+
* `POST /v1/tool-approvals/:approvalId/respond`,不在本闭集里);本闭集回答的是「同一扇门后面是哪一种卡」。
|
|
8
|
+
*
|
|
9
|
+
* · `checkpoint` —— 停驻门(durable park):行 = 店的 `listPending` 那一行(键集照旧,另带 `kind`),门的 `:id` = 行的 `sessionId`,
|
|
10
|
+
* 再按 `gateKind`(core 的门型闭集)分路;
|
|
11
|
+
* · `peer_inbound` —— 被 `crossSessionInbound: hold` 扣住、等收信方决断的跨会话来信:行 = core 进程级 held 队列那一张卡的投影
|
|
12
|
+
* (`peer-inbound-face.ts`),门的 `:id` = 行的 `id`(`peer_inbound:` 前缀的坐标串),决断 = core `PeerHeldQueue.settle`。
|
|
13
|
+
*
|
|
14
|
+
* 加一族 = 在 {@link APPROVAL_QUEUE_KINDS} 加一个词:决断口分派、流的键与离场帧({@link approvalQueueKindOfDoorId} 的调用点)
|
|
15
|
+
* 都是对 {@link ApprovalQueueKind} 的穷举 `switch`,漏一臂 = **编译红**(`test/peer-inbound-approvals.test.ts` 的 P6 钉)。
|
|
16
|
+
*/
|
|
17
|
+
/** 行族闭集(序无义;加词 = 全部穷举点编译红)。 */
|
|
18
|
+
export declare const APPROVAL_QUEUE_KINDS: readonly ["checkpoint", "peer_inbound"];
|
|
19
|
+
export type ApprovalQueueKind = (typeof APPROVAL_QUEUE_KINDS)[number];
|
|
20
|
+
/**
|
|
21
|
+
* `peer_inbound` 族决断口 id 的前缀 —— 一个 id 是不是这一族,**只**看它(`checkpoint` 族的 id 是会话 id:规范 uuid 或服务现铸的
|
|
22
|
+
* 内部 id,都不含 `:`,所以两族的 id 空间不交)。
|
|
23
|
+
*/
|
|
24
|
+
export declare const PEER_INBOUND_ID_PREFIX = "peer_inbound:";
|
|
25
|
+
/** 决断口 `:id` 段 → 它属于哪一族(唯一判据:前缀)。前缀对而其余不成形的 id 仍归 `peer_inbound`(那一族自己答 404,不落回停驻门)。 */
|
|
26
|
+
export declare function approvalQueueKindOfDoorId(id: string): ApprovalQueueKind;
|
|
27
|
+
//# sourceMappingURL=approval-queue-kind.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const APPROVAL_QUEUE_KINDS = ["checkpoint", "peer_inbound"];
|
|
2
|
+
export const PEER_INBOUND_ID_PREFIX = "peer_inbound:";
|
|
3
|
+
export function approvalQueueKindOfDoorId(id) {
|
|
4
|
+
return id.startsWith(PEER_INBOUND_ID_PREFIX) ? "peer_inbound" : "checkpoint";
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=approval-queue-kind.js.map
|
|
@@ -79,7 +79,7 @@ export type GateMatchOutcome =
|
|
|
79
79
|
kind: "hash_mismatch";
|
|
80
80
|
candidates: number;
|
|
81
81
|
}
|
|
82
|
-
/** 三元组身份就对不上(不是摘要的事):别的
|
|
82
|
+
/** 三元组身份就对不上(不是摘要的事):别的 sourceSessionId / 别的 callId / 早于本 ask 的 park。 */
|
|
83
83
|
| {
|
|
84
84
|
kind: "identity_miss";
|
|
85
85
|
};
|
|
@@ -91,8 +91,8 @@ export type GateMatchOutcome =
|
|
|
91
91
|
* `delegation.parentToolCallId` 是必填字段,铸行时逐字透传进 `parent_tool_call_id` 列 ⇒ **列非 NULL
|
|
92
92
|
* ⇔ delegation 在场**(不需要新列,也不需要回 `req` 里再读一次)。
|
|
93
93
|
*
|
|
94
|
-
* 第二维 `
|
|
95
|
-
* `AskRequest.
|
|
94
|
+
* 第二维 `sourceSessionId !== sessionId` 把「根腿」摘出去:根腿的 `sourceSessionId` 恒等于会话锚(core 给
|
|
95
|
+
* `AskRequest.sourceSessionId` 填的就是该腿 sessionId),它根本不在任何 fold 里,`parent_tool_call_id`
|
|
96
96
|
* 为 NULL 是它的常态而不是信号。
|
|
97
97
|
*
|
|
98
98
|
* 命中 ⇒ 退出硬相等(判据 1 结构上不命中,行落 ②③④⑤)。实测(test AI 围栏)checkpoint 存的摘要**逐字
|
|
@@ -105,11 +105,11 @@ export declare function isAncestorFoldMint(ask: AskRow): boolean;
|
|
|
105
105
|
* 逐条:
|
|
106
106
|
* - 祖先层 fold 中途铸点 ⇒ 直接退出(见 {@link isAncestorFoldMint});
|
|
107
107
|
* - `unparseable` 候选直接出局(读不出 ⇒ 不确定 ⇒ 不命中);
|
|
108
|
-
* - **身份三元组**:`
|
|
108
|
+
* - **身份三元组**:`sourceSessionId` **相等** ∧ `toolCallId` **相等** ∧ 时间维的**因果下界**
|
|
109
109
|
* `candidate.createdAtMs >= ask.createdAtMs`(⚠️ 第三维**不是等式** —— 把它读成等式会让合法的 park
|
|
110
110
|
* 几乎命不中,行随后落 ②③⑤ 被误判)—— 摘要**不是**身份([ref]③①:
|
|
111
111
|
* 同 args 的两次调用摘要天然相同,只靠摘要硬相等会把第二次投递并进第一次的票);任一维在候选侧
|
|
112
|
-
* 缺席(读不出的 blob 没有 `
|
|
112
|
+
* 缺席(读不出的 blob 没有 `sourceSessionId`)即身份不成立;
|
|
113
113
|
* - **hash 双等**:两侧都必须在场且相等,任一侧缺席一律不 bind;禁「能取到时才比」的可选谓词。
|
|
114
114
|
* 归因分两级(顺序即代码顺序):身份这一层先判 —— 同身份候选为空**且**候选集非空 ⇒ `identity_miss`;
|
|
115
115
|
* 走到 hash 这一层才把缺席记成**单铸** `single_mint`(见 {@link GateMatchOutcome})。
|
|
@@ -178,10 +178,10 @@ export interface ReconcileStats {
|
|
|
178
178
|
*
|
|
179
179
|
* 🔴 **今天恒零,而且是结构性的**(扫描P2,2026-08-12 亲读验真;登记而不假称已解):读口
|
|
180
180
|
* `findCheckpointCandidatesForAsk` 按 `session_id = ask.sessionId` 查,`session_id` 列写的是
|
|
181
|
-
* `cp.sessionId`,而 core 每个 checkpoint 铸点都填 `
|
|
181
|
+
* `cp.sessionId`,而 core 每个 checkpoint 铸点都填 `sourceSessionId: sessionId`
|
|
182
182
|
* (`runner/prepare-task.js` 四处;`checkpoint-store.d.ts` 逐字「= sessionId at the mint」)⇒ 读口
|
|
183
|
-
* 返回的候选恒满足 `c.
|
|
184
|
-
* ask.
|
|
183
|
+
* 返回的候选恒满足 `c.sourceSessionId === ask.sessionId`;而判据 1 命中要 `c.sourceSessionId ===
|
|
184
|
+
* ask.sourceSessionId`,祖先层否决要 `ask.sourceSessionId !== ask.sessionId` —— 两者互斥,这一臂在生产
|
|
185
185
|
* 接线下一次也不会被返回。
|
|
186
186
|
*
|
|
187
187
|
* 所以**别把零读数当健康信号**(与 {@link unmatchableNoHash} 的「恒零才是健康态」正相反:那条能非零,
|
|
@@ -21,7 +21,7 @@ function withDeadline(op, label, timeoutMs = RECONCILE_STORE_TIMEOUT_MS) {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
export function isAncestorFoldMint(ask) {
|
|
24
|
-
return ask.parentToolCallId === null && ask.
|
|
24
|
+
return ask.parentToolCallId === null && ask.sourceSessionId !== ask.sessionId;
|
|
25
25
|
}
|
|
26
26
|
export function classifyGateMatch(ask, candidates) {
|
|
27
27
|
const outcome = classifyGateMatchBeforeFoldVeto(ask, candidates);
|
|
@@ -30,7 +30,7 @@ export function classifyGateMatch(ask, candidates) {
|
|
|
30
30
|
return outcome;
|
|
31
31
|
}
|
|
32
32
|
function classifyGateMatchBeforeFoldVeto(ask, candidates) {
|
|
33
|
-
const sameIdentity = candidates.filter((c) => c.unparseable !== true && c.
|
|
33
|
+
const sameIdentity = candidates.filter((c) => c.unparseable !== true && c.sourceSessionId !== null && c.sourceSessionId === ask.sourceSessionId && c.boundCallId === ask.toolCallId && c.createdAtMs >= ask.createdAtMs);
|
|
34
34
|
const askHash = ask.boundInputHash;
|
|
35
35
|
if (sameIdentity.length === 0) {
|
|
36
36
|
if (candidates.length > 0)
|
|
@@ -96,7 +96,7 @@ export async function bindParkingAskAtGate(deps, ask, nowMs, deadlineMs) {
|
|
|
96
96
|
taskId: ask.taskId,
|
|
97
97
|
toolCallId: ask.toolCallId,
|
|
98
98
|
candidates: gateMatch.candidates,
|
|
99
|
-
reason: "ask and checkpoint minted the same bound input under the same (
|
|
99
|
+
reason: "ask and checkpoint minted the same bound input under the same (sourceSessionId, toolCallId) identity but the digests differ — " +
|
|
100
100
|
"the known causes are deployment-side (a hook/policy mutating an object it still holds a reference to between the two mints, " +
|
|
101
101
|
"or a stateful getter returning a fresh value per read), not out-of-band interference; this ask keeps its fail-safe route (no park, no denial)",
|
|
102
102
|
});
|
package/dist/audit.js
CHANGED
|
@@ -129,8 +129,9 @@ const MESSAGE_READ_TREATMENT = {
|
|
|
129
129
|
runOrigin: "verbatim",
|
|
130
130
|
taskId: "verbatim",
|
|
131
131
|
attachment: "verbatim",
|
|
132
|
+
rewrittenBy: "verbatim",
|
|
132
133
|
},
|
|
133
|
-
custom: { role: "verbatim", customType: "verbatim", content: "verbatim", display: "verbatim", details: "verbatim", provenance: "verbatim", timestamp: "verbatim" },
|
|
134
|
+
custom: { role: "verbatim", customType: "verbatim", content: "verbatim", display: "verbatim", details: "verbatim", provenance: "verbatim", timestamp: "verbatim", rewrittenBy: "verbatim" },
|
|
134
135
|
assistant: {
|
|
135
136
|
role: "verbatim", content: "parts", errorMessage: "text", diagnostics: "structure", repetition: "structure",
|
|
136
137
|
api: "verbatim", provider: "verbatim", model: "verbatim", responseModel: "verbatim", responseId: "verbatim", requestId: "verbatim",
|
|
@@ -139,8 +140,9 @@ const MESSAGE_READ_TREATMENT = {
|
|
|
139
140
|
staticReasoningCut: "verbatim", timestamp: "verbatim",
|
|
140
141
|
providerCondition: "verbatim", providerConditionSource: "verbatim", providerResetAtMs: "verbatim", apiErrorRoute: "verbatim",
|
|
141
142
|
errorBodyIncomplete: "verbatim", modelOutputInvalid: "verbatim", finishReasonUnknown: "verbatim", streamEndIgnored: "verbatim",
|
|
143
|
+
refusedTool: "verbatim", rewrittenBy: "verbatim",
|
|
142
144
|
},
|
|
143
|
-
toolResult: { role: "verbatim", toolCallId: "verbatim", toolName: "verbatim", content: "parts", details: "toolCard", isError: "verbatim", timestamp: "verbatim" },
|
|
145
|
+
toolResult: { role: "verbatim", toolCallId: "verbatim", toolName: "verbatim", content: "parts", details: "toolCard", isError: "verbatim", timestamp: "verbatim", rewrittenBy: "verbatim" },
|
|
144
146
|
bashExecution: {
|
|
145
147
|
role: "verbatim", command: "text", output: "text", exitCode: "verbatim", cancelled: "verbatim", truncated: "verbatim",
|
|
146
148
|
fullOutputPath: "verbatim", timestamp: "verbatim", excludeFromContext: "verbatim",
|
|
@@ -45,7 +45,7 @@ export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
|
|
|
45
45
|
fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
|
|
46
46
|
tracer: import("@sema-agent/core").TracerHook;
|
|
47
47
|
sideQueryAccounting: import("../budget.js").SideQueryAccounting;
|
|
48
|
-
toolResultStore: import("
|
|
48
|
+
toolResultStore: import("@sema-agent/core").ToolResultStore | undefined;
|
|
49
49
|
sessionPolicyStore: import("@sema-agent/core").SessionPolicyStore | undefined;
|
|
50
50
|
fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
|
|
51
51
|
fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
|
|
@@ -416,7 +416,7 @@ export function createExecutionEnv(ctx) {
|
|
|
416
416
|
});
|
|
417
417
|
}
|
|
418
418
|
else if (config.remoteExec?.provider === "host") {
|
|
419
|
-
const gitBaseEnv = new RemoteHostExecutionEnv({
|
|
419
|
+
const gitBaseEnv = new RemoteHostExecutionEnv({ workspaceDir: config.worktreeIsolation.repoRoot, inheritEnv: "all", backgroundShell: config.requirePrincipal !== true });
|
|
420
420
|
executionEnvFactory = withWorktreeIsolation(executionEnvFactory, {
|
|
421
421
|
repoRoot: config.worktreeIsolation.repoRoot,
|
|
422
422
|
...(config.worktreeIsolation.allowedRoots ? { allowedRoots: config.worktreeIsolation.allowedRoots } : {}),
|
|
@@ -29,15 +29,22 @@
|
|
|
29
29
|
* 补偿是这条 env 旋钮本身:要档位的部署显式写下它。将来 center 真有了档位下发面,「多租户必须能解析出
|
|
30
30
|
* 档位」才是一条有对象可指的判据,那时再立。
|
|
31
31
|
*
|
|
32
|
-
* ##
|
|
32
|
+
* ## 装配相容性:一条判据、一个时点(见 {@link assertGovernanceAssemblable})
|
|
33
33
|
*
|
|
34
|
-
* core 的锁按「spec
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* · `spec.mcp` —— 部署自带的 MCP
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `
|
|
34
|
+
* core 的锁按「spec 是否占了这个字段」判、合规否决按「被禁能力是否已在 spec 上」判 —— 占了 / 在场都是**整拒 prepare**
|
|
35
|
+
* (`config.locked_key` / `config.compliance_denied`),不是「不挂载」。而本仓有几张 spec 面是**部署自己**在铸:
|
|
36
|
+
* · `spec.toolPolicy` —— 治理拍(`applyRuntimeGovernance`)在**任何**客户端表态下都铸(敏感路径守卫集默认非空)⇒ 恒在;
|
|
37
|
+
* · `spec.mcp` / `spec.a2a` —— 部署自带的 MCP 服务器 / A2A 对端(config.d 或中心下发)经同一字段进 core(引擎没有部署级的服务器 / 对端席)⇒ 配了就在;
|
|
38
|
+
* · WebFetch 工具面 —— 单用户花名册恒挂(多租户 roster 才摘)。
|
|
39
|
+
* 判据只有一条:**锁住的键 / 否决的能力所守的面 ∩ 本部署装配的面 ≠ ∅ ⇒ 起服拒**(安全控件不得半开 §3.4 C 行)—— 那不是「配得比较严」,
|
|
40
|
+
* 是这台部署的每条腿(或某场景的每条腿)必失败,而运维手里没有任何线索指向那把锁 / 那条否决。两张对照表是 core 两个闭集的 `Record`:
|
|
41
|
+
* 引擎加一个锁键或合规能力 ⇒ 这里缺行 ⇒ tsc 红,逼人当场判它守不守本部署铸的面(修前是两只 `has()` 判据,引擎后加的 `a2a` 锁与
|
|
42
|
+
* `a2a_peers` 否决静默不判)。
|
|
43
|
+
*
|
|
44
|
+
* **时点**:部署 MCP / A2A 基线在能力层才物化(server = 第 7 段的 `applyCenterCapabilities`;run-local = 候选采纳段的同一行),所以这道门
|
|
45
|
+
* 在**物化之后**由每个起服入口调恰好一次,出口只有一个:抛 ⇒ 起服拒(退出码 1)。座席铸点 {@link createGovernanceSeams}(第 6 段)**不判**:
|
|
46
|
+
* 那一刻两格基线恒空(修前判据就跑在那里,于是 `mcp` 锁 / `mcp_servers` 否决对 config.d 与中心声明的服务器从不拒启,任务腿各自在引擎门口
|
|
47
|
+
* 撞拒);它的 config 窄口里也就没有那两格 —— 「物化之前判」这一形在编译期就写不出来。没有运行期补判:不留每腿 400 臂、不留静默卸臂。
|
|
41
48
|
*
|
|
42
49
|
* ## 件D 的边界(成文,别读成「数据会被删」)
|
|
43
50
|
*
|
|
@@ -62,7 +69,8 @@ export interface GovernanceSeams {
|
|
|
62
69
|
}
|
|
63
70
|
/** 依赖收窄到真实消费面(接口隔离 —— 测试用真形字面量,零铸形)。 */
|
|
64
71
|
export interface GovernanceSeamsCtx {
|
|
65
|
-
|
|
72
|
+
/** 没有 `mcpServers` / `a2aPeers`:座席铸在部署基线物化之前,装配相容性判据不在这里(见文件头「时点」)。 */
|
|
73
|
+
config: Pick<ServiceConfig, "compliancePosture" | "lockedConfigKeys" | "retentionPolicy" | "retentionSweep">;
|
|
66
74
|
logger: Pick<Logger, "info" | "warn">;
|
|
67
75
|
/**
|
|
68
76
|
* core 留存能力校验的对象集(名字进拒启文案)。喂的是**装配现场的真店**,不是「我以为装了什么」——
|
|
@@ -79,41 +87,18 @@ export interface GovernanceSeamsCtx {
|
|
|
79
87
|
*/
|
|
80
88
|
retentionExecutor: ManagedRetentionCapability | null;
|
|
81
89
|
}
|
|
90
|
+
/** 装配相容性门的输入 = **装配完成后**的部署事实(部署 MCP / A2A 基线物化之后读;见文件头「时点」)。 */
|
|
91
|
+
export type GovernanceAssemblyFacts = Pick<ServiceConfig, "lockedConfigKeys" | "compliancePosture" | "mcpServers" | "a2aPeers" | "requirePrincipal">;
|
|
82
92
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
93
|
+
* **装配相容性门**(见文件头「一条判据、一个时点」):锁住的键 / 否决的能力所守的面撞上本部署装配的面 ⇒ 抛(起服拒)。
|
|
94
|
+
* 🔴 时点即契约:调用方必须在部署 MCP / A2A 基线**物化之后**调它(server 第 7 段紧跟 `applyCenterCapabilities`;run-local 在候选采纳段之后),
|
|
95
|
+
* 每个起服入口恰好一次。中心侧后来加了 MCP 服务器 / A2A 对端:那一族是 restart-to-apply 面,变更翻 `restartRequired` 让编排器滚动重启 ——
|
|
96
|
+
* 重启就撞上这道门,所以没有第二条热路径判据。
|
|
87
97
|
*/
|
|
88
|
-
export declare function
|
|
89
|
-
lockedKeys: ReadonlySet<LockedKey>;
|
|
90
|
-
deploymentMcpConfigured: boolean;
|
|
91
|
-
}): void;
|
|
92
|
-
/**
|
|
93
|
-
* 治理面的**装配相容性**总判据(codex 交叉复审 R2-F2 起扩面)——锁与合规档位在这条轴上是同一件事:
|
|
94
|
-
* 两者都可能把「本部署自己的装配」判成 core 门口的违规,而那不是「配得更严」,是**这台从此每条腿必失败**。
|
|
95
|
-
*
|
|
96
|
-
* 合规半场的判据来自亲核 core 的 prepare 门(`prepare-task` 的 compliance 段):被否决的能力若**已在
|
|
97
|
-
* spec 上**,core 是**整拒 prepare**(`config.compliance_denied`),不是「不挂载」。于是:
|
|
98
|
-
* · `mcp_servers` 被禁 ∧ 部署自带 MCP 基线 ⇒ 每条腿的 `spec.mcp` 都在 ⇒ 必拒;
|
|
99
|
-
* · `web_fetch` 被禁 ∧ 花名册挂了 WebFetch ⇒ 必拒。⚠️ `hipaa` 的**内建 floor 就含 `web_fetch`**,而本仓
|
|
100
|
-
* 的单用户花名册**恒挂** WebFetch(`capabilities/scenarios.ts` 的 roster 过滤只在多租户下摘它)——
|
|
101
|
-
* 也就是说「单用户 + hipaa」在本仓是一个每条腿必失败的组合。这条判据的全部价值就在这儿:让它在
|
|
102
|
-
* 启动期说出来,而不是让运维看着一台"启动成功、任务全失败"的 worker 猜。
|
|
103
|
-
* · `workflows` 被禁只在 `spec.selfOrchestration === true` 的腿上拒(per-task,不是部署恒真)⇒ 不在本
|
|
104
|
-
* 判据内,由那条腿自己响亮拒;`org_memory_mount` 走件A 的准入面。**成文定界,不留白。**
|
|
105
|
-
*/
|
|
106
|
-
export declare function assertGovernanceAssemblable(input: {
|
|
107
|
-
lockedKeys: ReadonlySet<LockedKey>;
|
|
108
|
-
/** 已解析的合规否决集(空集 = 无档位)。 */
|
|
109
|
-
complianceDenies: ReadonlySet<string>;
|
|
110
|
-
deploymentMcpConfigured: boolean;
|
|
111
|
-
/** 本部署的花名册是否会挂 WebFetch(= 单用户形;多租户下 roster 过滤掉它)。 */
|
|
112
|
-
webFetchMounted: boolean;
|
|
113
|
-
}): void;
|
|
98
|
+
export declare function assertGovernanceAssemblable(facts: GovernanceAssemblyFacts): void;
|
|
114
99
|
/**
|
|
115
|
-
*
|
|
116
|
-
* (
|
|
100
|
+
* 装配三座席(件B/C/D)与 core 的留存能力校验 —— **不**判装配相容性:座席铸在部署 MCP / A2A 基线物化之前,那道门由起服入口在物化之后调
|
|
101
|
+
* {@link assertGovernanceAssemblable}(见文件头「时点」)。
|
|
117
102
|
*/
|
|
118
103
|
export declare function createGovernanceSeams(ctx: GovernanceSeamsCtx): GovernanceSeams;
|
|
119
104
|
//# sourceMappingURL=governance-seams.d.ts.map
|
|
@@ -1,44 +1,77 @@
|
|
|
1
1
|
import { assertRetentionCapability, resolveComplianceDenies, resolveLockedKeys } from "@sema-agent/core";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
|
|
3
|
+
const FACE_ASSEMBLED = {
|
|
4
|
+
toolPolicy: () => true,
|
|
5
|
+
mcp: (facts) => (facts.mcpServers?.servers.length ?? 0) > 0,
|
|
6
|
+
a2a: (facts) => (facts.a2aPeers?.length ?? 0) > 0,
|
|
7
|
+
webFetch: (facts) => !DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(facts),
|
|
8
|
+
};
|
|
9
|
+
const LOCK_CONFLICTS = {
|
|
10
|
+
toolPolicy: {
|
|
11
|
+
face: "toolPolicy",
|
|
12
|
+
refusal: 'LOCKED_CONFIG_KEYS contains "toolPolicy", which this server cannot arm: it COMPOSES the effective tool policy itself on ' +
|
|
5
13
|
"every leg (deployment approval baseline + the tighten-only governance pass over the sensitive-path guard set), so " +
|
|
6
14
|
"`TaskSpec.toolPolicy` is always present at the engine's prepare door and the lock would refuse EVERY task with " +
|
|
7
15
|
"config.locked_key. The policy is already deployment-owned and no request can loosen it (the fold is deny-wins), so " +
|
|
8
|
-
"this lock has nothing to guard here — remove it from LOCKED_CONFIG_KEYS."
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
"this lock has nothing to guard here — remove it from LOCKED_CONFIG_KEYS.",
|
|
17
|
+
},
|
|
18
|
+
mcp: {
|
|
19
|
+
face: "mcp",
|
|
20
|
+
refusal: 'LOCKED_CONFIG_KEYS contains "mcp" while this deployment configures MCP servers of its own (config.d/mcp.json or the center\'s mcp domain): the ' +
|
|
12
21
|
"deployment's servers ride the SAME `TaskSpec.mcp` field the lock guards (the engine has no deployment-level MCP seat), " +
|
|
13
22
|
"so every task would be refused at prepare with config.locked_key. Either drop the `mcp` lock, or remove the configured " +
|
|
14
|
-
"MCP servers — a locked deployment mounts no MCP at all."
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
"MCP servers — a locked deployment mounts no MCP at all.",
|
|
24
|
+
},
|
|
25
|
+
a2a: {
|
|
26
|
+
face: "a2a",
|
|
27
|
+
refusal: 'LOCKED_CONFIG_KEYS contains "a2a" while this deployment configures A2A peers of its own (the center\'s a2a domain): the ' +
|
|
28
|
+
"deployment's peers ride the SAME `TaskSpec.a2a` field the lock guards (the engine has no deployment-level A2A peer seat), " +
|
|
29
|
+
"so every task would be refused at prepare with config.locked_key. Either drop the `a2a` lock, or remove the configured " +
|
|
30
|
+
"A2A peers — a locked deployment dials no A2A peer at all.",
|
|
31
|
+
},
|
|
32
|
+
compliancePosture: null,
|
|
33
|
+
retentionPolicy: null,
|
|
34
|
+
};
|
|
35
|
+
const DENY_CONFLICTS = {
|
|
36
|
+
mcp_servers: {
|
|
37
|
+
face: "mcp",
|
|
38
|
+
refusal: 'the compliance posture denies "mcp_servers" while this deployment configures MCP servers of its own: the engine REFUSES ' +
|
|
21
39
|
"(not silently narrows) every prepare whose TaskSpec.mcp is non-empty, so every task would fail with config.compliance_denied. " +
|
|
22
|
-
"Remove the configured MCP servers, or drop mcp_servers from the posture."
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
"Remove the configured MCP servers, or drop mcp_servers from the posture.",
|
|
41
|
+
},
|
|
42
|
+
a2a_peers: {
|
|
43
|
+
face: "a2a",
|
|
44
|
+
refusal: 'the compliance posture denies "a2a_peers" while this deployment configures A2A peers of its own: the engine REFUSES ' +
|
|
45
|
+
"(not silently narrows) every prepare whose TaskSpec.a2a is non-empty, so every task would fail with config.compliance_denied. " +
|
|
46
|
+
"Remove the configured A2A peers, or drop a2a_peers from the posture.",
|
|
47
|
+
},
|
|
48
|
+
web_fetch: {
|
|
49
|
+
face: "webFetch",
|
|
50
|
+
refusal: 'the compliance posture denies "web_fetch" while this deployment mounts the WebFetch tool: the engine REFUSES every prepare ' +
|
|
26
51
|
"whose roster carries it, so every task on the default roster would fail with config.compliance_denied. This server drops " +
|
|
27
52
|
"WebFetch from the roster only on a MULTI-TENANT deployment (REQUIRE_PRINCIPAL=true) — a single-user deployment cannot " +
|
|
28
|
-
"currently honor a web_fetch denial. Run the posture multi-tenant, or drop the profile/deny."
|
|
29
|
-
}
|
|
53
|
+
"currently honor a web_fetch denial. Run the posture multi-tenant, or drop the profile/deny.",
|
|
54
|
+
},
|
|
55
|
+
workflows: null,
|
|
56
|
+
org_memory_mount: null,
|
|
57
|
+
};
|
|
58
|
+
function resolveGovernanceDeclarations(config) {
|
|
59
|
+
return {
|
|
60
|
+
lockedKeys: resolveLockedKeys(config.lockedConfigKeys !== undefined ? { keys: config.lockedConfigKeys } : undefined),
|
|
61
|
+
complianceDenies: config.compliancePosture !== undefined ? resolveComplianceDenies(config.compliancePosture) : new Set(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function assertGovernanceAssemblable(facts) {
|
|
65
|
+
const { lockedKeys, complianceDenies } = resolveGovernanceDeclarations(facts);
|
|
66
|
+
const conflicts = [...[...lockedKeys].map((key) => LOCK_CONFLICTS[key]), ...[...complianceDenies].map((cap) => DENY_CONFLICTS[cap])];
|
|
67
|
+
for (const conflict of conflicts)
|
|
68
|
+
if (conflict !== null && FACE_ASSEMBLED[conflict.face](facts))
|
|
69
|
+
throw new Error(conflict.refusal);
|
|
30
70
|
}
|
|
31
71
|
export function createGovernanceSeams(ctx) {
|
|
32
72
|
const { config, logger, retentionStores } = ctx;
|
|
33
|
-
const
|
|
73
|
+
const { lockedKeys, complianceDenies } = resolveGovernanceDeclarations(config);
|
|
34
74
|
const posture = config.compliancePosture;
|
|
35
|
-
const complianceDenies = posture !== undefined ? resolveComplianceDenies(posture) : new Set();
|
|
36
|
-
assertGovernanceAssemblable({
|
|
37
|
-
lockedKeys,
|
|
38
|
-
complianceDenies,
|
|
39
|
-
deploymentMcpConfigured: (config.mcpServers?.servers.length ?? 0) > 0,
|
|
40
|
-
webFetchMounted: config.requirePrincipal !== true,
|
|
41
|
-
});
|
|
42
75
|
const lockedConfig = config.lockedConfigKeys !== undefined && config.lockedConfigKeys.length > 0 ? { keys: config.lockedConfigKeys } : undefined;
|
|
43
76
|
if (lockedConfig)
|
|
44
77
|
logger.info("locked_config_enabled", { keys: [...lockedKeys].sort() });
|