@sema-agent/server 7.148.0 → 7.149.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 +429 -0
- package/README.md +16 -1
- package/README.zh-CN.md +4 -0
- package/USAGE.md +89 -55
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +32 -32
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/resolve-spec.js +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +2 -1
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +5 -4
- package/dist/capability-facts.js +3 -14
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +36 -32
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +37 -5
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/admission.js +3 -3
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/claim-heartbeat.d.ts +3 -3
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +5 -3
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +30 -11
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +3 -2
- package/dist/http/owner-access.js +2 -3
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +8 -2
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +244 -92
- package/dist/http/route-ctx.d.ts +6 -0
- package/dist/http/routes/a2a-serve.js +2 -0
- package/dist/http/routes/approvals-assistant.js +3 -2
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/runs.js +10 -7
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +46 -34
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/tasks.js +127 -93
- package/dist/http/server.d.ts +4 -5
- package/dist/http/server.js +30 -5
- package/dist/http/submit-ctx.d.ts +1 -0
- package/dist/http/submit-ctx.js +2 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leg-facts.d.ts +7 -2
- package/dist/leg-facts.js +1 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/fail-open.d.ts +14 -2
- package/dist/observability/fail-open.js +14 -2
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +3 -2
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +50 -22
- package/dist/plugins/local-checkpoint-store.d.ts +4 -0
- package/dist/plugins/local-checkpoint-store.js +2 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +47 -21
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +46 -20
- package/dist/plugins/one-time-migrations.js +102 -79
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +10 -12
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +1 -7
- package/dist/plugins/remote-env-e2b.js +10 -7
- package/dist/plugins/remote-env-host.d.ts +50 -9
- package/dist/plugins/remote-env-host.js +77 -20
- package/dist/plugins/remote-env-k8s.js +4 -2
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +8 -23
- package/dist/plugins/run-store-sql.d.ts +26 -7
- package/dist/plugins/run-store-sql.js +44 -16
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +3 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +4 -3
- package/dist/plugins/schema-consistency.d.ts +89 -5
- package/dist/plugins/schema-consistency.js +176 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +16 -0
- package/dist/plugins/stop-then-start-sentence.js +7 -0
- package/dist/plugins/store-backend.d.ts +22 -8
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +33 -3
- package/dist/plugins/store-contracts.js +16 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +18 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +19 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +3 -2
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
- package/dist/stores-sql/checkpoint-store-sql.js +3 -5
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +23 -7
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.js +1 -0
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +44 -36
- package/dist/tool-approval.js +66 -47
- package/dist/trace/engine-notice-wire.d.ts +24 -21
- package/dist/trace/engine-notice-wire.js +7 -3
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +4 -2
- package/dist/trace/project.js +8 -7
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +131 -52
- package/dist/trace/self-minted-wire.js +71 -39
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +3 -1
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/dist/plain-string-record.js +0 -70
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
* + 可选 `urgent` 与显示名;不写 `fromSession / fromMode / hopChain`)。本仓不另铸任何记录形。
|
|
16
16
|
* · **限流是配额店的两个原子动作,各管一件事**:`admitAttempt` 记发件人的每一次尝试(碰目录之前;界住刷目录的速率),
|
|
17
17
|
* `take` 在同一把锁下判并记 (from→to) 对偶界与收件人聚合界(投递额度)。SQL 两方言各是一个事务,见
|
|
18
|
-
* `plugins/org-delivery-quota-store.ts` 头注;File / Memory =
|
|
18
|
+
* `plugins/org-delivery-quota-store.ts` 头注;File / Memory = 单进程。两个动作的拒都带店算的重试秒数,门原样放进 `rate_limited` 的答案里。
|
|
19
|
+
* · **答案是 core 闭集的答案加一个本服务的成员**:core 的 `OrgDeliveryOutcome` 对 `rate_limited` 只有码与句;本服务的 wire 另带 `retryAfterSec`,
|
|
20
|
+
* 所以门交出的是 {@link ServerOrgDeliveryOutcome} —— 对 core 的类型结构上是子型(多一个成员,别的词不变),`rate_limited` 一只词多一个必填成员。
|
|
19
21
|
* · **收件人的 scope 就是收件人主体串本身**:本服务的引擎 scope = `spec.principal`(`resolve-spec.ts` 落
|
|
20
22
|
* `auth.principal`;同一事实的推导见 `peer-directory.ts` 的「scope 轴」段)。门不另造编解码。
|
|
21
23
|
*
|
|
@@ -33,7 +35,8 @@
|
|
|
33
35
|
* 本仓不另抄宽度公式。部署没声明收件箱消费方(`no_consumer`)或别的铸信失败 ⇒ 门抛(core:「the gate maps that to its own failure」),HTTP 面答 500,同样没吃额度。
|
|
34
36
|
* 3. **收件箱上界**(`peekCount` 读):满 ⇒ `inbox_full`,在吃配额之前判,满箱不烧发件人的额度。与引擎同主体车道的
|
|
35
37
|
* `maxQueuedPeerMessages` 同一形(入口处读一次占用);两封并发信最多越界并发数那么多,上界由配额再界一次。
|
|
36
|
-
* 4. **配额**(原子判并记):任一界满 ⇒ `rate_limited`,与第 0 步同一句(
|
|
38
|
+
* 4. **配额**(原子判并记):任一界满 ⇒ `rate_limited`,与第 0 步同一句(不让发件人从拒句里分辨出「收件人这个窗里收了多少」);
|
|
39
|
+
* 同一个 `retryAfterSec` 口径 —— 配额店答的、到店钟当前窗结束的整秒数(第 0 步与第 4 步读同一口钟、同一道算式,数里也分辨不出是哪一界)。
|
|
37
40
|
* 5. **追加**:`append(收件人 scope, PRINCIPAL_INBOX_HANDLE, 记录)`。店的预删除态拒码(`mailbox.recipient_tombstoned`)
|
|
38
41
|
* 原样转成 `recipient_tombstoned`(core 对这只词的定义:「the store's own pre-delete code, relayed」);其余错误抛。
|
|
39
42
|
* 被拒的投递在任何地方都不追加任何东西(core 契约原句);投递额度(第 4 步)在之后只有追加失败这一种「吃了额度没投到」—— 方向是收紧;
|
|
@@ -44,13 +47,14 @@
|
|
|
44
47
|
* 跨 scope 写径:受理口(`POST /v1/principal-inbox/messages`)只调门,本仓源码里拼得出 `principal.inbox` 这只箱址的只有本文件
|
|
45
48
|
* (`test/org-delivery-gate-single-path.test.ts` 按源码钉)。
|
|
46
49
|
*/
|
|
47
|
-
import { type MailboxStore, type OrgDeliveryGate, type OrgDeliveryRefusalCode, type PrincipalInboxConsumer } from "@sema-agent/core";
|
|
50
|
+
import { type CrossPrincipalBody, type MailboxStore, type OrgAuthenticatedSender, type OrgDeliveryGate, type OrgDeliveryOutcome, type OrgDeliveryRefusalCode, type PrincipalInboxConsumer } from "@sema-agent/core";
|
|
48
51
|
import type { OrgMemoryDirectory } from "./org-memory-admission.js";
|
|
49
52
|
import type { OrgDeliveryQuotaPolicy, OrgDeliveryQuotaStore } from "./plugins/org-delivery-quota-store.js";
|
|
50
53
|
/**
|
|
51
54
|
* 跨主体配额(**唯一**的数字源;店只按它数)。默认显著紧于同主体车道(引擎 `PEER_LANE_DEFAULTS`:每发件人突发 30、
|
|
52
|
-
* 每秒回填 0.5)
|
|
53
|
-
*
|
|
55
|
+
* 每秒回填 0.5):同一对主体每个挂钟整点桶 10 封,一位收件人每个挂钟整点桶合计 30 封 —— org 里一个吵闹的主体淹不了别人的箱;
|
|
56
|
+
* 一个发件人每个挂钟整点桶 60 次尝试(成没成都算)—— 拿收件人串去问组织目录的速率有界(头注「次序」第 0 步)。
|
|
57
|
+
* 「桶」= 店钟对齐的固定窗(窗宽一小时,`floor(店钟 / windowMs)` 一处公式,`plugins/org-delivery-quota-store.ts` 头注),不是滑动的「过去 60 分钟」。
|
|
54
58
|
*/
|
|
55
59
|
export declare const ORG_DELIVERY_QUOTA: OrgDeliveryQuotaPolicy;
|
|
56
60
|
/** 收件箱上界(在箱、未被消费方确认的信封数)。与引擎同主体车道的箱上界缺省(`maxQueuedPeerMessages`)同值。 */
|
|
@@ -61,6 +65,27 @@ export declare const PRINCIPAL_INBOX_BOUND = 50;
|
|
|
61
65
|
* `not_member` 一句同时服务「不同 org / 查无此人 / 目录答不上来 / 收件人串不成形 / 发件人主体拼不进信封的发件人位」—— 改它要保持各臂同字节。
|
|
62
66
|
*/
|
|
63
67
|
export declare const ORG_DELIVERY_REFUSAL_MESSAGES: Readonly<Record<OrgDeliveryRefusalCode, string>>;
|
|
68
|
+
/**
|
|
69
|
+
* 本服务的投递答案 = core 的 {@link OrgDeliveryOutcome} 加上额度拒的重试指引:`rate_limited` 一只词必带 `retryAfterSec`(配额店算的、到店钟当前窗结束的整秒数,
|
|
70
|
+
* ≥ 1),另三只词的答案与 core 的形逐字相同。对 core 的 `OrgDeliveryGate` 契约结构上是子型,所以这只门照样能交给任何读 core 契约的人。
|
|
71
|
+
* 数只在配额店算一次;HTTP 面把它投影成体里的 `retryAfterSec` 与同值 `Retry-After` 头(`routes/principal-inbox.ts`,不再算)。
|
|
72
|
+
*/
|
|
73
|
+
export type ServerOrgDeliveryOutcome = Extract<OrgDeliveryOutcome, {
|
|
74
|
+
ok: true;
|
|
75
|
+
}> | {
|
|
76
|
+
readonly ok: false;
|
|
77
|
+
readonly code: Exclude<OrgDeliveryRefusalCode, "org_delivery.rate_limited">;
|
|
78
|
+
readonly message: string;
|
|
79
|
+
} | {
|
|
80
|
+
readonly ok: false;
|
|
81
|
+
readonly code: "org_delivery.rate_limited";
|
|
82
|
+
readonly message: string;
|
|
83
|
+
readonly retryAfterSec: number;
|
|
84
|
+
};
|
|
85
|
+
/** {@link createOrgDeliveryGate} 交出的门:core 契约的子型,答案是 {@link ServerOrgDeliveryOutcome}。装配点、`ServiceDeps.orgDeliveryGate` 与受理口读这一只型。 */
|
|
86
|
+
export interface ServerOrgDeliveryGate extends OrgDeliveryGate {
|
|
87
|
+
deliverCrossPrincipal(authedFrom: OrgAuthenticatedSender, toPrincipal: string, body: CrossPrincipalBody): Promise<ServerOrgDeliveryOutcome>;
|
|
88
|
+
}
|
|
64
89
|
export interface OrgDeliveryGateDeps {
|
|
65
90
|
/** org 成员目录 —— **必须**与记忆准入 seam / memory-policy / 共享记忆读面同一个实例(头注)。 */
|
|
66
91
|
readonly directory: OrgMemoryDirectory;
|
|
@@ -78,7 +103,7 @@ export interface OrgDeliveryGateDeps {
|
|
|
78
103
|
/**
|
|
79
104
|
* 活对象(闭包持有目录 / 两只店 / 消费方)⇒ `create*`(CLAUDE.md 工厂命名律)。
|
|
80
105
|
*/
|
|
81
|
-
export declare function createOrgDeliveryGate(deps: OrgDeliveryGateDeps):
|
|
106
|
+
export declare function createOrgDeliveryGate(deps: OrgDeliveryGateDeps): ServerOrgDeliveryGate;
|
|
82
107
|
/**
|
|
83
108
|
* 箱址的读侧映射也归门(单一属主:箱址常量只在本文件出现,`test/org-delivery-gate-single-path.test.ts` ①):
|
|
84
109
|
* 被扣的跨会话信在收信会话的哪条车道上 ⇄ core 队列键的 `box`。消费方 = `src/peer-inbound-face.ts`。
|
|
@@ -11,6 +11,7 @@ export const ORG_DELIVERY_REFUSAL_MESSAGES = Object.freeze({
|
|
|
11
11
|
"org_delivery.recipient_tombstoned": "the recipient is being deleted — nothing was delivered",
|
|
12
12
|
});
|
|
13
13
|
const refuse = (code) => ({ ok: false, code, message: ORG_DELIVERY_REFUSAL_MESSAGES[code] });
|
|
14
|
+
const refuseRateLimited = (retryAfterSec) => ({ ok: false, code: "org_delivery.rate_limited", message: ORG_DELIVERY_REFUSAL_MESSAGES["org_delivery.rate_limited"], retryAfterSec });
|
|
14
15
|
async function shareAnOrg(directory, a, b) {
|
|
15
16
|
const [la, lb] = await Promise.all([directory.lookup(a), directory.lookup(b)]);
|
|
16
17
|
if (la.kind !== "granted" || lb.kind !== "granted")
|
|
@@ -24,8 +25,9 @@ export function createOrgDeliveryGate(deps) {
|
|
|
24
25
|
return {
|
|
25
26
|
async deliverCrossPrincipal(authedFrom, toPrincipal, body) {
|
|
26
27
|
const senderPerson = personOf(recoveredSubject(authedFrom.scope));
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const attempt = await deps.quota.admitAttempt(senderPerson, ORG_DELIVERY_QUOTA);
|
|
29
|
+
if (!attempt.granted)
|
|
30
|
+
return refuseRateLimited(attempt.retryAfterSec);
|
|
29
31
|
if (!principalShaped(toPrincipal) || !(await shareAnOrg(deps.directory, senderPerson, toPrincipal))) {
|
|
30
32
|
return refuse("org_delivery.not_member");
|
|
31
33
|
}
|
|
@@ -42,8 +44,9 @@ export function createOrgDeliveryGate(deps) {
|
|
|
42
44
|
}
|
|
43
45
|
if ((await deps.mailbox.peekCount(toScope, PRINCIPAL_INBOX_HANDLE)) >= PRINCIPAL_INBOX_BOUND)
|
|
44
46
|
return refuse("org_delivery.inbox_full");
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const taken = await deps.quota.take(senderPerson, toScope, ORG_DELIVERY_QUOTA);
|
|
48
|
+
if (!taken.granted)
|
|
49
|
+
return refuseRateLimited(taken.retryAfterSec);
|
|
47
50
|
let seq;
|
|
48
51
|
try {
|
|
49
52
|
seq = await deps.mailbox.append(toScope, PRINCIPAL_INBOX_HANDLE, record);
|
package/dist/owner-faces.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ import type { SessionOwnerCell } from "./security.js";
|
|
|
31
31
|
import { type DurableOwnerKey, type Subject } from "./owner-brands.js";
|
|
32
32
|
/** 行资格:调用方对**这一行**的关系(四格,见 {@link qualsOf})。 */
|
|
33
33
|
export type RowQual = "owner" | "open" | "j4" | "anon_tree";
|
|
34
|
-
/** fleet 资格:与行无关的调用方事实 ——
|
|
34
|
+
/** fleet 资格:与行无关的调用方事实 —— 没有身份边界的部署上不报主体的调用方(`dev_open`)、代部署本人说话的 service 凭据(`deployment_token`)、只读 trace 凭据(`trace_read`)。 */
|
|
35
35
|
export type FleetQual = "dev_open" | "deployment_token" | "trace_read";
|
|
36
36
|
/** 资格闭集:行资格 ∪ fleet 资格 ∪ 运维(`operator`,与行无关的调用方事实)。 */
|
|
37
37
|
export type Qual = RowQual | FleetQual | "operator";
|
|
@@ -39,7 +39,8 @@ export type Qual = RowQual | FleetQual | "operator";
|
|
|
39
39
|
* 一次判定的调用方(四格事实 + 部署两格)。铸点闭集:`http/owner-access.ts` 的 `callerOf`(请求口)/ `callerOfDecision`(决定口),`security.ts` 的 `claimCallerOf`(认领面与协议口:不放 fleet、不放运维),别处不手搓。
|
|
40
40
|
* · `subject` —— 验过的主体(分区键,`owner-brands.ts`;`gatedSubject`;决定口是证明推出的人的主体);载体只带这一格,人经 `personOf(subject)` 读;
|
|
41
41
|
* · `operator` —— `explicitOperatorOk(personOf(subject))`(运维名册的唯一读法:读人,不读分区),与 `subject` 同一次铸;
|
|
42
|
-
* · `fleet` —— `dev_open`(装配没接任何身份来源)/ `deployment_token`(出示 service 凭据、不报主体、部署没声明关掉)/
|
|
42
|
+
* · `fleet` —— `dev_open`(装配没接任何身份来源 ∧ 不报主体)/ `deployment_token`(出示 service 凭据、不报主体、部署没声明关掉)/ 缺席;两来源互斥,报了主体 ⇒ 恒缺席
|
|
43
|
+
* (唯一读法 `http/principal-gate.ts` 的 `actsForDeployment`:报了主体的调用方在每一口都是那位主体);
|
|
43
44
|
* · `traceRead` —— 出示了只读 trace 凭据;
|
|
44
45
|
* · `principalRequired` / `multiTenant` —— 部署形表的两只谓词(身份前门的条件 / `j4` 的条件)。
|
|
45
46
|
*/
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **卡已提交之后,停驻的唯一提交点** —— run 行的停驻写(`setSuspended` / `setNeedsReview`:对本腿持有的这条行的 CAS,保 `task_active` 认领)。
|
|
3
|
+
*
|
|
4
|
+
* 引擎交回停驻之前已把卡落进 checkpoint 店(卡已提交):这条 run 此刻在等那张卡。腿的收场只由行的这一笔写回答,规则一句 ——
|
|
5
|
+
* **店答出的拒是答案,店的故障不是**(与续跑认领之后的提交同一条律、同一对类别词:店答出 = `refused`,店没答出来 = `faulted`;
|
|
6
|
+
* 判在写口的两只出口上,结构判 —— 交回值 vs 抛 —— 不读错误文案)。
|
|
7
|
+
*
|
|
8
|
+
* 停驻写的 CAS = **行仍归本腿(持有腿 `legId`,认领交给本腿的那一只)∧ 仍在跑(或已停在本词上)∧ 没挂取消请求**(三件的属主 = 店的两只写口,
|
|
9
|
+
* `run-store-sql.ts` 的 `setSuspended` 头注;内存 / 文件孪生同判)。第一笔与每一次再提交是**同一个形**:写;店答出「不」⇒ 读行、由行答 ——
|
|
10
|
+
* · 行不在 ⇒ 兑现卡的 run 没了 ⇒ 作废卡(`card-void.ts` 的结算作废;卡的命运跟着行,行先、卡后);
|
|
11
|
+
* · 行已终局(不论哪条腿持有)⇒ run 收场了 ⇒ 作废卡(已不 pending 的卡作废是空操作);
|
|
12
|
+
* · 行归别的腿、还活着(在跑 / 停驻)⇒ 本腿先前那一笔落下了、卡被决、下一条腿接走了这条行(重开的同一张卡此刻归它)⇒ 停驻成立过,不写、卡不碰;
|
|
13
|
+
* · 行归本腿、停在本词上 ⇒ 本腿先前那一笔落下了(一笔抛了的写结局未知)⇒ 停驻成立;
|
|
14
|
+
* · 行归本腿、在跑、挂着取消请求(`POST /v1/runs/:id/cancel` 对在跑行只置旗)⇒ 取消先到:先作废卡、再落取消终局
|
|
15
|
+
* (唯一铸点 `mintCancelledResult`,码 `cancelled`)—— 不把一次已答「取消中」的 run 停成可决的停驻;
|
|
16
|
+
* · 行归本腿、在跑、没挂取消 ⇒ 店自相矛盾(CAS 说不、读说该写)= 不变量破 ⇒ 响亮记一次(`server.park.row-commit-contradicted`,带 taskId 与读到的词),
|
|
17
|
+
* 再当店故障处置(下面的再提交路)。
|
|
18
|
+
* 店故障(写 / 读 / 收场写抛:故障 / 超时 / 程序错)= `faulted` ⇒ **不收场**:不落 failed、不作废卡、认领原样留着,同一笔停驻再提交 ——
|
|
19
|
+
* 当拍一次,仍没答出 ⇒ 按心跳节拍续,直到店答出。上界 = 这台部署判活窗的两倍(`REAP_RUN_STALE_SEC`:一窗让这条不再有心跳的行落到既有的
|
|
20
|
+
* 失联判决射程里,一窗让判决落下、被下一次再提交读到 —— 读到即作废卡)。过了上界仍没答 ⇒ 停手,交既有出路:行由失联收割(`reapStale` /
|
|
21
|
+
* 观察口)按同一只判活窗收成 failed、放认领,卡由审批兜底收割按它的截止 / 绝对兜底时刻作废。不新造出路。
|
|
22
|
+
* 停驻的账本投影(停驻帧、它之前的用量差)不在这里:写口 `appendParkRow` 写不下只留痕,不改判词。
|
|
23
|
+
*/
|
|
24
|
+
import { type CardRef, type CardVoidPort } from "./card-void.js";
|
|
25
|
+
import type { RunStore } from "./plugins/store-backend.js";
|
|
26
|
+
import { type PersistedTaskResult, type RunRecord } from "./plugins/store-contracts.js";
|
|
27
|
+
import type { ParkLedgerEventType } from "./trace/ledger-events.js";
|
|
28
|
+
/**
|
|
29
|
+
* 一次停驻提交交回的读数(调用方按它收场,三形穷举):
|
|
30
|
+
* · `parked` —— 停驻成立(这一笔落下,或本腿先前那一笔已落下 —— 此后行可能已交给接手腿);
|
|
31
|
+
* · `pending` —— 店故障,再提交在续(行留 `running`、卡待决);
|
|
32
|
+
* · `ended` —— 这条 run 没有停住、已收场:`row` = 提交点读到 / 写下之后读回的那一行(终局;`undefined` = 行不在),卡已交作废。
|
|
33
|
+
* 这次收场是不是取消,读行的码(`row.errorCode === "cancelled"`,终局码的唯一读法)。
|
|
34
|
+
*/
|
|
35
|
+
export type ParkCommitOutcome = {
|
|
36
|
+
readonly kind: "parked";
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: "pending";
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: "ended";
|
|
41
|
+
readonly row: RunRecord | undefined;
|
|
42
|
+
};
|
|
43
|
+
export interface ParkRowCommit {
|
|
44
|
+
readonly runStore: Pick<RunStore, "setSuspended" | "setNeedsReview" | "getRun" | "setTerminal">;
|
|
45
|
+
readonly taskId: string;
|
|
46
|
+
/** 停在哪个词上(同一扇门的词:`plan_review` 落 `needs_review`,其余落 `suspended`)。 */
|
|
47
|
+
readonly word: ParkLedgerEventType;
|
|
48
|
+
/** 落在行上的结果(店入口再封口;`null` = 这条腿手里没有停驻结果 —— 重开 / 放回 / 兜底腿)。 */
|
|
49
|
+
readonly result: PersistedTaskResult | null;
|
|
50
|
+
/** 这次停驻在等的那张卡(凭据 + 它应归的会话 = 本 run 的会话)。店答出 run 已收场 ⇒ 作废的就是它。 */
|
|
51
|
+
readonly card: CardRef;
|
|
52
|
+
/** 读卡 / 作废的 checkpoint 店(缺席 ⇒ 作废那一步响亮留痕,见 `card-void.ts`)。 */
|
|
53
|
+
readonly cards: CardVoidPort | undefined;
|
|
54
|
+
/** 探针行上的产地(`leg=<…>`)。 */
|
|
55
|
+
readonly leg: "bg" | "resume" | "sync";
|
|
56
|
+
/** 本腿的持有腿(`RunRecord.legId`):认领交给本腿的那一只 —— 建行 `createRun` 的 `{ ok: true, legId }`、续跑认领 `markResuming` 的交回值。
|
|
57
|
+
* 停驻写只许它落下;拒后判拿它比行上的那一只。不是读来的:读到的持有腿可能已是接手腿的。 */
|
|
58
|
+
readonly legId: string;
|
|
59
|
+
/** 这台部署的判活窗(ms,`REAP_RUN_STALE_SEC` × 1000,活配置);缺席 ⇒ 缺省窗(不经装配直接调用的嵌入方 / 夹具)。 */
|
|
60
|
+
readonly staleWindowMs: number | undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 提交一次停驻(见模块头注)。不抛:第一笔抛 ⇒ 当拍再提交一次,仍抛 ⇒ 交回 `pending` 并起续提交;每一次失败、以及到上界停手,都经 F 类 tag
|
|
64
|
+
* `server.park.row-commit-deferred` 计数 + 探针行(detail `leg=<…> step=<first|again|retry|gave_up> err=<原句>`,不含凭据)。
|
|
65
|
+
*/
|
|
66
|
+
export declare function commitParkRow(c: ParkRowCommit): Promise<ParkCommitOutcome>;
|
|
67
|
+
//# sourceMappingURL=park-row-commit.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { voidPendingCard } from "./card-void.js";
|
|
2
|
+
import { LIVENESS_HEARTBEAT_MS } from "./config-invariants.js";
|
|
3
|
+
import { RUN_STALE_SEC_DEFAULT } from "./run-stale-default.js";
|
|
4
|
+
import { mintCancelledResult } from "./run-cancel-context.js";
|
|
5
|
+
import { errorText } from "./observability/err-text.js";
|
|
6
|
+
import { recordFailOpen } from "./observability/fail-open.js";
|
|
7
|
+
import { isTerminalRunStatus } from "./plugins/store-contracts.js";
|
|
8
|
+
const DEFERRED_TAG = "server.park.row-commit-deferred";
|
|
9
|
+
const CONTRADICTED_TAG = "server.park.row-commit-contradicted";
|
|
10
|
+
const VOID_TAG = "server.park.held-card-void-failed";
|
|
11
|
+
const CANCELLED_WHILE_PARKING = "cancelled before its park was committed (the pending approval was settled)";
|
|
12
|
+
const PARK_WRITE = {
|
|
13
|
+
suspended: (s, taskId, result, legId) => s.setSuspended(taskId, result, legId),
|
|
14
|
+
needs_review: (s, taskId, result, legId) => s.setNeedsReview(taskId, result, legId),
|
|
15
|
+
};
|
|
16
|
+
async function ended(c, row, at) {
|
|
17
|
+
await voidPendingCard(c.card, c.cards, VOID_TAG, `leg=${c.leg} at=${at}`);
|
|
18
|
+
return { kind: "ended", row };
|
|
19
|
+
}
|
|
20
|
+
async function settleByRow(c, row) {
|
|
21
|
+
if (row === undefined || isTerminalRunStatus(row.status))
|
|
22
|
+
return ended(c, row, "park_commit_refused");
|
|
23
|
+
if (row.legId !== c.legId)
|
|
24
|
+
return { kind: "parked" };
|
|
25
|
+
if (row.status !== "running")
|
|
26
|
+
return { kind: "parked" };
|
|
27
|
+
if (row.cancelRequested) {
|
|
28
|
+
await voidPendingCard(c.card, c.cards, VOID_TAG, `leg=${c.leg} at=park_commit_cancelled`);
|
|
29
|
+
const cancelled = mintCancelledResult({ taskId: c.taskId, sessionId: row.sessionId, startedAtMs: Date.parse(row.createdAt), message: CANCELLED_WHILE_PARKING, cause: "cancel_verb" });
|
|
30
|
+
await c.runStore.setTerminal(c.taskId, "failed", cancelled, CANCELLED_WHILE_PARKING);
|
|
31
|
+
return { kind: "ended", row: await c.runStore.getRun(c.taskId) };
|
|
32
|
+
}
|
|
33
|
+
recordFailOpen(CONTRADICTED_TAG, `leg=${c.leg} taskId=${c.taskId} word=${c.word} status=${row.status} holder=self cancel=false`);
|
|
34
|
+
throw new Error(`park commit: the store refused the ${c.word} write of run ${c.taskId} while its row reads as this leg's, running, with no cancel request`);
|
|
35
|
+
}
|
|
36
|
+
async function attempt(c) {
|
|
37
|
+
if ((await PARK_WRITE[c.word](c.runStore, c.taskId, c.result, c.legId)) !== false)
|
|
38
|
+
return { kind: "parked" };
|
|
39
|
+
return settleByRow(c, await c.runStore.getRun(c.taskId));
|
|
40
|
+
}
|
|
41
|
+
export async function commitParkRow(c) {
|
|
42
|
+
try {
|
|
43
|
+
return await attempt(c);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
recordFailOpen(DEFERRED_TAG, `leg=${c.leg} step=first err=${errorText(e)}`);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return await attempt(c);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
recordFailOpen(DEFERRED_TAG, `leg=${c.leg} step=again err=${errorText(e)}`);
|
|
53
|
+
}
|
|
54
|
+
continueParkCommit(c);
|
|
55
|
+
return { kind: "pending" };
|
|
56
|
+
}
|
|
57
|
+
function continueParkCommit(c) {
|
|
58
|
+
const untilMs = Date.now() + 2 * (c.staleWindowMs ?? RUN_STALE_SEC_DEFAULT * 1000);
|
|
59
|
+
let inFlight = false;
|
|
60
|
+
const timer = setInterval(() => {
|
|
61
|
+
if (inFlight)
|
|
62
|
+
return;
|
|
63
|
+
inFlight = true;
|
|
64
|
+
void attempt(c).then(() => clearInterval(timer), (e) => {
|
|
65
|
+
inFlight = false;
|
|
66
|
+
if (Date.now() < untilMs) {
|
|
67
|
+
recordFailOpen(DEFERRED_TAG, `leg=${c.leg} step=retry err=${errorText(e)}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
clearInterval(timer);
|
|
71
|
+
recordFailOpen(DEFERRED_TAG, `leg=${c.leg} step=gave_up err=${errorText(e)}`);
|
|
72
|
+
});
|
|
73
|
+
}, LIVENESS_HEARTBEAT_MS);
|
|
74
|
+
timer.unref?.();
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=park-row-commit.js.map
|
package/dist/parked-decide.d.ts
CHANGED
|
@@ -16,8 +16,10 @@ import type { ParkedReviveSurfaceFactory } from "./parked-revive-surface.js";
|
|
|
16
16
|
import type { RebuiltInheritedGate } from "./boot/parked-revive-gate.js";
|
|
17
17
|
import { type WorkflowRunParkKey, type WorkflowAgentSessionIndex } from "./orchestration/workflow-agent-session-index.js";
|
|
18
18
|
import type { WorkflowParkDecisionStore, WorkflowParkOutcome } from "./orchestration/workflow-park-decision.js";
|
|
19
|
+
import { type ApprovalLegOrigin } from "./approval-leg-origin.js";
|
|
19
20
|
import type { SessionFace } from "./session-incarnation.js";
|
|
20
|
-
import { type
|
|
21
|
+
import { type CardSettlePort } from "./card-settle-stored-scope.js";
|
|
22
|
+
import { type ClaimFace, type ExecutionCommitDeps, type RootedExecutionContext } from "./http/execution-context.js";
|
|
21
23
|
import type { GateIdentity } from "./checkpoint-identity.js";
|
|
22
24
|
/**
|
|
23
25
|
* 本腿的**装配前提**(`createHttpServer` 起服拒的拒句;规则见 {@link ParkedDecideDeps} 的 `execution`):接了本腿(`parkedReviveSurface` 在场)的部署,
|
|
@@ -155,8 +157,8 @@ export declare function findParkedAgentForCheckpoint(agentStore: BackgroundAgent
|
|
|
155
157
|
}): Promise<ParkedAgentMatch | undefined>;
|
|
156
158
|
export interface ParkedDecideDeps {
|
|
157
159
|
agentStore: BackgroundAgentStore;
|
|
158
|
-
/** `expire
|
|
159
|
-
checkpointStore: Pick<CheckpointStore, "get" | "expire"
|
|
160
|
+
/** `expire` + `peekScopeByToken` + 状态读:[ref] deny 作废复活不了的卡(`expireCardUnderStoredScope`:先读状态、再按店里该卡的属主格 CAS);`get`:引擎的 claim / 对账读卡。 */
|
|
161
|
+
checkpointStore: Pick<CheckpointStore, "get" | "expire"> & CardSettlePort;
|
|
160
162
|
/** 与 reaper/scenarios 同一个 registry(claim/rollback 的 CAS 与 in-memory handle 收敛在同处;[ref] 作废之后的对账也是它)。 */
|
|
161
163
|
registry: Pick<TaskRegistry, "claimParkedAgent" | "rollbackParkedClaim" | "reconcileParkedAgents">;
|
|
162
164
|
/** [ref](core 7.32.0):按**行**解析赎回面 —— 驱动哪一只 Agent 工具(生产装配,与 spawn 同源:subRunner / bg opts /
|
|
@@ -188,19 +190,19 @@ export interface ParkedDecideDeps {
|
|
|
188
190
|
owner: string | null;
|
|
189
191
|
taskId: string;
|
|
190
192
|
sessionId: string;
|
|
191
|
-
|
|
192
|
-
}) => NonNullable<ToolExecuteContext["onAsk"]>;
|
|
193
|
+
} & ApprovalLegOrigin) => NonNullable<ToolExecuteContext["onAsk"]>;
|
|
193
194
|
/**
|
|
194
195
|
* 边界 9 的执行上下文(设计 §3.2;与提交腿 / 续跑家族同一只解析器、同一只写口)—— 本腿对根会话的住客**一读一判**:
|
|
195
196
|
* · `resolve` —— **认领之前**按行的根取上下文:解析器那一次行读(执行席:住客 + 格)就是本腿对根会话的**唯一一读**;根行不在 ⇒ `gone`
|
|
196
|
-
* (本腿答既有的 `decide.not_parked`);其余拒(mirror / 车道不再服务 / 主体的车道裁定)按续跑家族的折叠律答好交回(`answer`)
|
|
197
|
+
* (本腿答既有的 `decide.not_parked`);其余拒(mirror / 车道不再服务 / 主体的车道裁定)按续跑家族的折叠律答好交回(`answer`);店没答出来 ⇒ 本腿交来的这一面
|
|
198
|
+
* `face` 的瞬时答复(`answer`,与认领那一笔撞店故障同答)—— 不认领、不驱动。
|
|
197
199
|
* 成功形的 `occupant` **必有**:接本腿的部署必须带执行席(无席的会话店交不出这一读;`createHttpServer` 在装配时拒无席形);
|
|
198
200
|
* · `commitDeps` —— 认领之后 `commitExecutionContext` 读的部署件:唯一写口按那一读的住客定址写 / 再读(化身 ∧ 属主),这就是认领之后对根住客的
|
|
199
201
|
* **唯一一判**(拒 ⇒ 先回滚认领)。写口放行的那一位就是赎回面的根住客见证;写口之后本腿不再读根。
|
|
200
202
|
* 复活子代的审批席带这只上下文的车道(`approverSeatFor` 的 `executionLane`)。
|
|
201
203
|
*/
|
|
202
204
|
execution: {
|
|
203
|
-
resolve: (root: string) => Promise<{
|
|
205
|
+
resolve: (root: string, face: ClaimFace) => Promise<{
|
|
204
206
|
readonly ok: true;
|
|
205
207
|
readonly ctx: RootedExecutionContext & {
|
|
206
208
|
readonly occupant: SessionFace;
|
package/dist/parked-decide.js
CHANGED
|
@@ -5,10 +5,12 @@ import { recordFailOpen } from "./observability/fail-open.js";
|
|
|
5
5
|
import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
|
|
6
6
|
import { backgroundScopesForCheckpointScope, decodeCheckpointScope, ownerOfCheckpointScope } from "./security.js";
|
|
7
7
|
import { ASK_USER_QUESTION_TOOL_NAME, isEngineQuestionRow } from "./approval-content-kind.js";
|
|
8
|
+
import { approvalLegOriginOf } from "./approval-leg-origin.js";
|
|
8
9
|
import { refusalBody, resumeRefusalFacts } from "./http/refusal-body.js";
|
|
9
10
|
import { backgroundAgentNameOf } from "./background-agent-name.js";
|
|
10
11
|
import { buildSettlementEvidence, NO_SETTLEMENT_EVIDENCE } from "./gate-settlement.js";
|
|
11
|
-
import {
|
|
12
|
+
import { CARD_EXPIRE_OUTCOME, expireCardUnderStoredScope } from "./card-settle-stored-scope.js";
|
|
13
|
+
import { cardWaitingRefusalAnswer, commitExecutionContext, faultedAnswerOf, isRootSessionGoneRefusal } from "./http/execution-context.js";
|
|
12
14
|
import { EXECUTION_PLACEMENT_SEAT_CARRIERS } from "./execution-placement.js";
|
|
13
15
|
export const PARKED_REVIVE_EXECUTION_SEAT_REQUIRED = `the parked-agent revive leg (parkedReviveSurface) needs a session store with an execution placement seat (sessionStorage.executionPlacement) — after the claim, the root session's occupant is judged only by the write on that seat; ${EXECUTION_PLACEMENT_SEAT_CARRIERS}; refusing to start rather than revive parked agents with nobody judging the root session's occupant after the claim`;
|
|
14
16
|
export async function findParkOrigin(sources, cp) {
|
|
@@ -346,6 +348,15 @@ function sessionGone(taskId) {
|
|
|
346
348
|
}),
|
|
347
349
|
};
|
|
348
350
|
}
|
|
351
|
+
function parkedClaimFaultAnswer(taskId) {
|
|
352
|
+
return { status: 503, body: refusalBody({ errorCode: "decide.store_unreachable", message: "the stored state this decision needs could not be read or written just now (the store failed) — nothing was decided and the agent is still waiting for it; retry", facts: { taskId } }) };
|
|
353
|
+
}
|
|
354
|
+
function parkedRedeemFace(taskId) {
|
|
355
|
+
return {
|
|
356
|
+
refused: (refusal) => (isRootSessionGoneRefusal(refusal) ? sessionGone(taskId) : cardWaitingRefusalAnswer(refusal)),
|
|
357
|
+
faulted: () => parkedClaimFaultAnswer(taskId),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
349
360
|
function gateMoved(taskId) {
|
|
350
361
|
return { status: 409, body: refusalBody({ errorCode: "decide.gate_moved", message: "the approval gate moved under this decide (re-parked or already settled) — re-check the pending list", facts: { taskId } }) };
|
|
351
362
|
}
|
|
@@ -356,7 +367,7 @@ function revivabilityOf(row, taskId) {
|
|
|
356
367
|
return { kind: "revivable", root: row.rootSessionId };
|
|
357
368
|
}
|
|
358
369
|
async function voidUnrevivableCard(deps, req, match) {
|
|
359
|
-
const voided = await deps.checkpointStore
|
|
370
|
+
const voided = (await expireCardUnderStoredScope(deps.checkpointStore, req.token)) === CARD_EXPIRE_OUTCOME.expired;
|
|
360
371
|
if (!voided) {
|
|
361
372
|
return gateMoved(match.handle);
|
|
362
373
|
}
|
|
@@ -385,7 +396,8 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
385
396
|
if (revivability.kind === "unrevivable")
|
|
386
397
|
return req.decision === "deny" ? voidUnrevivableCard(deps, req, match) : revivability.approveRefusal;
|
|
387
398
|
const root = revivability.root;
|
|
388
|
-
const
|
|
399
|
+
const face = parkedRedeemFace(match.handle);
|
|
400
|
+
const resolved = await deps.execution.resolve(root, face);
|
|
389
401
|
if (!resolved.ok)
|
|
390
402
|
return resolved.gone ? sessionGone(match.handle) : resolved.answer;
|
|
391
403
|
const execution = resolved.ctx;
|
|
@@ -395,7 +407,7 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
395
407
|
if (!claim.ok) {
|
|
396
408
|
switch (claim.reason) {
|
|
397
409
|
case "store_unreachable":
|
|
398
|
-
return
|
|
410
|
+
return face.faulted();
|
|
399
411
|
case "claim_lost":
|
|
400
412
|
return { status: 409, body: refusalBody({ errorCode: "decide.claim_lost", message: "the parked agent is already claimed — either a concurrent decide won (re-check the pending list), or a stale claim is pending automatic cleanup (retry after the stale-claim window)", facts: { taskId: match.handle } }) };
|
|
401
413
|
case "binding_broken":
|
|
@@ -428,7 +440,14 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
428
440
|
await rollback("claimed_token_moved");
|
|
429
441
|
return gateMoved(match.handle);
|
|
430
442
|
}
|
|
431
|
-
|
|
443
|
+
let placementRefusal;
|
|
444
|
+
try {
|
|
445
|
+
const refusal = await commitExecutionContext(deps.execution.commitDeps, { kind: "parked", taskId: match.handle, rollback }, execution);
|
|
446
|
+
placementRefusal = refusal === null ? null : face.refused(refusal);
|
|
447
|
+
}
|
|
448
|
+
catch (e) {
|
|
449
|
+
placementRefusal = faultedAnswerOf(e, face.faulted);
|
|
450
|
+
}
|
|
432
451
|
if (placementRefusal)
|
|
433
452
|
return placementRefusal;
|
|
434
453
|
let surface;
|
|
@@ -457,7 +476,7 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
457
476
|
...(surface.mountsCallerTool !== undefined ? { mountsCallerTool: surface.mountsCallerTool } : {}),
|
|
458
477
|
...(surface.onBackgroundChildEvent !== undefined ? { onBackgroundChildEvent: surface.onBackgroundChildEvent } : {}),
|
|
459
478
|
...(deps.approverSeatFor !== undefined && seatRoot !== undefined
|
|
460
|
-
? { onAsk: deps.approverSeatFor({ owner: ownerOfCheckpointScope(req.scope), taskId: match.handle, sessionId: seatRoot,
|
|
479
|
+
? { onAsk: deps.approverSeatFor({ owner: ownerOfCheckpointScope(req.scope), taskId: match.handle, sessionId: seatRoot, ...approvalLegOriginOf(execution) }) }
|
|
461
480
|
: {}),
|
|
462
481
|
reviveClaim: {
|
|
463
482
|
row: { ...ticket.row, parkClaimId: ticket.claimId },
|
package/dist/peer-directory.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* ⚠️ **共享的是谓词,不是最终集合**(codex 对抗复审 r1 ② 采纳,如实收窄 —— 稿里与本注初版都写成了
|
|
20
20
|
* 「两面集合相等」,那是**过度承诺**):两面在下面这些地方按设计给出不同的集合,逐条都不是 bug ——
|
|
21
|
-
* · **fleet-wide 调用方**:`GET /v1/sessions`
|
|
21
|
+
* · **fleet-wide 调用方**:`GET /v1/sessions` 对不报主体的 service-token / dev-open 调用方不钉 owner、还认 `?owner=`
|
|
22
22
|
* (运维视图);目录**只**按 core 递进来的那一个 scope 分区,永不跨;
|
|
23
23
|
* · **分页与搜索**:那条路由有 `?limit`/`?cursor`/`?q=`,目录是一次定量读(见 {@link PEER_DIRECTORY_CAP});
|
|
24
24
|
* · **peer 可寻址性过滤**:语法外 / 非小写规范形 / 与别条会话折到同一只箱的 id 都不入目录(下面那道
|
|
@@ -193,9 +193,9 @@ export declare class SqlAdoptionLogStore implements AdoptionLogStore {
|
|
|
193
193
|
/**
|
|
194
194
|
* 收编弧的 advisory 锁(183 I1 的 form b 形:「与任何活写互斥」在 SQL 侧 = 一次只有一个副本在推这条弧)。
|
|
195
195
|
*
|
|
196
|
-
* **非阻塞取 + 有界轮询**(
|
|
197
|
-
*
|
|
198
|
-
* 返回 `undefined`,调用方按「在飞」应答 —— **不假装成功,也不无限等**。
|
|
196
|
+
* **非阻塞取 + 有界轮询**(阻塞式 GET_LOCK 会把并发调用方全部 park 在 TiDB 的悲观锁行上,既耗它的重试预算又饿死应用自己的 FOR UPDATE 路)。
|
|
197
|
+
* 取锁走本仓**唯一的取锁器**(`ensure-schema-lock-policy.ts` 的 {@link acquireAdvisoryLockWithin}):探针句、答案读法、TiDB「悲观锁重试上限」
|
|
198
|
+
* 读成「别人持着」、坏连接计数全在那里,本店不自带第二只探针。取不到 ⇒ 返回 `undefined`,调用方按「在飞」应答 —— **不假装成功,也不无限等**。
|
|
199
199
|
*
|
|
200
200
|
* 🔴 入参 `name` 是**逻辑**锁名(`adoptionLockName(fromPrincipal)`);真正发给引擎的是它被
|
|
201
201
|
* {@link qualifyAdoptionLockName} 限定到本库之后的形([ref]:两方言键空间不等价)。限定发生在
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeLogText } from "../observability/logger.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
4
|
+
import { acquireAdvisoryLockWithin, releaseAdvisoryLock } from "./ensure-schema-lock-policy.js";
|
|
4
5
|
import { ensureIndexes } from "./ensure-index.js";
|
|
5
6
|
export const ADOPTION_LOG_TABLE = "adoption_log";
|
|
6
7
|
export const ADOPTION_PHASE = {
|
|
@@ -77,6 +78,18 @@ const PG_ADOPTION_LOCK_CLASS = 4907;
|
|
|
77
78
|
function pgLockObjectKey(name) {
|
|
78
79
|
return createHash("sha256").update(name).digest().readInt32BE(0);
|
|
79
80
|
}
|
|
81
|
+
function arcLockId(dialect, qualifiedName) {
|
|
82
|
+
switch (dialect) {
|
|
83
|
+
case "mysql":
|
|
84
|
+
return { dialect, name: qualifiedName };
|
|
85
|
+
case "pg":
|
|
86
|
+
return { dialect, keys: [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(qualifiedName)] };
|
|
87
|
+
default: {
|
|
88
|
+
const unreachable = dialect;
|
|
89
|
+
throw new Error(`unreachable SQL dialect: ${String(unreachable)}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
80
93
|
export function adoptionLockName(fromPrincipal) {
|
|
81
94
|
return `sema_adoption:${createHash("sha256").update(fromPrincipal).digest("hex").slice(0, 32)}`;
|
|
82
95
|
}
|
|
@@ -188,30 +201,17 @@ export class SqlAdoptionLogStore {
|
|
|
188
201
|
}
|
|
189
202
|
async withLock(name, fn, attempts = 20, sleepMs = 100) {
|
|
190
203
|
const conn = await this.db.connect();
|
|
191
|
-
let held
|
|
192
|
-
let lockName = "";
|
|
204
|
+
let held;
|
|
193
205
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const res = await conn.query(this.q("SELECT GET_LOCK(?, 0) AS got", "SELECT pg_try_advisory_lock($1, $2) AS got"), this.db.dialect === "mysql" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
|
|
197
|
-
const got = res.rows[0]?.got;
|
|
198
|
-
held = got === 1 || got === true || got === "1";
|
|
199
|
-
if (!held)
|
|
200
|
-
await new Promise((r) => setTimeout(r, sleepMs));
|
|
201
|
-
}
|
|
202
|
-
if (!held)
|
|
206
|
+
const lock = arcLockId(this.db.dialect, qualifyAdoptionLockName(await this.resolveLockKeyspace(conn), name));
|
|
207
|
+
if (!(await acquireAdvisoryLockWithin(conn, lock, "adoption-log", attempts, sleepMs)))
|
|
203
208
|
return undefined;
|
|
209
|
+
held = lock;
|
|
204
210
|
return await fn(conn);
|
|
205
211
|
}
|
|
206
212
|
finally {
|
|
207
|
-
if (held)
|
|
208
|
-
|
|
209
|
-
await conn.query(this.q("SELECT RELEASE_LOCK(?) AS released", "SELECT pg_advisory_unlock($1, $2) AS released"), this.db.dialect === "mysql" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
|
|
210
|
-
}
|
|
211
|
-
catch (releaseErr) {
|
|
212
|
-
writeLogText("stderr", "[adoption-log] releasing the adoption arc lock failed (it auto-frees when the connection closes):", releaseErr);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
213
|
+
if (held !== undefined)
|
|
214
|
+
await releaseAdvisoryLock(conn, held, "adoption-log");
|
|
215
215
|
conn.release();
|
|
216
216
|
}
|
|
217
217
|
}
|
|
@@ -715,7 +715,7 @@ export declare function ensurePgApprovalAskSchema(q: PgQueryFn): Promise<void>;
|
|
|
715
715
|
*
|
|
716
716
|
* 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**(判据属主 `sql-errors.ts` 的
|
|
717
717
|
* {@link isMissingColumnError});其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
|
|
718
|
-
* 比它要挡的缺陷更贵([ref] 那两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link
|
|
718
|
+
* 比它要挡的缺陷更贵([ref] 那两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal}),没有销毁性的一条。
|
|
719
719
|
*
|
|
720
720
|
* 🔴 位置与两条先例同款:**不在** `ensureSchema` 里(那条通道的契约是「只发 CREATE」,有运行时门看着),
|
|
721
721
|
* 放在 DDL 之后、任何路由装配之前;且**只在流内审批协议真会上场时**跑(判据 = 与协调器同一个符号
|
|
@@ -4,7 +4,7 @@ import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
|
|
|
4
4
|
import { DECIDED_VIA } from "@sema-agent/core";
|
|
5
5
|
import { APPROVAL_ENDING_CAUSES, askRowEndingCause, askRowParkTrigger } from "../approval-deny-reasons.js";
|
|
6
6
|
import { isDupKeyError, isMissingColumnError } from "./sql-errors.js";
|
|
7
|
-
import {
|
|
7
|
+
import { probedMissingColumnRefusal } from "./schema-drift-sentence.js";
|
|
8
8
|
import { decodeJsonColumn, foldJsonColumn, jsonColumnText, readJsonObjectColumn, readTriStateJsonValueColumn } from "./sql-json-column.js";
|
|
9
9
|
import { ensureIndexes } from "./ensure-index.js";
|
|
10
10
|
import { isExecutionLane } from "../execution-lane-caps.js";
|
|
@@ -334,7 +334,7 @@ export async function assertApprovalAskRuleMaterialSchema(query, dialect) {
|
|
|
334
334
|
catch (err) {
|
|
335
335
|
if (!isMissingColumnError(err, dialect))
|
|
336
336
|
throw err;
|
|
337
|
-
throw
|
|
337
|
+
throw await probedMissingColumnRefusal(query, dialect, [APPROVAL_ASK_TABLE], `${APPROVAL_ASK_TABLE} is missing the #363 rule-material columns (rule_command / rule_scope_root) and/or the S-26 edited-input column (updated_input) — refusing to start. ` +
|
|
338
338
|
`A parked approval's late decision can now carry \`persistRule\` ("don't ask again"), and the row is where the ` +
|
|
339
339
|
`adjudicated command and the project root of the authorization site live; a decision's edited input (ctrl+g) is ` +
|
|
340
340
|
`persisted on the row too, so a replica that does not hold the waiting leg replays the approved bytes; every ask-row ` +
|
|
@@ -342,9 +342,7 @@ export async function assertApprovalAskRuleMaterialSchema(query, dialect) {
|
|
|
342
342
|
`is persisted, the replay list stays empty, late decisions 404) while /v1/capabilities still reports it active. ` +
|
|
343
343
|
`The table holds IN-FLIGHT approval asks (cards awaiting an answer) plus settled bookkeeping; the durable park face ` +
|
|
344
344
|
`(checkpoint rows) and the persisted rules themselves are untouched, so an in-flight card falls back to the durable gate ` +
|
|
345
|
-
`(\`POST /v1/approvals/:sessionId/decide\`) until the columns exist
|
|
346
|
-
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
347
|
-
(await describeProbedTableDrift(query, dialect, [APPROVAL_ASK_TABLE])));
|
|
345
|
+
`(\`POST /v1/approvals/:sessionId/decide\`) until the columns exist.`, err);
|
|
348
346
|
}
|
|
349
347
|
}
|
|
350
348
|
function askCols(dialect) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Logger } from "../observability/logger.js";
|
|
2
|
+
import type { RetiredSchemaProbeTarget } from "./schema-consistency.js";
|
|
3
|
+
import type { SqlDialect } from "./sql-driver.js";
|
|
4
|
+
import { StoreBootRefusalError } from "./store-contracts.js";
|
|
5
|
+
/** 被判的行的 wire 工具名 —— 引擎 `Skill` 工具的名字,core 导出(不手抄;core 改名 ⇒ 谓词跟着走)。 */
|
|
6
|
+
export declare const SKILL_EXEMPTION_TOOL_NAME: string;
|
|
7
|
+
/** 第一只落截点、删截点之前的行的版本(一次性迁移 M46 的引入版本;登记表 M46 行在时两者逐字相同 —— `test/approval-exemption-skill-cutoff.test.ts` 钉)。拒句点名的出路版本下限。 */
|
|
8
|
+
export declare const SKILL_EXEMPTION_CUTOFF_SINCE = "7.148.0";
|
|
9
|
+
/** local 车道的截点文件(数据根顶层;文件名沿用 M46 —— 7.148.0 起的数据根里已经是它)。 */
|
|
10
|
+
export declare const LOCAL_SKILL_EXEMPTION_CUTOFF_FILE = "one-time-migration-M46.marker";
|
|
11
|
+
/** 截点之前的行写下时的意思与今天读法的差 —— SQL 拒句与 local 读数段同一句。 */
|
|
12
|
+
export declare const PRE_CUTOFF_SKILL_EXEMPTION_CONSEQUENCE: string;
|
|
13
|
+
/** 判用的读口:一句带参数的查询 ⇒ 行。 */
|
|
14
|
+
type CutoffQuery = (sql: string, params?: readonly unknown[]) => Promise<Array<Record<string, unknown>>>;
|
|
15
|
+
/**
|
|
16
|
+
* **SQL 截点的唯一读者**(常设臂与一次性迁移 M46 的删句同读;调用方先确认标记表在):行不在 ⇒ `undefined`(缺席);行在而读不出时刻 ⇒ 拒启 {@link StoreBootRefusalError}
|
|
17
|
+
* —— 不按「此刻」、也不按「没有」猜(非法时刻进比较,驱动把它写成 `NULL`,`created_at < NULL` 数出 0 = 静默放行)。拒句只带读到的数值与出路。
|
|
18
|
+
*/
|
|
19
|
+
export declare function readSqlSkillExemptionCutoff(query: CutoffQuery, dialect: SqlDialect): Promise<number | undefined>;
|
|
20
|
+
/**
|
|
21
|
+
* SQL 腿的拒启:这只库里有首次放行早于截点(或截点缺席)的 `Skill` 豁免行,而本构建已不带删它们的一次性迁移。**具名类型**(族基类 {@link StoreBootRefusalError}:
|
|
22
|
+
* 起服降级臂不吞、开库等待窗不重试);码与退役 schema / 列册对账同一个 `config.schema_mismatch`。拒句只带行数、截点与工具名(行的会话 / 放行人不进句子)。
|
|
23
|
+
*/
|
|
24
|
+
export declare class LegacySkillExemptionsPresentError extends StoreBootRefusalError {
|
|
25
|
+
/** 截点之前的 `Skill` 行数。 */
|
|
26
|
+
readonly rows: number;
|
|
27
|
+
/** 截点(ISO 时刻;`null` = 这只库没记截点:从没在落截点的版本上起过服)。 */
|
|
28
|
+
readonly cutoff: string | null;
|
|
29
|
+
/** 机读码(与退役 schema / 列册对账的拒启同一个)。 */
|
|
30
|
+
readonly code = "config.schema_mismatch";
|
|
31
|
+
constructor(
|
|
32
|
+
/** 截点之前的 `Skill` 行数。 */
|
|
33
|
+
rows: number,
|
|
34
|
+
/** 截点(ISO 时刻;`null` = 这只库没记截点:从没在落截点的版本上起过服)。 */
|
|
35
|
+
cutoff: string | null);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* SQL 腿的判(纯读,**唯一一只**;律见文件头):读截点({@link readSqlSkillExemptionCutoff};标记表不在 ⇒ 缺席,读不出 ⇒ 拒 —— `carried` 也判,建表之前)→
|
|
39
|
+
* `carried`(本构建还带 M46)或豁免表不在(新库,建表之前)⇒ 无事 → 数截点之前的 `Skill` 行(截点缺席 ⇒ 全部 `Skill` 行)→ 非零 ⇒ 拒。
|
|
40
|
+
* 谓词与 M46 的删句同一条,比较在库里做(`created_at < ?`,参数是截点的 `Date`)。读抛 ⇒ 原样上抛(判不了 ⇒ 不放行;不是确定的拒启表态)。
|
|
41
|
+
*/
|
|
42
|
+
export declare function judgeSqlSkillExemptionCutoff(query: CutoffQuery, dialect: SqlDialect, carried: boolean): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* **探针口**(SQL 腿的判唯一的读处;唯一调用点 = `store-backend.ts` 的 `withRetiredSchemaProbe`,先于第一条 CREATE):借一条连接判一次、用完即还(拒时连接也已归还);
|
|
45
|
+
* `carried`(本构建还带 M46)⇒ 只验截点读得出、不数行(存量行交给 M46 删)。不取 `ensureSchema` 那把锁:纯读。
|
|
46
|
+
*/
|
|
47
|
+
export declare function assertSkillExemptionCutoffHeldOnPool(target: RetiredSchemaProbeTarget, carried: boolean): Promise<void>;
|
|
48
|
+
/** local 截点文件正文 ⇒ T0(epoch 毫秒);读不出时刻 ⇒ `undefined`(调用方决定拒形:准入点名文件拒、M46 的腿拒并给「删掉再起」)。 */
|
|
49
|
+
export declare function skillExemptionCutoffOfText(text: string): number | undefined;
|
|
50
|
+
/** 写截点文件(原子、`required`:目录项没证落盘也算没写成 ⇒ 抛)。本车道截点文件唯一的写口:准入的锁内一步与 M46 的腿同调。 */
|
|
51
|
+
export declare function writeLocalSkillExemptionCutoff(path: string, cutoffMs: number): void;
|
|
52
|
+
/**
|
|
53
|
+
* 截点之前的 `Skill` 行所在的会话数(`cutoffMs` 缺席 ⇒ 任一 `Skill` 行都算):逐只日志经店的唯一读者 {@link readExemptionLog} 重放(同一套撤销 / 首次放行读法);
|
|
54
|
+
* 闸合上的日志不算(店对那只会话拒读、照问)。坏行的披露走 `warn`(缺席 ⇒ 日志接缝;同一次起服里店的 hydrate 会再披露一次)。
|
|
55
|
+
*/
|
|
56
|
+
export declare function localSkillExemptionSessionsBefore(root: string, cutoffMs: number | undefined, warn?: Logger["warn"]): number;
|
|
57
|
+
/** 豁免日志目录的指纹(文件名 · 字节数 · 修改时刻;按名排序)—— 准入与锁内「没动」核对比同一只(锁内不再重放日志)。 */
|
|
58
|
+
export declare function exemptionLogFingerprints(root: string): string[];
|
|
59
|
+
/** 截点在 local 上的动作(准入判、锁内做):`lay` = 截点缺席,落「此刻」;`none` = 不写。 */
|
|
60
|
+
export type SkillExemptionCutoffAction = "none" | "lay";
|
|
61
|
+
/** **落截点**(锁内;准入的读数核过之后、抬引擎版本高水位之前;唯一调用点 = 数据根准入的锁内一步)。写不进 ⇒ 抛(抬之前拒)。 */
|
|
62
|
+
export declare function settleLocalSkillExemptionCutoff(physicalRoot: string, action: SkillExemptionCutoffAction, now?: () => number): void;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=approval-exemption-skill-cutoff.d.ts.map
|