@sema-agent/server 7.151.0 → 7.152.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 +266 -0
- package/README.md +15 -3
- package/README.zh-CN.md +10 -2
- package/USAGE.md +93 -64
- package/deploy/sema-up/README.md +5 -4
- package/deploy/sema-up/cluster-up.sh +6 -6
- package/deploy/sema-up/kube-up.sh +5 -5
- package/deploy/sema-up/lib/flagshape.sh +36 -8
- package/deploy/sema-up/machines.example.yaml +3 -1
- package/deploy/sema-up/sema-up.sh +22 -16
- package/dist/approval-ask-audit-store.d.ts +1 -1
- package/dist/approval-ask-machine.d.ts +4 -10
- package/dist/approval-ask-retention.d.ts +1 -1
- package/dist/approval-card.d.ts +10 -12
- package/dist/approval-gate-currency.d.ts +8 -8
- package/dist/approval-gate-currency.js +3 -4
- package/dist/approval-hmac.d.ts +17 -1
- package/dist/approval-hmac.js +7 -1
- package/dist/approval-queue-kind.d.ts +1 -1
- package/dist/approval.d.ts +2 -2
- package/dist/approval.js +2 -2
- package/dist/bind-host.d.ts +1 -3
- package/dist/bind-host.js +2 -3
- package/dist/boot/coordinators.d.ts +1 -1
- package/dist/boot/coordinators.js +9 -10
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/execution-env.d.ts +1 -0
- package/dist/boot/execution-env.js +31 -5
- package/dist/boot/leader.d.ts +31 -30
- package/dist/boot/leader.js +38 -28
- package/dist/boot/leg-path-adjudication.d.ts +40 -0
- package/dist/boot/leg-path-adjudication.js +18 -0
- package/dist/boot/parked-revive-gate.js +3 -15
- package/dist/boot/reapers.d.ts +1 -1
- package/dist/boot/reapers.js +1 -1
- package/dist/boot/resolve-spec.js +12 -26
- package/dist/boot/runner-deps.d.ts +39 -35
- package/dist/boot/runner-deps.js +92 -89
- package/dist/boot/shutdown.d.ts +6 -0
- package/dist/boot/shutdown.js +10 -4
- package/dist/boot/stage-02-web-admin-pull.js +4 -5
- package/dist/boot/stage-05-execution-env.js +2 -1
- package/dist/boot/stage-06-runners.d.ts +2 -0
- package/dist/boot/stage-06-runners.js +10 -4
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +8 -0
- package/dist/boot/stage-10-http-server.js +11 -15
- package/dist/boot/stores.js +7 -4
- package/dist/boot/web-admin.js +2 -7
- package/dist/capabilities/approvals-push.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +28 -13
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capability-facts.d.ts +22 -20
- package/dist/capability-facts.js +18 -34
- package/dist/card-rule-offers.d.ts +32 -0
- package/dist/card-rule-offers.js +130 -0
- package/dist/config-catalog.js +12 -11
- package/dist/config-dir-trust.js +4 -2
- package/dist/config-invariants.d.ts +18 -7
- package/dist/config-invariants.js +51 -8
- package/dist/config-lkg.d.ts +5 -19
- package/dist/config-lkg.js +8 -62
- package/dist/config-provider.d.ts +1 -1
- package/dist/config-root-migration.d.ts +2 -2
- package/dist/config-types.d.ts +28 -38
- package/dist/config.d.ts +7 -5
- package/dist/config.js +55 -16
- package/dist/control-socket.d.ts +51 -0
- package/dist/control-socket.js +115 -0
- package/dist/deployment-governance.d.ts +2 -1
- package/dist/device-ws-hub.d.ts +7 -0
- package/dist/device-ws-hub.js +7 -1
- package/dist/elicitation.js +2 -2
- package/dist/entry-problems.d.ts +40 -3
- package/dist/entry-problems.js +3 -1
- package/dist/execution-env-refusal.d.ts +8 -2
- package/dist/execution-env-refusal.js +3 -0
- package/dist/fleet/engine-signal.d.ts +12 -29
- package/dist/fleet/engine-signal.js +1 -7
- package/dist/fleet-client.js +2 -5
- package/dist/gate-settlement.d.ts +1 -8
- package/dist/gate-settlement.js +0 -4
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +2 -3
- package/dist/hitl-delivery.js +0 -2
- package/dist/hooks/hook-llm.js +4 -2
- package/dist/hooks/hook-runner.d.ts +3 -2
- package/dist/hooks/hook-runner.js +16 -1
- package/dist/host-decision.d.ts +11 -11
- package/dist/http/active-run-conflict.d.ts +35 -2
- package/dist/http/active-run-conflict.js +48 -19
- package/dist/http/approval-sweeps.d.ts +1 -1
- package/dist/http/approval-sweeps.js +5 -2
- package/dist/http/checkpoint-ctx.d.ts +2 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/dispatch.d.ts +6 -2
- package/dist/http/dispatch.js +25 -5
- package/dist/http/durable-run-leg.js +0 -1
- package/dist/http/health-reader.d.ts +16 -9
- package/dist/http/health-reader.js +2 -1
- package/dist/http/health-self-report.js +1 -1
- package/dist/http/owner-access.d.ts +2 -3
- package/dist/http/owner-access.js +3 -4
- package/dist/http/principal-gate.d.ts +49 -21
- package/dist/http/principal-gate.js +61 -11
- package/dist/http/request-channel.d.ts +23 -0
- package/dist/http/request-channel.js +9 -0
- package/dist/http/request-guards.js +17 -2
- package/dist/http/resume-ctx-read.js +20 -3
- package/dist/http/resume-legs.d.ts +2 -2
- package/dist/http/resume-legs.js +111 -41
- package/dist/http/route-ctx.d.ts +73 -7
- package/dist/http/route-ctx.js +17 -0
- package/dist/http/route-table.d.ts +4 -0
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/admin-config-refresh.js +3 -3
- package/dist/http/routes/admin-drain.d.ts +3 -0
- package/dist/http/routes/admin-drain.js +7 -7
- package/dist/http/routes/admin-memory-optout.js +3 -3
- package/dist/http/routes/adoption.js +3 -5
- package/dist/http/routes/approvals-assistant.d.ts +2 -1
- package/dist/http/routes/approvals-assistant.js +95 -48
- package/dist/http/routes/capabilities.d.ts +4 -0
- package/dist/http/routes/capabilities.js +30 -5
- package/dist/http/routes/config-catalog.js +2 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +19 -7
- package/dist/http/routes/images.js +10 -10
- package/dist/http/routes/memory-bundle.js +3 -3
- package/dist/http/routes/memory-compliance.js +3 -3
- package/dist/http/routes/memory-consolidation.js +2 -2
- package/dist/http/routes/memory-origin.js +3 -3
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/observability.js +3 -3
- package/dist/http/routes/retention-ops.js +3 -3
- package/dist/http/routes/rules.js +5 -5
- package/dist/http/routes/run-permission-mode.d.ts +2 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +4 -9
- package/dist/http/routes/runs.js +73 -33
- package/dist/http/routes/session-sync.d.ts +18 -6
- package/dist/http/routes/session-sync.js +38 -36
- package/dist/http/routes/sessions.js +20 -9
- package/dist/http/routes/tasks.js +28 -37
- package/dist/http/routes/workflows.js +1 -1
- package/dist/http/server.d.ts +9 -3
- package/dist/http/server.js +15 -3
- package/dist/http/submit-ctx.d.ts +1 -1
- package/dist/http/submit-ctx.js +2 -0
- package/dist/human-facing-args.d.ts +33 -0
- package/dist/human-facing-args.js +33 -0
- package/dist/index.d.ts +1 -1
- package/dist/k8s-api-credentials.d.ts +29 -0
- package/dist/k8s-api-credentials.js +25 -0
- package/dist/lane-home-probe.d.ts +1 -1
- package/dist/lane-home-probe.js +2 -1
- package/dist/leader/diffup.d.ts +19 -0
- package/dist/leader/diffup.js +7 -0
- package/dist/leader/leader.d.ts +114 -10
- package/dist/leader/leader.js +69 -5
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/run-entry.d.ts +2 -1
- package/dist/leader/run-entry.js +1 -1
- package/dist/leader/wire.d.ts +42 -132
- package/dist/leader/wire.js +81 -67
- package/dist/memory-scope.d.ts +20 -9
- package/dist/memory-scope.js +15 -3
- package/dist/model-entry-headers.d.ts +9 -10
- package/dist/model-entry-headers.js +0 -4
- package/dist/model-entry-refusal.d.ts +7 -4
- package/dist/model-entry-refusal.js +11 -5
- package/dist/model-plane-face.d.ts +1 -1
- package/dist/model-plane-face.js +3 -1
- package/dist/model-route-endpoint.d.ts +13 -7
- package/dist/model-route-endpoint.js +14 -11
- package/dist/model-select.d.ts +22 -0
- package/dist/model-select.js +9 -0
- package/dist/model-unreachable-notice.d.ts +11 -0
- package/dist/model-unreachable-notice.js +4 -4
- package/dist/object-store-buckets.d.ts +640 -6
- package/dist/object-store-buckets.js +166 -0
- package/dist/observability/fail-open.d.ts +20 -16
- package/dist/observability/fail-open.js +20 -16
- package/dist/observability/logger.js +3 -1
- package/dist/operator-roster.d.ts +68 -0
- package/dist/operator-roster.js +32 -0
- package/dist/orchestration/workflow-park-decision.d.ts +1 -1
- package/dist/outbound-dispatcher.d.ts +71 -8
- package/dist/outbound-dispatcher.js +48 -7
- package/dist/owner-faces.d.ts +1 -1
- package/dist/parked-decide.d.ts +2 -2
- package/dist/permission-mode-admission.d.ts +59 -26
- package/dist/permission-mode-admission.js +37 -10
- package/dist/plugins/approval-ask-store-file.d.ts +4 -0
- package/dist/plugins/approval-ask-store-file.js +3 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
- package/dist/plugins/approval-ask-store-memory.js +7 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
- package/dist/plugins/approval-ask-store-sql.js +12 -1
- package/dist/plugins/blob-backend.d.ts +83 -7
- package/dist/plugins/blob-backend.js +57 -6
- package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
- package/dist/plugins/checkpoint-index-columns.js +3 -6
- package/dist/plugins/checkpoint-store-face.d.ts +119 -61
- package/dist/plugins/checkpoint-store-face.js +134 -55
- package/dist/plugins/landed-attendants.d.ts +2 -2
- package/dist/plugins/local-checkpoint-store.d.ts +13 -3
- package/dist/plugins/local-checkpoint-store.js +38 -49
- package/dist/plugins/one-time-migrations.d.ts +51 -21
- package/dist/plugins/one-time-migrations.js +67 -45
- package/dist/plugins/pg-pool.js +11 -9
- package/dist/plugins/remote-env-host.d.ts +29 -10
- package/dist/plugins/remote-env-host.js +99 -19
- package/dist/plugins/remote-env-k8s.d.ts +68 -2
- package/dist/plugins/remote-env-k8s.js +107 -24
- package/dist/plugins/replica-registration.d.ts +1 -1
- package/dist/plugins/replica-registration.js +1 -1
- package/dist/plugins/s3-presign.d.ts +23 -0
- package/dist/plugins/s3-presign.js +37 -3
- package/dist/plugins/scheduler-support.d.ts +2 -2
- package/dist/plugins/scheduler-support.js +0 -3
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-file-ledger.d.ts +2 -2
- package/dist/plugins/send-file-ledger.js +2 -2
- package/dist/plugins/send-user-file.d.ts +3 -4
- package/dist/plugins/send-user-file.js +56 -52
- package/dist/plugins/session-blob-claim-token.d.ts +15 -0
- package/dist/plugins/session-blob-claim-token.js +20 -0
- package/dist/plugins/session-blob-namespace.d.ts +46 -0
- package/dist/plugins/session-blob-namespace.js +116 -0
- package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
- package/dist/plugins/snapshot-bucket-presence.js +8 -2
- package/dist/plugins/sql-json-column.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +44 -2
- package/dist/plugins/store-backend.js +75 -53
- package/dist/plugins/task-attachment-store.d.ts +2 -1
- package/dist/plugins/task-attachment-store.js +5 -1
- package/dist/plugins/tidb-pool.js +19 -24
- package/dist/plugins/workspace-custody.d.ts +4 -3
- package/dist/question.d.ts +2 -1
- package/dist/question.js +5 -13
- package/dist/request-form.d.ts +6 -1
- package/dist/request-form.js +4 -1
- package/dist/rule-issue-reading.d.ts +1 -1
- package/dist/rules-consent.d.ts +1 -1
- package/dist/run-local.js +8 -9
- package/dist/runs.d.ts +17 -8
- package/dist/runs.js +15 -2
- package/dist/runtime-governance.d.ts +2 -2
- package/dist/sema-config-dir.d.ts +2 -1
- package/dist/session-sync.d.ts +27 -11
- package/dist/session-sync.js +15 -7
- package/dist/stores-sql/blob-index-sql.d.ts +30 -9
- package/dist/stores-sql/blob-index-sql.js +40 -21
- package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
- package/dist/stores-sql/checkpoint-store-sql.js +70 -146
- package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
- package/dist/stores-sql/file-history-store-sql.js +100 -28
- package/dist/stores-sql/retention-store-sql.js +3 -9
- package/dist/task-settings.d.ts +6 -5
- package/dist/task-settings.js +3 -3
- package/dist/tool-approval.d.ts +342 -518
- package/dist/tool-approval.js +498 -1207
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/ledger-events.d.ts +3 -3
- package/dist/trace/project.d.ts +2 -3
- package/dist/trace/redact.js +3 -2
- package/dist/trace/self-minted-wire.d.ts +287 -157
- package/dist/trace/self-minted-wire.js +93 -48
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/read-warnings.js +1 -1
- package/dist/web-admin-client/types.d.ts +1 -1
- package/dist/workspace-space.d.ts +10 -4
- package/dist/workspace-space.js +4 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
- package/package.json +6 -2
- package/scripts/embed-minimal.mjs +3 -3
- package/wire-changes/server@7.152.0.json +900 -0
- package/dist/config-d-migration.d.ts +0 -37
- package/dist/config-d-migration.js +0 -60
package/dist/boot/web-admin.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFile as fsReadFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { WEB_ADMIN_ENV
|
|
5
|
+
import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
|
|
6
6
|
import { stableStringify } from "@sema-agent/settings-schema/hash";
|
|
7
7
|
import { buildModelRefusalsSignal } from "../model-entry-refusal.js";
|
|
8
8
|
import { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, } from "@sema-agent/core";
|
|
@@ -523,12 +523,7 @@ export async function createWebAdminRuntime(ctx) {
|
|
|
523
523
|
}
|
|
524
524
|
if (read.migrated !== undefined) {
|
|
525
525
|
const m = read.migrated;
|
|
526
|
-
logger.info("config_lkg_migrated", {
|
|
527
|
-
to: lkgPath,
|
|
528
|
-
fromFormat: m.fromFormat,
|
|
529
|
-
...(m.note !== undefined ? { note: m.note } : {}),
|
|
530
|
-
receipts: m.receipts.map((r) => ({ migration: r.migration, document: r.document, path: formatConfigPath(r.path), appliedAs: r.appliedAs })),
|
|
531
|
-
});
|
|
526
|
+
logger.info("config_lkg_migrated", { to: lkgPath, fromFormat: m.fromFormat, note: m.note });
|
|
532
527
|
try {
|
|
533
528
|
await saveLkg(lkgPath, cc?.worker, identity, read.lkg.effective, read.lkg.etag);
|
|
534
529
|
}
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
*
|
|
23
23
|
* 🔴 **刻意不合并进 `approvals` 或 `toolApproval`**:
|
|
24
24
|
* · `approvals` 答的是「这条车道在不在」(它为真而本位为假的部署是**常态**:没开工具审批的那些);
|
|
25
|
-
* · `toolApproval` 答的是「活体工具审批 HITL
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* · `toolApproval` 答的是「活体工具审批 HITL 在不在」。协调器只在呈卡协议上场(含 park 设施 = checkpoint 底座)时构造,
|
|
26
|
+
* 所以生产装配上 `toolApproval` 为真蕴含 ①,本位与 `toolApproval` 同值;本位仍逐项对着腿的判据写合取(不读另一位的投影)。
|
|
27
|
+
* 三位(`toolApproval` / `streamApproval` / 本位)如今说的是同一件事 —— 合并成一位是另一件 wire 变更,不在这里做。
|
|
28
28
|
*
|
|
29
29
|
* ⚠️ **位为真只保证这条源在场**,不保证这一次有任何悬挂 ask、也不保证跨副本可见:live 半场是**进程内
|
|
30
30
|
* 投影**(与 `GET /v1/approvals` 的 `livePending` 逐字同限,非本件引入),durable park 行那一半才是
|
|
@@ -347,12 +347,26 @@ export interface ScenarioRuling {
|
|
|
347
347
|
*/
|
|
348
348
|
export type ScenarioRulingRead = ScenarioRuling | "unavailable" | undefined;
|
|
349
349
|
/**
|
|
350
|
-
*
|
|
351
|
-
* · `
|
|
352
|
-
* · `
|
|
353
|
-
*
|
|
350
|
+
* 一次请求生效的场景名**从哪一层来**(闭集,唯一属主;wire 上 `GET /v1/capabilities/scenario` 的 `source`、`state.scenario_assignment_unknown` 拒体的 `source` 读的都是它):
|
|
351
|
+
* · `request` —— 这次请求点了名(体 `scenario` / 场景头 `x-agent-scenario` / 读面的 `?scenario=`;点名读法 `request-form.ts` `scenarioRequestedBy`);
|
|
352
|
+
* · `ruling` —— 没点名,中心按主体指派的场景(`ScenarioRuling.scenario`);
|
|
353
|
+
* · `deployment` —— 没点名、也没有指派,部署自己写的缺省(`DEFAULT_SCENARIO` / config `defaultScenario`)。
|
|
354
|
+
* 新增一词 ⇒ 自铸登记 `selfMinted.scenarioSource` 的 `satisfies Record<ScenarioSource, …>` 编译红;链上两层另由 {@link danglingScenarioRefusal} 的穷尽 switch 判。
|
|
354
355
|
*/
|
|
355
|
-
export type
|
|
356
|
+
export type ScenarioSource = "request" | "ruling" | "deployment";
|
|
357
|
+
/**
|
|
358
|
+
* 指派链上的两层(请求没点名时走的链)—— 「名字悬空」只在这两层上成立:点名的不在册名是调用方的错,门先答 400 `scenario_unknown`,不走悬空判据。
|
|
359
|
+
* 所以 `state.scenario_assignment_unknown` 拒体的 `source` 只会是这两词。
|
|
360
|
+
*/
|
|
361
|
+
export type ScenarioChainSource = Exclude<ScenarioSource, "request">;
|
|
362
|
+
/**
|
|
363
|
+
* **门的答**(唯一一种形):这次请求生效的场景名(一定在册)与它来自哪一层。提交腿、`run-local` 与每一张读面读的都是这一只答;
|
|
364
|
+
* `GET /v1/capabilities/scenario` 的 200 体就是它(键名同 wire)。
|
|
365
|
+
*/
|
|
366
|
+
export interface ScenarioChoice {
|
|
367
|
+
readonly scenario: string;
|
|
368
|
+
readonly source: ScenarioSource;
|
|
369
|
+
}
|
|
356
370
|
/**
|
|
357
371
|
* 🔴 **悬空场景名的判据与拒句 —— 全仓唯一属主。**
|
|
358
372
|
*
|
|
@@ -365,10 +379,10 @@ export type ScenarioNameSource = "ruling" | "deployment";
|
|
|
365
379
|
* 返回 `undefined` = 在册;否则是**拒句**(点名名字与来源、给在册词表、说怎么修)。判据按**自有键**(`known` 是场景表的
|
|
366
380
|
* `Object.keys`,`constructor` / `__proto__` 不是场景名)。
|
|
367
381
|
*/
|
|
368
|
-
export declare function danglingScenarioRefusal(name: string, source:
|
|
382
|
+
export declare function danglingScenarioRefusal(name: string, source: ScenarioChainSource, known: readonly string[]): string | undefined;
|
|
369
383
|
/**
|
|
370
|
-
* resolveSpec 的执法入口(指派为默认、body.scenario 可换但限 allowlist)
|
|
371
|
-
* **在册**生效场景名 ——
|
|
384
|
+
* resolveSpec 的执法入口(指派为默认、body.scenario 可换但限 allowlist)。返回本请求的**门的答**({@link ScenarioChoice}):
|
|
385
|
+
* **在册**生效场景名 + 它来自哪一层 —— 名字一定在 `known` 里:下游直接按它取场景,没有「未知名 → default」的回退臂;来层在铸名的那一臂上同时写下,读面不另算。
|
|
372
386
|
* 读数 `ruling` 三态各一条出路({@link ScenarioRulingRead}),判序:
|
|
373
387
|
*
|
|
374
388
|
* 1. body.scenario 显式给出而不在册 ⇒ typed 400 `scenario_unknown`(调用方的错;词表面先于治理面,不等中心)。
|
|
@@ -383,7 +397,7 @@ export declare function danglingScenarioRefusal(name: string, source: ScenarioNa
|
|
|
383
397
|
*
|
|
384
398
|
* `principal` 只进运维日志,不参与判定。
|
|
385
399
|
*/
|
|
386
|
-
export declare function gateScenarioRequest(ruling: ScenarioRulingRead, bodyScenario: unknown, defaultScenario: string, known: readonly string[], logger?: Logger, principal?: string):
|
|
400
|
+
export declare function gateScenarioRequest(ruling: ScenarioRulingRead, bodyScenario: unknown, defaultScenario: string, known: readonly string[], logger?: Logger, principal?: string): ScenarioChoice;
|
|
387
401
|
/**
|
|
388
402
|
* 一条请求的场景名过门 —— {@link gateScenarioRequest} 的**唯一一处实参装配**。
|
|
389
403
|
*
|
|
@@ -393,8 +407,9 @@ export declare function gateScenarioRequest(ruling: ScenarioRulingRead, bodyScen
|
|
|
393
407
|
* 每个读口自己拼一遍这六个实参 = 第二只门:两口对「在册」「放行」「缺席取谁」各判各的,读口就开始对不上 run。
|
|
394
408
|
* 所以装配只在这里,读口持有的是**绑好的门**;凡回答这类问题的口,场景名都由门给 —— 点名与缺席都问。
|
|
395
409
|
*
|
|
396
|
-
* 契约:`(principal, requested) =>
|
|
397
|
-
* 读口不改写)。`requested` 非串(缺席)⇒ 门走指派链(ruling 胜者,否则部署缺省),与 run
|
|
410
|
+
* 契约:`(principal, requested) => 门的答 { scenario, source }`({@link ScenarioChoice};名字一定在册;被拒 ⇒ 抛 {@link gateScenarioRequest} 的那几只 typed 错,
|
|
411
|
+
* 读口不改写)。`requested` 非串(缺席)⇒ 门走指派链(ruling 胜者,否则部署缺省),与 run 时同一读法。`requested` 由调用方经 `scenarioRequestedBy`
|
|
412
|
+
* (`request-form.ts`,场景头 × 点名的唯一读法)取 —— 提交腿与读面同调。
|
|
398
413
|
* `principalCaps` 缺席(无中心 / 干跑 / 嵌入方 / `run-local`)⇒ 读数恒缺席,门走「请求名或部署缺省」那条链;
|
|
399
414
|
* 在场 ⇒ 它的读数三态({@link ScenarioRulingRead})原样交门,故障态由门拒。
|
|
400
415
|
*/
|
|
@@ -411,14 +426,14 @@ export interface ScenarioGateSources {
|
|
|
411
426
|
readonly logger?: Logger;
|
|
412
427
|
}
|
|
413
428
|
export interface ScenarioGate {
|
|
414
|
-
(principal: string | undefined, requested: unknown): Promise<
|
|
429
|
+
(principal: string | undefined, requested: unknown): Promise<ScenarioChoice>;
|
|
415
430
|
/** 在册词表 —— 门判「在不在册」读的**同一张**场景表的自有键(活读,不抄快照)。请求的场景头在分派层判在册读它(读面没有体,不过整只门)。 */
|
|
416
431
|
readonly known: () => readonly string[];
|
|
417
432
|
}
|
|
418
433
|
/**
|
|
419
434
|
* 门问中心裁定走哪条腿(闭集;新增一词 ⇒ 下面的穷尽 `switch` 编译红)—— 与 `resolveRuntimeCaps` / `peekRuntimeCapsReading` 是同一对:
|
|
420
435
|
* · `enforce` —— 任务提交腿:中心裁定走执法腿,center 硬失败铸下的 fail-closed 降级项两腿都认;
|
|
421
|
-
* · `peek` —— 读面(`GET /v1/capabilities/mcp` 状态面、`GET /v1/sessions/:id/mcp` / `…/a2a` 两张会话面板):只读腿,同一只缓存 / 同一条抓取 / 同一份读数(中心答不出 ⇒ 读面同拒),但硬失败铸的降级项只对读面自己算数,
|
|
436
|
+
* · `peek` —— 读面(`GET /v1/capabilities/scenario` 生效场景面、`GET /v1/capabilities/mcp` 状态面、`GET /v1/sessions/:id/mcp` / `…/a2a` 两张会话面板):只读腿,同一只缓存 / 同一条抓取 / 同一份读数(中心答不出 ⇒ 读面同拒),但硬失败铸的降级项只对读面自己算数,
|
|
422
437
|
* 不替这个主体的真提交留下 `{allowWorkflows:false, allowFork:false, autoMode:false}` 的短窗 deny(读面不许成为别人被拒的原因)。
|
|
423
438
|
*/
|
|
424
439
|
export type ScenarioGateLeg = "enforce" | "peek";
|
|
@@ -315,7 +315,7 @@ export function gateScenarioRequest(ruling, bodyScenario, defaultScenario, known
|
|
|
315
315
|
extra: { allowlist: allowed },
|
|
316
316
|
});
|
|
317
317
|
}
|
|
318
|
-
return bodyScenario;
|
|
318
|
+
return { scenario: bodyScenario, source: "request" };
|
|
319
319
|
}
|
|
320
320
|
const [name, source] = ruling?.scenario != null ? [ruling.scenario, "ruling"] : [defaultScenario, "deployment"];
|
|
321
321
|
const refusal = danglingScenarioRefusal(name, source, known);
|
|
@@ -323,7 +323,7 @@ export function gateScenarioRequest(ruling, bodyScenario, defaultScenario, known
|
|
|
323
323
|
logger?.warn("scenario_assignment_unknown", { scenario: name, source, ...(principal !== undefined ? { principal } : {}) });
|
|
324
324
|
throw createCodedHttpError(503, "state.scenario_assignment_unknown", refusal, { scenario: name, source });
|
|
325
325
|
}
|
|
326
|
-
return name;
|
|
326
|
+
return { scenario: name, source };
|
|
327
327
|
}
|
|
328
328
|
export function createScenarioGate(sources, leg) {
|
|
329
329
|
const rulingOf = (principal) => {
|
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
import type { FlatServiceDeps } from "./http/server.js";
|
|
22
22
|
import type { ServiceConfig } from "./config.js";
|
|
23
23
|
import type { OwnerAwareSessionStore } from "./security.js";
|
|
24
|
-
import type {
|
|
24
|
+
import type { ServiceFileHistoryStore, StoreBackend } from "./plugins/store-backend.js";
|
|
25
|
+
import type { OperatorPower, OperatorRoster } from "./operator-roster.js";
|
|
25
26
|
import { resolveStreamApprovalGate, type ApprovalFrameFamily } from "./tool-approval.js";
|
|
27
|
+
import { type LandingBlobFaces } from "./session-sync.js";
|
|
26
28
|
import { type WorkspaceSyncCapability } from "./workspace-space.js";
|
|
27
29
|
import type { WorkspaceCacheCapability } from "./workspace-cache.js";
|
|
28
30
|
import type { HistoryAnchorReaders } from "./history-anchor.js";
|
|
@@ -53,7 +55,7 @@ export declare const CAPABILITY_FACT_WHY: {
|
|
|
53
55
|
readonly session_export_absent: "the session store has no entry export faces (exportEntries / exportEntriesStream)";
|
|
54
56
|
readonly session_import_staging_absent: "the session store has no staged-import face (beginImportStaging)";
|
|
55
57
|
readonly policy_purge_absent: "the session policy store has no deleteBySession purge verb";
|
|
56
|
-
readonly blob_exchange_absent: "the file-history store carries no blob exchange
|
|
58
|
+
readonly blob_exchange_absent: "the file-history store carries no complete blob face (the blob exchange FileHistoryStore.blobExchange together with the per-landing provenance face blobLanding — SQL backends only)";
|
|
57
59
|
readonly device_hub_absent: "the device lane hub is not wired";
|
|
58
60
|
readonly device_store_absent: "the device store is not wired";
|
|
59
61
|
readonly device_enrollment_absent: "device enrollment is not wired";
|
|
@@ -82,8 +84,7 @@ export declare const CAPABILITY_FACT_WHY: {
|
|
|
82
84
|
readonly a2a_serve_off: "A2A_SERVE_ENABLED is off";
|
|
83
85
|
readonly elicitation_disabled: "inbound MCP elicitation is off (MCP_ELICITATION_ENABLED)";
|
|
84
86
|
readonly ask_question_disabled: "AskUserQuestion live HITL is off (ASK_QUESTION_ENABLED)";
|
|
85
|
-
readonly tool_approval_disabled: "
|
|
86
|
-
readonly stream_approval_disabled: "the stream approval protocol is off (STREAM_APPROVAL_ENABLED)";
|
|
87
|
+
readonly tool_approval_disabled: "this worker presents no approval cards (needs TOOL_APPROVAL_ENABLED, a store backend (DB_BACKEND) and DURABLE_APPROVAL)";
|
|
87
88
|
readonly read_root_grant_posture_refused: "this is not a single-user host-lane deployment (REMOTE_EXEC=host without REQUIRE_PRINCIPAL), the only posture that honors a read-directory grant";
|
|
88
89
|
readonly background_agent_store_absent: "no background-agent store is wired";
|
|
89
90
|
readonly sendfile_ledger_absent: "no SendUserFile ledger is wired";
|
|
@@ -227,11 +228,11 @@ export declare function sessionSync<D extends Pick<Deps, "backend" | "sessionSto
|
|
|
227
228
|
sessionStorage: SyncSessionStore<D>;
|
|
228
229
|
historyStore: ServiceFileHistoryStore;
|
|
229
230
|
}>;
|
|
230
|
-
/** 会话同步的**文件历史半族**:对话半族 ∧
|
|
231
|
-
*
|
|
231
|
+
/** 会话同步的**文件历史半族**:对话半族 ∧ 文件历史店带完整的 blob 面 —— 交换组 + 按落点来历面(只有 SQL 两只店有)。「店有没有这只面」只在 `landingBlobFacesOf` 判一次
|
|
232
|
+
* (交换组经 core 判官),判过的面随事实交出:路由的 PUT / 在场预检 / 重放取值 / GET 只读它(按落点,没有全局读者)。 */
|
|
232
233
|
export declare function sessionSyncFileHistory(d: Pick<Deps, "backend" | "sessionStorage">): Fact<SessionSyncWhy | "blob_exchange_absent", {
|
|
233
234
|
historyStore: ServiceFileHistoryStore;
|
|
234
|
-
|
|
235
|
+
blobs: LandingBlobFaces;
|
|
235
236
|
}>;
|
|
236
237
|
type RewindFilesWhy = "file_history_store_absent" | "session_head_face_absent" | "resume_anchor_store_absent" | "execution_lane_handless" | "workspace_rebuilt_per_task" | "session_store_shared";
|
|
237
238
|
/**
|
|
@@ -272,7 +273,7 @@ export declare function askUserQuestion(d: Pick<Deps, "question">): Fact<"ask_qu
|
|
|
272
273
|
export declare function toolApproval(d: Pick<Deps, "toolApproval">): Fact<"tool_approval_disabled", {
|
|
273
274
|
coordinator: Store<"toolApproval">;
|
|
274
275
|
}>;
|
|
275
|
-
type StreamApprovalWhy = "tool_approval_disabled" | "
|
|
276
|
+
type StreamApprovalWhy = "tool_approval_disabled" | "store_backend_absent" | "checkpoint_store_absent";
|
|
276
277
|
type ActiveStreamApproval = Extract<ReturnType<typeof resolveStreamApprovalGate>, {
|
|
277
278
|
active: true;
|
|
278
279
|
}>;
|
|
@@ -283,7 +284,7 @@ export declare function streamApproval(d: Pick<Deps, "toolApproval" | "config" |
|
|
|
283
284
|
}>;
|
|
284
285
|
/**
|
|
285
286
|
* 读根授权(「Yes, and add directory」)这台部署**受理不受理** —— `GET /v1/capabilities` 的 `approvalReadRootGrant` 位,与
|
|
286
|
-
* `POST /v1/
|
|
287
|
+
* 决断口 `POST /v1/tasks/:taskId/asks/:askId/decision` 对决定体 `readRootGrant` 的受理门**读同一只**(位说不受理而口照收,就是「位说 X、行为不是 X」)。
|
|
287
288
|
*
|
|
288
289
|
* 判据 = 部署形表行 `callerOwnsWorkerDisk`(手在本 worker 盘上 ∧ 单主;与调用方附加目录同一条信任边界):授权把一个**宿主目录**加进本 run 的读目录,
|
|
289
290
|
* 只有「这台机器一个可信主人」的姿态才有这个授权的语义;多租户与云上 / 容器车道的读面另有归宿(沙箱内的读面档位见 `READ_FACE`)。
|
|
@@ -294,14 +295,13 @@ export declare function readRootGrant(d: Pick<Deps, "config">): Fact<"read_root_
|
|
|
294
295
|
/**
|
|
295
296
|
* 审批开卡帧的族 —— `GET /v1/capabilities` 的 `approvalFrameFamilies` 位。**值族投影**(出形是闭集词数组,不是 `{ available }`;同
|
|
296
297
|
* `engineCapabilityBits` 一类),入参是 {@link streamApproval} **已求出的那一份事实**而不是 deps:一次请求里 `streamApproval` 位与本位读同一次判据求值
|
|
297
|
-
* (判据读活配置与 backend
|
|
298
|
+
* (判据读活配置与 backend,两次求值会撕出两位互相矛盾的应答;也多取一次 ask 店)。
|
|
298
299
|
* · 流内审批在场(呈卡协议上场)⇒ `["approval_request"]`;
|
|
299
|
-
* ·
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* [ref]
|
|
304
|
-
* `presentedApprovalFrameFamily`);不含 ⇒ 这条腿不呈卡,问当场交无人值守政策口。本位的值不因声明而变。
|
|
300
|
+
* · 其余每一形(审批席缺席 / 无 store backend / 无 park 设施)⇒ `[]`:这台 worker 不呈卡,每条腿的问当场交无人值守政策口
|
|
301
|
+
* (审批只有一个帧族,没有「协议没上场就回退发另一族活卡」的腿)。
|
|
302
|
+
* 族词由 `presentedApprovalFrameFamily` 一处映射(三条执行腿判「这台 worker 呈不呈卡 / 这条 run 声明了没有」读同一只 —— 壳照本位声明,服务端照同一只函数判;
|
|
303
|
+
* 真腿对拍钉在 `test/stream-approval-on-e2e.test.ts`)。没有端点按它拒:它是消费方分派帧族与声明的读点,`streamApproval` 的词义不变。
|
|
304
|
+
* [ref]:一条 run 只在 `settings.approvalFrameFamilies` 含本位那一个族时有活审批通道;本位为空 ⇒ 这台 worker 上没有 run 有。本位的值不因声明而变。
|
|
305
305
|
*/
|
|
306
306
|
export declare function approvalFrameFamilies(stream: ReturnType<typeof streamApproval>): readonly ApprovalFrameFamily[];
|
|
307
307
|
export declare function deviceLane(d: Pick<Deps, "deviceHub">): Fact<"device_hub_absent", {
|
|
@@ -377,14 +377,16 @@ export declare function principalInboxDelivery(d: Pick<Deps, "orgDeliveryGate">)
|
|
|
377
377
|
export declare function engineHandover(d: Pick<Deps, "drainState">): Fact<"engine_handover_lane_absent" | "engine_lease_stopped", {
|
|
378
378
|
door: EngineHandoverDoor;
|
|
379
379
|
}>;
|
|
380
|
-
/**
|
|
381
|
-
* ⇒ 这一族的口对谁都打不通 = 部署不可用(不是鉴权拒)
|
|
380
|
+
/** 这台部署上有人能持这只运维权力(档表里持 `power` 的某档名单非空 ∨ 单用户声明 `OPERATOR_SINGLE_USER`;唯一读法 `operatorRosterPresent`)。没有时 `explicitOperatorOk(…, power)`
|
|
381
|
+
* 对任何身份恒假 ⇒ 这一族的口对谁都打不通 = 部署不可用(不是鉴权拒)。`power` 必填:运维面族传 `"operate"`,排空口传它声明行的 `power`。
|
|
382
|
+
* 原因词只有 `operator_principals_empty` 一个:全运维档持每一只权力,所以「没人持 `power`」恒蕴含「没有全运维」—— 这个词的义对每只权力都真;
|
|
383
|
+
* 各权力的完整出路句由 `sendOperatorPrincipalsRequired(res, power)` 在拒发口说。
|
|
382
384
|
*
|
|
383
385
|
* operator 族的复合事实**先判名单、再判设施**:门序是「身份 → 名单空(501)→ 授权(403)→ 设施缺席(501)」——
|
|
384
386
|
* 名单空时没有人过得了授权,不先判它,这一形就永远答成 403;设施缺席则留在授权之后答(不对非 operator 透露部署形态)。 */
|
|
385
387
|
export declare function operatorLane(d: {
|
|
386
|
-
readonly config:
|
|
387
|
-
}): Fact<"operator_principals_empty">;
|
|
388
|
+
readonly config: OperatorRoster;
|
|
389
|
+
}, power: OperatorPower): Fact<"operator_principals_empty">;
|
|
388
390
|
/** 带收编日志的后端(见证随调用方的 deps:路由层的后端视图上店工厂照样封着)。 */
|
|
389
391
|
type AdoptionCapableBackend<D extends Pick<Deps, "backend">> = Held<D, "backend"> & {
|
|
390
392
|
adoptionLog(): ReturnType<NonNullable<Store<"backend">["adoptionLog"]>>;
|
package/dist/capability-facts.js
CHANGED
|
@@ -6,7 +6,7 @@ import { presentedApprovalFrameFamily, resolveStreamApprovalGate } from "./tool-
|
|
|
6
6
|
import { selfOrchestrationDenial } from "./task-workflow.js";
|
|
7
7
|
import { centerEntitlementSourceWired } from "./runtime-caps-resolver.js";
|
|
8
8
|
import { WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
|
|
9
|
-
import {
|
|
9
|
+
import { landingBlobFacesOf } from "./session-sync.js";
|
|
10
10
|
import { overwriteWipeIncapable } from "./plugins/landed-attendants.js";
|
|
11
11
|
import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, workspaceSnapshotTimeoutOf } from "./workspace-space.js";
|
|
12
12
|
import { sessionKindFaceOf } from "./session-kind.js";
|
|
@@ -38,7 +38,7 @@ export const CAPABILITY_FACT_WHY = {
|
|
|
38
38
|
session_export_absent: "the session store has no entry export faces (exportEntries / exportEntriesStream)",
|
|
39
39
|
session_import_staging_absent: "the session store has no staged-import face (beginImportStaging)",
|
|
40
40
|
policy_purge_absent: "the session policy store has no deleteBySession purge verb",
|
|
41
|
-
blob_exchange_absent: "the file-history store carries no blob exchange
|
|
41
|
+
blob_exchange_absent: "the file-history store carries no complete blob face (the blob exchange FileHistoryStore.blobExchange together with the per-landing provenance face blobLanding — SQL backends only)",
|
|
42
42
|
device_hub_absent: "the device lane hub is not wired",
|
|
43
43
|
device_store_absent: "the device store is not wired",
|
|
44
44
|
device_enrollment_absent: "device enrollment is not wired",
|
|
@@ -49,7 +49,7 @@ export const CAPABILITY_FACT_WHY = {
|
|
|
49
49
|
shared_memory_store_absent: "no shared memory store provider is wired",
|
|
50
50
|
org_memory_directory_absent: "no org memory directory (membership fold) is wired",
|
|
51
51
|
org_delivery_gate_absent: "no organization delivery gate is mounted (PRINCIPAL_INBOX_ASSISTANT is unset: the deployment declares no principal-inbox consumer)",
|
|
52
|
-
operator_principals_empty: `no operator roster (OPERATOR_PRINCIPALS is empty and OPERATOR_SINGLE_USER is not declared), so no caller can ever be an operator; ${buildOperatorRosterWayOut()}`,
|
|
52
|
+
operator_principals_empty: `no operator roster (OPERATOR_PRINCIPALS is empty and OPERATOR_SINGLE_USER is not declared), so no caller can ever be an operator; ${buildOperatorRosterWayOut("operate")}`,
|
|
53
53
|
adoption_store_absent: "no adoption log store is wired (SQL backends only)",
|
|
54
54
|
memory_bundle_faces_absent: "the memory engine's governance bundle faces are not wired",
|
|
55
55
|
memory_compliance_faces_absent: "the memory engine's provenance/erasure faces are not wired",
|
|
@@ -67,8 +67,7 @@ export const CAPABILITY_FACT_WHY = {
|
|
|
67
67
|
a2a_serve_off: "A2A_SERVE_ENABLED is off",
|
|
68
68
|
elicitation_disabled: "inbound MCP elicitation is off (MCP_ELICITATION_ENABLED)",
|
|
69
69
|
ask_question_disabled: "AskUserQuestion live HITL is off (ASK_QUESTION_ENABLED)",
|
|
70
|
-
tool_approval_disabled: "
|
|
71
|
-
stream_approval_disabled: "the stream approval protocol is off (STREAM_APPROVAL_ENABLED)",
|
|
70
|
+
tool_approval_disabled: "this worker presents no approval cards (needs TOOL_APPROVAL_ENABLED, a store backend (DB_BACKEND) and DURABLE_APPROVAL)",
|
|
72
71
|
read_root_grant_posture_refused: "this is not a single-user host-lane deployment (REMOTE_EXEC=host without REQUIRE_PRINCIPAL), the only posture that honors a read-directory grant",
|
|
73
72
|
background_agent_store_absent: "no background-agent store is wired",
|
|
74
73
|
sendfile_ledger_absent: "no SendUserFile ledger is wired",
|
|
@@ -217,8 +216,8 @@ export function sessionSyncFileHistory(d) {
|
|
|
217
216
|
const sync = sessionSync(d);
|
|
218
217
|
if (!sync.available)
|
|
219
218
|
return sync;
|
|
220
|
-
const
|
|
221
|
-
return
|
|
219
|
+
const blobs = landingBlobFacesOf(sync.historyStore);
|
|
220
|
+
return blobs !== undefined ? yes({ historyStore: sync.historyStore, blobs }) : no("blob_exchange_absent");
|
|
222
221
|
}
|
|
223
222
|
export function sessionRewindFiles(d) {
|
|
224
223
|
const { config, backend } = d;
|
|
@@ -262,7 +261,6 @@ export function streamApproval(d) {
|
|
|
262
261
|
const coordinator = d.toolApproval;
|
|
263
262
|
const gate = resolveStreamApprovalGate({
|
|
264
263
|
toolApprovalEnabled: true,
|
|
265
|
-
streamApprovalEnabled: d.config.streamApproval?.enabled === true,
|
|
266
264
|
backend: d.backend,
|
|
267
265
|
checkpointStore: d.checkpointStore,
|
|
268
266
|
});
|
|
@@ -271,8 +269,6 @@ export function streamApproval(d) {
|
|
|
271
269
|
switch (gate.reason) {
|
|
272
270
|
case "no_tool_approval":
|
|
273
271
|
return no("tool_approval_disabled");
|
|
274
|
-
case "protocol_disabled":
|
|
275
|
-
return no("stream_approval_disabled");
|
|
276
272
|
case "no_backend":
|
|
277
273
|
return no("store_backend_absent");
|
|
278
274
|
case "no_park_facility":
|
|
@@ -287,20 +283,8 @@ export function readRootGrant(d) {
|
|
|
287
283
|
return DEPLOYMENT_SHAPE_PREDICATES.callerOwnsWorkerDisk.holds(d.config) ? YES : no("read_root_grant_posture_refused");
|
|
288
284
|
}
|
|
289
285
|
export function approvalFrameFamilies(stream) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
switch (stream.why) {
|
|
293
|
-
case "tool_approval_disabled":
|
|
294
|
-
return [];
|
|
295
|
-
case "stream_approval_disabled":
|
|
296
|
-
case "store_backend_absent":
|
|
297
|
-
case "checkpoint_store_absent":
|
|
298
|
-
return [presentedApprovalFrameFamily(false)];
|
|
299
|
-
default: {
|
|
300
|
-
const unhandled = stream.why;
|
|
301
|
-
throw new Error(`unhandled stream-approval why: ${JSON.stringify(unhandled)}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
286
|
+
const family = presentedApprovalFrameFamily(stream.available);
|
|
287
|
+
return family === undefined ? [] : [family];
|
|
304
288
|
}
|
|
305
289
|
export function deviceLane(d) {
|
|
306
290
|
return d.deviceHub !== undefined ? yes({ hub: d.deviceHub }) : no("device_hub_absent");
|
|
@@ -377,15 +361,15 @@ export function engineHandover(d) {
|
|
|
377
361
|
return no("engine_handover_lane_absent");
|
|
378
362
|
return door.observing ? yes({ door }) : no("engine_lease_stopped");
|
|
379
363
|
}
|
|
380
|
-
export function operatorLane(d) {
|
|
381
|
-
return operatorRosterPresent(d.config) ? YES : no("operator_principals_empty");
|
|
364
|
+
export function operatorLane(d, power) {
|
|
365
|
+
return operatorRosterPresent(d.config, power) ? YES : no("operator_principals_empty");
|
|
382
366
|
}
|
|
383
367
|
function adoptionLogStore(d) {
|
|
384
368
|
const b = d.backend;
|
|
385
369
|
return b !== undefined && typeof b.adoptionLog === "function" ? yes({ backend: b }) : no("adoption_store_absent");
|
|
386
370
|
}
|
|
387
371
|
export function adoption(d) {
|
|
388
|
-
const lane = operatorLane(d);
|
|
372
|
+
const lane = operatorLane(d, "operate");
|
|
389
373
|
if (!lane.available)
|
|
390
374
|
return lane;
|
|
391
375
|
return adoptionLogStore(d);
|
|
@@ -400,7 +384,7 @@ export function adoptionSessionImport(d) {
|
|
|
400
384
|
return yes({ backend: log.backend, sessionStorage: s });
|
|
401
385
|
}
|
|
402
386
|
export function memoryBundle(d) {
|
|
403
|
-
const lane = operatorLane(d);
|
|
387
|
+
const lane = operatorLane(d, "operate");
|
|
404
388
|
if (!lane.available)
|
|
405
389
|
return lane;
|
|
406
390
|
return d.memoryBundleExport !== undefined && d.memoryBundleImport !== undefined
|
|
@@ -408,13 +392,13 @@ export function memoryBundle(d) {
|
|
|
408
392
|
: no("memory_bundle_faces_absent");
|
|
409
393
|
}
|
|
410
394
|
export function memoryCompliance(d) {
|
|
411
|
-
const lane = operatorLane(d);
|
|
395
|
+
const lane = operatorLane(d, "operate");
|
|
412
396
|
if (!lane.available)
|
|
413
397
|
return lane;
|
|
414
398
|
return d.memoryCompliance !== undefined ? yes({ faces: d.memoryCompliance }) : no("memory_compliance_faces_absent");
|
|
415
399
|
}
|
|
416
400
|
export function memoryOrigin(d) {
|
|
417
|
-
const lane = operatorLane(d);
|
|
401
|
+
const lane = operatorLane(d, "operate");
|
|
418
402
|
if (!lane.available)
|
|
419
403
|
return lane;
|
|
420
404
|
return d.memoryOriginFace !== undefined ? yes({ faces: d.memoryOriginFace }) : no("memory_origin_faces_absent");
|
|
@@ -422,17 +406,17 @@ export function memoryOrigin(d) {
|
|
|
422
406
|
export function memoryConsolidation(d) {
|
|
423
407
|
if (d.memoryConsolidation === undefined)
|
|
424
408
|
return no("consolidation_valve_closed");
|
|
425
|
-
const lane = operatorLane(d);
|
|
409
|
+
const lane = operatorLane(d, "operate");
|
|
426
410
|
return lane.available ? yes({ valve: d.memoryConsolidation }) : lane;
|
|
427
411
|
}
|
|
428
412
|
export function memoryOptOutGrant(d) {
|
|
429
|
-
const lane = operatorLane(d);
|
|
413
|
+
const lane = operatorLane(d, "operate");
|
|
430
414
|
if (!lane.available)
|
|
431
415
|
return lane;
|
|
432
416
|
return d.memoryOptOutGrant !== undefined ? yes({ store: d.memoryOptOutGrant }) : no("memory_optout_grant_absent");
|
|
433
417
|
}
|
|
434
418
|
export function outcomeLedger(d) {
|
|
435
|
-
const lane = operatorLane(d);
|
|
419
|
+
const lane = operatorLane(d, "operate");
|
|
436
420
|
if (!lane.available)
|
|
437
421
|
return lane;
|
|
438
422
|
const sink = d.outcomeSink;
|
|
@@ -441,7 +425,7 @@ export function outcomeLedger(d) {
|
|
|
441
425
|
export function configCatalog(d) {
|
|
442
426
|
if (d.config.configCatalogEnabled !== true)
|
|
443
427
|
return no("config_catalog_disabled");
|
|
444
|
-
return operatorLane(d);
|
|
428
|
+
return operatorLane(d, "operate");
|
|
445
429
|
}
|
|
446
430
|
export function ruleLane(d) {
|
|
447
431
|
return d.ruleConsent !== undefined ? yes({ lane: d.ruleConsent }) : no("rule_store_absent");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **一张审批卡的规则候选:唯一读者**(活卡铸点、两只 checkpoint 店的铸行与回读投影、停驻行的规则记录读者同调这一只)。
|
|
3
|
+
*
|
|
4
|
+
* 规则一句:每一张面上投出的候选,都是同一份记录过**这一只**读者得到的投影 —— 同一个下标空间。读法 = core `readRuleOffers` 的闭集词
|
|
5
|
+
* (经 `approval-card.ts` 的 `readRuleOfferList`)→ 基数前缀帽 {@link MAX_RULE_OFFERS}(扣在读之前:exact single 恒 0、batch 恒末,前缀截不动选择键)→
|
|
6
|
+
* 兑付座超尺整条撤回 → 派生事实一致性(自称的 kind / match / command / directory 座与共享校验器从 `rule` 文本派生的三元组逐字一致,且是这张卡的工具与卡形)。
|
|
7
|
+
* 此前店侧另有一只读者(超尺截长、不判派生事实):一只展示成目录授权、规则文本却是 Bash 放行的 offer 能在队列行上出现,同一张停驻卡的重放帧与队列行
|
|
8
|
+
* 下标对不上。展示变换(逐条脱敏 + 截长,1:1 保位、只改字)归各展示腿,不是读法(`checkpoint-store-face.ts` 的 `displayRuleOffers`)。
|
|
9
|
+
*/
|
|
10
|
+
import { type RuleOffer } from "@sema-agent/core";
|
|
11
|
+
/**
|
|
12
|
+
* 读一张卡的候选(唯一读者;头注)。`undefined` = 没有素材可投 / 可兑:原值不是非空列表(引擎没铸),或有兑付座超了不可截的尺(整条车道撤回)。
|
|
13
|
+
* `[]` = 原值有、全被丢光(闭集词不认 / 派生事实不一致):活卡铸点据它把绑定值回落到调用自己的命令(编辑臂兑的就是它),展示腿读作无供给。
|
|
14
|
+
*/
|
|
15
|
+
export declare function readCardRuleOffers(raw: unknown, cardTool: string): readonly RuleOffer[] | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* **已落盘投影的解码**(SQL 腿的 `checkpoint.rule_suggestions` 列:铸行那一刻 {@link readCardRuleOffers} 读出、经展示变换落下的那一份)。读者在铸行时已经跑过;
|
|
18
|
+
* 这里只认闭集词形 + 同一组界(扫描窗 / 成员前缀 / 基数),**不再判**派生事实与兑付座尺 —— 展示形是脱敏、截长过的字(截长之后的规则文本不再是一条可解析的规则),
|
|
19
|
+
* 再过一遍读者会把铸行时留下的候选丢掉,同一份素材两个后端两种卡面。手改过的列由这只解码落成丢弃(响亮,同一只 offer-dropped 计数)。
|
|
20
|
+
*/
|
|
21
|
+
export declare function decodeRuleOfferProjection(raw: unknown): readonly RuleOffer[];
|
|
22
|
+
/**
|
|
23
|
+
* 这张卡的**绑定值**(`prepareCardApproval` 的 `command`,也是耐久行的 `rule_command`)= **卡形的绑定值**(core `prepareCardApproval` 对 `command` 入参的逐形定义):
|
|
24
|
+
* · 值形(整工具 / 名字 / 主机:core 对这三形只铸**一只** single,它的 `command` 座就是那个值 —— 整工具 `""`、名字工具是调用带的名字、主机工具是调用到达的主机)
|
|
25
|
+
* ⇒ 那一只候选自己的 `command`;
|
|
26
|
+
* · 其余(命令形候选 / 批,或读者把候选丢光了)⇒ 被裁决的命令原字节 `args.command`(非空串;读不出 ⇒ 没有素材)。空候选表不说任何值形:
|
|
27
|
+
* 绑定值回到调用自己的命令(编辑面兑的就是它)。
|
|
28
|
+
* 候选走到这里之前已过 {@link singleOfferBelongsOnCard}:每只 single 都是这张卡的工具、这只工具的卡形 ⇒ 值形与命令形不会同卡。值形候选不止一只 = 不是 core 铸得出的卡
|
|
29
|
+
* ⇒ 没有素材(整条车道撤回,与本文件其余撤回臂同形)。`req.args` 窄读,禁裸 as-cast。
|
|
30
|
+
*/
|
|
31
|
+
export declare function cardBindingOf(args: unknown, offers: readonly RuleOffer[]): string | undefined;
|
|
32
|
+
//# sourceMappingURL=card-rule-offers.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { MAX_RULE_TEXT_CHARS, parseRuleText } from "@sema-agent/core";
|
|
2
|
+
import { MAX_RULE_OFFERS, MAX_RULE_OFFER_BATCH_MEMBERS, readRuleOfferList } from "./approval-card.js";
|
|
3
|
+
import { CARD_RULE_FORM_OF_MATCH, cardRuleFormOf } from "./permission-rule-vocab.js";
|
|
4
|
+
import { createLogger } from "./observability/logger.js";
|
|
5
|
+
const defaultLogger = createLogger();
|
|
6
|
+
export function readCardRuleOffers(raw, cardTool) {
|
|
7
|
+
if (raw === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (!Array.isArray(raw)) {
|
|
10
|
+
readRuleOfferList(raw);
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
if (raw.length === 0)
|
|
14
|
+
return undefined;
|
|
15
|
+
return boundRuleOfferTexts(raw.slice(0, MAX_RULE_OFFER_SCAN).map(preboundBatchMembers), cardTool);
|
|
16
|
+
}
|
|
17
|
+
export function decodeRuleOfferProjection(raw) {
|
|
18
|
+
return readRuleOfferList(Array.isArray(raw) ? raw.slice(0, MAX_RULE_OFFER_SCAN).map(preboundBatchMembers) : raw)
|
|
19
|
+
.map(({ offer }) => offer)
|
|
20
|
+
.slice(0, MAX_RULE_OFFERS);
|
|
21
|
+
}
|
|
22
|
+
const MAX_RULE_OFFER_SCAN = 32;
|
|
23
|
+
function preboundBatchMembers(item) {
|
|
24
|
+
if (typeof item !== "object" || item === null)
|
|
25
|
+
return item;
|
|
26
|
+
const candidate = item;
|
|
27
|
+
if (candidate.kind !== "batch" || !Array.isArray(candidate.rules) || candidate.rules.length <= MAX_RULE_OFFER_BATCH_MEMBERS)
|
|
28
|
+
return item;
|
|
29
|
+
return { ...candidate, rules: candidate.rules.slice(0, MAX_RULE_OFFER_BATCH_MEMBERS) };
|
|
30
|
+
}
|
|
31
|
+
function ruleOfferTextsOverSize(offer) {
|
|
32
|
+
const over = (s) => s.length > MAX_RULE_TEXT_CHARS;
|
|
33
|
+
if (offer.kind === "single")
|
|
34
|
+
return over(offer.rule) || over(offer.command);
|
|
35
|
+
return offer.rules.some((r) => {
|
|
36
|
+
switch (r.kind) {
|
|
37
|
+
case "command":
|
|
38
|
+
return over(r.rule) || over(r.command);
|
|
39
|
+
case "directoryRead":
|
|
40
|
+
return over(r.rule) || over(r.directory);
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(`unknown rule-offer batch member kind: ${JSON.stringify(r)}`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function ruleOfferDerivedFactsAgree(offer, cardTool) {
|
|
47
|
+
const derive = (rule) => {
|
|
48
|
+
const p = parseRuleText(rule, "allow");
|
|
49
|
+
return "rule" in p ? p.rule : undefined;
|
|
50
|
+
};
|
|
51
|
+
if (offer.kind === "single") {
|
|
52
|
+
const d = derive(offer.rule);
|
|
53
|
+
return d !== undefined && d.rule === offer.rule && d.match === offer.match && d.command === offer.command && singleOfferBelongsOnCard(d.match, d.tool, cardTool);
|
|
54
|
+
}
|
|
55
|
+
return offer.rules.every((m) => {
|
|
56
|
+
const d = derive(m.rule);
|
|
57
|
+
if (d === undefined || d.rule !== m.rule)
|
|
58
|
+
return false;
|
|
59
|
+
switch (m.kind) {
|
|
60
|
+
case "command":
|
|
61
|
+
return d.tool === "Bash" && d.match === m.match && d.command === m.command;
|
|
62
|
+
case "directoryRead":
|
|
63
|
+
return d.tool === "Read" && d.match === "subpath" && d.command === m.directory;
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(`unknown rule-offer batch member kind: ${JSON.stringify(m)}`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function singleOfferBelongsOnCard(match, ruleTool, cardTool) {
|
|
70
|
+
const form = CARD_RULE_FORM_OF_MATCH[match];
|
|
71
|
+
return ruleTool === cardTool && form !== undefined && form === cardRuleFormOf(cardTool);
|
|
72
|
+
}
|
|
73
|
+
function boundRuleOfferTexts(offers, cardTool) {
|
|
74
|
+
const over = (s) => s.length > MAX_RULE_TEXT_CHARS;
|
|
75
|
+
const clipSegment = (s) => (over(s) ? s.slice(0, MAX_RULE_TEXT_CHARS) : s);
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const { offer } of readRuleOfferList(offers)) {
|
|
78
|
+
if (out.length >= MAX_RULE_OFFERS)
|
|
79
|
+
break;
|
|
80
|
+
if (ruleOfferTextsOverSize(offer))
|
|
81
|
+
return undefined;
|
|
82
|
+
if (!ruleOfferDerivedFactsAgree(offer, cardTool)) {
|
|
83
|
+
defaultLogger.warn("rule_offer_dropped_derived_facts_disagree", {
|
|
84
|
+
kind: offer.kind,
|
|
85
|
+
note: "an offer's kind/match/command/directory seats do not agree with what the shared validator derives from its rule text, or it is not this card's tool / card form — dropped whole so the card never shows one grant while redeeming another (core/dist skew or forged material?)",
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
switch (offer.kind) {
|
|
90
|
+
case "single":
|
|
91
|
+
out.push({ kind: "single", rule: offer.rule, match: offer.match, command: offer.command });
|
|
92
|
+
break;
|
|
93
|
+
case "batch": {
|
|
94
|
+
const rules = [];
|
|
95
|
+
for (const r of offer.rules) {
|
|
96
|
+
switch (r.kind) {
|
|
97
|
+
case "command":
|
|
98
|
+
rules.push({ kind: "command", rule: r.rule, match: r.match, command: r.command, segment: clipSegment(r.segment) });
|
|
99
|
+
break;
|
|
100
|
+
case "directoryRead":
|
|
101
|
+
rules.push({ kind: "directoryRead", rule: r.rule, directory: r.directory, segment: clipSegment(r.segment) });
|
|
102
|
+
break;
|
|
103
|
+
default:
|
|
104
|
+
throw new Error(`unknown rule-offer batch member kind: ${JSON.stringify(r)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
out.push({
|
|
108
|
+
kind: "batch",
|
|
109
|
+
rules,
|
|
110
|
+
uncoveredSegments: offer.uncoveredSegments,
|
|
111
|
+
...(offer.uncoveredDetail !== undefined ? { uncoveredDetail: offer.uncoveredDetail.map((d) => ({ segment: clipSegment(d.segment), reason: d.reason })) } : {}),
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`unknown rule-offer kind: ${JSON.stringify(offer)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
export function cardBindingOf(args, offers) {
|
|
122
|
+
const valueOffers = offers.filter((o) => o.kind === "single" && CARD_RULE_FORM_OF_MATCH[o.match] !== "command");
|
|
123
|
+
if (valueOffers.length > 0)
|
|
124
|
+
return valueOffers.length === 1 && offers.length === 1 ? valueOffers[0].command : undefined;
|
|
125
|
+
if (args === null || typeof args !== "object" || Array.isArray(args))
|
|
126
|
+
return undefined;
|
|
127
|
+
const command = args.command;
|
|
128
|
+
return typeof command === "string" && command !== "" ? command : undefined;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=card-rule-offers.js.map
|