@sema-agent/server 7.108.0 → 7.110.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 +60 -1
- package/README.md +8 -0
- package/README.zh-CN.md +5 -0
- package/USAGE.md +19 -29
- package/dist/approval-ask-machine.d.ts +9 -25
- package/dist/approval-ask-machine.js +5 -2
- package/dist/approval-card.d.ts +33 -3
- package/dist/approval-card.js +26 -2
- package/dist/bench/s1/live-deps.d.ts +4 -5
- package/dist/bench/s1/live-deps.js +9 -10
- package/dist/boot/leader.js +0 -2
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +3 -2
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +6 -12
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +17 -3
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/capabilities/scenarios.d.ts +3 -2
- package/dist/config-catalog.js +2 -4
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +5 -11
- package/dist/config.js +21 -12
- package/dist/deployment-governance.d.ts +8 -2
- package/dist/deployment-governance.js +9 -8
- package/dist/hooks/hook-runner.d.ts +2 -2
- package/dist/hooks/hook-runner.js +40 -26
- package/dist/http/admission.js +8 -2
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/resume-legs.js +6 -1
- package/dist/http/route-ctx.d.ts +3 -3
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/runs.d.ts +0 -1
- package/dist/http/routes/runs.js +22 -0
- package/dist/http/routes/sessions.js +12 -2
- package/dist/http/routes/tasks.js +5 -1
- package/dist/http/server.d.ts +7 -2
- package/dist/http/wire-types.d.ts +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/instruction-source-notice.d.ts +3 -3
- package/dist/leader/diffup.d.ts +53 -0
- package/dist/leader/diffup.js +95 -1
- package/dist/leader/leader.d.ts +0 -6
- package/dist/leader/leader.js +1 -1
- package/dist/leader/repair-wire.js +1 -1
- package/dist/leader/wire.d.ts +3 -10
- package/dist/leader/wire.js +39 -13
- package/dist/memory-layer-legacy-lock.d.ts +3 -3
- package/dist/model-entry-headers.d.ts +5 -2
- package/dist/model-entry-headers.js +4 -4
- package/dist/model-unreachable-notice.d.ts +2 -2
- package/dist/observability/corrupt-read-seat.d.ts +6 -0
- package/dist/observability/corrupt-read-seat.js +6 -0
- package/dist/observability/fail-open.d.ts +13 -5
- package/dist/observability/fail-open.js +13 -5
- package/dist/observability/metrics.js +2 -1
- package/dist/observability/run-terminal.js +10 -0
- package/dist/operator-ask.d.ts +9 -8
- package/dist/operator-ask.js +9 -1
- package/dist/plugins/approval-ask-store-file.d.ts +2 -1
- package/dist/plugins/approval-ask-store-file.js +3 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +7 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +18 -0
- package/dist/plugins/approval-ask-store-sql.js +7 -0
- package/dist/plugins/file-run-store.js +2 -2
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-run-store.js +2 -1
- package/dist/plugins/pg-pool.js +15 -0
- package/dist/plugins/run-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.js +3 -2
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/store-backend.d.ts +4 -0
- package/dist/plugins/store-backend.js +8 -0
- package/dist/plugins/store-contracts.d.ts +7 -0
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/tidb-pool.js +17 -0
- package/dist/plugins/user-input-ledger-file.d.ts +33 -0
- package/dist/plugins/user-input-ledger-file.js +98 -0
- package/dist/plugins/user-input-ledger-sql.d.ts +44 -0
- package/dist/plugins/user-input-ledger-sql.js +101 -0
- package/dist/request-key-closure.js +1 -1
- package/dist/run-local.d.ts +18 -5
- package/dist/run-local.js +26 -12
- package/dist/runs.d.ts +8 -1
- package/dist/runs.js +6 -2
- package/dist/runtime-governance.d.ts +23 -11
- package/dist/runtime-governance.js +7 -12
- package/dist/session-records-skipped-notice.d.ts +4 -3
- package/dist/task-settings.d.ts +0 -6
- package/dist/task-settings.js +0 -1
- package/dist/tool-approval.d.ts +44 -15
- package/dist/tool-approval.js +12 -8
- package/dist/trace/core-keyset-guard.d.ts +17 -2
- package/dist/trace/engine-notice-wire.d.ts +30 -35
- package/dist/trace/engine-notice-wire.js +23 -23
- package/dist/trace/project.js +2 -3
- package/dist/trace/self-minted-wire.d.ts +14 -0
- package/dist/trace/self-minted-wire.js +6 -0
- package/dist/unattended-permission-mode.d.ts +54 -93
- package/dist/unattended-permission-mode.js +8 -61
- package/dist/user-input-ledger.d.ts +296 -0
- package/dist/user-input-ledger.js +337 -0
- package/package.json +4 -4
|
@@ -16,38 +16,8 @@
|
|
|
16
16
|
* 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
|
|
17
17
|
* 刚立,立完就在下一次加码时自己咬住了)。
|
|
18
18
|
*/
|
|
19
|
-
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable"];
|
|
19
|
+
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "task.tool_turn_incomplete"];
|
|
20
20
|
export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
|
|
21
|
-
/**
|
|
22
|
-
* S-690 —— **本仓自铸**的通告码与它们的受众(码 / 铸点 / 受众三者的属主都是本仓)。
|
|
23
|
-
*
|
|
24
|
-
* 为什么可以有这一族:core 的 `EngineNotice.code` 刻意是开放 `string`,顶注逐字「a host forwarding its own notices
|
|
25
|
-
* through the same sink is a supported shape」—— 部署经同一只 `onNotice` 转发体投自己的通告,白名单 / 归属 / 消毒 /
|
|
26
|
-
* 隔离四件判据一个不绕。为什么受众写在这里:core 的 `NOTICE_AUDIENCE` 只收录 core 自己铸的码,对本仓的码
|
|
27
|
-
* `noticeAudienceOf` 只会读保守默认 `"operator"` —— 那是「不认识」,不是判决。顶注的「本仓不许自造第二份受众语义」
|
|
28
|
-
* 说的是 **core 的码**;本表只收 core 码册里**没有**的码,这里是它们唯一的一份受众语义。
|
|
29
|
-
*
|
|
30
|
-
* 🔴 机器门(`test/engine-notice-wire.test.ts` 的 server 自铸码属主门):① 本表的码与 core 运行期码册 `ENGINE_NOTICE_CODES`
|
|
31
|
-
* **零交集**(core 哪天铸了同名码 ⇒ 属主变成两个,当场红:销本表的行、改走 core 的受众与工厂);② 判 `"user"` 的码必须
|
|
32
|
-
* 入白名单;③ 白名单里的码,受众一律由「本表 ∪ core 注册表」给,判出来必须是 `"user"`。
|
|
33
|
-
*/
|
|
34
|
-
export declare const SERVER_NOTICE_AUDIENCE: {
|
|
35
|
-
readonly "instructions.source_changed": "user";
|
|
36
|
-
/**
|
|
37
|
-
* O2(7.101.0 合并复审)—— boot 期探测到 ≤7.30.x 引擎留下的锁住的记忆层(唯一铸点 `src/memory-layer-legacy-lock.ts`,
|
|
38
|
-
* 调用点 = `boot/stores.ts` 的 file 记忆引擎装配段)。受众 **operator**:恢复动词是运维在数据根上执行一次 `chmod -R u+w`,
|
|
39
|
-
* 而且铸在 boot 期、没有任何会话 ⇒ 不上 wire、不进白名单,只走转发体的日志终点(`engine_notice` warn)。
|
|
40
|
-
* `detail: { layers, remedy }` —— 层名(挂载面的直接子目录名)与那一句补救;条目名 / 内容不进。
|
|
41
|
-
*/
|
|
42
|
-
readonly "memory.layer_locked_legacy": "operator";
|
|
43
|
-
/** S-612 —— 会话存档部分记录被跳过(唯一铸点 `src/session-records-skipped-notice.ts`);受众 user,理由见白名单键注。 */
|
|
44
|
-
readonly "session.records_skipped": "user";
|
|
45
|
-
/** S-925 —— 模型调用因建连失败终态(唯一铸点 `src/model-unreachable-notice.ts`);受众 user,理由见白名单键注。 */
|
|
46
|
-
readonly "server.model_unreachable": "user";
|
|
47
|
-
};
|
|
48
|
-
export type ServerNoticeCode = keyof typeof SERVER_NOTICE_AUDIENCE;
|
|
49
|
-
/** 该码是否本仓自铸(属主判据单点:门与投递面都读这一个)。 */
|
|
50
|
-
export declare function isServerNoticeCode(code: string): code is ServerNoticeCode;
|
|
51
21
|
/** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
|
|
52
22
|
export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
|
|
53
23
|
/**
|
|
@@ -104,8 +74,19 @@ export declare function engineNoticeEventData(notice: {
|
|
|
104
74
|
message: string;
|
|
105
75
|
detail?: Record<string, unknown>;
|
|
106
76
|
}, sessionId: string, nowMs: number): EngineNoticeWireRow;
|
|
107
|
-
/**
|
|
108
|
-
|
|
77
|
+
/**
|
|
78
|
+
* 一条 run 腿注册进来的投递口。抛错由 {@link EngineNoticeRouter.route} 隔离(见那里的 fail-open 登记)。
|
|
79
|
+
* 返回值只对 {@link EngineNoticeRouter.routeConfirmed} 有意义(S-928 codex r1-F2 / r2-F1):durable 口交回它那一次账本写的 thenable,
|
|
80
|
+
* 兑现 `true` = 这一帧落进了账本;`false` / 拒绝 = 没落(写失败已由口自己留痕);同步口(live SSE)不交 thenable,**不是**落账确认。`route()` 不读返回值。
|
|
81
|
+
*/
|
|
82
|
+
export type EngineNoticeWireSink = (row: EngineNoticeWireRow) => unknown;
|
|
83
|
+
/** 口的槽位(闭集):`live` = 流上的人(SSE),`durable` = 这条 run 的事件账。{@link EngineNoticeRouter.routeConfirmed} 只数 `durable` 槽的落账确认。 */
|
|
84
|
+
export type EngineNoticeSlot = "live" | "durable";
|
|
85
|
+
/** 一条腿的两只具名口(各自可缺席:bg 腿没有 live SSE,ephemeral sync 腿没有账本)。 */
|
|
86
|
+
export interface EngineNoticeLegSlots {
|
|
87
|
+
readonly live?: EngineNoticeWireSink;
|
|
88
|
+
readonly durable?: EngineNoticeWireSink;
|
|
89
|
+
}
|
|
109
90
|
/**
|
|
110
91
|
* sessionId → 该会话活跃 run 腿的投递口。**进程内**(`defaultSubagentTailBus` 同款姿势):
|
|
111
92
|
* `onNotice` 是同进程的同步回调,通告与它要投的那条腿必然同副本,跨副本路由这件事结构上不存在。
|
|
@@ -130,8 +111,8 @@ export declare class EngineNoticeRouter {
|
|
|
130
111
|
*
|
|
131
112
|
* 每只口各自 try 隔离在 {@link route} 里,单口抛不牵连同腿另一口。
|
|
132
113
|
*/
|
|
133
|
-
registerLeg(sessionId: string,
|
|
134
|
-
/** 单口腿的便捷形(
|
|
114
|
+
registerLeg(sessionId: string, slots: EngineNoticeLegSlots): () => void;
|
|
115
|
+
/** 单口腿的便捷形(测试用):登记为 **durable** 槽 —— 这样 `route()` 与 `routeConfirmed()` 两条路都能用一只口测到(live 槽对后者结构上恒不计)。 */
|
|
135
116
|
register(sessionId: string, sink: EngineNoticeWireSink): () => void;
|
|
136
117
|
/** 诊断/门用:某会话此刻**最新代**有几只口在听(旧代不计——它们收不到帧)。 */
|
|
137
118
|
sinkCount(sessionId: string): number;
|
|
@@ -148,6 +129,20 @@ export declare class EngineNoticeRouter {
|
|
|
148
129
|
detail?: Record<string, unknown>;
|
|
149
130
|
sessionId?: unknown;
|
|
150
131
|
}, nowMs?: number): number;
|
|
132
|
+
/**
|
|
133
|
+
* S-928(codex r1-F2 / r2-F1;[ref] opus 腿 L2 改按槽判):同一只扇出,但**等**每只口交回的持久化结果 —— 返回**确认落账**的口数:
|
|
134
|
+
* 只数 **`durable` 槽**里交回 thenable 且兑现 `true` 的口;兑现 `false` / 拒绝不算;`live` 槽**结构上不计**(不管它返回什么 —— 流上的人收没收到说明不了落账;
|
|
135
|
+
* 修前按「交回 thenable」反推是不是 durable 口,哪天 live 口改成 async 返回 `true` 就会被计成落账)。给「投出之后要记一笔『告知过』」的铸点用:
|
|
136
|
+
* 记账必须跟在**落账**之后,不能跟在「调用了口」之后(后台腿唯一的口是事件账 append,写失败时 `route()` 的计数照样是 1)。判据(白名单 / 归属 / 最新代 / 隔离)与 `route()` 逐字同一条。
|
|
137
|
+
*/
|
|
138
|
+
routeConfirmed(notice: {
|
|
139
|
+
code: string;
|
|
140
|
+
message: string;
|
|
141
|
+
detail?: Record<string, unknown>;
|
|
142
|
+
sessionId?: unknown;
|
|
143
|
+
}, nowMs?: number): Promise<number>;
|
|
144
|
+
/** 两只入口共用的扇出:返回每只**没抛**的口的持久化结果(promise 形一律先接住拒绝 ⇒ 绝不留未处理拒绝)。 */
|
|
145
|
+
private fanOut;
|
|
151
146
|
}
|
|
152
147
|
/** 进程单例(`defaultSubagentTailBus` 同款):分流器与 run 腿直接 import,不穿长参数列表;测试自 new 独立实例。 */
|
|
153
148
|
export declare const defaultEngineNoticeRouter: EngineNoticeRouter;
|
|
@@ -33,16 +33,8 @@ export const ENGINE_NOTICE_WIRE_CODES = [
|
|
|
33
33
|
"permission.ask_released_by_mode",
|
|
34
34
|
"permission.read_deny_unapplied",
|
|
35
35
|
"permission.rule_store_unavailable",
|
|
36
|
+
"task.tool_turn_incomplete",
|
|
36
37
|
];
|
|
37
|
-
export const SERVER_NOTICE_AUDIENCE = {
|
|
38
|
-
"instructions.source_changed": "user",
|
|
39
|
-
"memory.layer_locked_legacy": "operator",
|
|
40
|
-
"session.records_skipped": "user",
|
|
41
|
-
"server.model_unreachable": "user",
|
|
42
|
-
};
|
|
43
|
-
export function isServerNoticeCode(code) {
|
|
44
|
-
return Object.hasOwn(SERVER_NOTICE_AUDIENCE, code);
|
|
45
|
-
}
|
|
46
38
|
const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
|
|
47
39
|
export function isEngineNoticeWireCode(code) {
|
|
48
40
|
return WIRE_CODES.has(code);
|
|
@@ -107,11 +99,13 @@ export function engineNoticeEventData(notice, sessionId, nowMs) {
|
|
|
107
99
|
ts: nowMs,
|
|
108
100
|
};
|
|
109
101
|
}
|
|
102
|
+
const isThenableResult = (v) => typeof v === "object" && v !== null && typeof v.then === "function";
|
|
110
103
|
let nextLegId = 1;
|
|
111
104
|
export class EngineNoticeRouter {
|
|
112
105
|
legs = new Map();
|
|
113
|
-
registerLeg(sessionId,
|
|
114
|
-
const
|
|
106
|
+
registerLeg(sessionId, slots) {
|
|
107
|
+
const sinks = [...(slots.live ? [{ slot: "live", sink: slots.live }] : []), ...(slots.durable ? [{ slot: "durable", sink: slots.durable }] : [])];
|
|
108
|
+
const leg = { legId: nextLegId++, sinks };
|
|
115
109
|
const list = this.legs.get(sessionId) ?? [];
|
|
116
110
|
list.push(leg);
|
|
117
111
|
this.legs.set(sessionId, list);
|
|
@@ -131,7 +125,7 @@ export class EngineNoticeRouter {
|
|
|
131
125
|
};
|
|
132
126
|
}
|
|
133
127
|
register(sessionId, sink) {
|
|
134
|
-
return this.registerLeg(sessionId,
|
|
128
|
+
return this.registerLeg(sessionId, { durable: sink });
|
|
135
129
|
}
|
|
136
130
|
sinkCount(sessionId) {
|
|
137
131
|
const list = this.legs.get(sessionId);
|
|
@@ -141,27 +135,34 @@ export class EngineNoticeRouter {
|
|
|
141
135
|
return this.legs.get(sessionId)?.length ?? 0;
|
|
142
136
|
}
|
|
143
137
|
route(notice, nowMs = Date.now()) {
|
|
138
|
+
return this.fanOut(notice, nowMs).length;
|
|
139
|
+
}
|
|
140
|
+
async routeConfirmed(notice, nowMs = Date.now()) {
|
|
141
|
+
const outcomes = await Promise.all(this.fanOut(notice, nowMs));
|
|
142
|
+
return outcomes.filter((ok) => ok).length;
|
|
143
|
+
}
|
|
144
|
+
fanOut(notice, nowMs) {
|
|
144
145
|
if (!isEngineNoticeWireCode(notice.code))
|
|
145
|
-
return
|
|
146
|
+
return [];
|
|
146
147
|
const sessionId = engineNoticeSessionId(notice);
|
|
147
148
|
if (sessionId === undefined)
|
|
148
|
-
return
|
|
149
|
+
return [];
|
|
149
150
|
const list = this.legs.get(sessionId);
|
|
150
151
|
const leg = list && list.length > 0 ? list[list.length - 1] : undefined;
|
|
151
152
|
if (!leg || leg.sinks.length === 0)
|
|
152
|
-
return
|
|
153
|
+
return [];
|
|
153
154
|
const row = engineNoticeEventData(notice, sessionId, nowMs);
|
|
154
|
-
|
|
155
|
-
for (const sink of leg.sinks) {
|
|
155
|
+
const outcomes = [];
|
|
156
|
+
for (const { slot, sink } of leg.sinks) {
|
|
156
157
|
try {
|
|
157
|
-
sink(row);
|
|
158
|
-
|
|
158
|
+
const result = sink(row);
|
|
159
|
+
outcomes.push(isThenableResult(result) ? Promise.resolve(result).then((v) => slot === "durable" && v === true, () => false) : Promise.resolve(false));
|
|
159
160
|
}
|
|
160
161
|
catch {
|
|
161
162
|
recordFailOpen("server.engine-notice.wire-sink-threw", `code=${notice.code}`);
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
|
-
return
|
|
165
|
+
return outcomes;
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
export const defaultEngineNoticeRouter = new EngineNoticeRouter();
|
|
@@ -169,11 +170,10 @@ export function registerEngineNoticeLeg(opts) {
|
|
|
169
170
|
const { sessionId, live, durable } = opts;
|
|
170
171
|
if (sessionId === undefined || sessionId.length === 0)
|
|
171
172
|
return () => undefined;
|
|
172
|
-
|
|
173
|
-
if (sinks.length === 0)
|
|
173
|
+
if (live === undefined && durable === undefined)
|
|
174
174
|
return () => undefined;
|
|
175
175
|
const router = opts.router ?? defaultEngineNoticeRouter;
|
|
176
|
-
const unregister = router.registerLeg(sessionId,
|
|
176
|
+
const unregister = router.registerLeg(sessionId, { ...(live ? { live } : {}), ...(durable ? { durable } : {}) });
|
|
177
177
|
for (const notice of opts.pending ?? [])
|
|
178
178
|
router.route(notice);
|
|
179
179
|
return unregister;
|
package/dist/trace/project.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { admitProviderEcho, redactErrorMessage, redactModelText } from "../observability/run-terminal.js";
|
|
2
|
-
import { isAskClass, isAskOrigin, isPermissionMode, isPersistedRuleMandate, MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, screenGateOutcome, TEAM_ROLES, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
|
|
2
|
+
import { isAskClass, isAskOrigin, isModeReleaseReason, isPermissionMode, isPersistedRuleMandate, 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";
|
|
@@ -126,11 +126,10 @@ function modeLedgerDefect(d) {
|
|
|
126
126
|
return "disposition.released.origin is outside the closed set";
|
|
127
127
|
if (o.mandate !== undefined && !isPersistedRuleMandate(o.mandate))
|
|
128
128
|
return "disposition.released.mandate is outside the closed set";
|
|
129
|
-
if (o.reason !== undefined && !(
|
|
129
|
+
if (o.reason !== undefined && !isModeReleaseReason(o.reason))
|
|
130
130
|
return "disposition.released.reason is outside the closed set";
|
|
131
131
|
return false;
|
|
132
132
|
}
|
|
133
|
-
const RELEASE_REASON_WORDS = { rule_unreadable: true };
|
|
134
133
|
function screenedGateOrThrow(v) {
|
|
135
134
|
if (v === null || typeof v !== "object") {
|
|
136
135
|
if (v !== undefined)
|
|
@@ -18,6 +18,20 @@ export declare const SELF_MINTED_WIRE: {
|
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* S-928(7.110.0):`engine_notice` `task.user_steer_undrained` 上**本仓铸**的那一臂。词表属主是 core(`USER_STEER_UNDRAINED_CAUSES`,
|
|
23
|
+
* `leg_end` 由引擎铸、逐字过境,不在本表);本表登记的是「这个值自哪一版起由本服务出现在 wire 上」。`satisfies Record<CrashedCause, …>`:
|
|
24
|
+
* core 删掉 `crashed` ⇒ `CrashedCause` 成 never ⇒ 本行编译红。
|
|
25
|
+
*/
|
|
26
|
+
readonly userSteerUndrainedServerCause: {
|
|
27
|
+
readonly at: "engine_notice task.user_steer_undrained detail.cause(本服务铸的臂;下一条同会话 run 开通告口那一拍)";
|
|
28
|
+
readonly members: {
|
|
29
|
+
crashed: {
|
|
30
|
+
attr: string;
|
|
31
|
+
since: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
};
|
|
21
35
|
/**
|
|
22
36
|
* 会话所有者自助抹除面的**已分类拒** → HTTP 状态(S-930 起与 operator 面 `POST /v1/memory/erase` 同一只映射;`since` 记的是**本面**
|
|
23
37
|
* 自哪一版起这样答 —— 7.107.0 及以前本面只认前两只,其余落 500 `internal.error`)。
|
|
@@ -6,6 +6,12 @@ export const SELF_MINTED_WIRE = {
|
|
|
6
6
|
shutdown_before_park: { attr: "-", since: "7.108.0" },
|
|
7
7
|
},
|
|
8
8
|
},
|
|
9
|
+
userSteerUndrainedServerCause: {
|
|
10
|
+
at: "engine_notice task.user_steer_undrained detail.cause(本服务铸的臂;下一条同会话 run 开通告口那一拍)",
|
|
11
|
+
members: {
|
|
12
|
+
crashed: { attr: "-", since: "7.110.0" },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
9
15
|
sessionMemoryEraseStatus: {
|
|
10
16
|
at: "POST /v1/sessions/:id/memory/erase(已分类拒 errorCode → HTTP 状态)",
|
|
11
17
|
members: {
|
|
@@ -1,110 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* S-
|
|
2
|
+
* S-934 ①(S-906 / clay 裁 [ref];core 7.37.0 #1214,`docs/INTEGRATION-CORE.md` §4.6e)—— **无人值守腿:`default` 模式 + 按类作答的 onAsk**。
|
|
3
3
|
*
|
|
4
4
|
* ## 规则(一句)
|
|
5
|
-
* 本仓**自己铸** spec
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* 本仓**自己铸** spec、**没有调用方**能在请求上表态模式的腿 —— leader worker(`src/leader/wire.ts` provisionWorker 两车道)、leader 的
|
|
6
|
+
* 修复腿(集成沙箱修复 `mkRepair`、模块门修复 `leader.ts` `runModuleGate`、单代理修复环 `repair-wire.ts` `attachRepairLoopDeps`)、run-local
|
|
7
|
+
* (`src/run-local.ts`,一次性 CLI)与这些腿的 durable 续跑重挂(`CheckpointCtx.unattendedLeg`)—— 经 {@link withUnattendedLeg} 这**一只**
|
|
8
|
+
* 合并口铸两键:`permissionMode: "default"`(显式:续跑时盖过 checkpoint 记下的旧模式)与 `onAsk` = {@link createUnattendedOnAsk}。
|
|
9
|
+
* 那只 onAsk 读 `AskRequest.classes` **整体**:集非空且每一类都在 {@link UNATTENDED_ANSWERED_CLASSES}(= `{ordinary}`,常量,不是旋钮)⇒ 答 yes;
|
|
10
|
+
* 否则不答 —— 本腿**有席**(run-local 的 `createLocalApprover`:有 TTY 问人、无 TTY 当场拒;续跑腿的协调器席)⇒ 交给席;**无席** ⇒ 答
|
|
11
|
+
* `"unavailable"` = 交回 core 的缺席路(本腿武装了 durable park ⇒ park,等人经 `/decide` 答;否则 headless 拒 —— fail-closed)。
|
|
9
12
|
*
|
|
10
|
-
* ##
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* ## 为什么是这一形(core §4.6e 推荐形逐条)
|
|
14
|
+
* · **读整体,不读 `askClass`**:一问可以同属几类(探针问与写保护并存、组织裁决与 hook 并存),`askClass` 只是表序第一个;集里任一类
|
|
15
|
+
* 不答 ⇒ 整问不答。
|
|
16
|
+
* · **缺席 = 不答的类**:没盖 `origin` 的站(共享制品读确认,KL-1214-a)、部署自铸的请求、7.37.0 之前 park 的行续跑(KL-1214-b,续跑不补)
|
|
17
|
+
* 都不带类 ⇒ 不答。
|
|
18
|
+
* · **答集只 `ordinary`**:七个保护类(`probe` / `write_protection` / `rule_unreadable` / `tool_marks_mcp` / `tool_marks_a2a` /
|
|
19
|
+
* `transcript_write` / `artifact_shared_read`)与 `mode`(plan 的写问)留给人;`RELEASABLE_ASK_CLASSES` 之外的类(hook / ask_rule /
|
|
20
|
+
* org_rule / operator_forced / …,权威为人提的问)恒不答。function approver 的 yes 是「这一次调用的答」—— 它连 `requiresRealApproval`
|
|
21
|
+
* 都清得掉(引擎分不出函数与人),所以带那一位的类(`transcript_write` / `rule_unreadable` / `artifact_shared_read` / `policy_rra`)不进答集
|
|
22
|
+
* (钉:test/unattended-permission-mode.test.ts「答集 ⊆ RELEASABLE_ASK_CLASSES,且与 RRA_EXPLAINERS ∪ {policy_rra} 不交」)。
|
|
23
|
+
* · **`ordinary` 覆盖什么**(core 原句):`Read` 工具的根外读、shell 的根外**写**(`echo x > <outside>` —— 写守卫只判保护表)、模式写门对每一次
|
|
24
|
+
* covered write 的问、形状层对非只读 shell 的问;shell 的根外**读**(`cat <outside>`)是 `probe`,不答。
|
|
17
25
|
*
|
|
18
26
|
* ## 三问
|
|
19
|
-
* · **谁需要**:用 leader / run-local 的部署
|
|
20
|
-
* ·
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* 候裁 18 → D:缺省 bypass 会释放写保护 ⇒ 本腿把写保护从问改成拒({@link createUnattendedWriteProtectionPolicy});另六类释放 = 相对 7.105.0 的宽化,
|
|
25
|
-
* 如实披露(见那只函数的「射程外」)。**过渡形**:core 7.37.0(S-906)后退成 `default` + 按类作答的 onAsk,旋钮硬删(拒启点名)。
|
|
27
|
+
* · **谁需要**:用 leader / run-local 的部署 —— 普通写与非只读命令照跑(7.105.0 行为;7.106.0–7.108.0 过渡形靠 `bypassPermissions` 宽放给出的同一结果)。
|
|
28
|
+
* · **谁受伤**:① 设了 `UNATTENDED_PERMISSION_MODE` 的部署 —— 起服拒启(`config.ts` 墓碑表,`removed` 族);② 靠过渡形缺省 bypass 放行
|
|
29
|
+
* `probe` / MCP·A2A 标记工具 / 转录写 / 规则店读不出 / 共享制品读这六类的无人值守流程 —— 改成不答(有席交席、无席 park 或拒)。安全轴收紧。
|
|
30
|
+
* · **什么补偿**:有人的腿照问人(run-local 在终端里跑;leader 配 durable 审批块 ⇒ park 到 `/decide`);答集是 core 推荐形的常量 ——
|
|
31
|
+
* 要放更多类的部署先立案候裁,不开旋钮。
|
|
26
32
|
*
|
|
27
|
-
* ##
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* **逐字**同形,运维把旋钮设成任何词也不会顺手拿到根外写面(那是 HTTP 腿上 `TASK_WRITE_FACE_ESCALATION` 管的另一件事,
|
|
32
|
-
* S-591;这几条腿不是请求,不经那道门)。⚠️ 这一格是按裁定的**意图**(「= 7.105.0 行为」)施工、比裁定字面多出的一半,
|
|
33
|
-
* 收车档 ⑩ 单列,主会话可翻。
|
|
33
|
+
* ## 退役(硬破坏,零别名 / 零双读 / 零过渡臂)
|
|
34
|
+
* `UNATTENDED_PERMISSION_MODE` 旋钮(`config-catalog` 行 / `config` 解析 / `ServiceConfig.unattendedPermissionMode`)、`createUnattendedWriteProtectionPolicy`
|
|
35
|
+
* (#1211 的 `pathDenyRules` 过渡形)、`createUnattendedLeg` 的模式 / Runner 座参数、写面钉 `writeFace: "roots"`(那一钉只为不拿 bypass 的开放写面;
|
|
36
|
+
* `default` 模式行不给写面 ⇒ 引擎缺省 roots,与 7.105.0 同)、P-DEBT 臂 `server.unattended-leg.mode-releases-protective-asks`(FAIL-OPEN-CENSUS 第 101 行终态)。
|
|
34
37
|
*
|
|
35
38
|
* ## 射程外(成文)
|
|
36
|
-
* · HTTP 腿(`POST /v1/tasks|runs`)
|
|
37
|
-
*
|
|
38
|
-
* ·
|
|
39
|
-
*
|
|
40
|
-
* —— 组织级拒优先。本仓今天不写这一位([ref] 未闭环),故这条今天只在 core 侧可达。
|
|
39
|
+
* · HTTP 腿(`POST /v1/tasks|runs`):模式在请求上,有调用方能表态,不经本合并口。
|
|
40
|
+
* · leader 的 planner 路由(无手 Runner,没有写 / shell 可问)不经本合并口。
|
|
41
|
+
* · 续跑腿上的协调器席:`http/resume-legs.ts` 只在「run 行在场 ∧ 活卡协调器在场」时给 spec 另装活席(`deps.toolApproval && taskId`)——
|
|
42
|
+
* leader worker 的会话没有 run 行,那一装今天够不着本腿;哪天够着了,活席会盖掉本层(方向 = 多问人,fail-closed),见收车档 ⑤。
|
|
41
43
|
*/
|
|
42
|
-
import {
|
|
43
|
-
/** 旋钮的出厂值(主会话裁定候裁 15 B / 18 D)。写面由 {@link buildUnattendedLegSpec} 钉 roots;写保护由 {@link createUnattendedWriteProtectionPolicy} 改拒。 */
|
|
44
|
-
export declare const UNATTENDED_PERMISSION_MODE_DEFAULT: "bypassPermissions";
|
|
45
|
-
/** 解析结果:值 + 它从哪来(boot 一行 `unattended_permission_mode` 的两个字段)。 */
|
|
46
|
-
export interface UnattendedPermissionModeSetting {
|
|
47
|
-
readonly value: PermissionMode;
|
|
48
|
-
readonly source: "env" | "default";
|
|
49
|
-
}
|
|
44
|
+
import type { AskClass, AskRequest, OnAsk, TaskSpec } from "@sema-agent/core";
|
|
50
45
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* 未设 / 空串 ⇒ 出厂值,来源 `default`;词表外 ⇒ **拒启**,拒因点名旋钮与全部合法词([ref]:安全轴旋钮不许「以为设了其实没设」)。
|
|
46
|
+
* 按类作答的**答集** —— core §4.6e 推荐形:机器只答 `ordinary`。**常量,不是旋钮**([ref]):要放更多类是部署级的安全决定,先立案候裁。
|
|
47
|
+
* 不变量(测试钉):答集 ⊆ core `RELEASABLE_ASK_CLASSES`,且与带 `requiresRealApproval` 的类(core `RRA_EXPLAINERS` ∪ `policy_rra`)不交;`mode` 不在其中。
|
|
54
48
|
*/
|
|
55
|
-
export declare
|
|
56
|
-
/**
|
|
57
|
-
export
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/** 无人值守腿铸进 spec 的两键(纯数据 ⇒ `build*`):模式 = 旋钮值;写面 = `"roots"`(理由见模块头注「写面」)。 */
|
|
61
|
-
export declare function buildUnattendedLegSpec(mode: PermissionMode): Required<Pick<TaskSpec, "permissionMode" | "writeFace">>;
|
|
49
|
+
export declare const UNATTENDED_ANSWERED_CLASSES: ReadonlySet<AskClass>;
|
|
50
|
+
/** onAsk 的函数臂(`"allow"` / `"deny"` 两个字面臂是姿态设置,不是席)—— 本腿的活审批席与本层自己都是这一形。 */
|
|
51
|
+
export type UnattendedSeat = Extract<OnAsk, (req: AskRequest, signal?: AbortSignal) => unknown>;
|
|
52
|
+
/** 这一问是不是机器答得了的:读 `classes` **整体** —— 在场、非空、且每一类都在答集;缺席 / 空 / 任一类在答集外 ⇒ 否。 */
|
|
53
|
+
export declare function unattendedAnswers(req: Pick<AskRequest, "classes">): boolean;
|
|
62
54
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* 目标零询问放行;7.105.0 这些腿无模式 ⇒ 写保护是一问 ⇒ 无人可答 ⇒ 拒。core 7.35 没有「放普通问、留保护问」的模式,也没有限定模式可释放类的座。
|
|
67
|
-
*
|
|
68
|
-
* **规则(一条)**:本腿的模式**会释放**写保护一问时(读 core 仲裁本尊 `arbitrateByMode`,不手抄模式词),把**本腿引擎判官用的同一张写保护表**
|
|
69
|
-
* (`runnerSeats` = 构造本腿 Runner 的那份 deps 的两只座 {@link UnattendedLegRunnerSeats};`writeProtectedPaths` 缺席 = core 源表 `WRITE_PROTECTED_DEFAULT_TABLE`)经 core 的
|
|
70
|
-
* `compileWriteProtection` 编成判官,每一行铸成一条 **core 路径拒行**(`ToolPolicy.pathDenyRules`,`faces:["write"]`)—— 引擎的写面把它们并进本腿的拒表:
|
|
71
|
-
* 写工具的路径解析与 shell 站(重定向 / `tee` / `sed -i` / `cp`·`mv`·`touch` 操作数)都按拒表拒,模式**不释放拒**。**路径匹配全在 core**:每行的
|
|
72
|
-
* `reaches` 就是判官自己的 `matchPath`(同一只折叠、同一只数据根放行 —— `dataRoot` 与引擎判官同源:本腿 Runner 的 `memoryEngineDir`),本仓不写一个字的
|
|
73
|
-
* 路径比较。模式不释放写保护(`default` / `acceptEdits` / `plan` / `auto` / `dontAsk`)⇒ 不铸(那一问照旧问,有席的腿上人能答 —— run-local 有 TTY 时)。
|
|
74
|
-
*
|
|
75
|
-
* **射程外**(如实,收车档 ⑩ 候裁 18):缺省 bypass 还释放 `probe` / `tool_marks_mcp` / `tool_marks_a2a` / `transcript_write` / `rule_unreadable` /
|
|
76
|
-
* `artifact_shared_read` 六类(core 对它们没有「拒」的座),相对 7.105.0 是宽化 —— CHANGELOG「谁受伤」与 MIGRATION 写明,补偿 = 旋钮 `acceptEdits` / `default`。
|
|
77
|
-
* **过渡形**:core 7.37.0(S-906,`AskRequest.askClass?` / `classes?` 到货)后本仓改成 `default` 模式 + fail-closed 按类作答的 `onAsk`,本座与旋钮一并硬删。
|
|
78
|
-
* **续跑**(codex r3 F1):本层是活闭包、进不了 checkpoint,而 core 续跑在 spec 不说模式时取 checkpoint 记下的模式 ⇒ durable 续跑必须重挂整条腿 ——
|
|
79
|
-
* leader worker 的续跑上下文带 `CheckpointCtx.unattendedLeg`,续跑家族据此让 `resolveSpec` 在全部折叠之后调 {@link createUnattendedLeg} / {@link withUnattendedLeg}。
|
|
80
|
-
* **已知过拒**(codex r3 F2,fail-closed 方向,随过渡形退役):core 的路径拒座在 shell 站把 `cp` 的**源**操作数也按写目标判(CC 同形,cli280.js:216993
|
|
81
|
-
* `cp:"write"`),引擎写保护判官只判真写目标 ⇒ 从写保护目标复制出来在本层下被拒、7.105.0 上照跑。本仓不补路径比较(那是第二个判官),特征化钉在
|
|
82
|
-
* test/run-local.test.ts「🚧 codex r3 F2」。
|
|
83
|
-
* **附带效果**(S-877 四续 ⑫b,随过渡形退役):本层只能是一只 `ToolPolicy`(路径拒座挂在策略上,`check` 必填 —— 这里恒 `allow`),原先没有 toolPolicy
|
|
84
|
-
* 的腿因此被引擎读成「有策略层」:系统提示多 `# Execution environment` 一块、UNGATED 告警不再发。7.35.2 上表达不了「只供拒行、不算策略层」;
|
|
85
|
-
* 特征化钉在 test/unattended-permission-mode.test.ts「🚧 四续 ⑫b」。
|
|
86
|
-
*
|
|
87
|
-
* 返回 `undefined` = 本腿不需要这一层(模式不释放写保护,或运维把表清空 `[]`)。活对象(带判官闭包)⇒ `create*`。
|
|
88
|
-
*/
|
|
89
|
-
export declare function createUnattendedWriteProtectionPolicy(mode: PermissionMode, runnerSeats: UnattendedLegRunnerSeats): ToolPolicy | undefined;
|
|
90
|
-
/** 无人值守腿的全部铸件:模式 + 写面(纯数据)与写保护拒层(活对象,可缺席)。由 {@link createUnattendedLeg} 铸、{@link withUnattendedLeg} 并进 spec。 */
|
|
91
|
-
export interface UnattendedLeg {
|
|
92
|
-
readonly spec: Required<Pick<TaskSpec, "permissionMode" | "writeFace">>;
|
|
93
|
-
readonly writeProtection: ToolPolicy | undefined;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* 引擎写保护判官读的两只 Runner 座(`RunnerDeps` 的同名切片):表(`writeProtectedPaths`,缺席 = core 源表)与数据根放行(`memoryEngineDir`,缺席 = 引擎缺省)。
|
|
97
|
-
* 调用方递**构造本腿 Runner 的那一份** deps(或它的展开源)—— 表与根从哪来、根要不要在,都由那份 deps 说了算,腿的接线处不再单独读任何一只坐标
|
|
98
|
-
* (本模块是唯一读者;S-877 四续:接线处各读一份就是「两边同一张表、同一个根」这句话的第二、三个写者)。
|
|
55
|
+
* 按类作答的 onAsk(活对象 ⇒ `create*`)。答得了 ⇒ `true`(这一次调用的答);答不了 ⇒ 有席交席(席的答原样返回,席自己的
|
|
56
|
+
* fail-closed 语义不动),无席 ⇒ `"unavailable"`(core G1:这一问此刻没有人够得着 ⇒ 改道 durable park;本腿没武装 park ⇒ headless 拒)。
|
|
57
|
+
* 不答的臂**从不**自己拒成 `false`:那会把「交给 park 的人」折成「机器拒了」,durable 审批块的 leader worker 就再也 park 不下来。
|
|
99
58
|
*/
|
|
100
|
-
export
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
59
|
+
export declare function createUnattendedOnAsk(seat: UnattendedSeat | undefined): UnattendedSeat;
|
|
60
|
+
/** {@link withUnattendedLeg} 铸进 spec 的两键。 */
|
|
61
|
+
export type UnattendedLegSpec = {
|
|
62
|
+
readonly permissionMode: "default";
|
|
63
|
+
readonly onAsk: UnattendedSeat;
|
|
64
|
+
};
|
|
104
65
|
/**
|
|
105
|
-
*
|
|
106
|
-
* (
|
|
107
|
-
* 调用点必须在 spec 其余键(含治理折叠)
|
|
66
|
+
* 无人值守腿并进一条 spec —— 各腿的**唯一**合并口:`permissionMode: "default"` + `onAsk: createUnattendedOnAsk(seat)`。`seat` = 本腿的
|
|
67
|
+
* 活审批席(run-local 递它的 `createLocalApprover`;leader 各腿与续跑重挂无席,不递)—— 席只经这个参数进来,从不从 spec 上读。
|
|
68
|
+
* 调用点必须在 spec 其余键(含治理折叠)**之后**调它;本口不碰 `toolPolicy`(没有策略层要并:过渡形的写保护拒层已退役)。
|
|
108
69
|
*/
|
|
109
|
-
export declare function withUnattendedLeg<S extends Partial<TaskSpec>>(spec: S,
|
|
70
|
+
export declare function withUnattendedLeg<S extends Partial<TaskSpec>>(spec: S, seat?: UnattendedSeat): S & UnattendedLegSpec;
|
|
110
71
|
//# sourceMappingURL=unattended-permission-mode.d.ts.map
|
|
@@ -1,65 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const trimmed = (raw ?? "").trim();
|
|
6
|
-
if (trimmed === "")
|
|
7
|
-
return { value: UNATTENDED_PERMISSION_MODE_DEFAULT, source: "default" };
|
|
8
|
-
const word = PERMISSION_MODES.find((m) => m === trimmed);
|
|
9
|
-
if (word === undefined) {
|
|
10
|
-
throw new Error(`env UNATTENDED_PERMISSION_MODE="${trimmed.slice(0, 120)}" is not a permission mode — use one of: ${PERMISSION_MODES.join(", ")} ` +
|
|
11
|
-
`(or leave it unset for the default "${UNATTENDED_PERMISSION_MODE_DEFAULT}", which keeps the unattended legs — leader workers / repairs and run-local — acting as they did before server 7.106.0). ` +
|
|
12
|
-
`The word is matched EXACTLY (surrounding whitespace trimmed, no case folding). ` +
|
|
13
|
-
`Refusing to boot rather than treating it as unset: that would run those legs under a mode the operator did not choose`);
|
|
14
|
-
}
|
|
15
|
-
return { value: word, source: "env" };
|
|
1
|
+
export const UNATTENDED_ANSWERED_CLASSES = new Set(["ordinary"]);
|
|
2
|
+
export function unattendedAnswers(req) {
|
|
3
|
+
const classes = req.classes;
|
|
4
|
+
return classes !== undefined && classes.length > 0 && classes.every((c) => UNATTENDED_ANSWERED_CLASSES.has(c));
|
|
16
5
|
}
|
|
17
|
-
export function
|
|
18
|
-
return
|
|
6
|
+
export function createUnattendedOnAsk(seat) {
|
|
7
|
+
return (req, signal) => (unattendedAnswers(req) ? true : seat !== undefined ? seat(req, signal) : "unavailable");
|
|
19
8
|
}
|
|
20
|
-
export function
|
|
21
|
-
return { permissionMode:
|
|
22
|
-
}
|
|
23
|
-
export function createUnattendedWriteProtectionPolicy(mode, runnerSeats) {
|
|
24
|
-
if (!modeReleases(mode, "write_protection"))
|
|
25
|
-
return undefined;
|
|
26
|
-
const dataRoot = runnerSeats.memoryEngineDir;
|
|
27
|
-
const judge = compileWriteProtection(runnerSeats.writeProtectedPaths, dataRoot !== undefined ? { dataRoot } : undefined);
|
|
28
|
-
if (judge === undefined)
|
|
29
|
-
return undefined;
|
|
30
|
-
const pathDenyRules = judge.rows.map((row) => ({
|
|
31
|
-
rule: `WriteProtected(${row.name})`,
|
|
32
|
-
tool: "Edit",
|
|
33
|
-
pattern: row.name,
|
|
34
|
-
faces: ["write"],
|
|
35
|
-
reaches: (absolutePath) => judge.matchPath(absolutePath)?.name === row.name,
|
|
36
|
-
}));
|
|
37
|
-
return {
|
|
38
|
-
check: () => ({ action: "allow" }),
|
|
39
|
-
pathDenyRules,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function modeReleases(mode, askClass) {
|
|
43
|
-
const classes = new Set([askClass]);
|
|
44
|
-
return arbitrateByMode(mode, { classes, askClass, row: foldArbitrationRow(classes) }).kind === "released";
|
|
45
|
-
}
|
|
46
|
-
const INTENDED_RELEASES = new Set(["ordinary", "mode"]);
|
|
47
|
-
function releasedProtectiveClasses(mode) {
|
|
48
|
-
return ASK_CLASSES.filter((c) => !INTENDED_RELEASES.has(c) && c !== "write_protection" && modeReleases(mode, c));
|
|
49
|
-
}
|
|
50
|
-
const RELEASED_PROTECTIVE_ASKS_TAG = "server.unattended-leg.mode-releases-protective-asks";
|
|
51
|
-
export function createUnattendedLeg(mode, runnerSeats) {
|
|
52
|
-
const debt = releasedProtectiveClasses(mode);
|
|
53
|
-
if (debt.length > 0)
|
|
54
|
-
recordFailOpen(RELEASED_PROTECTIVE_ASKS_TAG, `mode=${mode} released=${debt.join(",")}`);
|
|
55
|
-
return { spec: buildUnattendedLegSpec(mode), writeProtection: createUnattendedWriteProtectionPolicy(mode, runnerSeats) };
|
|
56
|
-
}
|
|
57
|
-
export function withUnattendedLeg(spec, leg) {
|
|
58
|
-
const wp = leg.writeProtection;
|
|
59
|
-
return {
|
|
60
|
-
...spec,
|
|
61
|
-
...leg.spec,
|
|
62
|
-
...(wp !== undefined ? { toolPolicy: spec.toolPolicy !== undefined ? combinePolicies(spec.toolPolicy, wp) : wp } : {}),
|
|
63
|
-
};
|
|
9
|
+
export function withUnattendedLeg(spec, seat) {
|
|
10
|
+
return { ...spec, permissionMode: "default", onAsk: createUnattendedOnAsk(seat) };
|
|
64
11
|
}
|
|
65
12
|
//# sourceMappingURL=unattended-permission-mode.js.map
|