@sema-agent/server 7.98.0 → 7.100.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 +19 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +86 -60
- package/deploy/sema-up/lib/envf.sh +110 -42
- package/deploy/sema-up/sema-up.sh +76 -34
- package/dist/approval-ask-machine.d.ts +10 -0
- package/dist/approval-ask-machine.js +3 -0
- package/dist/approval-card.d.ts +74 -1
- package/dist/approval-card.js +41 -8
- package/dist/approval.d.ts +44 -20
- package/dist/approval.js +14 -6
- package/dist/boot/config-center.d.ts +16 -8
- package/dist/boot/config-center.js +206 -103
- package/dist/boot/execution-env.d.ts +1 -2
- package/dist/boot/execution-env.js +2 -2
- package/dist/boot/leader.js +12 -9
- package/dist/boot/memory-consolidation.d.ts +38 -0
- package/dist/boot/memory-consolidation.js +35 -0
- package/dist/boot/org-memory.d.ts +2 -1
- package/dist/boot/org-memory.js +9 -5
- package/dist/boot/resolve-spec.d.ts +0 -1
- package/dist/boot/resolve-spec.js +4 -3
- package/dist/boot/runner-deps.d.ts +2 -2
- package/dist/boot/runtime-caps.js +4 -4
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +3 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -3
- package/dist/boot/stage-07-capability-layer.js +6 -6
- package/dist/boot/stage-08-reapers.d.ts +3 -4
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -3
- package/dist/boot/stage-10-http-server.d.ts +2 -3
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/capabilities/center-plugins.d.ts +14 -2
- package/dist/capabilities/center-plugins.js +13 -5
- package/dist/capabilities/center-prompts.d.ts +3 -0
- package/dist/capabilities/center-prompts.js +1 -1
- package/dist/capabilities/hands-lane.js +2 -1
- package/dist/capabilities/scenarios.js +18 -5
- package/dist/capabilities/tool-defer.d.ts +2 -2
- package/dist/center-credential.d.ts +112 -0
- package/dist/center-credential.js +131 -0
- package/dist/config-catalog.d.ts +2 -2
- package/dist/config-catalog.js +15 -15
- package/dist/config-center/apply-effective.d.ts +34 -39
- package/dist/config-center/apply-effective.js +100 -64
- package/dist/config-center/apply-ledger.d.ts +35 -16
- package/dist/config-center/apply-ledger.js +25 -18
- package/dist/config-center/center-request.d.ts +60 -0
- package/dist/config-center/center-request.js +71 -0
- package/dist/config-center/facade.d.ts +11 -5
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/hot-keys-registry.d.ts +11 -4
- package/dist/config-center/hot-keys-registry.js +11 -7
- package/dist/config-center/http-client.d.ts +17 -9
- package/dist/config-center/http-client.js +27 -36
- package/dist/config-center/restart-signal.d.ts +21 -14
- package/dist/config-center/restart-signal.js +16 -22
- package/dist/config-center/skills-mcp.d.ts +4 -1
- package/dist/config-center/skills-mcp.js +2 -2
- package/dist/config-center/types.d.ts +3 -3
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +0 -8
- package/dist/config-lkg.d.ts +47 -8
- package/dist/config-lkg.js +107 -20
- package/dist/config-provider.d.ts +8 -7
- package/dist/config-provider.js +5 -5
- package/dist/config-types.d.ts +30 -11
- package/dist/config.js +11 -13
- package/dist/cross-session-settings.js +1 -1
- package/dist/fleet-client.d.ts +8 -8
- package/dist/fleet-client.js +7 -7
- package/dist/fleet-lease.d.ts +11 -11
- package/dist/fleet-lease.js +17 -13
- package/dist/host-lsp-manager.d.ts +29 -0
- package/dist/host-lsp-manager.js +14 -0
- package/dist/hosted-posture.d.ts +49 -93
- package/dist/hosted-posture.js +1 -25
- package/dist/http/active-run-conflict.d.ts +2 -12
- package/dist/http/active-run-conflict.js +1 -6
- package/dist/http/admission.js +8 -0
- package/dist/http/resume-legs.d.ts +1 -1
- package/dist/http/resume-legs.js +14 -10
- package/dist/http/route-ctx.d.ts +2 -2
- package/dist/http/routes/approvals-assistant.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +2 -2
- package/dist/http/routes/memory-compliance.js +5 -0
- package/dist/http/routes/memory-origin.js +5 -0
- package/dist/http/routes/sessions.js +6 -0
- package/dist/http/routes/tasks.js +7 -6
- package/dist/http/server.d.ts +6 -5
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/leader/wire.d.ts +20 -12
- package/dist/leader/wire.js +7 -6
- package/dist/memory-layer-preflight.d.ts +46 -0
- package/dist/memory-layer-preflight.js +121 -0
- package/dist/memory-operator-faces.js +17 -1
- package/dist/observability/corrupt-read-seat.d.ts +21 -4
- package/dist/observability/corrupt-read-seat.js +31 -0
- package/dist/observability/fail-open.d.ts +8 -4
- package/dist/observability/fail-open.js +8 -4
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +3 -26
- package/dist/observability/secret-env-scrub.js +3 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
- package/dist/orchestration/workflow-completion-inbox.js +8 -3
- package/dist/plugins/approval-ask-store-file.d.ts +20 -2
- package/dist/plugins/approval-ask-store-file.js +21 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
- package/dist/plugins/approval-ask-store-memory.js +39 -13
- package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
- package/dist/plugins/approval-ask-store-sql.js +27 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
- package/dist/plugins/checkpoint-store-sql.js +19 -2
- package/dist/plugins/local-checkpoint-store.js +2 -0
- package/dist/plugins/mailbox-store-sql.d.ts +7 -5
- package/dist/plugins/mailbox-store-sql.js +6 -4
- package/dist/plugins/permission-rule-store-file.js +3 -2
- package/dist/plugins/remote-env-host.d.ts +3 -1
- package/dist/plugins/remote-env-host.js +3 -2
- package/dist/plugins/sql-json-column.d.ts +16 -3
- package/dist/plugins/sql-json-column.js +15 -4
- package/dist/plugins/web-search.d.ts +42 -22
- package/dist/plugins/web-search.js +141 -49
- package/dist/project-memory.d.ts +28 -1
- package/dist/project-memory.js +22 -22
- package/dist/run-local.js +6 -7
- package/dist/runs.js +6 -5
- package/dist/runtime-caps-resolver.d.ts +8 -8
- package/dist/runtime-caps-resolver.js +16 -14
- package/dist/runtime-governance.d.ts +50 -3
- package/dist/runtime-governance.js +5 -0
- package/dist/sealed-key.js +3 -7
- package/dist/server-secret-env.d.ts +43 -0
- package/dist/server-secret-env.js +18 -0
- package/dist/task-settings.d.ts +1 -1
- package/dist/tool-approval.d.ts +144 -9
- package/dist/tool-approval.js +74 -18
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +22 -5
- package/dist/trace/projection-drop.d.ts +2 -0
- package/dist/trace/projection-drop.js +6 -0
- package/dist/trace/sema-provenance.d.ts +4 -1
- package/dist/trace/sema-provenance.js +1 -1
- package/dist/trace/task-notification-facets.d.ts +65 -0
- package/dist/trace/task-notification-facets.js +31 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +3 -3
- package/package.json +5 -4
package/dist/tool-approval.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { HostDecidedBy, HostSettledBy } from "./host-decision.js";
|
|
2
2
|
import type { IncomingMessage } from "node:http";
|
|
3
|
-
import { type AskRequest, type AskOutcome, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
|
|
3
|
+
import { type AskRequest, type AskOutcome, type CheckpointGate, type PendingAction, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
|
|
4
4
|
import { type CardRulePersisted, type RuleConsentLane } from "./rules-consent.js";
|
|
5
5
|
import { type DenialLimitFallback, type RuleOffersAbsence, type ApprovalRequestFrame, type ApprovalRevokeFrame, type RuleEvidenceProjection } from "./approval-card.js";
|
|
6
6
|
import { type ApprovalAskStore, type AskRow } from "./plugins/approval-ask-store-sql.js";
|
|
7
7
|
import type { ApprovalAskAuditSink } from "./approval-ask-audit-store.js";
|
|
8
8
|
import { type GovernanceAskMarks } from "./governance-ask-marks.js";
|
|
9
|
+
import { type GovernancePosture } from "./runtime-governance.js";
|
|
9
10
|
/** [ref]:审批 gate kind 闭集(core gateMatch 的 `human`/`irreversible_ask` ↔ outcome.gate "policy_ask")。
|
|
10
11
|
* 此前 7 份手写副本散在两只 checkpoint store 的数组/SQL 字面与 /decide 守卫——core 加审批味 kind 时
|
|
11
12
|
* 全部静默漂移。core 无导出词表(全大写导出面零命中,2026-08-09 亲验),属主落此;SQL IN 片段从
|
|
@@ -76,6 +77,87 @@ export type ParkedAskRedeem = (req: ParkedAskRedeemRequest) => Promise<{
|
|
|
76
77
|
status: number;
|
|
77
78
|
body: Record<string, unknown>;
|
|
78
79
|
}>;
|
|
80
|
+
/**
|
|
81
|
+
* 一问身上与「站着的答案」有关的三个事实位 —— {@link standingAnswerMayRetire} 的全部入参。
|
|
82
|
+
*
|
|
83
|
+
* 🔴 **三格全必填布尔**,刻意不做 `?: true` 的「只记真」形:判据哪天多一位,每个读者都要当场编译红,
|
|
84
|
+
* 而不是漏传一格被静默读成 `false`(那是安全轴上的 fail-open)。读者各自从自己手里的形折成这三格,
|
|
85
|
+
* 然后交**同一只** {@link standingAnswerMayRetire}(判据只有一份,折法各归读者):
|
|
86
|
+
* · live 读侧取 `req` + 本地治理 peek;live 写侧取 pending 条目;
|
|
87
|
+
* · 耐久 `/decide` 腿(S-663 → NP-1,7.100.0)取 park 行,经 {@link parkRowStandingAnswerFacts}(三格全从行上读)。
|
|
88
|
+
* 本型**不绑** live 条目。
|
|
89
|
+
*/
|
|
90
|
+
export interface StandingAnswerFacts {
|
|
91
|
+
/** 本仓治理层(`AUTONOMY` / `commandPolicy` / shellGate / `SENSITIVE_WRITE_PATTERNS`)产的门。 */
|
|
92
|
+
readonly governanceForced: boolean;
|
|
93
|
+
/** core 闸站盖的「必须真人逐次批」位(连一揽子 `onAsk:"allow"` 席也不得答)。 */
|
|
94
|
+
readonly requiresRealApproval: boolean;
|
|
95
|
+
/** core 闸站盖的强制位([ref]):规则清不掉、站着的答案退不掉,只有对**这一次调用**的应答能答。 */
|
|
96
|
+
readonly mandated: boolean;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 🔴 **S-660 —— 会话级放行的唯一判据:站着的答案能不能退掉这一问。** 纯谓词,三个读者:
|
|
100
|
+
* · **读侧**(`askBroadcast` 的会话授权短路):`true` 才可能由既有 grant 代答,否则往下走真出卡;
|
|
101
|
+
* · **写侧**(`finishRespond` 的 `grantable`):`true` 才可能把这张卡上的 `allow_session` 铸成 grant,
|
|
102
|
+
* 否则只放行这一次、`rememberApplied:false`;
|
|
103
|
+
* · **耐久 `/decide` 腿**(NP-1,`http/resume-legs.ts` 的 remember 可记性):入参由 {@link parkRowStandingAnswerFacts}
|
|
104
|
+
* 从 park 行铸 —— `false` ⇒ 可记工具名 `null` ⇒ 放行本次、不落豁免、回执 `rememberApplied:false`。
|
|
105
|
+
* 两臂读同一份事实、说同一句话:一问若不许被记住的放行答掉,它的应答也不许变成一条记住的放行 —— 否则
|
|
106
|
+
* 同一只卡「重跑一遍」就被自己上一次的回答代答(test [ref] U1s-off 的真形)。
|
|
107
|
+
*
|
|
108
|
+
* **为什么三位是同一类**(「站着的答案退不掉的一问」):会话授权 = **上一次**应答记下的一揽子放行,不是对
|
|
109
|
+
* **这一次**调用的应答 —— 与持久 allow 规则同族,只是更短命(`askBroadcast` 短路处的注早已这么定性)。
|
|
110
|
+
* · `mandated`:core `PermissionResult` ask 臂 `mandated` 成员的文档。7.29.0 原句「no persisted allow rule and no standing
|
|
111
|
+
* posture may retire it, only an answer to THIS call」;**7.30.0 起 d.ts 已落 [ref] 澄清**(S-616 在 7.30.1 装树亲核):
|
|
112
|
+
* core `PermissionResult` ask 臂的 `mandated` 成员文档「no persisted allow rule and no STANDING ANSWER (a release remembered
|
|
113
|
+
* from an earlier call, at any scope, a session included) may retire it」,`AskRequest.mandated` 孪生句同义 ——
|
|
114
|
+
* 「从早先某次回答记住的放行,**任何 scope、会话级也算**」都是站着的答案。core `ruleClearanceOf` 的文档
|
|
115
|
+
* 「Allow rules silence the classifier's questions, never a mandated one」。它**不**绑
|
|
116
|
+
* 这一次调用的应答者(人、hook、部署的一揽子 `onAsk:"allow"` 席、mandate 表允许的活判定者 —— 同一句 d.ts 逐一点名)——
|
|
117
|
+
* 那一半本仓不动(见 `mandated` 帧键顶注)。本谓词行为对 7.29.0 零改(文档到货,判据本来就是这么读的)。
|
|
118
|
+
* · `requiresRealApproval`([ref]):更强的那条,连一揽子席也不得答,当然也不许记住的放行答。
|
|
119
|
+
* · `governanceForced`([ref]):运维治理层的门,客户端表态掀不掉;持久规则车道早已同判(`buildRuleLaneMaterial`)。
|
|
120
|
+
* 本谓词**不重推** core 的 `ruleClearanceOf` / `ruleMayClear`(core 刻意不导出:闸外重算拿不到同一份标记快照,
|
|
121
|
+
* 会分叉),只读闸站**盖好**的两位 + 本仓自己的治理标。
|
|
122
|
+
*
|
|
123
|
+
* **为什么读侧的行复核(`sessionGrantUncontestedByRow`)不必为 `mandated` 加一次行读**:`mandated` 是 `req`
|
|
124
|
+
* **自己**的位 —— core 每一次过闸(含 failover / 重启后的重入)都由 `runGateLanes` 里的 `stampAskOrigin`
|
|
125
|
+
* 重新盖、经同一处的 `carryOf` 带上 `AskRequest`,读侧手里的 `req` 恒是这一次过闸的真值。`governanceForced`
|
|
126
|
+
* 需要行读,是因为它来自本仓的**进程内**治理标表:failover / 重启后幂等命中既有行时本地表恒空,行上的卡才
|
|
127
|
+
* 存着落库那一刻的判定。何况行上本来就没有 `mandated`(耐久面不造孪生座)—— 读了也读不到。
|
|
128
|
+
*/
|
|
129
|
+
export declare function standingAnswerMayRetire(facts: StandingAnswerFacts): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* 🔴 **S-663 → NP-1(7.100.0 合并复审 [ref] 交叉表 #2)—— 耐久 `/decide` 腿的 {@link StandingAnswerFacts}:从 park 行铸三位。**
|
|
132
|
+
* 纯投影,**不判**:判据是同一只 {@link standingAnswerMayRetire}(唯一读点 = `http/resume-legs.ts` 的 remember 可记性,
|
|
133
|
+
* `standingAnswerMayRetire(parkRowStandingAnswerFacts(…))`)。修前耐久腿自成一只谓词、只读 `ruleOffersAbsence` 一词,同一行上
|
|
134
|
+
* core 盖好的 `requiresRealApproval` 与治理出身都不读 ⇒ 那两类卡的 `remember:"session"` 照旧铸会话免询问,而 live 腿对同一张卡
|
|
135
|
+
* 答 `false`(两腿同一个问题、两份事实覆盖;两腿复审同报)。三格各自的来源(全在**这一条**已属主校验的载入行上,不另读):
|
|
136
|
+
* · `governanceForced` —— 行上的治理标:`gate.riskDescriptor.shellGateDoctrine` × 本部署治理姿态,经 **同一只**
|
|
137
|
+
* `governanceOriginOf`(`runtime-governance.ts`;运维队列 `projectPendingForWire` / 409 体同一个属主,不另判)。它在
|
|
138
|
+
* 本读者这里只**收窄**(失真两个方向的代价登记在 `governanceMandatesShellGateAlways` 顶注)。⚠️ 它**只覆盖 shell 门的
|
|
139
|
+
* `always` 档** —— live 腿的治理标来自进程内标记表(敏感写路径 / `commandPolicy` / `AUTONOMY` 内层策略 / shellGate 各档),
|
|
140
|
+
* park 行上唯一的取证格只有 `riskDescriptor.shellGateDoctrine`(见下方 🚧);
|
|
141
|
+
* · `requiresRealApproval` —— 行上 core 铸的那一位,经 `approval-card.ts` 的 {@link readRequiresRealApprovalSeat}(与运维
|
|
142
|
+
* 队列 `parkRowFacts` 同一只读法,`=== true`)。刻意**不**经 `parkRowFacts` 本身:那只展示投影顺手对 `ruleOffersAbsence`
|
|
143
|
+
* 的集外词计展示面 F 类 tag,而本读者对同一格是 fail-closed(S-663 codex r1 钉「权限面不计展示面 tag」);
|
|
144
|
+
* · `mandated` —— 行上**没有**这一位(core 7.30.1 `PendingAction` 无 `mandated`,#1079 候 7.31.0),只读得到强制的子集:
|
|
145
|
+
* `ruleOffersAbsence` 经三态窄读 {@link readRuleOffersAbsenceSeat},`"mandated"` **或读不出**(集外词 / 非串值,世代差)
|
|
146
|
+
* ⇒ `true`(fail-closed 不变:一个本仓不认识的新词可能正是一种强制因由)。
|
|
147
|
+
*
|
|
148
|
+
* 🚧 **剩余不对称两处**(耐久腿读得到的事实比 live 腿少,读不到的一律照旧可记):
|
|
149
|
+
* ① **`mandated` 孪生位(候 core #1079)**:一张强制卡若行上词为 `shadowed` / 缺席、又无 `requiresRealApproval` 与治理出身
|
|
150
|
+
* (非 Bash 工具 / 车道未武装 / 匿名部署上的规则强制 —— core 只在「车道武装 ∧ `Bash` ∧ 有 principal」时铸那一词,且
|
|
151
|
+
* `ruleClearanceOf` 的词序让 `persistedRuleShadowed` / `matchedAskRule` 先得 `"shadowed"`),本折法读不到它的强制。#1079 到货后
|
|
152
|
+
* 改的**只是 `mandated` 这一格**(改读行上那一位),谓词与调用点零改(`test/approval-exemption-http.test.ts` 的 🚧 格按 #1079
|
|
153
|
+
* 行形钉着);
|
|
154
|
+
* ② **非 shell 治理(候 S-707)**:治理位经 `governanceOriginOf` 只覆盖 shell 门 `always` 档;敏感写路径
|
|
155
|
+
* (`SENSITIVE_WRITE_PATTERNS`)、`commandPolicy`、`AUTONOMY` 内层策略产的门与 shellGate `classify` 档,park 行上没有取证格
|
|
156
|
+
* (live 腿靠进程内标记表,跨副本 / 重启后够不着),本折法读不到它们的治理出身。
|
|
157
|
+
*/
|
|
158
|
+
export declare function parkRowStandingAnswerFacts(pending: Extract<PendingAction, {
|
|
159
|
+
kind: "tool_approval";
|
|
160
|
+
}>, gate: CheckpointGate, governance: GovernancePosture | undefined): StandingAnswerFacts;
|
|
79
161
|
/** A live approval frame delivered to whoever tails this run's stream. `type` IS the SSE event name (named-event
|
|
80
162
|
* convention, same as question). The shell renders `tool_approval` as the CC three-choice card and dismisses on
|
|
81
163
|
* `tool_approval_complete`. */
|
|
@@ -142,6 +224,8 @@ export interface ToolApprovalFrame {
|
|
|
142
224
|
* 车道**有什么可给,而那条车道对它不说话的工具、**没接规则店的部署**(本仓的 live-only 形)、装不下规则的
|
|
143
225
|
* 任务都是结构性沉默 —— 它的缺席**不是**断言。本键是这一问**自己**的属性,凡强制皆在场。读错的代价 core
|
|
144
226
|
* 实测过:同一条根外读命令在 shell 工具上是强制问、在 monitor 工具上看起来是普通问。
|
|
227
|
+
* (唯一例外是**手里没有本键**的那条腿:耐久 `/decide` 腿只有 park 行,S-663 / NP-1 只读那一词的**在场**、从不读它的
|
|
228
|
+
* 缺席 —— 见 {@link parkRowStandingAnswerFacts},子集与剩余不对称写在那里。live 腿恒读本键。)
|
|
145
229
|
*
|
|
146
230
|
* 🔴 **它不是 `requiresRealApproval`**:强制只说「没有规则能清掉」,**不**说「没有应答者可以答」——
|
|
147
231
|
* 一个人、一只 hook、一张一揽子 `onAsk:"allow"` 席照常答得了强制卡。两键别互推。
|
|
@@ -150,7 +234,10 @@ export interface ToolApprovalFrame {
|
|
|
150
234
|
* 「不是强制」「老引擎」「这是一张从 park 行上重铸的耐久卡」三形 —— **耐久面上本仓不造孪生座**:core 的
|
|
151
235
|
* 寄存行(`PendingAction`)没有这一位(KL-997a 亲读登记),行上强制表现为 `ruleOffersAbsence:"mandated"`
|
|
152
236
|
* 且无 `ruleOffers`;在 `card_json` 侧自铸一个就是本仓成了这个事实的第二个源(而 park 那侧根本没有输入)。
|
|
153
|
-
* 🔴
|
|
237
|
+
* 🔴 **server 不判「是不是强制」**(判定属主在 core 的闸站),只**读**闸站盖好的这一位 —— 读者恰一个:
|
|
238
|
+
* S-660 起的会话级放行判据 {@link standingAnswerMayRetire}(会话授权是**上一次**应答记下的站着的答案,
|
|
239
|
+
* 不是对这一次调用的应答 ⇒ 强制卡上既不被既有 grant 代答、也不铸 grant)。上一段「一揽子 `onAsk:"allow"`
|
|
240
|
+
* 席照常答得了」说的是**这一次调用的应答者**,本仓不改它。
|
|
154
241
|
*/
|
|
155
242
|
mandated?: true;
|
|
156
243
|
/**
|
|
@@ -637,6 +724,29 @@ export type RespondSettledBy = Extract<HostSettledBy, HostDecidedBy>;
|
|
|
637
724
|
* (`config.ts` 的解析腿,[ref] A 档)。
|
|
638
725
|
*/
|
|
639
726
|
export type UnattendedApprovalPolicy = "park" | "deny";
|
|
727
|
+
/**
|
|
728
|
+
* S-689 —— `deny` 政策下「无人可答」交给 core 的形:core `AskOutcome` 对象臂的**部署政策拒**
|
|
729
|
+
* (`settledBy:"policy"` ⇒ core 结算 `policy_refused`、`who.party:"none"`,模型读的是策略句)。
|
|
730
|
+
*
|
|
731
|
+
* 修前这里交**裸 `false`** —— 而在 core 的 onAsk 契约里裸 `false` 就是「一个人拒了」:结算记
|
|
732
|
+
* `human_refused` / `who.party:"person"`,模型被告知「用户不想继续,停下来等他」,run 以
|
|
733
|
+
* `haltedOnUserRejection` 收束。可这台部署自己声明了没有人会来 —— 拒的是它的政策。
|
|
734
|
+
*
|
|
735
|
+
* 型从 core 的 `AskOutcome` 对象臂**收窄**而不是另写一份(`settledBy` 取 core 那一格词表里的 `"policy"`:
|
|
736
|
+
* core 哪天退役这个词,这里编译期先红)。`reason` 必填:core 以 `policy note` 围栏把它交给模型,内容见
|
|
737
|
+
* {@link unattendedRefusalReason}。
|
|
738
|
+
*/
|
|
739
|
+
export type UnattendedPolicyRefusal = Extract<AskOutcome, {
|
|
740
|
+
allow: boolean;
|
|
741
|
+
}> & {
|
|
742
|
+
allow: false;
|
|
743
|
+
settledBy: Extract<HostSettledBy, "policy">;
|
|
744
|
+
reason: string;
|
|
745
|
+
};
|
|
746
|
+
/** 「无人可答」终局的**闭集** —— core `AskOutcome` 联合里只有这两臂由 {@link ToolApprovalCoordinator.unattendedOutcome}
|
|
747
|
+
* 铸:park 政策的路由答 `"unavailable"`,deny 政策的 {@link UnattendedPolicyRefusal}。没有布尔臂(S-689:裸 `false`
|
|
748
|
+
* 是人的拒绝,这个口从不替人说话)。 */
|
|
749
|
+
export type UnattendedAskOutcome = Extract<AskOutcome, "unavailable"> | UnattendedPolicyRefusal;
|
|
640
750
|
/**
|
|
641
751
|
* [ref] 车3 刀 3b —— 「流内审批协议到底上不上场」的**单一谓词**(设计稿 §2.3 注入 / §2.4 能力面 /
|
|
642
752
|
* §8.4 park 设施自检,三处同一份判据)。
|
|
@@ -959,6 +1069,10 @@ export declare class ToolApprovalCoordinator {
|
|
|
959
1069
|
/**
|
|
960
1070
|
* [ref] R-13 C —— 「**无人可答**」这一类终局的**唯一**成形口(park 路由 vs deny 政策)。
|
|
961
1071
|
*
|
|
1072
|
+
* 终局形(S-689):park ⇒ `"unavailable"`;deny ⇒ {@link UnattendedPolicyRefusal}
|
|
1073
|
+
* `{allow:false, settledBy:"policy", reason}`(core 记 `policy_refused`)。**从不**交裸 `false`:那在 core 的
|
|
1074
|
+
* onAsk 契约里是「一个人拒了」,而本口的全部臂都是「没有人能答」。
|
|
1075
|
+
*
|
|
962
1076
|
* 五臂(设计稿 §1 的表)全部读它,`unattendedPolicy` 因此是一处施加、五处消费:
|
|
963
1077
|
* (a) 有店窗到期 `expireAsk` 赢 CAS / (b) 无店窗到期(D1)/ (c) 有店但 `expireAsk` 报错
|
|
964
1078
|
* —— 三条走 `windowRouteUnavailable` 标志,在 {@link shapeOutcome}(askBroadcast 内)成形;
|
|
@@ -976,12 +1090,17 @@ export declare class ToolApprovalCoordinator {
|
|
|
976
1090
|
* **身份不确定**的处置(与「有没有人」无关),且背后可能正躺着一条真行。于是 `deny` 部署上仍可能落
|
|
977
1091
|
* 极少数 park(店抖动期),已知且有意的残余,成文在 config-types 的域注。
|
|
978
1092
|
*
|
|
979
|
-
*
|
|
980
|
-
* 造一条原子 deny
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
*
|
|
1093
|
+
* 🔴 **`deny` 部署上政策结算当场落行**(NP-2,7.100.0 合并复审 [ref] 交叉表 #4;codex R1-F1 当年**不采纳**的那一半
|
|
1094
|
+
* 「给 deny 造一条原子 deny 终态转移」在这里收了):窗到期 / emit 全灭两臂在 deny 政策下的终局 claim 是
|
|
1095
|
+
* `refuse` 意图(见 {@link unattendedClaimIntent})—— STREAM_PENDING→DECIDED(deny) + `settled_by = "policy"`,**按 ask
|
|
1096
|
+
* 作用域**(不碰批、不连坐兄弟)。落的来源词与本口交给 core 的是**同一个常量**(`POLICY_REFUSAL_DECISION`),于是
|
|
1097
|
+
* 同 ask 重入 / 收敛后重入由 `outcomeOfDecidedRow` 读回同一个对象。当年「危害为零」的论证(PARKING 行绑不上、
|
|
1098
|
+
* 自行收敛成 DENIED/VOID)在 S-689 之后不再成立:首判交 `{allow:false, settledBy:"policy"}`,行停 PARKING 的回放
|
|
1099
|
+
* 却交裸 `false`(收敛后 DENIED 无来源)—— core 当成人拒、可停批,两次判决第一次可被观测地分家。
|
|
1100
|
+
* 仍按 PARKING 记账的只剩两形(都不是本进程这一次政策结算):别的写者(park 政策副本 / 切换政策之前)推进过的行,
|
|
1101
|
+
* 与崩溃后收敛器孤儿腿代打 expire 的行 —— 前者重入走本口(`replayed_parked`),后者登记在收车档的未闭环项。
|
|
1102
|
+
* deny 政策下批仍是**单 ask 批**(见 {@link batchIdFor}):`refuse` 本来就不碰批,单 ask 批让新行与存量整腿批行
|
|
1103
|
+
* 的差别只剩「批 id 是谁」,结算语义一致。
|
|
985
1104
|
*/
|
|
986
1105
|
private unattendedOutcome;
|
|
987
1106
|
/** 政策**读**口(不是终局铸口)—— 回答「这台部署把无人可答翻成 park 吗」。
|
|
@@ -990,6 +1109,22 @@ export declare class ToolApprovalCoordinator {
|
|
|
990
1109
|
* 纯粹的**政策查询**(`unattendedOutcome() !== false`)会和同一只 ask 的真终局各记一条遥测 ——
|
|
991
1110
|
* 「一只 ask 一条线」当场不成立。读口不落日志,铸口落;谁也不兼职。 */
|
|
992
1111
|
private get unattendedRoutesToPark();
|
|
1112
|
+
/**
|
|
1113
|
+
* 一只 ask 落在**哪只批**里 —— 批作用域随政策(S-689 codex R1-F1,判据全文见 {@link deriveAskScopedBatchId})。
|
|
1114
|
+
* · park 路由 ⇒ 整腿一批(`deriveBatchId`):窗到期赢家整批转投递面、兄弟连坐,core 随后 park,腿就此结束;
|
|
1115
|
+
* · deny 政策 ⇒ 单 ask 批:没有 park 路由,腿在一次部署政策拒之后继续跑 —— 整腿一批会让首只到期把批关掉,
|
|
1116
|
+
* 同腿后续的卡人批不动(`decideAsk` batch_closed ⇒ 404),兄弟还被连坐成 VOID、以裸 `false` 交 core(人拒)。
|
|
1117
|
+
* 读的是政策**读**口(不落遥测)。
|
|
1118
|
+
*/
|
|
1119
|
+
private batchIdFor;
|
|
1120
|
+
/**
|
|
1121
|
+
* 窗到期 / emit 全灭两臂的**终局 claim 意图**(NP-2)—— 与 {@link batchIdFor} 读同一个政策读口:
|
|
1122
|
+
* · park 路由 ⇒ `expire`(行进投递面、批转 ROUTING_UNBOUND、兄弟连坐,core 随后 park);
|
|
1123
|
+
* · deny 政策 ⇒ `refuse`(按 ask 作用域当场落 DECIDED(deny) + `settled_by="policy"`,与 {@link unattendedOutcome}
|
|
1124
|
+
* 交给 core 的那一只同一个来源词 —— 回放 / 收敛后重入读回同一个对象;不碰批、不连坐兄弟,存量整腿批行同判)。
|
|
1125
|
+
* 两臂之外的终局 claim(取消臂 `cancel`)与政策无关,不读本口。
|
|
1126
|
+
*/
|
|
1127
|
+
private get unattendedClaimIntent();
|
|
993
1128
|
/** {@link unattendedOutcome} 的**公开**口:`windowZero` 的 immediate-unavailable 闭包长在装配层
|
|
994
1129
|
* (`http/routes/tasks.ts` 的 sync 腿),它必须与协调器内五臂读**同一个**旋钮值,而不是各算一遍。
|
|
995
1130
|
* 出处随调用方给(那条腿知道自己是哪一只 ask)。 */
|
|
@@ -997,7 +1132,7 @@ export declare class ToolApprovalCoordinator {
|
|
|
997
1132
|
taskId?: string;
|
|
998
1133
|
owner?: string | null;
|
|
999
1134
|
toolName?: string;
|
|
1000
|
-
}):
|
|
1135
|
+
}): UnattendedAskOutcome;
|
|
1001
1136
|
/**
|
|
1002
1137
|
* [ref] 车3 刀 3b —— [ref] §3.3 / 设计稿 §0 X-2 的**写侧准入门**。
|
|
1003
1138
|
*
|
package/dist/tool-approval.js
CHANGED
|
@@ -7,10 +7,11 @@ import { hasBidiControls } from "./text-bidi.js";
|
|
|
7
7
|
import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerprint.js";
|
|
8
8
|
import { createLogger } from "./observability/logger.js";
|
|
9
9
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
10
|
-
import { deriveAskId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
11
|
-
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
|
|
12
|
-
import { projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
|
|
10
|
+
import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
|
|
11
|
+
import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readRequiresRealApprovalSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
|
|
12
|
+
import { POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
|
|
13
13
|
import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
|
|
14
|
+
import { governanceOriginOf } from "./runtime-governance.js";
|
|
14
15
|
const defaultLogger = createLogger();
|
|
15
16
|
export const APPROVAL_GATE_KINDS = ["human", "irreversible_ask"];
|
|
16
17
|
export function isApprovalGateKind(k) {
|
|
@@ -79,6 +80,27 @@ function sessionAllowCategory(toolName) {
|
|
|
79
80
|
function sessionAllowKey(owner, sessionId, category) {
|
|
80
81
|
return JSON.stringify([owner, sessionId, category]);
|
|
81
82
|
}
|
|
83
|
+
export function standingAnswerMayRetire(facts) {
|
|
84
|
+
return !facts.governanceForced && !facts.requiresRealApproval && !facts.mandated;
|
|
85
|
+
}
|
|
86
|
+
export function parkRowStandingAnswerFacts(pending, gate, governance) {
|
|
87
|
+
const riskDescriptor = "riskDescriptor" in gate ? gate.riskDescriptor : undefined;
|
|
88
|
+
return {
|
|
89
|
+
governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
|
|
90
|
+
requiresRealApproval: readRequiresRealApprovalSeat(pending),
|
|
91
|
+
mandated: mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function mandatedFromSeat(seat) {
|
|
95
|
+
switch (seat.state) {
|
|
96
|
+
case "word":
|
|
97
|
+
return seat.word === "mandated";
|
|
98
|
+
case "unreadable":
|
|
99
|
+
return true;
|
|
100
|
+
case "absent":
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
82
104
|
export const PERSIST_RULE_TEXT_ERROR = `persistRule.rule must be a non-empty string of at most ${MAX_RULE_TEXT_CHARS} characters`;
|
|
83
105
|
export const PERSIST_RULE_EDITED_FLAG_ERROR = "persistRule.edited must be a boolean when present";
|
|
84
106
|
export const PERSIST_RULE_BATCH_INDEX_ERROR = "persistRule.batchOfferIndex must be a non-negative integer when present";
|
|
@@ -149,6 +171,10 @@ export function parseToolApprovalResponse(body) {
|
|
|
149
171
|
}
|
|
150
172
|
return { ok: false, error: 'decision must be one of "allow" | "allow_session" | "deny"' };
|
|
151
173
|
}
|
|
174
|
+
function unattendedRefusalReason(arm) {
|
|
175
|
+
return ("UNATTENDED_APPROVAL_POLICY=deny: this deployment declares that no one attends its approvals, so an approval " +
|
|
176
|
+
`request no one can answer is refused here instead of being parked for a person (arm: ${arm}).`);
|
|
177
|
+
}
|
|
152
178
|
function isChildAsk(req) {
|
|
153
179
|
return req.fromSubagent === true;
|
|
154
180
|
}
|
|
@@ -318,11 +344,26 @@ export class ToolApprovalCoordinator {
|
|
|
318
344
|
this.askAudit = opts?.askAudit;
|
|
319
345
|
}
|
|
320
346
|
unattendedOutcome(site) {
|
|
321
|
-
|
|
347
|
+
let outcome;
|
|
348
|
+
let outcomeWord;
|
|
349
|
+
switch (this.unattendedPolicy) {
|
|
350
|
+
case "park":
|
|
351
|
+
outcome = "unavailable";
|
|
352
|
+
outcomeWord = "unavailable";
|
|
353
|
+
break;
|
|
354
|
+
case "deny":
|
|
355
|
+
outcome = { allow: false, settledBy: POLICY_REFUSAL_DECISION.settledBy, reason: unattendedRefusalReason(site.arm) };
|
|
356
|
+
outcomeWord = "deny";
|
|
357
|
+
break;
|
|
358
|
+
default: {
|
|
359
|
+
const unreachable = this.unattendedPolicy;
|
|
360
|
+
throw new Error(`unattendedOutcome: unknown UNATTENDED_APPROVAL_POLICY word ${String(unreachable)}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
322
363
|
defaultLogger.warn("approver_unavailable", {
|
|
323
364
|
arm: site.arm,
|
|
324
365
|
policy: this.unattendedPolicy,
|
|
325
|
-
outcome:
|
|
366
|
+
outcome: outcomeWord,
|
|
326
367
|
owner: site.owner ?? null,
|
|
327
368
|
taskId: site.taskId ?? null,
|
|
328
369
|
toolName: site.toolName ?? null,
|
|
@@ -333,6 +374,12 @@ export class ToolApprovalCoordinator {
|
|
|
333
374
|
get unattendedRoutesToPark() {
|
|
334
375
|
return this.unattendedPolicy !== "deny";
|
|
335
376
|
}
|
|
377
|
+
batchIdFor(askId, sourceTaskId, runId, legKey) {
|
|
378
|
+
return this.unattendedRoutesToPark ? deriveBatchId(sourceTaskId, runId, legKey) : deriveAskScopedBatchId(askId);
|
|
379
|
+
}
|
|
380
|
+
get unattendedClaimIntent() {
|
|
381
|
+
return this.unattendedRoutesToPark ? "expire" : "refuse";
|
|
382
|
+
}
|
|
336
383
|
unattendedAskOutcome(site) {
|
|
337
384
|
return this.unattendedOutcome({
|
|
338
385
|
arm: "window_zero",
|
|
@@ -829,8 +876,7 @@ export class ToolApprovalCoordinator {
|
|
|
829
876
|
}
|
|
830
877
|
const primary = aliveCtxs[0];
|
|
831
878
|
const governanceForced = (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(origin.owner, origin.sessionId, origin.taskId))).isMarked(req.toolCallId);
|
|
832
|
-
if (
|
|
833
|
-
req.requiresRealApproval !== true &&
|
|
879
|
+
if (standingAnswerMayRetire({ governanceForced, requiresRealApproval: req.requiresRealApproval === true, mandated: req.mandated === true }) &&
|
|
834
880
|
origin.sessionId &&
|
|
835
881
|
this.allowAllSessions.has(sessionAllowKey(origin.owner, origin.sessionId, sessionAllowCategory(req.toolName))) &&
|
|
836
882
|
(await this.sessionGrantUncontestedByRow(origin, req))) {
|
|
@@ -915,7 +961,7 @@ export class ToolApprovalCoordinator {
|
|
|
915
961
|
const runId = origin.taskId;
|
|
916
962
|
const legKey = origin.legKey;
|
|
917
963
|
askId = deriveAskId(sourceTaskId, runId, req.toolCallId, legKey, req.delegation?.parentToolCallId);
|
|
918
|
-
batchId =
|
|
964
|
+
batchId = this.batchIdFor(askId, sourceTaskId, runId, legKey);
|
|
919
965
|
try {
|
|
920
966
|
const existingOrCreated = await this.withStoreDeadline(this.askStore.ensureAsk({
|
|
921
967
|
askId,
|
|
@@ -938,6 +984,7 @@ export class ToolApprovalCoordinator {
|
|
|
938
984
|
if (abandonedEnsure && lateEnsuredAskId !== undefined)
|
|
939
985
|
voidAbandonedRow(lateEnsuredAskId);
|
|
940
986
|
});
|
|
987
|
+
batchId = existingOrCreated.row.batchId;
|
|
941
988
|
if (existingOrCreated.row.state !== "STREAM_PENDING") {
|
|
942
989
|
releaseAdmission();
|
|
943
990
|
const replayed = replayTerminalAskRow(existingOrCreated.row);
|
|
@@ -999,7 +1046,10 @@ export class ToolApprovalCoordinator {
|
|
|
999
1046
|
let indeterminate = true;
|
|
1000
1047
|
for (let attempt = 0; attempt <= DURABLE_CONVERGE_BACKOFF_MS.length; attempt++) {
|
|
1001
1048
|
try {
|
|
1002
|
-
|
|
1049
|
+
const confirmedRow = await this.withStoreDeadline(this.askStore.getAsk(askId), "getAsk(ensureAsk-confirm)");
|
|
1050
|
+
confirmedRowExists = confirmedRow !== null;
|
|
1051
|
+
if (confirmedRow !== null)
|
|
1052
|
+
batchId = confirmedRow.batchId;
|
|
1003
1053
|
indeterminate = false;
|
|
1004
1054
|
break;
|
|
1005
1055
|
}
|
|
@@ -1042,7 +1092,7 @@ export class ToolApprovalCoordinator {
|
|
|
1042
1092
|
let selfEntry;
|
|
1043
1093
|
let hostSettled;
|
|
1044
1094
|
let denyReasonSettled;
|
|
1045
|
-
const shapeDeny = () => {
|
|
1095
|
+
const shapeDeny = (bare) => {
|
|
1046
1096
|
if (hostSettled !== undefined || (denyReasonSettled !== undefined && denyReasonSettled !== "")) {
|
|
1047
1097
|
return {
|
|
1048
1098
|
allow: false,
|
|
@@ -1050,18 +1100,17 @@ export class ToolApprovalCoordinator {
|
|
|
1050
1100
|
...(denyReasonSettled !== undefined && denyReasonSettled !== "" ? { reason: denyReasonSettled } : {}),
|
|
1051
1101
|
};
|
|
1052
1102
|
}
|
|
1053
|
-
return
|
|
1103
|
+
return bare;
|
|
1054
1104
|
};
|
|
1055
1105
|
const shapeOutcome = (settled) => {
|
|
1056
1106
|
if (windowRouteUnavailable) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
: "unavailable";
|
|
1107
|
+
const unattended = this.unattendedOutcome({ arm: "window_expired", taskId: origin.taskId, ...(origin.owner !== null ? { owner: origin.owner } : {}), toolName: req.toolName, reachability });
|
|
1108
|
+
return unattended === "unavailable" ? unattended : shapeDeny(unattended);
|
|
1060
1109
|
}
|
|
1061
1110
|
if (settled.allowed) {
|
|
1062
1111
|
return settled.updatedInput !== undefined ? { allow: true, updatedInput: settled.updatedInput } : true;
|
|
1063
1112
|
}
|
|
1064
|
-
return shapeDeny();
|
|
1113
|
+
return shapeDeny(false);
|
|
1065
1114
|
};
|
|
1066
1115
|
const settle = (allowed, outcome, updatedInput, hostSettledBy, denyReason) => {
|
|
1067
1116
|
if (done)
|
|
@@ -1266,10 +1315,11 @@ export class ToolApprovalCoordinator {
|
|
|
1266
1315
|
const store = this.askStore;
|
|
1267
1316
|
const pinnedAskId = askId;
|
|
1268
1317
|
const pinnedBatchId = batchId;
|
|
1318
|
+
const claimIntent = this.unattendedClaimIntent;
|
|
1269
1319
|
await resolveLocalOutcome({
|
|
1270
1320
|
intent: "expire",
|
|
1271
1321
|
storeLabel: "claimTerminal(window)",
|
|
1272
|
-
claim: (timeoutMs) => boundedClaim(() => store.claimTerminal(pinnedAskId, pinnedBatchId,
|
|
1322
|
+
claim: (timeoutMs) => boundedClaim(() => store.claimTerminal(pinnedAskId, pinnedBatchId, claimIntent), "claimTerminal(window)", timeoutMs, (late) => {
|
|
1273
1323
|
if (!late.claimed) {
|
|
1274
1324
|
lateClaimLossSettle(late.current, "expire");
|
|
1275
1325
|
return;
|
|
@@ -1385,6 +1435,7 @@ export class ToolApprovalCoordinator {
|
|
|
1385
1435
|
...(ruleLaneMaterial !== undefined ? { ruleLane: ruleLaneMaterial } : {}),
|
|
1386
1436
|
...(effectiveGovernanceForced ? { governanceForced: true } : {}),
|
|
1387
1437
|
...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
1438
|
+
...(req.mandated === true ? { mandated: true } : {}),
|
|
1388
1439
|
...(askId !== undefined && batchId !== undefined ? { askId, batchId } : {}),
|
|
1389
1440
|
};
|
|
1390
1441
|
selfEntry = pendingEntry;
|
|
@@ -1447,6 +1498,7 @@ export class ToolApprovalCoordinator {
|
|
|
1447
1498
|
const store = this.askStore;
|
|
1448
1499
|
const pinnedAskId = askId;
|
|
1449
1500
|
const pinnedBatchId = batchId;
|
|
1501
|
+
const claimIntent = this.unattendedClaimIntent;
|
|
1450
1502
|
const settleUnreachable = () => {
|
|
1451
1503
|
if (done)
|
|
1452
1504
|
return;
|
|
@@ -1456,7 +1508,7 @@ export class ToolApprovalCoordinator {
|
|
|
1456
1508
|
await resolveLocalOutcome({
|
|
1457
1509
|
intent: "unreachable",
|
|
1458
1510
|
storeLabel: "claimTerminal(emit-failed)",
|
|
1459
|
-
claim: (timeoutMs) => boundedClaim(() => store.claimTerminal(pinnedAskId, pinnedBatchId,
|
|
1511
|
+
claim: (timeoutMs) => boundedClaim(() => store.claimTerminal(pinnedAskId, pinnedBatchId, claimIntent), "claimTerminal(emit-failed)", timeoutMs, (late) => {
|
|
1460
1512
|
if (!late.claimed) {
|
|
1461
1513
|
lateClaimLossSettle(late.current, "unreachable");
|
|
1462
1514
|
return;
|
|
@@ -1962,7 +2014,11 @@ export class ToolApprovalCoordinator {
|
|
|
1962
2014
|
}
|
|
1963
2015
|
finishRespond(id, entry, parsed, opts) {
|
|
1964
2016
|
let rememberApplied;
|
|
1965
|
-
const grantable =
|
|
2017
|
+
const grantable = standingAnswerMayRetire({
|
|
2018
|
+
governanceForced: entry.governanceForced === true,
|
|
2019
|
+
requiresRealApproval: entry.requiresRealApproval === true,
|
|
2020
|
+
mandated: entry.mandated === true,
|
|
2021
|
+
});
|
|
1966
2022
|
if (parsed.value === "allow_session")
|
|
1967
2023
|
rememberApplied = Boolean(entry.sessionId) && grantable;
|
|
1968
2024
|
if (parsed.value === "allow_session" && entry.sessionId && grantable) {
|
|
@@ -25,7 +25,7 @@ import type { ToolApprovalFrame } from "../tool-approval.js";
|
|
|
25
25
|
* `type X<T extends never> = T`(抄一份就是第二个写者,两处哪天分岔谁都发现不了)。
|
|
26
26
|
* 本文件只有类型、不产 JS,所以被 `import type` 引用不新增任何运行期边。 */
|
|
27
27
|
export type AssertAllKeysHandled<T extends never> = T;
|
|
28
|
-
type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure" | "agentMessage" | "_sema_provenance";
|
|
28
|
+
type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure" | "agentMessage" | "_sema_provenance" | "spawnName" | "handback" | "compaction" | "resupplied" | "attempt";
|
|
29
29
|
type NotificationExcluded = (typeof import("./wire-projection-faces.js").WIRE_PROJECTION_FACES)["task_notification"]["excluded"][number];
|
|
30
30
|
type _GuardNotification = AssertAllKeysHandled<Exclude<keyof TaskNotificationPayload, NotificationProjected | NotificationExcluded>>;
|
|
31
31
|
type _GuardNotificationReverse = AssertAllKeysHandled<Exclude<NotificationProjected | NotificationExcluded, keyof TaskNotificationPayload>>;
|
|
@@ -59,8 +59,8 @@ type _GuardMailboxReverse = AssertAllKeysHandled<Exclude<MailboxProjected, keyof
|
|
|
59
59
|
type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
|
|
60
60
|
type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
|
|
61
61
|
type _GuardMailboxAppendReverse = AssertAllKeysHandled<Exclude<MailboxAppendProjected, keyof MailboxAppendMessage>>;
|
|
62
|
-
type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection";
|
|
63
|
-
type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny";
|
|
62
|
+
type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection" | "teamRole";
|
|
63
|
+
type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny" | "peerPresence";
|
|
64
64
|
type _GuardWiringSections = AssertAllKeysHandled<Exclude<keyof WiringManifest, WiringSectionProjected | WiringSectionExcluded>>;
|
|
65
65
|
type _GuardWiringSectionsReverse = AssertAllKeysHandled<Exclude<WiringSectionProjected | WiringSectionExcluded, keyof WiringManifest>>;
|
|
66
66
|
type WiringSection<K extends keyof WiringManifest> = NonNullable<WiringManifest[K]>;
|
|
@@ -135,10 +135,10 @@ type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "prev
|
|
|
135
135
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
136
136
|
type _GuardInboxSummaryReverse = AssertAllKeysHandled<Exclude<SummaryProjected | SummaryExcluded, keyof CheckpointSummary>>;
|
|
137
137
|
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "approverPosture" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "autoModeRequested" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools";
|
|
138
|
-
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars";
|
|
138
|
+
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
|
|
139
139
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
140
140
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
141
|
-
type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel";
|
|
141
|
+
type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel" | "compaction";
|
|
142
142
|
type TaskResultStripped = never;
|
|
143
143
|
type _GuardTaskResult = AssertAllKeysHandled<Exclude<keyof TaskResult, TaskResultForwarded | TaskResultStripped>>;
|
|
144
144
|
type _GuardTaskResultReverse = AssertAllKeysHandled<Exclude<TaskResultForwarded | TaskResultStripped, keyof TaskResult>>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {} from "@sema-agent/core";
|
|
2
2
|
import { outwardPlane } from "../terminal.js";
|
|
3
3
|
import { modelTextEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, turnEndEventData, contextUsageEventData } from "./project.js";
|
|
4
|
-
import { NotifiedKeys,
|
|
4
|
+
import { NotifiedKeys, taskNotificationStreamKeyOf } from "../orchestration/workflow-completion-inbox.js";
|
|
5
5
|
import { isParkLedgerEventType } from "./ledger-events.js";
|
|
6
6
|
import { isModelSegmentBoundary, reasoningDeltaBytes, reasoningSegmentEventId } from "./model-segment.js";
|
|
7
7
|
export function createSerialLedgerAppend(appendEvent, startSeq) {
|
|
@@ -119,7 +119,7 @@ export function createLedgerSink(opts) {
|
|
|
119
119
|
break;
|
|
120
120
|
case "task_notification": {
|
|
121
121
|
const tn = ev.notification;
|
|
122
|
-
const tnKey =
|
|
122
|
+
const tnKey = taskNotificationStreamKeyOf(tn);
|
|
123
123
|
const tnPrior = tnKey !== undefined ? keys.get(tnKey) : undefined;
|
|
124
124
|
if (tnPrior === undefined || !(await tnPrior)) {
|
|
125
125
|
const write = append("task_notification", taskNotificationEventData(ev));
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -422,6 +422,17 @@ export declare function taskNotificationEventData(ev: {
|
|
|
422
422
|
* `kind` 过闭集(`trace/sema-provenance.ts` 单点,park 半场同一判据);`agentType` 本 mount 恒缺席(工具无
|
|
423
423
|
* agent-type 席),在场即透传(fleet spawn 帧按 taskId 可 join 时上游给)。 */
|
|
424
424
|
_sema_provenance?: SemaProvenanceInput;
|
|
425
|
+
/** core 7.30.0 [ref] S1 / #1034 / #1036(S-616):委派终态帧三枚 facet + 重供两键 —— 判据单点在
|
|
426
|
+
* `trace/task-notification-facets.ts`(与 park 半场同一只;入参按 `unknown` 收、判过形才成 typed)。 */
|
|
427
|
+
spawnName?: string;
|
|
428
|
+
handback?: string;
|
|
429
|
+
compaction?: {
|
|
430
|
+
passes: number;
|
|
431
|
+
breakerOpen: boolean;
|
|
432
|
+
kind?: string;
|
|
433
|
+
};
|
|
434
|
+
resupplied?: boolean;
|
|
435
|
+
attempt?: number;
|
|
425
436
|
};
|
|
426
437
|
eventId?: string;
|
|
427
438
|
parentToolCallId?: string;
|
|
@@ -840,6 +851,8 @@ export declare function compactedEventData(ev: {
|
|
|
840
851
|
* 的数、post 恒是结构和 + 固定提示词开销,两者的差混坐标,实测在普通 run 上是负数)。本仓的处置因此
|
|
841
852
|
* 是**三条都逐字透传、一条都不合成**:谁要画上下文表就读 `freedTokens`,读不到就渲染 unknown。 */
|
|
842
853
|
freedTokens?: number;
|
|
854
|
+
/** core 7.30.0 #1036(S-616):这一次压缩**摘要掉**了几条之前已交给模型的跨会话消息(在场 iff ≥1)。 */
|
|
855
|
+
summarizedCrossSessionMessages?: number;
|
|
843
856
|
durationMs?: number;
|
|
844
857
|
phaseDurations?: {
|
|
845
858
|
prepareMs?: number;
|
package/dist/trace/project.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { redactErrorMessage, redactModelText } from "../observability/run-terminal.js";
|
|
2
|
-
import { MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, screenGateOutcome, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
|
|
2
|
+
import { MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, screenGateOutcome, TEAM_ROLES, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
4
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
5
5
|
import { redactSecrets, redactDeep } from "./redact.js";
|
|
6
6
|
import { injectionTierOrAbsent } from "./injection-tier.js";
|
|
7
7
|
import { semaProvenanceOrAbsent } from "./sema-provenance.js";
|
|
8
|
+
import { resupplyFields, terminalFrameFacetFields } from "./task-notification-facets.js";
|
|
9
|
+
import { dropProjectedFace } from "./projection-drop.js";
|
|
8
10
|
export async function appendModelUsageDelta(append, modelUsage, taskId) {
|
|
9
11
|
const delta = modelUsage?.drain(taskId);
|
|
10
12
|
if (delta) {
|
|
@@ -229,10 +231,6 @@ export function toolEndEventData(ev) {
|
|
|
229
231
|
...identityFields(ev),
|
|
230
232
|
};
|
|
231
233
|
}
|
|
232
|
-
const dropProjectedFace = (what) => {
|
|
233
|
-
recordFailOpen("server.trace.projection-input-malformed", what);
|
|
234
|
-
return undefined;
|
|
235
|
-
};
|
|
236
234
|
export function toolProgressEventData(ev) {
|
|
237
235
|
const id = typeof ev.toolCallId === "string" && ev.toolCallId !== "" ? ev.toolCallId : undefined;
|
|
238
236
|
const name = typeof ev.toolName === "string" && ev.toolName !== "" ? ev.toolName : undefined;
|
|
@@ -326,6 +324,8 @@ export function taskNotificationEventData(ev) {
|
|
|
326
324
|
const p = semaProvenanceOrAbsent(n._sema_provenance);
|
|
327
325
|
return p !== undefined ? { _sema_provenance: p } : {};
|
|
328
326
|
})(),
|
|
327
|
+
...terminalFrameFacetFields(n),
|
|
328
|
+
...resupplyFields(n),
|
|
329
329
|
...(() => {
|
|
330
330
|
const tier = injectionTierOrAbsent(ev.priority);
|
|
331
331
|
return tier !== undefined ? { priority: tier } : {};
|
|
@@ -535,9 +535,17 @@ export function wiringManifestEventData(ev) {
|
|
|
535
535
|
return { targetView };
|
|
536
536
|
})();
|
|
537
537
|
const mcp = projectWiringManifestMcpRows(m.mcp);
|
|
538
|
+
const teamRole = (() => {
|
|
539
|
+
if (m.teamRole === undefined)
|
|
540
|
+
return undefined;
|
|
541
|
+
if (!TEAM_ROLES.includes(m.teamRole))
|
|
542
|
+
return dropProjectedFace("wiring_manifest.teamRole is outside core TEAM_ROLES");
|
|
543
|
+
return m.teamRole;
|
|
544
|
+
})();
|
|
538
545
|
return {
|
|
539
546
|
...(num(m.schemaVersion) !== undefined ? { schemaVersion: m.schemaVersion } : {}),
|
|
540
547
|
...(str(legIn.kind) !== undefined ? { leg: { kind: legIn.kind } } : {}),
|
|
548
|
+
...(teamRole !== undefined ? { teamRole } : {}),
|
|
541
549
|
...(ask !== undefined ? { ask } : {}),
|
|
542
550
|
...(question !== undefined ? { question } : {}),
|
|
543
551
|
...(str(interactionIn.posture) !== undefined ? { interaction: { posture: interactionIn.posture } } : {}),
|
|
@@ -676,6 +684,15 @@ export function compactedEventData(ev) {
|
|
|
676
684
|
...(num(ev.tokensAfter) !== undefined ? { tokensAfter: num(ev.tokensAfter) } : {}),
|
|
677
685
|
...(num(ev.triggerTokensBefore) !== undefined ? { triggerTokensBefore: num(ev.triggerTokensBefore) } : {}),
|
|
678
686
|
...(num(ev.freedTokens) !== undefined ? { freedTokens: num(ev.freedTokens) } : {}),
|
|
687
|
+
...(() => {
|
|
688
|
+
const v = ev.summarizedCrossSessionMessages;
|
|
689
|
+
if (v === undefined)
|
|
690
|
+
return {};
|
|
691
|
+
if (Number.isInteger(v) && v >= 1)
|
|
692
|
+
return { summarizedCrossSessionMessages: v };
|
|
693
|
+
dropProjectedFace("compacted.summarizedCrossSessionMessages is not an integer >= 1");
|
|
694
|
+
return {};
|
|
695
|
+
})(),
|
|
679
696
|
...(num(ev.durationMs) !== undefined ? { durationMs: num(ev.durationMs) } : {}),
|
|
680
697
|
...(phaseDurations !== undefined ? { phaseDurations } : {}),
|
|
681
698
|
};
|
|
@@ -26,8 +26,11 @@ export declare const SEMA_PROVENANCE_KINDS: readonly ["agent_message"];
|
|
|
26
26
|
* (core-keyset-guard.ts ① 的同名排除键)一同候 [ref] 表态。放进 EXCLUDED 而不是 PROJECTED,是**沿用**上一行的
|
|
27
27
|
* 裁定而不是新增特判:表态未出前放行一个 lane 词,等于替它预先声明一种 cli 渲染形。表态落定那天三处(本簿、
|
|
28
28
|
* 载体键、①-nested 的 `peerMeta`)同批翻。
|
|
29
|
+
* · `inbox_notice` —— core 7.30.0 #1036(S-616 提货;本闭集当拍真红出它)。**同一条车道的第三个载体**(收件箱通告,
|
|
30
|
+
* `crossSessionMessage` / `crossSessionNotice` 之后),同一个 session-box drain 铸;载体键 `inboxNotice` 在
|
|
31
|
+
* core-keyset-guard.ts ① 同批进 EXCLUDED。沿用上两行的裁定,不是新判据:[ref] 表态落定那天四处同批翻。
|
|
29
32
|
*/
|
|
30
|
-
export declare const SEMA_PROVENANCE_KINDS_EXCLUDED: readonly ["cross_session_message", "cross_session_notice"];
|
|
33
|
+
export declare const SEMA_PROVENANCE_KINDS_EXCLUDED: readonly ["cross_session_message", "cross_session_notice", "inbox_notice"];
|
|
31
34
|
/** 本仓两条腿入参的**形**(不是 core 类型:入参是运行期载荷,`kind` 先当 string 收、经闭集才成 typed)。 */
|
|
32
35
|
export interface SemaProvenanceInput {
|
|
33
36
|
kind: string;
|