@sema-agent/server 7.142.0 → 7.143.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 +442 -0
- package/USAGE.md +41 -24
- 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 +124 -25
- package/dist/approval-card.js +103 -23
- 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/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 +10 -7
- package/dist/boot/runner-deps.js +5 -17
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +16 -2
- 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 +5 -1
- 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 +9 -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 +11 -1
- package/dist/capabilities/scenarios.js +1 -0
- 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-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 +5 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- 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/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/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +71 -49
- 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 +142 -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/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/fail-open.d.ts +20 -4
- package/dist/observability/fail-open.js +17 -4
- package/dist/observability/run-terminal.js +4 -0
- 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.js +1 -1
- package/dist/peer-inbound-face.d.ts +127 -0
- package/dist/peer-inbound-face.js +158 -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/checkpoint-store-sql.d.ts +4 -4
- package/dist/plugins/checkpoint-store-sql.js +9 -7
- 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/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 +1 -1
- package/dist/plugins/local-checkpoint-store.js +2 -2
- package/dist/plugins/local-session-store.d.ts +6 -2
- package/dist/plugins/local-session-store.js +8 -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 +32 -2
- package/dist/plugins/one-time-migrations.js +106 -3
- 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/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-k8s.d.ts +37 -24
- package/dist/plugins/remote-env-k8s.js +50 -17
- 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/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/store-backend.d.ts +23 -28
- package/dist/plugins/store-backend.js +9 -1
- 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/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 +9 -5
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +6 -7
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +12 -0
- package/dist/session-error-wire.js +6 -0
- package/dist/session-incarnation.d.ts +1 -1
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +93 -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 +52 -34
- package/dist/tool-approval.js +66 -57
- package/dist/trace/core-keyset-guard.d.ts +11 -8
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +3 -1
- 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/model-segment.d.ts +55 -25
- package/dist/trace/model-segment.js +101 -33
- package/dist/trace/project.d.ts +7 -4
- package/dist/trace/project.js +6 -0
- package/dist/trace/redact.d.ts +61 -9
- package/dist/trace/redact.js +1490 -468
- package/dist/trace/self-minted-wire.d.ts +510 -38
- package/dist/trace/self-minted-wire.js +165 -17
- 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
|
@@ -672,19 +672,37 @@ function noteVerdictUntrusted(ctx, args) {
|
|
|
672
672
|
return;
|
|
673
673
|
emitHookNotice(ctx, notice);
|
|
674
674
|
}
|
|
675
|
+
const VERDICT_GAP_CAUSE = {
|
|
676
|
+
matcher_invalid: "verdict_unfinished",
|
|
677
|
+
llm_carrier_unavailable: "verdict_unfinished",
|
|
678
|
+
llm_cap: "verdict_unfinished",
|
|
679
|
+
powershell_unsupported: "verdict_unfinished",
|
|
680
|
+
if_unevaluable: "verdict_unfinished",
|
|
681
|
+
budget_exhausted: "verdict_unfinished",
|
|
682
|
+
spawn_failed: "verdict_unfinished",
|
|
683
|
+
timeout: "verdict_unfinished",
|
|
684
|
+
exit_nonzero: "verdict_unfinished",
|
|
685
|
+
bad_json: "verdict_unreadable",
|
|
686
|
+
decision_out_of_set: "verdict_unreadable",
|
|
687
|
+
evidence_unavailable: "verdict_unfinished",
|
|
688
|
+
rewrite_unsettled: "verdict_unfinished",
|
|
689
|
+
url_not_allowed: "verdict_unfinished",
|
|
690
|
+
input_truncated: "verdict_unreadable",
|
|
691
|
+
matcher_over_budget: "verdict_unreadable",
|
|
692
|
+
};
|
|
675
693
|
function createUnrunLedger(ctx, event, toolName) {
|
|
676
|
-
let
|
|
694
|
+
let managedVerdictGap;
|
|
677
695
|
return {
|
|
678
696
|
untrusted(source, entry, reason) {
|
|
679
697
|
if (hookPhaseOf(source) === "unmanaged")
|
|
680
698
|
return;
|
|
681
|
-
|
|
699
|
+
managedVerdictGap ??= VERDICT_GAP_CAUSE[reason];
|
|
682
700
|
noteVerdictUntrusted(ctx, { event, reason, entry, source, toolName });
|
|
683
701
|
},
|
|
684
702
|
unrun(source, entry, reason, detail) {
|
|
685
703
|
if (source === undefined || hookPhaseOf(source) === "unmanaged")
|
|
686
704
|
return;
|
|
687
|
-
|
|
705
|
+
managedVerdictGap ??= VERDICT_GAP_CAUSE[reason];
|
|
688
706
|
ctx.logger.warn("hook_managed_unrun", { event, reason, source: echoCallerText(source), type: entry.type, ...(toolName !== undefined ? { tool: toolName } : {}) });
|
|
689
707
|
if (!ctx.onHookNotice)
|
|
690
708
|
return;
|
|
@@ -699,8 +717,8 @@ function createUnrunLedger(ctx, event, toolName) {
|
|
|
699
717
|
...(detail !== undefined ? { detail: boundedNoticeText(detail) } : {}),
|
|
700
718
|
});
|
|
701
719
|
},
|
|
702
|
-
get
|
|
703
|
-
return
|
|
720
|
+
get managedVerdictGap() {
|
|
721
|
+
return managedVerdictGap;
|
|
704
722
|
},
|
|
705
723
|
};
|
|
706
724
|
}
|
|
@@ -1494,12 +1512,14 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1494
1512
|
}
|
|
1495
1513
|
}
|
|
1496
1514
|
if (asked !== undefined) {
|
|
1497
|
-
return { action: "ask", message: asked, ...(rewritten ? { updatedInput: currentInput } : {}), ...contextField() };
|
|
1515
|
+
return { action: "ask", message: asked, questionCause: "asked", ...(rewritten ? { updatedInput: currentInput } : {}), ...contextField() };
|
|
1498
1516
|
}
|
|
1499
|
-
|
|
1517
|
+
const managedGap = ledger.managedVerdictGap;
|
|
1518
|
+
if (managedGap !== undefined) {
|
|
1500
1519
|
return {
|
|
1501
1520
|
action: "ask",
|
|
1502
1521
|
message: "a managed PreToolUse hook did not run to a verdict — approval required",
|
|
1522
|
+
questionCause: managedGap,
|
|
1503
1523
|
...(rewritten ? { updatedInput: currentInput } : {}),
|
|
1504
1524
|
...contextField(),
|
|
1505
1525
|
};
|
|
@@ -1508,6 +1528,7 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1508
1528
|
return {
|
|
1509
1529
|
action: "ask",
|
|
1510
1530
|
message: "a PreToolUse hook returned an unrecognized permissionDecision — approval required",
|
|
1531
|
+
questionCause: "verdict_unreadable",
|
|
1511
1532
|
...(rewritten ? { updatedInput: currentInput } : {}),
|
|
1512
1533
|
...contextField(),
|
|
1513
1534
|
};
|
package/dist/host-decision.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* ⇒ 本仓的规则从「记住一张词×决定的相容表」变成「说出你知道的那两件事」。相容性也不再靠记忆:一次
|
|
16
16
|
* sweep 不可能 `allow`,core pre-CAS 拒。
|
|
17
17
|
*/
|
|
18
|
-
import { type AskOutcome, type HostDecider, type PermissionResult, type ResumeOutcome } from "@sema-agent/core";
|
|
18
|
+
import { type AskOutcome, type DecidedVia, type HostDecider, type PermissionResult, type ResumeOutcome } from "@sema-agent/core";
|
|
19
19
|
/**
|
|
20
20
|
* 谁结束了这次等待,以及通道自报的归属。
|
|
21
21
|
* · `decidedBy: "person"` —— 一个人经审批通道做的决定(决定体缺席 `decidedBy` 即此);
|
|
@@ -32,7 +32,7 @@ import { type AskOutcome, type HostDecider, type PermissionResult, type ResumeOu
|
|
|
32
32
|
* · **铸**(本仓自己观察到的事实):只有 `"timeout"` —— 同步 `onAsk` 腿的等待窗归**宿主**所有(引擎不为
|
|
33
33
|
* 一个它没排程的回调起表),所以「本仓的 TTL 窗走完了、没人答」这件事引擎结构上观察不到,不自报就
|
|
34
34
|
* 永远被记成「一个人拒绝了」;
|
|
35
|
-
* · **转**(调用方在决定体里陈述的事实):决定体的 `decidedBy:"policy"` 经 {@link
|
|
35
|
+
* · **转**(调用方在决定体里陈述的事实):决定体的 `decidedBy:"policy"` 经 {@link liveHostSource} 落成这一格的
|
|
36
36
|
* `"policy"` = 答这张卡的是**这台部署自己的策略**,不是人。wire 上的词只有 {@link APPROVAL_DECIDED_BY} 两个,
|
|
37
37
|
* 映射只有那一只函数 —— 这一格的「转」不再是一个原样过境的串。
|
|
38
38
|
* 逐词(词表属主恒是 core):
|
|
@@ -53,6 +53,15 @@ import { type AskOutcome, type HostDecider, type PermissionResult, type ResumeOu
|
|
|
53
53
|
export type HostSettledBy = NonNullable<Extract<AskOutcome, {
|
|
54
54
|
allow: boolean;
|
|
55
55
|
}>["settledBy"]>;
|
|
56
|
+
/**
|
|
57
|
+
* **同步座的宿主自报**,一只值 —— `AskOutcome` 对象臂上 `settledBy` 与 `decidedVia`(core 7.61.0 [ref])这两格,交引擎时**原样摊开**
|
|
58
|
+
* (`{ allow, ...source }`),所以结算腿、判决、行读回都传这一只、不各传两格。`decidedVia` 只在 `settledBy: "policy"` 旁
|
|
59
|
+
* (core 的同一条律;本仓两个产地都守它:决定体经 {@link liveHostSource},问账行经店门 `assertDecideSource`);本仓自己的窗只说 `{ settledBy: "timeout" }`。
|
|
60
|
+
*/
|
|
61
|
+
export type HostSource = {
|
|
62
|
+
readonly settledBy: HostSettledBy;
|
|
63
|
+
readonly decidedVia?: DecidedVia;
|
|
64
|
+
};
|
|
56
65
|
/** 直接取 core `ResumeOutcome` policy_ask 臂的 `hostDecision` 型 —— 本仓不复述那几个词(第二铸点)。它的 `decidedBy` 就是 core 的 `HostDecider`
|
|
57
66
|
* (core 7.45.0 [ref]:三个型位读同一只闭集)。**谁结束了这次等待**的闭集只有 core 包根那一只(`HOST_DECIDERS` / `HostDecider` / `isHostDecider`)。 */
|
|
58
67
|
export type HostDecision = Extract<ResumeOutcome, {
|
|
@@ -62,9 +71,9 @@ export type HostDecision = Extract<ResumeOutcome, {
|
|
|
62
71
|
* ══ 决定体的出处位 `decidedBy`:一只闭集、一只读法、一只映射 ═══════════════════════════════════════════════
|
|
63
72
|
*
|
|
64
73
|
* 规则一句话:**一个审批决定用一只键说是谁做的** —— `decidedBy ∈ {"person","policy"}`,缺席 = `"person"`;读它的只有
|
|
65
|
-
* {@link
|
|
74
|
+
* {@link readDecider}(三条工具审批决定口共读:live `POST /v1/tool-approvals/:id/respond`、耐久 ask 账
|
|
66
75
|
* `POST /v1/tasks/:id/asks/:askId/decision`、耐久 `POST /v1/approvals/:sessionId/decide`),把它交给引擎的只有
|
|
67
|
-
* {@link
|
|
76
|
+
* {@link liveHostSource}(同步座 `AskOutcome.settledBy`)与 {@link hostDecisionFacts}(停驻座 `hostDecision.decidedBy`)。
|
|
68
77
|
* · 词与决定动词**正交**:策略可以放行也可以拒(core 7.39.0 起引擎铸 `policy_allowed` / `policy_refused`)。唯一的例外是
|
|
69
78
|
* 引擎自己的那一条 —— 一张要人的卡(`requiresRealApproval`)不归策略答,见 {@link heldAskOf}(判官是 core 的 `policyCannotAnswer`)。
|
|
70
79
|
* · `"sla_timeout"` **永不从 wire 收**:那是「等待到点、没人答」,只有本服务自己的截止扫说得出来(`approval-sweeps.ts`
|
|
@@ -80,10 +89,15 @@ declare const SWEEP_ONLY_DECIDER = "sla_timeout";
|
|
|
80
89
|
/** wire 上的出处词(决定体 `decidedBy` 的值域)= core 闭集去掉扫的那一个。 */
|
|
81
90
|
export type ApprovalDecidedBy = Exclude<HostDecider, typeof SWEEP_ONLY_DECIDER>;
|
|
82
91
|
/** wire 闭集的运行期值:core `HOST_DECIDERS`([ref])去掉扫的那一个,顺序随 core。core 加一个词 ⇒ 它在这里自动在场(决定口的门照收;两只座的映射
|
|
83
|
-
* {@link
|
|
92
|
+
* {@link liveHostSource} 的穷举 `switch` 编译红,由人裁它在同步座上怎么落)。 */
|
|
84
93
|
export declare const APPROVAL_DECIDED_BY: readonly ApprovalDecidedBy[];
|
|
85
|
-
/** 闭集外的拒句(
|
|
94
|
+
/** 闭集外的拒句(四条决定口同一句;码按各口既有约定,见 {@link readDecider})。成员表派生自 {@link APPROVAL_DECIDED_BY}(词表一个来源;今天字节与 7.118.0 同)。 */
|
|
86
95
|
export declare const DECIDED_BY_OUTSIDE_CLOSED_SET: string;
|
|
96
|
+
/**
|
|
97
|
+
* 机器座位 `decidedVia` 的拒句(四条决定口同一句,与 {@link DECIDED_BY_OUTSIDE_CLOSED_SET} 同码同位)。成员表派生自 core `DECIDED_VIA`。
|
|
98
|
+
* 一句覆盖两种拒:词表外(含 `null` / 空串 / 非串 —— 在场即陈述,与 `decidedBy` 同一条在场口径),与**旁边不是策略**(`decidedBy` 缺席 = 一个人,人的决定没有机器座位)。
|
|
99
|
+
*/
|
|
100
|
+
export declare const DECIDED_VIA_OUTSIDE_SEAT: string;
|
|
87
101
|
/** 直连门上的拒句(与 `remember` / `updatedInput` / `checkpointToken` 同族:键不在决策证明的签名载荷里)。 */
|
|
88
102
|
export declare const DECIDED_BY_NOT_IN_PROOF = "decidedBy is not supported on a direct-door worker (not covered by the decision proof)";
|
|
89
103
|
/** 一张要人的卡上策略的放行(两条决定口同一句;判官 core `policyCannotAnswer`,问形见 {@link heldAskOf})。 */
|
|
@@ -115,31 +129,51 @@ export interface RetiredDecisionKeyFacts {
|
|
|
115
129
|
};
|
|
116
130
|
}
|
|
117
131
|
export declare function retiredDecisionKeyFacts(body: unknown): RetiredDecisionKeyFacts | undefined;
|
|
118
|
-
/**
|
|
119
|
-
|
|
132
|
+
/**
|
|
133
|
+
* 决定体陈述的**决定者**:谁做的(`decidedBy`),以及 —— 只当答话的是这台部署的策略时 —— 哪一个机器座位答的(`decidedVia`,core 7.61.0 [ref]:
|
|
134
|
+
* `"policy"` = 部署自己的策略,`"permission_request_hook"` = 一只 PermissionRequest 钩子替部署答的;缺席与 `"policy"` 同读)。
|
|
135
|
+
* 只有 {@link readDecider} 铸它;两只座各一只映射({@link liveHostSource} / {@link hostDecisionFacts})。
|
|
136
|
+
*/
|
|
137
|
+
export interface ApprovalDecider {
|
|
138
|
+
readonly decidedBy: ApprovalDecidedBy;
|
|
139
|
+
readonly decidedVia?: DecidedVia;
|
|
140
|
+
}
|
|
141
|
+
/** {@link readDecider} 的判决(调用点做穷举 `switch` + `never` 兜底,各按自己的码约定发出;`outside_closed_set` 自带拒句 —— 哪一格越界由读法说,口不猜)。 */
|
|
142
|
+
export type DeciderReading = {
|
|
120
143
|
kind: "accepted";
|
|
121
|
-
|
|
144
|
+
decider: ApprovalDecider;
|
|
122
145
|
} | {
|
|
123
146
|
kind: "not_in_proof";
|
|
124
147
|
} | {
|
|
125
148
|
kind: "outside_closed_set";
|
|
149
|
+
message: string;
|
|
126
150
|
};
|
|
127
151
|
/**
|
|
128
|
-
*
|
|
129
|
-
* ① 缺席 ⇒ `"person"`(缺席律:一条不说话的决定就是一个人按的 —— 与修前逐字同形);
|
|
130
|
-
* ②
|
|
152
|
+
* 决定体决定者两格(`decidedBy` + `decidedVia`)的**唯一读法**。次序即契约:
|
|
153
|
+
* ① `decidedBy` 缺席 ⇒ `"person"`(缺席律:一条不说话的决定就是一个人按的 —— 与修前逐字同形);
|
|
154
|
+
* ② 直连门在场且 `decidedBy` 在场 ⇒ `not_in_proof`(不论值:它不在签名载荷里,能改线上字节的一方可以把人翻成策略;
|
|
131
155
|
* 第二参给字面 `false` 的调用点 = 那条口没有决策证明,live `/respond` 即此);
|
|
132
|
-
* ③
|
|
156
|
+
* ③ `decidedBy` 闭集外(`"sla_timeout"`、结算词、别的串、非串、`null`)⇒ `outside_closed_set`({@link DECIDED_BY_OUTSIDE_CLOSED_SET});
|
|
157
|
+
* ④ `decidedVia` 在场(`undefined` 之外一律算在场,`null` 也是 —— 与 `decidedBy` 同一条在场口径)而它不在 core `DECIDED_VIA` 里,或旁边的决定者
|
|
158
|
+
* 没有座位(人;含 ① 落成的缺席 person ⇒ 直连门上只带 `decidedVia` 也走这一臂,直连门从不受理它)⇒ `outside_closed_set`({@link DECIDED_VIA_OUTSIDE_SEAT});
|
|
159
|
+
* ⑤ 受理:`{ decidedBy, decidedVia? }`(座位缺席 ⇒ 读数里没有这一格)。
|
|
160
|
+
* 问账行上 `decided_via` 列的 NULL 不经本读法:那是店的读口,NULL ⇒ 缺席(旧行与不带座位的决定同读 = 部署自己的策略)。
|
|
133
161
|
*/
|
|
134
|
-
export declare function
|
|
162
|
+
export declare function readDecider(body: {
|
|
163
|
+
readonly decidedBy?: unknown;
|
|
164
|
+
readonly decidedVia?: unknown;
|
|
165
|
+
}, directDoorActive: false): Exclude<DeciderReading, {
|
|
135
166
|
kind: "not_in_proof";
|
|
136
167
|
}>;
|
|
137
|
-
export declare function
|
|
168
|
+
export declare function readDecider(body: {
|
|
169
|
+
readonly decidedBy?: unknown;
|
|
170
|
+
readonly decidedVia?: unknown;
|
|
171
|
+
}, directDoorActive: boolean): DeciderReading;
|
|
138
172
|
/**
|
|
139
|
-
* **同步座的映射**(`AskOutcome
|
|
140
|
-
*
|
|
173
|
+
* **同步座的映射**(`AskOutcome` 的 `settledBy` / `decidedVia`;问账行的 `settled_by` / `decided_via` 两列存的也是这一只值):`person` ⇒ 不自报
|
|
174
|
+
* (引擎缺席即按人读,与修前逐字同形 —— 人的放行仍是裸 `true`),`policy` ⇒ `{ settledBy: "policy", decidedVia? }`。总函数,无 cast。
|
|
141
175
|
*/
|
|
142
|
-
export declare function
|
|
176
|
+
export declare function liveHostSource(decider: ApprovalDecider): HostSource | undefined;
|
|
143
177
|
/**
|
|
144
178
|
* 引擎自己的那一条例外:**一张要人的卡不归策略答** —— 判官是 core 的 `policyCannotAnswer(question, decidedBy)`([ref] 包根导出;引擎的续跑入口、
|
|
145
179
|
* 执行带与同步座读同一条)。本仓**只在自己的副作用先于引擎落地的两条口**上于裁决之前问它(live `/respond`:会话放行 / 常驻规则 / 一条按 approve
|
|
@@ -155,7 +189,7 @@ export declare function heldAskOf(demandsAPerson: boolean): Extract<PermissionRe
|
|
|
155
189
|
/**
|
|
156
190
|
* 一次 decide 的**停驻座事实**,一处成形(耐久 `/decide` 与活卡 PARKED 赎回席共用;截止扫直接铸 `{decidedBy:"sla_timeout"}`)。
|
|
157
191
|
*
|
|
158
|
-
* `decidedBy` = 已经过 {@link
|
|
192
|
+
* `decidedBy` = 已经过 {@link readDecider} 的 wire 词:两个词在停驻座上**就是它自己**(`person` / `policy`,
|
|
159
193
|
* 两只词表的交集由上面的编译期门证),所以这里没有 cast,也没有缺席律(缺席已在读法里落成 `"person"`)。
|
|
160
194
|
*
|
|
161
195
|
* `principal` = 那条腿已经验过身份的决策者(缺席 = 匿名档的部署,通道确实没有名字可报)。
|
|
@@ -167,6 +201,6 @@ export declare function heldAskOf(demandsAPerson: boolean): Extract<PermissionRe
|
|
|
167
201
|
* 🔴 归属**不按词分岔**:无论 core 的记录怎么写,**本服务观察到的事实**不变:送出这次请求的那把已验证身份是真的,本仓的
|
|
168
202
|
* 迟到结算账本(`approval-ask-audit-store.ts`)记的就是它。按词删一格 = 给账本挖一个只在某个词上出现的洞。
|
|
169
203
|
*/
|
|
170
|
-
export declare function hostDecisionFacts(
|
|
204
|
+
export declare function hostDecisionFacts(decider: ApprovalDecider, principal: string | undefined): HostDecision;
|
|
171
205
|
export {};
|
|
172
206
|
//# sourceMappingURL=host-decision.d.ts.map
|
package/dist/host-decision.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { HOST_DECIDERS, isHostDecider, screenApproverAttribution } from "@sema-agent/core";
|
|
1
|
+
import { DECIDED_VIA, HOST_DECIDERS, isHostDecider, screenApproverAttribution } from "@sema-agent/core";
|
|
2
2
|
const SWEEP_ONLY_DECIDER = "sla_timeout";
|
|
3
3
|
function isApprovalDecidedBy(v) {
|
|
4
4
|
return isHostDecider(v) && v !== SWEEP_ONLY_DECIDER;
|
|
5
5
|
}
|
|
6
6
|
export const APPROVAL_DECIDED_BY = Object.freeze(HOST_DECIDERS.filter(isApprovalDecidedBy));
|
|
7
7
|
export const DECIDED_BY_OUTSIDE_CLOSED_SET = `decidedBy must be ${APPROVAL_DECIDED_BY.map((w) => `"${w}"`).join(" or ")} when present — who made this decision: a person, or this deployment's own policy (absent = "person"); "${SWEEP_ONLY_DECIDER}" is never accepted from a caller (only this server's own deadline sweep says a wait ended with nobody answering)`;
|
|
8
|
+
export const DECIDED_VIA_OUTSIDE_SEAT = `decidedVia must be ${DECIDED_VIA.map((w) => `"${w}"`).join(" or ")} when present, and only beside decidedBy "policy" — it names which machine seat answered for this deployment's policy (absent = the deployment's own policy); a person's decision has no seat`;
|
|
8
9
|
export const DECIDED_BY_NOT_IN_PROOF = "decidedBy is not supported on a direct-door worker (not covered by the decision proof)";
|
|
9
10
|
export const POLICY_CANNOT_ALLOW_PERSON_ASK = 'decidedBy "policy" cannot allow this ask — it demands a person\'s approval (risk.requiresRealApproval), and a policy\'s answer is not a person\'s; answer it as a person, or refuse it as the policy';
|
|
10
11
|
export const SETTLED_BY_RETIRED = 'settledBy is retired — send decidedBy: "person" | "policy" (who made this decision; absent = "person"); ignoring it would record a policy\'s answer as a person\'s';
|
|
@@ -14,30 +15,57 @@ export function retiredDecisionKeyFacts(body) {
|
|
|
14
15
|
return undefined;
|
|
15
16
|
return body[RETIRED_DECISION_KEY] !== undefined ? { error: SETTLED_BY_RETIRED, extra: { field: RETIRED_DECISION_KEY } } : undefined;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
if (raw === undefined)
|
|
19
|
-
return { kind: "accepted", decidedBy: "person" };
|
|
20
|
-
if (directDoorActive)
|
|
21
|
-
return { kind: "not_in_proof" };
|
|
22
|
-
return isApprovalDecidedBy(raw) ? { kind: "accepted", decidedBy: raw } : { kind: "outside_closed_set" };
|
|
23
|
-
}
|
|
24
|
-
export function liveSettledBy(decidedBy) {
|
|
18
|
+
function deciderHasSeat(decidedBy) {
|
|
25
19
|
switch (decidedBy) {
|
|
26
20
|
case "person":
|
|
27
|
-
return
|
|
21
|
+
return false;
|
|
28
22
|
case "policy":
|
|
29
|
-
return
|
|
23
|
+
return true;
|
|
30
24
|
default: {
|
|
31
25
|
const unreachable = decidedBy;
|
|
32
|
-
throw new Error(`
|
|
26
|
+
throw new Error(`deciderHasSeat: unknown decidedBy ${String(unreachable)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function readDecider(body, directDoorActive) {
|
|
31
|
+
let decidedBy = "person";
|
|
32
|
+
const rawBy = body.decidedBy;
|
|
33
|
+
if (rawBy !== undefined) {
|
|
34
|
+
if (directDoorActive)
|
|
35
|
+
return { kind: "not_in_proof" };
|
|
36
|
+
if (!isApprovalDecidedBy(rawBy))
|
|
37
|
+
return { kind: "outside_closed_set", message: DECIDED_BY_OUTSIDE_CLOSED_SET };
|
|
38
|
+
decidedBy = rawBy;
|
|
39
|
+
}
|
|
40
|
+
const rawVia = body.decidedVia;
|
|
41
|
+
if (rawVia === undefined)
|
|
42
|
+
return { kind: "accepted", decider: { decidedBy } };
|
|
43
|
+
const via = DECIDED_VIA.find((w) => w === rawVia);
|
|
44
|
+
if (via === undefined || !deciderHasSeat(decidedBy))
|
|
45
|
+
return { kind: "outside_closed_set", message: DECIDED_VIA_OUTSIDE_SEAT };
|
|
46
|
+
return { kind: "accepted", decider: { decidedBy, decidedVia: via } };
|
|
47
|
+
}
|
|
48
|
+
export function liveHostSource(decider) {
|
|
49
|
+
switch (decider.decidedBy) {
|
|
50
|
+
case "person":
|
|
51
|
+
return undefined;
|
|
52
|
+
case "policy":
|
|
53
|
+
return { settledBy: "policy", ...(decider.decidedVia !== undefined ? { decidedVia: decider.decidedVia } : {}) };
|
|
54
|
+
default: {
|
|
55
|
+
const unreachable = decider.decidedBy;
|
|
56
|
+
throw new Error(`liveHostSource: unknown decidedBy ${String(unreachable)}`);
|
|
33
57
|
}
|
|
34
58
|
}
|
|
35
59
|
}
|
|
36
60
|
export function heldAskOf(demandsAPerson) {
|
|
37
61
|
return demandsAPerson ? { action: "ask", requiresRealApproval: true } : { action: "ask" };
|
|
38
62
|
}
|
|
39
|
-
export function hostDecisionFacts(
|
|
63
|
+
export function hostDecisionFacts(decider, principal) {
|
|
40
64
|
const screened = screenApproverAttribution(principal);
|
|
41
|
-
return {
|
|
65
|
+
return {
|
|
66
|
+
decidedBy: decider.decidedBy,
|
|
67
|
+
...(screened.approver !== undefined ? { approver: screened.approver } : {}),
|
|
68
|
+
...(decider.decidedVia !== undefined ? { decidedVia: decider.decidedVia } : {}),
|
|
69
|
+
};
|
|
42
70
|
}
|
|
43
71
|
//# sourceMappingURL=host-decision.js.map
|
package/dist/http/admission.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export declare function createAdmission(ctx: {
|
|
|
68
68
|
deps: FlatServiceDeps;
|
|
69
69
|
runnerFor: FlatServiceDeps["runnerFor"];
|
|
70
70
|
} & Pick<RouteHelpers, "readJson">): {
|
|
71
|
-
prepareSpec: (req: IncomingMessage, res: ServerResponse, bodyIn?: TaskRequestBody, onTypedFailure?: (err: HttpError) => void,
|
|
71
|
+
prepareSpec: (req: IncomingMessage, res: ServerResponse, bodyIn?: TaskRequestBody, onTypedFailure?: (err: HttpError) => void, legRoute?: {
|
|
72
72
|
liveStream?: boolean;
|
|
73
73
|
}) => Promise<PreparedTaskSubmission | null>;
|
|
74
74
|
};
|
package/dist/http/admission.js
CHANGED
|
@@ -20,6 +20,7 @@ import { echoCallerText, quotedCallerText } from "../caller-text-echo.js";
|
|
|
20
20
|
import { MAX_ADDITIONAL_DIRS } from "../task-cwd.js";
|
|
21
21
|
import { verifyRoundsFromBody } from "./verify-rounds.js";
|
|
22
22
|
import { skillCarriesRetiredManifest } from "./retired-skill-manifest.js";
|
|
23
|
+
import { legFactsOf } from "../leg-facts.js";
|
|
23
24
|
import { resolveExecutionContext, stampSpecRoot } from "./execution-context.js";
|
|
24
25
|
import { callerTrust, requestTrustRefusal } from "./caller-trust.js";
|
|
25
26
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
@@ -115,7 +116,7 @@ function hostPathListRefusal(field, raw) {
|
|
|
115
116
|
const modelIdOf = (model) => (typeof model === "string" ? model : model?.id);
|
|
116
117
|
export function createAdmission(ctx) {
|
|
117
118
|
const { deps, readJson, runnerFor } = ctx;
|
|
118
|
-
async function prepareSpec(req, res, bodyIn, onTypedFailure,
|
|
119
|
+
async function prepareSpec(req, res, bodyIn, onTypedFailure, legRoute) {
|
|
119
120
|
const body = bodyIn ?? (await readJson(req));
|
|
120
121
|
{
|
|
121
122
|
const refusal = refuseSubmitBody(sqlWallsOf(deps.sqlEngine), body);
|
|
@@ -744,13 +745,16 @@ export function createAdmission(ctx) {
|
|
|
744
745
|
const auth = await deps.authorize({ req, body: admitted });
|
|
745
746
|
const execution = await resolveExecutionContext(deps, { kind: "fresh", root: auth.sessionId, principal: auth.principal, judged: auth.head, req, deviceId: admitted.deviceId });
|
|
746
747
|
const engineNotices = [];
|
|
747
|
-
const
|
|
748
|
+
const resolved = await deps.resolveSpec(admitted, req, auth, { leg: "fresh", ...(legRoute?.liveStream === true ? { liveStream: true } : {}), onNotice: (n) => engineNotices.push(n), lease, execution });
|
|
749
|
+
const facts = legFactsOf(resolved);
|
|
750
|
+
const runner = runnerFor(resolved);
|
|
751
|
+
const spec = stampSpecRoot(resolved, execution);
|
|
748
752
|
const model = modelIdOf(spec.model);
|
|
749
753
|
const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { sessionId: spec.sessionId, objective: spec.objective, ...(model !== undefined ? { model } : {}) });
|
|
750
754
|
const clampNotice = buildEntryModeClampNotice(entryMode, spec.sessionId);
|
|
751
755
|
const pendingNotices = clampNotice !== undefined ? [clampNotice, ...engineNotices] : engineNotices;
|
|
752
756
|
const warnings = allowSelectorWarningsOf(admitted, spec.tools ?? []);
|
|
753
|
-
return { spec, runner:
|
|
757
|
+
return { spec, runner, legFacts: facts, auth, verify, cascade, jobId: admitted.jobId, body: admitted, acceptance, peerLease: lease, execution, ...(pendingNotices.length > 0 ? { engineNotices: pendingNotices } : {}), ...(warnings.length > 0 ? { warnings } : {}) };
|
|
754
758
|
}
|
|
755
759
|
catch (err) {
|
|
756
760
|
lease.release();
|
|
@@ -83,26 +83,29 @@ export function createApprovalSweeps(ctx) {
|
|
|
83
83
|
const parkSources = deps.backgroundAgentStore !== undefined || deps.workflowAgentSessionIndex !== undefined;
|
|
84
84
|
const cardOf = async (sessionId) => {
|
|
85
85
|
let token;
|
|
86
|
+
let binding = {};
|
|
86
87
|
try {
|
|
87
88
|
token = (await cs.findPendingTokenBySession(sessionId, undefined, { gateKinds: APPROVAL_GATE_KINDS })) ?? undefined;
|
|
88
89
|
if (!token)
|
|
89
|
-
return { token, parked: false, rev: undefined };
|
|
90
|
+
return { token, parked: false, rev: undefined, binding };
|
|
90
91
|
const cp = await cs.get(token);
|
|
91
92
|
if (!cp)
|
|
92
|
-
return { token, parked: false, rev: undefined };
|
|
93
|
+
return { token, parked: false, rev: undefined, binding };
|
|
94
|
+
const pa = cp.pendingAction;
|
|
95
|
+
binding = { checkpointToken: token, ...(pa.kind === "tool_approval" ? { boundCallId: pa.toolCallId, boundInputHash: pa.boundInputHash } : {}) };
|
|
93
96
|
const rev = checkpointRevOf(cp);
|
|
94
97
|
if (!parkSources)
|
|
95
|
-
return { token, parked: false, rev };
|
|
98
|
+
return { token, parked: false, rev, binding };
|
|
96
99
|
const origin = await findParkOrigin({
|
|
97
100
|
...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
|
|
98
101
|
...(deps.workflowAgentSessionIndex !== undefined && deps.workflowRunStore !== undefined
|
|
99
102
|
? { workflowIndex: deps.workflowAgentSessionIndex, workflowRunStore: deps.workflowRunStore }
|
|
100
103
|
: {}),
|
|
101
104
|
}, { token, scope: cp.scope, sessionId: cp.sessionId });
|
|
102
|
-
return { token, parked: origin !== undefined, rev };
|
|
105
|
+
return { token, parked: origin !== undefined, rev, binding };
|
|
103
106
|
}
|
|
104
107
|
catch {
|
|
105
|
-
return { token, parked: false, rev: undefined };
|
|
108
|
+
return { token, parked: false, rev: undefined, binding };
|
|
106
109
|
}
|
|
107
110
|
};
|
|
108
111
|
const storedBodyUnservable = async (sessionId) => {
|
|
@@ -140,7 +143,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
140
143
|
const replaySkipped = [];
|
|
141
144
|
const seenCards = new Set();
|
|
142
145
|
for (const { sessionId } of expired) {
|
|
143
|
-
const { token: cardToken, parked, rev: cardRev } = await cardOf(sessionId);
|
|
146
|
+
const { token: cardToken, parked, rev: cardRev, binding: cardBinding } = await cardOf(sessionId);
|
|
144
147
|
if (cardToken !== undefined)
|
|
145
148
|
seenCards.add(cardToken);
|
|
146
149
|
if (parked) {
|
|
@@ -154,7 +157,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
154
157
|
}
|
|
155
158
|
let out;
|
|
156
159
|
try {
|
|
157
|
-
out = await resumeCheckpoint(sessionId, "deny", "approval SLA expired — auto-denied", { decidedBy: "sla_timeout" });
|
|
160
|
+
out = await resumeCheckpoint(sessionId, "deny", "approval SLA expired — auto-denied", { decidedBy: "sla_timeout" }, undefined, undefined, cardBinding);
|
|
158
161
|
}
|
|
159
162
|
catch (e) {
|
|
160
163
|
deps.logger?.warn?.("deny_sweep_resume_failed", { sessionId, err: errText(e) });
|
|
@@ -228,7 +231,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
228
231
|
...(req.gateBoundInputHash !== null ? { boundInputHash: req.gateBoundInputHash } : {}),
|
|
229
232
|
...(req.updatedInput !== undefined ? { updatedInput: req.updatedInput } : {}),
|
|
230
233
|
};
|
|
231
|
-
const out = await resumeCheckpoint(cp.sessionId, req.decision, req.note, hostDecisionFacts(req.
|
|
234
|
+
const out = await resumeCheckpoint(cp.sessionId, req.decision, req.note, hostDecisionFacts(req.decider, req.principal), req.httpReq, undefined, binding, undefined, true, { principal: req.principal, explicitOperator: false });
|
|
232
235
|
return { status: out.status, body: out.body };
|
|
233
236
|
}
|
|
234
237
|
return { denyExpiredApprovals, redeemParkedAsk };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ## 律
|
|
5
5
|
* 契约(settings-schema `SAFETY_MERGE_SPEC.permissions` 的成员阶梯)按**层的信任档**准入每个成员:`allow` 放宽免问面(`loosen`),要 `global`;`deny` / `ask` 只收紧,
|
|
6
|
-
* 文件层最低档 `local` 就收。准入不读层:提交体是合并后的结果,层只在合并端(cli 本地)
|
|
6
|
+
* 文件层最低档 `local` 就收。准入不读层:提交体是合并后的结果,层只在合并端(cli 本地)存在;三桶的条目可以带出处层(`{ rule, layer }`),但那个层只选这条规则的
|
|
7
7
|
* `/` 读在哪个根(`permission-rule-list.ts` 的 `layerRuleRoot`),自报的层不可验、**不进本判**。所以本服务准入的「层」= **这次请求验过的凭据**:档只由部署形 + 身份来源
|
|
8
8
|
* + 登录令牌的 scope 角色推,调用方说什么(含条目上的层)都不改档。
|
|
9
9
|
*
|
|
@@ -15,7 +15,7 @@ import { persistSubmitCtx } from "./submit-ctx.js";
|
|
|
15
15
|
import { releaseClaimedRun } from "./claimed-run.js";
|
|
16
16
|
import { serviceVersion } from "../service-version.js";
|
|
17
17
|
import { headReadOf } from "../session-incarnation.js";
|
|
18
|
-
import { createSessionWorkspaceRecorder,
|
|
18
|
+
import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../session-workspace.js";
|
|
19
19
|
export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
20
20
|
const { deps } = ctx;
|
|
21
21
|
const { inflightRuns, preemptableRuns, steerableRuns } = ctx.registry;
|
|
@@ -97,7 +97,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
97
97
|
declaredFamilies: declaredApprovalFrameFamilies(args.prepared.body.settings),
|
|
98
98
|
executionLane: args.prepared.execution.lane,
|
|
99
99
|
}
|
|
100
|
-
: undefined, deps.checkpointStore, args.prepared.engineNotices, trustedInternals, deps.userInputLedger, args.prepared.acceptance, createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage),
|
|
100
|
+
: undefined, deps.checkpointStore, args.prepared.engineNotices, trustedInternals, deps.userInputLedger, args.prepared.acceptance, createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), args.prepared.legFacts?.sessionWitness), args.prepared.legFacts?.skills));
|
|
101
101
|
return { status: 202, body: { taskId, sessionId, status: "running", ...(args.prepared.warnings !== undefined ? { warnings: args.prepared.warnings } : {}) } };
|
|
102
102
|
}
|
|
103
103
|
//# sourceMappingURL=durable-run-leg.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Refusal } from "./refusal-body.js";
|
|
2
|
+
/** 一只引擎拒的应答:状态(出自 {@link resumeAtHttpStatus} 的单表)+ 拒(码 / 句,交 `sendError` 渲)。 */
|
|
3
|
+
export interface EngineRefusalAnswer {
|
|
4
|
+
readonly status: number;
|
|
5
|
+
readonly refusal: Refusal;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 一只抛出的引擎拒按提交腿同一张表读成应答;不是登记过的拒 ⇒ `undefined`。
|
|
9
|
+
* 组句与同步腿零工作拒体同形(`http/routes/tasks.ts` 的 `syncRefusalHttpStatus` 臂):码经 {@link outwardPlane}(词法外的码折叠)与 {@link httpErrorCode},
|
|
10
|
+
* 句经出口座的 `peer-declaration-sentence` 一行出门(引擎判官对 MCP 声明说的句子,逐字带着声明方写的名字;座读本仓脱敏器〔`trace/redact.ts`〕的提案跨度 —— 提交腿上同一句
|
|
11
|
+
* 过的 `redactErrorMessage` 读的是同一只脱敏器);两条腿的同码同状态同句由 `test/prepare-refusal-one-http-shape.test.ts` ⑤ 钉住。
|
|
12
|
+
* ⚠️ 唯一的字面差:提交腿的句子在这之前还过引擎自己把抛出物折成终局句的那一道出口过滤,读面的句子没有(本仓不调引擎的脱敏函数,台账 skip 行)——
|
|
13
|
+
* 句子里若出现凭据形的拼写,两面都把它盖住、字节都不外露,只是记号的写法不同(引擎 `[redacted]` / 本服务 `«redacted:…»`)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function engineRefusalAnswerOf(err: unknown): EngineRefusalAnswer | undefined;
|
|
16
|
+
//# sourceMappingURL=engine-refusal.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { errorCodeOf, errorText } from "../observability/err-text.js";
|
|
2
|
+
import { resumeAtHttpStatus } from "../runs.js";
|
|
3
|
+
import { outwardPlane } from "../terminal.js";
|
|
4
|
+
import { seatText } from "../trace/exit-seat.js";
|
|
5
|
+
import { httpErrorCode } from "./send.js";
|
|
6
|
+
export function engineRefusalAnswerOf(err) {
|
|
7
|
+
const code = errorCodeOf(err);
|
|
8
|
+
if (code === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
const terminal = { kind: "failed", code, message: errorText(err) };
|
|
11
|
+
const status = resumeAtHttpStatus({ terminal });
|
|
12
|
+
if (status === undefined)
|
|
13
|
+
return undefined;
|
|
14
|
+
const plane = outwardPlane(terminal);
|
|
15
|
+
return { status, refusal: { errorCode: httpErrorCode(status, plane.errorCode), message: seatText("peer-declaration-sentence", plane.errorMessage ?? "engine refusal") } };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=engine-refusal.js.map
|
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
* core 的另外三只 409 与 `ControlPlaneCorruptError` 全落 500 `internal.error` —— 「同一个 requestId 拿去抹另一个会话」这种**调用方自己
|
|
9
9
|
* 能改**的错被说成服务器坏了。两份分类 = 两个判官,少的那份恒是静默的一份。现在**码与状态**只在这里判一次,两面同读。
|
|
10
10
|
*
|
|
11
|
-
* ## 规则(三层,与 operator
|
|
12
|
-
* ⓪ 本服务的抹除店护栏拒 {@link MemoryErasureStoreUnjudged} —— 409 `memory.erasure_store_unjudged`(抹除落到部署的店之外的那只店:本部署此刻判不了
|
|
13
|
-
* 同一个 `requestId` 跨店的幂等;什么都没删)。文案按受众,同下一节的规则;
|
|
11
|
+
* ## 规则(三层,与 operator 面既有层序逐字同序)
|
|
14
12
|
* ① core 的**本口专属**码(闭集穷举 `switch`,见 {@link sentCoreCode})—— 码与状态两面同;文案按受众(下一节,一条规则);
|
|
15
13
|
* ② **店形拒**两码 `memory.target_outside_store`(core 7.31.1 [ref])/ `memory.scope_home_not_directory`(core 7.44.0 [ref])—— 跨面 store 状态码,409、码原样
|
|
16
14
|
* (判据 `memory-store-refusal.ts` 一只;运维行 `memory_store_form_refused { code, path?, form? }` 两受众同一只铸点,路径只在那里);
|
|
@@ -3,7 +3,6 @@ import { logStoreFormRefusal, storeFormRefusalOf } from "../memory-store-refusal
|
|
|
3
3
|
import { sentMemoryStoreRefusal } from "./memory-store-refusal-reply.js";
|
|
4
4
|
import { sendError } from "./send.js";
|
|
5
5
|
import { assertNever } from "./route-ctx.js";
|
|
6
|
-
import { MemoryErasureStoreUnjudged } from "../memory-operator-faces.js";
|
|
7
6
|
function sentCoreCode(res, coreCode, coreMessage, audience) {
|
|
8
7
|
switch (coreCode) {
|
|
9
8
|
case "config.memory_erasure_request":
|
|
@@ -18,8 +17,11 @@ function sentCoreCode(res, coreCode, coreMessage, audience) {
|
|
|
18
17
|
case "memory.erasure_selector_mismatch":
|
|
19
18
|
sendError(res, 409, "memory.erasure_selector_mismatch", textFor(audience, coreMessage, "session memory erasure refused — this requestId already names a different erasure; resend with a new requestId"));
|
|
20
19
|
return true;
|
|
20
|
+
case "config.memory_scope_key":
|
|
21
|
+
sendError(res, 400, "request.memory_layer", textFor(audience, coreMessage, "session memory erasure refused — the request names a memory layer this deployment cannot read; correct it and resend"));
|
|
22
|
+
return true;
|
|
21
23
|
case "memory.erasure_census_incomplete":
|
|
22
|
-
sendError(res, 409, "memory.erasure_census_incomplete", textFor(audience, coreMessage, "session memory erasure refused —
|
|
24
|
+
sendError(res, 409, "memory.erasure_census_incomplete", textFor(audience, coreMessage, "session memory erasure refused — a memory store could not be read completely, so that store erased nothing; where this deployment keeps memory in two stores the other store may already have carried out this erasure — ask the operator to repair the store, then resend with the same requestId to complete it"));
|
|
23
25
|
return true;
|
|
24
26
|
default:
|
|
25
27
|
return false;
|
|
@@ -43,10 +45,6 @@ function codeOf(err) {
|
|
|
43
45
|
return typeof code === "string" ? code : undefined;
|
|
44
46
|
}
|
|
45
47
|
export function sentMemoryComplianceFailure(res, err, audience, logger) {
|
|
46
|
-
if (err instanceof MemoryErasureStoreUnjudged) {
|
|
47
|
-
sendError(res, 409, "memory.erasure_store_unjudged", textFor(audience, err.message, "session memory erasure refused — this erasure reaches memory kept in this deployment's second memory store, which this build does not erase from (an erasure requestId is judged on the deployment's main store only); nothing was erased — it becomes available with the next engine update, or ask the operator"));
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
48
|
const coreCode = codeOf(err);
|
|
51
49
|
if (coreCode !== undefined && sentCoreCode(res, coreCode, err instanceof Error ? err.message : String(err), audience))
|
|
52
50
|
return true;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* {@link readOwnedRun} / {@link readOwnedSession} 并删两只 helper;裸 store 读口按文件计数钉在 `test/owner-gate-single-owner.test.ts`(增即红)。
|
|
15
15
|
*/
|
|
16
16
|
import type { ServiceConfig } from "../config-types.js";
|
|
17
|
+
import type { Logger } from "../observability/logger.js";
|
|
17
18
|
import type { RunRecord } from "../plugins/store-contracts.js";
|
|
18
19
|
import { type OwnerAwareSessionStore, type Principal, type SessionCaller, type SessionOwnerCell } from "../security.js";
|
|
19
20
|
import { type IdentityWiring, type OperatorRoster } from "./principal-gate.js";
|
|
@@ -45,6 +46,16 @@ export declare function readOwnedRun(config: OwnerAccessConfig, runStore: {
|
|
|
45
46
|
export declare function readOwnedSession(w: IdentityWiring & {
|
|
46
47
|
readonly config: OwnerAccessConfig;
|
|
47
48
|
}, store: Pick<OwnerAwareSessionStore, "placementOf"> & Required<Pick<OwnerAwareSessionStore, "headOf">>, caller: SessionCaller, sessionId: string, arm: OwnerArm): Promise<SessionOwnerCell | undefined>;
|
|
49
|
+
/**
|
|
50
|
+
* 🔴 **会话读面的属主读的第三个结局**:有主 / 无此行 / **属主读不出**(店交出具名拒 {@link SessionOwnerUnreadableError} —— 会话的行事实没有落下,补铸它要读的存档又用不了;
|
|
51
|
+
* 谁也判不了「这是不是你的会话」)。第三个读作第二个:**与「没有这一行」同一个值**(`undefined`),调用点落的就是它们对「不存在 / 非属主」的那一臂 ——
|
|
52
|
+
* 读者拿不到「这个 id 在盘上、且坏了」这件事(对非属主、对属主、对不存在的会话是同一只 404 `not_found.session`,同码同文),也就没有第二种拒绝形可漏存在性。
|
|
53
|
+
* 原故障(码与句)进运维日志一行 `session_owner_unreadable`:这是拒,不是兜底,不走 `recordFailOpen`;被折的读不再经全局兜底,日志里不能因此少一行。
|
|
54
|
+
*
|
|
55
|
+
* 只折这一个具名拒:别的读故障(I/O 级、边档整只拒、SQL 店的读故障、别的 `SessionError`)一概照旧上抛 —— 折的是「属主读不出」这件确定的事,不是「头读抛了」。
|
|
56
|
+
* 读面的所有属主读(会话视图三口共段 / 头探针 / 事件流 / 执行放置)经这一只;写口的属主读不在本只射程(它们对这一形仍答原故障的 500,契约 §17.2)。
|
|
57
|
+
*/
|
|
58
|
+
export declare function foldOwnerUnreadable<T>(logger: Pick<Logger, "warn"> | undefined, face: string, sessionId: string, read: () => Promise<T | undefined>): Promise<T | undefined>;
|
|
48
59
|
/**
|
|
49
60
|
* leader 登记表的判官形(boot 装配一次,`createLeaderEndpoint` 的必填席)—— leader 模块不再自判属主(修前它手抄 `owner == null || owner === requester`,
|
|
50
61
|
* 缺 C8 窄互认臂而注称 mirrors runOwnerOk)。leader 腿的调用方身份经幂等身份结构(`IdemCaller.principal`,即 `gatedPrincipal` 的回值)转手而来,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { mintDurableOwnerKey, mintPrincipal, sessionOwnerCellOf } from "../security.js";
|
|
2
|
+
import { openFaceFaultWire, SessionOwnerUnreadableError } from "../session-error-wire.js";
|
|
3
|
+
import { seatText } from "../trace/exit-seat.js";
|
|
2
4
|
import { explicitOperatorOk, runOwnerReadable, sessionOwnerReadable } from "./principal-gate.js";
|
|
3
5
|
function operatorArmHolds(config, principal, arm) {
|
|
4
6
|
switch (arm) {
|
|
@@ -36,6 +38,17 @@ export async function readOwnedSession(w, store, caller, sessionId, arm) {
|
|
|
36
38
|
const cell = await sessionOwnerCellOf(store, sessionId, owner);
|
|
37
39
|
return operatorArmHolds(w.config, caller.principal, arm) || sessionOwnerReadable(caller, cell, w) ? cell : undefined;
|
|
38
40
|
}
|
|
41
|
+
export async function foldOwnerUnreadable(logger, face, sessionId, read) {
|
|
42
|
+
try {
|
|
43
|
+
return await read();
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if (!(e instanceof SessionOwnerUnreadableError))
|
|
47
|
+
throw e;
|
|
48
|
+
logger?.warn("session_owner_unreadable", { face, sessionId, errorCode: openFaceFaultWire(e).errorCode, err: seatText("operator-log-text", e.message) });
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
39
52
|
export function createLeaderRunVisibility(config) {
|
|
40
53
|
return (principal, owner) => runRowReadable(config, principal == null ? undefined : mintPrincipal(principal), owner ?? null, "owner");
|
|
41
54
|
}
|