@sema-agent/server 7.140.0 → 7.141.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 +290 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +75 -13
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +31 -16
- package/dist/approval-card.js +6 -3
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.js +8 -4
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/memory-consolidation.js +1 -1
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/resolve-spec.js +7 -4
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +5 -0
- package/dist/boot/stage-05-execution-env.js +3 -1
- package/dist/boot/stage-06-runners.d.ts +0 -2
- package/dist/boot/stage-06-runners.js +1 -1
- package/dist/boot/stage-07-capability-layer.d.ts +0 -1
- package/dist/boot/stage-07-capability-layer.js +8 -10
- package/dist/boot/stage-08-reapers.d.ts +0 -1
- package/dist/boot/stage-09-leader.d.ts +0 -1
- package/dist/boot/stage-10-http-server.d.ts +0 -1
- package/dist/boot/stage-10-http-server.js +1 -0
- package/dist/boot/stores.js +10 -4
- package/dist/boot/web-admin.js +1 -0
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +9 -0
- package/dist/capability-facts.js +9 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +11 -8
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +19 -33
- package/dist/config-types.d.ts +26 -8
- package/dist/config.js +55 -46
- package/dist/delegated-session-owner.d.ts +21 -19
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +49 -2
- package/dist/execution-lane-caps.js +90 -13
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +4 -0
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/resume-legs.js +37 -20
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions.js +5 -4
- package/dist/http/routes/tasks.js +63 -66
- package/dist/http/run-read-face.js +3 -3
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +2 -2
- package/dist/http/server.d.ts +9 -2
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +1 -1
- package/dist/memory-sync-client.d.ts +14 -2
- package/dist/memory-sync-client.js +80 -18
- package/dist/observability/fail-open.d.ts +30 -2
- package/dist/observability/fail-open.js +30 -2
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +6 -2
- package/dist/observability/run-terminal.js +25 -26
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +74 -3
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +4 -1
- package/dist/plugins/execution-placement-sql.d.ts +11 -1
- package/dist/plugins/execution-placement-sql.js +12 -1
- package/dist/plugins/file-run-store.d.ts +2 -2
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +227 -35
- package/dist/plugins/local-session-store.js +518 -37
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +42 -2
- package/dist/plugins/one-time-migrations.js +65 -3
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +24 -3
- package/dist/plugins/remote-env-e2b.js +131 -18
- package/dist/plugins/remote-env-k8s.d.ts +20 -1
- package/dist/plugins/remote-env-k8s.js +148 -25
- package/dist/plugins/remote-env-local-docker.d.ts +25 -11
- package/dist/plugins/remote-env-local-docker.js +87 -21
- package/dist/plugins/run-store-sql.d.ts +3 -3
- package/dist/plugins/run-store-sql.js +4 -3
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/session-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/store-backend.d.ts +31 -0
- package/dist/plugins/store-backend.js +21 -4
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +3 -2
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/runs.d.ts +39 -12
- package/dist/runs.js +68 -53
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +22 -4
- package/dist/session-error-wire.js +16 -0
- package/dist/session-incarnation.d.ts +9 -3
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +1 -1
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +52 -25
- package/dist/task-settings.js +91 -28
- package/dist/terminal.d.ts +77 -20
- package/dist/terminal.js +66 -20
- package/dist/trace/core-keyset-guard.d.ts +79 -2
- package/dist/trace/exit-seat.d.ts +7 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/redact.js +323 -90
- package/dist/trace/self-minted-wire.d.ts +168 -2
- package/dist/trace/self-minted-wire.js +63 -0
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +1 -1
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.141.0.json +367 -0
|
@@ -3,7 +3,7 @@ import { redactSecrets } from "../trace/redact.js";
|
|
|
3
3
|
import { isProviderEchoShape } from "@sema-agent/core";
|
|
4
4
|
import { isModelTextLedgerEventType, isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
5
5
|
import { isUnsuccessfulRunStatus } from "../plugins/store-contracts.js";
|
|
6
|
-
import { sealResultForExit } from "../terminal.js";
|
|
6
|
+
import { mapPauseSeats, PAUSE_SEATS, sealResultForExit } from "../terminal.js";
|
|
7
7
|
import { errorText } from "./err-text.js";
|
|
8
8
|
import { recordFailOpen } from "./fail-open.js";
|
|
9
9
|
import { toolEndMediaAsDescriptors } from "../trace/tool-output-media.js";
|
|
@@ -295,8 +295,11 @@ const PROVIDER_ECHO_SHAPERS = {
|
|
|
295
295
|
}
|
|
296
296
|
const LEGACY_RESULT_LEAVES = ["errorMessage", "blockedReason", "checkpointGate"];
|
|
297
297
|
export const TREATED_RESULT_LEAVES = [
|
|
298
|
-
...
|
|
299
|
-
|
|
298
|
+
...new Set([
|
|
299
|
+
...Object.entries(RESULT_KEY_TREATMENT).filter(([, t]) => t !== "verbatim").map(([k]) => k),
|
|
300
|
+
...LEGACY_RESULT_LEAVES,
|
|
301
|
+
...PAUSE_SEATS.map((seat) => seat.path[0]),
|
|
302
|
+
]),
|
|
300
303
|
];
|
|
301
304
|
export function redactTerminalResult(result) {
|
|
302
305
|
if (result === undefined || result === null)
|
|
@@ -333,24 +336,8 @@ export function redactTerminalResult(result) {
|
|
|
333
336
|
if (shaped !== detail)
|
|
334
337
|
nt = { ...cur, detail: shaped };
|
|
335
338
|
}
|
|
336
|
-
const gateOf = (cause) => {
|
|
337
|
-
if (cause === null || typeof cause !== "object" || !("gate" in cause))
|
|
338
|
-
return undefined;
|
|
339
|
-
const g = cause.gate;
|
|
340
|
-
const ng = redactCheckpointGate(g);
|
|
341
|
-
return { gate: ng, changed: ng !== g };
|
|
342
|
-
};
|
|
343
339
|
const base = nt ?? t;
|
|
344
|
-
if (t.kind === "
|
|
345
|
-
const g = gateOf(base);
|
|
346
|
-
if (g?.changed === true)
|
|
347
|
-
nt = { ...base, gate: g.gate };
|
|
348
|
-
}
|
|
349
|
-
else if (t.kind === "failed") {
|
|
350
|
-
const np = base.nestedPause;
|
|
351
|
-
const g = gateOf(np);
|
|
352
|
-
if (g?.changed === true)
|
|
353
|
-
nt = { ...base, nestedPause: { ...np, gate: g.gate } };
|
|
340
|
+
if (t.kind === "failed") {
|
|
354
341
|
const cur = nt ?? base;
|
|
355
342
|
const detail = cur.detail;
|
|
356
343
|
const shaped = shapeTerminalFailureDetail(detail);
|
|
@@ -362,6 +349,17 @@ export function redactTerminalResult(result) {
|
|
|
362
349
|
changed = true;
|
|
363
350
|
}
|
|
364
351
|
}
|
|
352
|
+
const gated = mapPauseSeats(out, (seat, keys) => {
|
|
353
|
+
if (!Object.hasOwn(seat, keys.gate))
|
|
354
|
+
return seat;
|
|
355
|
+
const g = seat[keys.gate];
|
|
356
|
+
const ng = redactCheckpointGate(g);
|
|
357
|
+
return ng === g ? seat : { ...seat, [keys.gate]: ng };
|
|
358
|
+
});
|
|
359
|
+
if (gated !== out) {
|
|
360
|
+
Object.assign(out, gated);
|
|
361
|
+
changed = true;
|
|
362
|
+
}
|
|
365
363
|
if (r.remoteEnvFailures !== undefined) {
|
|
366
364
|
out.remoteEnvFailures = Array.isArray(r.remoteEnvFailures)
|
|
367
365
|
? r.remoteEnvFailures.map((f) => (f && typeof f === "object" && typeof f.message === "string" ? { ...f, message: redactErrorMessage(f.message) } : f))
|
|
@@ -429,8 +427,8 @@ export function redactTerminalResult(result) {
|
|
|
429
427
|
}
|
|
430
428
|
return changed ? out : result;
|
|
431
429
|
}
|
|
432
|
-
export function sealRunRowResult(result) {
|
|
433
|
-
return result === null ? null : redactTerminalResult(sealResultForExit(result));
|
|
430
|
+
export function sealRunRowResult(result, readSeat) {
|
|
431
|
+
return result === null ? null : redactTerminalResult(sealResultForExit(result, readSeat));
|
|
434
432
|
}
|
|
435
433
|
export function redactLedgerEventData(type, data) {
|
|
436
434
|
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
@@ -439,7 +437,7 @@ export function redactLedgerEventData(type, data) {
|
|
|
439
437
|
const result = data.result;
|
|
440
438
|
if (result === null || typeof result !== "object")
|
|
441
439
|
return data;
|
|
442
|
-
return { ...data, result:
|
|
440
|
+
return { ...data, result: sealRunRowResult(result) };
|
|
443
441
|
}
|
|
444
442
|
if (type === "failed") {
|
|
445
443
|
const msg = data.errorMessage;
|
|
@@ -896,9 +894,10 @@ export function memoRedactedRead(seat, raw, redact) {
|
|
|
896
894
|
}
|
|
897
895
|
return out;
|
|
898
896
|
}
|
|
899
|
-
export function memoRedactTerminalResult(seat,
|
|
900
|
-
if (
|
|
901
|
-
return
|
|
897
|
+
export function memoRedactTerminalResult(seat, raw) {
|
|
898
|
+
if (raw === undefined || raw === null)
|
|
899
|
+
return raw;
|
|
900
|
+
const result = sealResultForExit(raw, seat);
|
|
902
901
|
const r = result;
|
|
903
902
|
if (TREATED_RESULT_LEAVES.every((k) => r[k] === undefined))
|
|
904
903
|
return result;
|
|
@@ -31,9 +31,16 @@
|
|
|
31
31
|
* git 腿是自由函数 / 闭包)。7.103.0 起各腿不再直接调本函数 —— 只经 `server-secret-env.ts` 的唯一组合 `hostSubprocessEnv`。
|
|
32
32
|
* 装配层用 {@link installSecretEnvScrubRecorder} 把句柄接上,装配之前命中的次数不会丢(计数照常累加,
|
|
33
33
|
* install 时一次性补进 metrics),披露行则在下一次命中时补发(未成功披露的 key 不进 `disclosed` 集)。
|
|
34
|
+
*
|
|
35
|
+
* ## 同族的第二件:自有凭据**值**没被引擎收下的点名({@link recordOwnCredentialValueNotWithheld})
|
|
36
|
+
* 起服第一步(三个进程入口在任何 logger 之前)把本进程自有凭据名与值交给引擎声明;引擎判「这不是一个凭据值」的那一个不扣、名照扣、不拒启,
|
|
37
|
+
* 需要一条运维看得见的行。它与上面的披露同一类东西(子进程 env 保护「没有按预期生效」的留痕),所以住同一个座:同一只 logger 句柄、同一条「装配之前不丢」纪律。
|
|
38
|
+
* 这一件的做法比披露行更简单:logger 已接上 ⇒ 走它;还没接上(起服第一步)⇒ **当拍**经一只进程默认的 JSON 行日志器(与进程入口自己建的日志器同一实现、同一行形)写 stderr ——
|
|
39
|
+
* 不缓冲、不补发:缓冲会在「接上 logger 之前就致命退出」的起服路径上把这一行吞掉(修前的裸 `console.warn` 当拍就出),而当拍写没有这一形。
|
|
40
|
+
* 判官只在引擎一处 —— 本座只转述引擎的拒句,不判。
|
|
34
41
|
*/
|
|
35
42
|
import { type SecretEnvFinding } from "@sema-agent/core";
|
|
36
|
-
import type
|
|
43
|
+
import { type Logger } from "./logger.js";
|
|
37
44
|
import type { Metrics } from "./metrics.js";
|
|
38
45
|
/**
|
|
39
46
|
* 一个剥除现场的登记:`note` = 这条腿上泄漏意味着什么;`remedy` = 部署方能不能自己把某个被剥的键放回来。
|
|
@@ -92,6 +99,13 @@ interface Sink {
|
|
|
92
99
|
logger?: Logger;
|
|
93
100
|
metrics?: Metrics;
|
|
94
101
|
}
|
|
102
|
+
/** 自有凭据值没被引擎收下的一次点名(引擎的拒句从不回显值,本件也不带值)。 */
|
|
103
|
+
export interface OwnCredentialValueRefusal {
|
|
104
|
+
/** 持有这个值的变量名。 */
|
|
105
|
+
readonly name: string;
|
|
106
|
+
/** 引擎拒这个值的原句(引擎是「什么不算凭据值」的唯一判官;本仓只转述)。 */
|
|
107
|
+
readonly reason: string;
|
|
108
|
+
}
|
|
95
109
|
/**
|
|
96
110
|
* 记一次剥除。**总不抛** —— 观测本身绝不能变成故障(同 `recordFailOpen` / reapers 的判据)。
|
|
97
111
|
* `findings` 为空(这一次没丢任何 key)时什么也不做:那不是事件,是常态。
|
|
@@ -99,6 +113,11 @@ interface Sink {
|
|
|
99
113
|
* `allowed` = 这一次生效的点名放行名单([ref]),只在有出路的现场(`remedy` 在场)写进披露行;没有放行面的腿传 `[]`。
|
|
100
114
|
*/
|
|
101
115
|
export declare function recordSecretEnvScrub(site: SecretEnvScrubSite, findings: readonly SecretEnvFinding[], allowed: readonly string[]): void;
|
|
116
|
+
/**
|
|
117
|
+
* 记一次「自有凭据值没被引擎收下」(`declareServerWithheldEnv` 值臂,引擎拒句原文进 `reason`)。**总不抛**(观测绝不能变成故障)。
|
|
118
|
+
* 一条 `warn` 结构化行,**当拍**写:logger 已接上 ⇒ 走它;还没接上(起服第一步)⇒ 走进程默认的 JSON 行日志器(同一行形,stderr)。
|
|
119
|
+
*/
|
|
120
|
+
export declare function recordOwnCredentialValueNotWithheld(refusal: OwnCredentialValueRefusal): void;
|
|
102
121
|
/** 装配层把 logger/metrics 接上(main.ts,与 `installFailOpenRecorder` 同一处)。装配前累计的次数在此补进 metrics。 */
|
|
103
122
|
export declare function installSecretEnvScrubRecorder(deps: Sink): void;
|
|
104
123
|
/** 已留驻的「现场×键」条数(测试/诊断读面)。恒 ≤ {@link DISCLOSED_CAP}。 */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { summarizeRedactions } from "@sema-agent/core";
|
|
2
2
|
import { describeAllowlistPostures } from "../env-name-allowlist-knobs.js";
|
|
3
|
+
import { createLogger } from "./logger.js";
|
|
3
4
|
export const SECRET_ENV_SCRUB_SITES = {
|
|
4
5
|
"server.remote-env-host.spawn-env": {
|
|
5
6
|
note: "模型可驱动的 host shell 继承 env(`inheritEnv` 三词任一,缺省 `\"scrub\"`;`\"all\"` 词下扣的只有本服务自己的凭据名)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令",
|
|
@@ -43,6 +44,7 @@ const SCRUB_COUNTER_HELP = "secret env entries not handed on as written (dropped
|
|
|
43
44
|
const DISCLOSED_CAP = 512;
|
|
44
45
|
const SEP = "=";
|
|
45
46
|
let sink = {};
|
|
47
|
+
let preInstallLogger;
|
|
46
48
|
let counts = new Map();
|
|
47
49
|
let pushed = new Map();
|
|
48
50
|
let disclosed = new Set();
|
|
@@ -101,6 +103,18 @@ export function recordSecretEnvScrub(site, findings, allowed) {
|
|
|
101
103
|
catch {
|
|
102
104
|
}
|
|
103
105
|
}
|
|
106
|
+
const OWN_CREDENTIAL_VALUE_NOT_WITHHELD = "own_credential_value_not_withheld";
|
|
107
|
+
function emitValueRefusal(logger, r) {
|
|
108
|
+
logger.warn(OWN_CREDENTIAL_VALUE_NOT_WITHHELD, { name: r.name, reason: r.reason });
|
|
109
|
+
}
|
|
110
|
+
export function recordOwnCredentialValueNotWithheld(refusal) {
|
|
111
|
+
try {
|
|
112
|
+
const logger = sink.logger ?? (preInstallLogger ??= createLogger("warn"));
|
|
113
|
+
emitValueRefusal(logger, refusal);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
104
118
|
export function installSecretEnvScrubRecorder(deps) {
|
|
105
119
|
sink = { ...deps };
|
|
106
120
|
deps.metrics?.counter(SCRUB_COUNTER, SCRUB_COUNTER_HELP);
|
|
@@ -18,8 +18,8 @@ export interface WorkflowCompletionInboxEntry {
|
|
|
18
18
|
* is the deployment's job). `runId` then holds the core task_id (disjoint id spaces). */
|
|
19
19
|
kind?: "workflow_complete" | "task_notification";
|
|
20
20
|
/** The per-kind extras as a JSON text — bounded + redacted AT ENQUEUE (the store carries only egress-safe bytes).
|
|
21
|
-
* `task_notification`: {task_type, toolUseId?, result?, output_file?, …}. `workflow_complete`: at most `{"completionId": …}`
|
|
22
|
-
* (see {@link buildWorkflowCompleteInboxEntry}; absent when the engine minted none). The SQL stores' existing `payload`
|
|
21
|
+
* `task_notification`: {task_type, toolUseId?, result?, output_file?, …}. `workflow_complete`: at most `{"completionId": …, "diagnostics": …}`
|
|
22
|
+
* (see {@link buildWorkflowCompleteInboxEntry}; each key absent when the engine minted none). The SQL stores' existing `payload`
|
|
23
23
|
* column carries it verbatim for both kinds — no DDL. */
|
|
24
24
|
payload?: string;
|
|
25
25
|
}
|
|
@@ -203,6 +203,8 @@ export declare class FileWorkflowCompletionInbox extends InMemoryWorkflowComplet
|
|
|
203
203
|
/**
|
|
204
204
|
* 一次 workflow 完成 → 入箱条目(`workflow_complete` 家族;启动期投递闭包的**唯一**构造点)。`summary` 在 core 的 notifier seam 上已脱敏 + 限长,这里原样入箱;
|
|
205
205
|
* `completionId`(引擎铸,`WorkflowCompletionPayload` 上在场 iff 引擎给了)逐字落进 `payload` 列 —— 同一次完成的补投帧与 live 帧同一个相关 id,本服务零自铸。
|
|
206
|
+
* `diagnostics`(引擎铸的诊断教学文本,在场 iff 引擎给了)是自由文本:**这里**过出口座(`inbox-diagnostics-text` 行 —— 与 live `task_notification.diagnostics` 同一个脱敏 scope,
|
|
207
|
+
* 加一条界)再落 `payload` 列,drain 逐字带出;店里不放未洗的字节。
|
|
206
208
|
*/
|
|
207
209
|
export declare function buildWorkflowCompleteInboxEntry(route: {
|
|
208
210
|
sessionId: string;
|
|
@@ -212,6 +214,7 @@ export declare function buildWorkflowCompleteInboxEntry(route: {
|
|
|
212
214
|
status: "completed" | "failed";
|
|
213
215
|
summary: string;
|
|
214
216
|
completionId?: string;
|
|
217
|
+
diagnostics?: string;
|
|
215
218
|
}, enqueuedAt: number): WorkflowCompletionInboxEntry;
|
|
216
219
|
/**
|
|
217
220
|
* P1 ①② delivery half, TWO-PHASE since 1.80: read the session's pending completions, emit
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, openSync, writeSync, fsyncSync, closeSync, renameSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { redactSecrets } from "../trace/redact.js";
|
|
4
|
+
import { seatText } from "../trace/exit-seat.js";
|
|
4
5
|
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
5
6
|
import { injectionTierOrAbsent } from "../trace/injection-tier.js";
|
|
6
7
|
import { semaProvenanceOrAbsent } from "../trace/sema-provenance.js";
|
|
@@ -273,12 +274,19 @@ function readEntryExtras(payload) {
|
|
|
273
274
|
}
|
|
274
275
|
return typeof extras === "object" && extras !== null && !Array.isArray(extras) ? extras : {};
|
|
275
276
|
}
|
|
276
|
-
function encodeWorkflowCompleteExtras(
|
|
277
|
-
|
|
277
|
+
function encodeWorkflowCompleteExtras(extras) {
|
|
278
|
+
const body = {
|
|
279
|
+
...(extras.completionId ? { completionId: extras.completionId } : {}),
|
|
280
|
+
...(extras.diagnostics ? { diagnostics: extras.diagnostics } : {}),
|
|
281
|
+
};
|
|
282
|
+
return Object.keys(body).length > 0 ? { payload: JSON.stringify(body) } : {};
|
|
278
283
|
}
|
|
279
284
|
function workflowCompleteFrameExtras(payload) {
|
|
280
|
-
const
|
|
281
|
-
return
|
|
285
|
+
const { completionId, diagnostics } = readEntryExtras(payload);
|
|
286
|
+
return {
|
|
287
|
+
...(typeof completionId === "string" && completionId !== "" ? { completionId } : {}),
|
|
288
|
+
...(typeof diagnostics === "string" && diagnostics !== "" ? { diagnostics } : {}),
|
|
289
|
+
};
|
|
282
290
|
}
|
|
283
291
|
export function buildWorkflowCompleteInboxEntry(route, p, enqueuedAt) {
|
|
284
292
|
return {
|
|
@@ -288,7 +296,7 @@ export function buildWorkflowCompleteInboxEntry(route, p, enqueuedAt) {
|
|
|
288
296
|
status: p.status,
|
|
289
297
|
summary: p.summary,
|
|
290
298
|
enqueuedAt,
|
|
291
|
-
...encodeWorkflowCompleteExtras(p.completionId),
|
|
299
|
+
...encodeWorkflowCompleteExtras({ completionId: p.completionId, diagnostics: p.diagnostics ? seatText("inbox-diagnostics-text", p.diagnostics) : undefined }),
|
|
292
300
|
};
|
|
293
301
|
}
|
|
294
302
|
export async function emitPendingWorkflowCompletions(inbox, sessionId, callerPrincipal, emit, diag) {
|
|
@@ -6,8 +6,10 @@ import { type WorkflowAgentSessionIndex } from "./workflow-agent-session-index.j
|
|
|
6
6
|
* core 给 `WorkflowCompletionNotifier.notify` 的入参加一个键 ⇒ 这里编译红(缺一行),逼人对「它上不上这条链」表态;core 删 / 改名一个键 ⇒ 同样红(多一行)。
|
|
7
7
|
* · `carry`:搬运到投递(journal 门 → 收件箱 → `workflow_complete` 帧 / 账本行)。`runId` / `status` / `summary` 必带;其余可选键引擎没给(或空串)⇒ 载荷上缺席。
|
|
8
8
|
* `completionId` = 引擎的一次完成的相关 id(fill-once,终态转换上铸):逐字、不脱敏、本服务零自铸。
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* `diagnostics` = 引擎在完成通知上铸的诊断教学文本(未完成 agent 的摘要行、完整记录的读取手柄、journal 坐标、续跑句):补投路径是它在原腿已不在(断线重连 / 重启)时唯一的去处,
|
|
10
|
+
* 所以搬运;它是自由文本,出门前在入箱处过出口座(见 `buildWorkflowCompleteInboxEntry`),载荷上仍是引擎值。**崩溃恢复臂不带**:`WorkflowRun` 行上没有这一位,本服务不编。
|
|
11
|
+
* · `drop`:这条持久链只搬运一句受限摘要 + 诊断文本 + 路由键 —— `task_id` / `task_type` / `toolUseId` 是 live `task_notification` 帧的键(那条路径自带),`result` / `usage` 是完整结果 / 计量
|
|
12
|
+
* (`WorkflowStatus` 读得到),不进补投帧。
|
|
11
13
|
* 键名直呼 core 的入参型(型文本里就有 core 名)⇒ 读表的 literals 段收得到这些键。
|
|
12
14
|
*/
|
|
13
15
|
export declare const WORKFLOW_NOTIFY_KEY_DISPOSITION: {
|
|
@@ -18,12 +20,12 @@ export declare const WORKFLOW_NOTIFY_KEY_DISPOSITION: {
|
|
|
18
20
|
readonly principal: "carry";
|
|
19
21
|
readonly originatingSessionId: "carry";
|
|
20
22
|
readonly completionId: "carry";
|
|
23
|
+
readonly diagnostics: "carry";
|
|
21
24
|
readonly task_id: "drop";
|
|
22
25
|
readonly task_type: "drop";
|
|
23
26
|
readonly toolUseId: "drop";
|
|
24
27
|
readonly result: "drop";
|
|
25
28
|
readonly usage: "drop";
|
|
26
|
-
readonly diagnostics: "drop";
|
|
27
29
|
};
|
|
28
30
|
type NotifyKeyDisposition = typeof WORKFLOW_NOTIFY_KEY_DISPOSITION;
|
|
29
31
|
/** 处置表里 `carry` 的键。 */
|
|
@@ -13,12 +13,12 @@ export const WORKFLOW_NOTIFY_KEY_DISPOSITION = {
|
|
|
13
13
|
principal: "carry",
|
|
14
14
|
originatingSessionId: "carry",
|
|
15
15
|
completionId: "carry",
|
|
16
|
+
diagnostics: "carry",
|
|
16
17
|
task_id: "drop",
|
|
17
18
|
task_type: "drop",
|
|
18
19
|
toolUseId: "drop",
|
|
19
20
|
result: "drop",
|
|
20
21
|
usage: "drop",
|
|
21
|
-
diagnostics: "drop",
|
|
22
22
|
};
|
|
23
23
|
const SUMMARY_MAX = 500;
|
|
24
24
|
export function recoverySummary(status, error) {
|
|
@@ -81,6 +81,7 @@ export class WorkflowNotifyGate {
|
|
|
81
81
|
...(input.principal ? { principal: input.principal } : {}),
|
|
82
82
|
...(input.originatingSessionId ? { originatingSessionId: input.originatingSessionId } : {}),
|
|
83
83
|
...(input.completionId ? { completionId: input.completionId } : {}),
|
|
84
|
+
...(input.diagnostics ? { diagnostics: input.diagnostics } : {}),
|
|
84
85
|
}, input.scope ?? input.principal ?? "default");
|
|
85
86
|
},
|
|
86
87
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **一个调用方交来的权限模式词,本部署收不收、收成什么** —— 唯一的受理判官(规则一处、三只属主各一处、序一处)。
|
|
3
|
+
*
|
|
4
|
+
* ## 一条规则,三只属主,本模块只排序
|
|
5
|
+
* ① 本任务 settings 的 kill-switch 折叠(`task-settings.ts` `foldDisableAutoMode`:`disableAutoMode` 在 ⇒ `auto` 折 `default`);
|
|
6
|
+
* ② 运维 `AUTONOMY` 的入口地板钳(`runtime-governance.ts` `entryPermissionMode` × `autonomyModeFloor`:`plan` 地板下比它松的词折成 `plan`);
|
|
7
|
+
* ③ 托管姿态的写围栏拒(`hosted-posture.ts` `permissionModeWriteFaceRefusal`:开写面的词在托管形上 422 `request.host_posture_refused`)。
|
|
8
|
+
* ①② 是**入口一步**({@link entryStep}:改词、不拒),③ 是**托管一步**(只拒、不改词)。顺序承重的只有「② 在 ③ 之前」(地板把 bypass 钳成 plan 之后就不开写面,不拒);
|
|
9
|
+
* ① 只动 `auto`、③ 只看开写面的词,两者互不相交。
|
|
10
|
+
*
|
|
11
|
+
* ## 两口的调用形(同一组函数、同一个序;同一个词 × 同一份部署事实 × 同一份任务 settings ⇒ 同一个结果,钉 `test/run-permission-mode-admission.test.ts` 的两口表)
|
|
12
|
+
* · 在飞 run 的切档口(`http/routes/run-permission-mode.ts`:`POST /v1/runs/:id/permission-mode` 的 `mode`,本任务 settings 读存储体)调整只判官 {@link admitPermissionModeWord}。
|
|
13
|
+
* · 提交口(`POST /v1/tasks|/v1/tasks/stream|/v1/runs` 的词 = 顶层 `permissionMode` ?? bundle `settings.permissions.defaultMode`)把两步落在**上一版就在的两处**:
|
|
14
|
+
* 入口一步在受理门盖章({@link stampSubmissionEntryMode},`http/admission.ts`:值形门之后、装配之前 —— 首跑装配与 `buildCheckpointCtx` 读的都是盖过章的体);
|
|
15
|
+
* 托管一步是装配期那一处判(`boot/resolve-spec.ts`,对盖过章的词调同一只 ③;提交腿与续跑重放腿同经)。不把整只判官提前到受理门:那会让托管拒抢在
|
|
16
|
+
* 属主 / 会话 / 执行上下文与装配期其余值判之前答(拒序变了 —— 钉同一文件的拒序组)。续跑重放不再过 ②:地板是**入口**事实,热改只影响之后提交的任务。
|
|
17
|
+
*
|
|
18
|
+
* ## 为什么切档口必须经这只判官(而不是靠引擎的 deny 位)
|
|
19
|
+
* 引擎在根腿上只认两只模式拒位(`RuntimeCaps.bypassPermissions` / `RuntimeCaps.autoMode`,本仓由 center 与本地 kill-switch 写);上面三问
|
|
20
|
+
* 都不是引擎位 —— 根腿没有天花板座,托管姿态与任务 settings 都是逐提交的事实。切档口若不经这只判官,提交口在受理时拒过 / 折过的词
|
|
21
|
+
* 就能在 run 跑起来之后原样切进去。
|
|
22
|
+
*
|
|
23
|
+
* ## 答
|
|
24
|
+
* · `refused` —— ③ 拒:状态 422、码 `request.host_posture_refused`、句子是属主的那一句;两口同码同句。
|
|
25
|
+
* · `admitted` —— `requested`(调用方说的词,缺席 = 没说)、`admitted`(交给引擎的词)、`entry`(② 那一次求值的读数,盖章与地板通告只读它)、
|
|
26
|
+
* `foldedBy`(本判官把词改了的那一站;没改 ⇒ 缺席。① ② 都改了 ⇒ `entry_floor`:落定的词是地板给的)。
|
|
27
|
+
*/
|
|
28
|
+
import type { PermissionMode } from "@sema-agent/core";
|
|
29
|
+
import { type Autonomy, type EntryPermissionMode } from "./runtime-governance.js";
|
|
30
|
+
import { HOST_POSTURE_REFUSED_CODE, type TaskWriteFaceInput } from "./hosted-posture.js";
|
|
31
|
+
import { type ParsedTaskSettings } from "./task-settings.js";
|
|
32
|
+
/** 本判官改词的站(闭集;wire 上切档回执的 `admission.foldedBy`)。引擎自己的折因词(`operator_deny` / `face_absent` / `inherited_ceiling`)在引擎回执里,不进这张表。 */
|
|
33
|
+
export declare const PERMISSION_MODE_ADMISSION_FOLDS: readonly ["entry_floor", "disable_auto_mode"];
|
|
34
|
+
export type PermissionModeAdmissionFold = (typeof PERMISSION_MODE_ADMISSION_FOLDS)[number];
|
|
35
|
+
/** 判官读的事实:部署(活 config:`AUTONOMY` 地板 + 托管射程与天花板旋钮)与本任务的 settings(提交口 = 提交体的,切档口 = 存储体的)。 */
|
|
36
|
+
export interface PermissionModeAdmissionFacts {
|
|
37
|
+
readonly deployment: TaskWriteFaceInput & {
|
|
38
|
+
readonly autonomy?: Autonomy | undefined;
|
|
39
|
+
};
|
|
40
|
+
readonly settings: ParsedTaskSettings | undefined;
|
|
41
|
+
}
|
|
42
|
+
export type PermissionModeAdmission = {
|
|
43
|
+
readonly kind: "refused";
|
|
44
|
+
readonly status: 422;
|
|
45
|
+
readonly code: typeof HOST_POSTURE_REFUSED_CODE;
|
|
46
|
+
readonly message: string;
|
|
47
|
+
} | {
|
|
48
|
+
readonly kind: "admitted";
|
|
49
|
+
readonly requested: PermissionMode | undefined;
|
|
50
|
+
readonly admitted: PermissionMode | undefined;
|
|
51
|
+
readonly entry: EntryPermissionMode;
|
|
52
|
+
readonly foldedBy?: PermissionModeAdmissionFold;
|
|
53
|
+
};
|
|
54
|
+
/** 整只判官(入口一步 → 托管一步)—— 在飞 run 的切档口调它。 */
|
|
55
|
+
export declare function admitPermissionModeWord(word: PermissionMode | undefined, facts: PermissionModeAdmissionFacts): PermissionModeAdmission;
|
|
56
|
+
/**
|
|
57
|
+
* 提交口的入口一步(`http/admission.ts` 唯一调用;直调装配腿的测试夹具也调它 —— 少了这一步的体是部署上到不了的体):
|
|
58
|
+
* 体上的词(顶层 `permissionMode` ?? bundle `settings.permissions.defaultMode`)过 {@link entryStep},本任务的 settings = 体上那一份;
|
|
59
|
+
* 入口词盖进体(`task-settings.ts` `stampEntryPermissionMode`:地板在场时写顶层 `permissionMode`,缺席原物)。不拒 —— 托管一步在装配期判(见顶注「两口的调用形」)。
|
|
60
|
+
*/
|
|
61
|
+
export declare function stampSubmissionEntryMode<B extends {
|
|
62
|
+
permissionMode?: unknown;
|
|
63
|
+
settings?: unknown;
|
|
64
|
+
}>(body: B, deployment: PermissionModeAdmissionFacts["deployment"]): {
|
|
65
|
+
readonly body: B;
|
|
66
|
+
readonly entry: EntryPermissionMode;
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=permission-mode-admission.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { autonomyModeFloor, entryPermissionMode } from "./runtime-governance.js";
|
|
2
|
+
import { HOST_POSTURE_REFUSED_CODE, permissionModeWriteFaceRefusal } from "./hosted-posture.js";
|
|
3
|
+
import { foldDisableAutoMode, parseTaskSettings, requestedPermissionMode, stampEntryPermissionMode } from "./task-settings.js";
|
|
4
|
+
export const PERMISSION_MODE_ADMISSION_FOLDS = ["entry_floor", "disable_auto_mode"];
|
|
5
|
+
function entryStep(word, facts) {
|
|
6
|
+
const folded = foldDisableAutoMode(word, facts.settings);
|
|
7
|
+
const entry = entryPermissionMode(folded, autonomyModeFloor(facts.deployment.autonomy));
|
|
8
|
+
const foldedBy = entry.clamped ? "entry_floor" : folded !== word ? "disable_auto_mode" : undefined;
|
|
9
|
+
return { entry, ...(foldedBy !== undefined ? { foldedBy } : {}) };
|
|
10
|
+
}
|
|
11
|
+
export function admitPermissionModeWord(word, facts) {
|
|
12
|
+
const { entry, foldedBy } = entryStep(word, facts);
|
|
13
|
+
const why = permissionModeWriteFaceRefusal(entry.effective, facts.deployment);
|
|
14
|
+
if (why !== undefined)
|
|
15
|
+
return { kind: "refused", status: 422, code: HOST_POSTURE_REFUSED_CODE, message: why };
|
|
16
|
+
return { kind: "admitted", requested: word, admitted: entry.effective, entry, ...(foldedBy !== undefined ? { foldedBy } : {}) };
|
|
17
|
+
}
|
|
18
|
+
export function stampSubmissionEntryMode(body, deployment) {
|
|
19
|
+
const settings = parseTaskSettings(body.settings).settings;
|
|
20
|
+
const { entry } = entryStep(requestedPermissionMode(body, settings), { deployment, settings });
|
|
21
|
+
return { body: stampEntryPermissionMode(body, entry), entry };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=permission-mode-admission.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* **规则表的唯一编译口** —— 一张工具名规则表(`ask` / `deny` 之一)编成一只 `ToolPolicy`。读者:请求腿的 `settings.permissions.{ask,deny}`
|
|
3
|
-
* 两桶(`task-settings.ts` 的 `deriveSettingsPolicy
|
|
3
|
+
* 两桶(`task-settings.ts` 的 `deriveSettingsPolicy`,每桶按根至多编两集,见文件尾 {@link ruleRootOf})。设置层条目的形与层 → 根表也住这里(文件尾一节)。
|
|
4
4
|
*
|
|
5
5
|
* 🔴 core 7.42.0 起策略只说 deny / ask:allow 规则有且只有引擎 allow 层的两个席位 —— 请求的 `settings.permissions.allow` 整份投
|
|
6
6
|
* `TaskSpec.allowedTools`(`task-settings.ts` 的 `applyTaskSettings`),运维的 `APPROVAL_GRANT` 投 `RunnerDeps.allowedTools`
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* 为什么住在一只叶模块里(不留在 `task-settings.ts`):本模块只 import core 与契约包 settings-schema(纯叶),读者引它不成环。
|
|
10
10
|
*/
|
|
11
11
|
import { type PathRuleBases, type PermissionRule, type PermissionRulePolicyOptions, type ToolPolicy } from "@sema-agent/core";
|
|
12
|
+
import { type SettingsSource } from "@sema-agent/settings-schema";
|
|
12
13
|
/** 一张表里规则条数的上限 —— 请求腿 wire 门(`task-settings.ts` 的 deny / ask 两桶判形)读的也是这一个名字:core 的缺省帽是 256,
|
|
13
14
|
* 不对齐的话第 257 条起编译当场抛,等于服务端自己收下的东西自己反悔。 */
|
|
14
15
|
export declare const MAX_PERMISSION_RULES_PER_LIST = 1000;
|
|
@@ -21,13 +22,13 @@ export declare const MAX_PERMISSION_RULES_PER_LIST = 1000;
|
|
|
21
22
|
* 交一只名册解析器会多出一种读法:目录没有的名字成 `unsupported.tool_unknown` —— 本仓自铸的工具(`repo_read_file(path:…)`)上一条编得出、真判的规则会在受理口被拒。
|
|
22
23
|
*/
|
|
23
24
|
export declare function buildRuleListJudgeOptions(pathBases: PathRuleBases | undefined): Pick<PermissionRulePolicyOptions, "caps" | "pathBases">;
|
|
24
|
-
/** A name-keyed rule-list ToolPolicy: a tool named by `
|
|
25
|
+
/** A name-keyed rule-list ToolPolicy: a tool named by `rules` gets `behavior` (`ask` tightens to an approval gate;
|
|
25
26
|
* `deny` hard-blocks); every other tool is out of scope (`defaultAction: "allow"` = this policy has no opinion about
|
|
26
27
|
* anything it does not name — core 7.42.0: it states nothing and clears nothing). Composed deny-wins with the rest of
|
|
27
28
|
* the fold, so both only ever tighten.
|
|
28
29
|
*
|
|
29
|
-
* `source` =
|
|
30
|
-
* 句里点名它:`flagged by permission rule "mcp__figma__*" (settings.permissions.ask)`)
|
|
30
|
+
* `source` = 这条规则**写在哪**(请求腿 `settings.permissions.<桶>`,带层条目再加层:`settings.permissions.ask, layer userSettings`)。它盖进每条规则(core 的 deny / ask
|
|
31
|
+
* 句里点名它:`flagged by permission rule "mcp__figma__*" (settings.permissions.ask)`),编不出来时的错误句点名桶;只是出处,
|
|
31
32
|
* 不带任何优先级(层序在各自的折叠里)。
|
|
32
33
|
*
|
|
33
34
|
* 🔴 [ref] / core [ref] —— 本函数曾是 `new Set(askNames).has(req.toolName)` 的**逐字**比对,那是
|
|
@@ -74,6 +75,54 @@ export declare function buildRuleListJudgeOptions(pathBases: PathRuleBases | und
|
|
|
74
75
|
* 排除在外),dist 亲读在案。审计归因改由 `matchedAskRule` + `source` 承担,后者比前者强:它还让 core 的 auto-mode 分类器
|
|
75
76
|
* **拒绝**替人清掉这条 ask(「运维写下的『每次问我』不是分类器的犹豫」),方向是收紧。
|
|
76
77
|
*/
|
|
77
|
-
/** `behavior` = 这张表的态度 —— 型直接是 core 的 `PermissionRule["behavior"]`(不手抄:core 加 / 减一种态度,这里与它同步、读者 tsc 上见到)。
|
|
78
|
-
|
|
78
|
+
/** `behavior` = 这张表的态度 —— 型直接是 core 的 `PermissionRule["behavior"]`(不手抄:core 加 / 减一种态度,这里与它同步、读者 tsc 上见到)。
|
|
79
|
+
* `rules` = 这一集的规则,**每条自带出处**({@link SourcedRule}:请求自己写的纯串 = 桶坐标;带层条目 = 桶坐标 + 层,{@link ruleSourceOf});
|
|
80
|
+
* `table` = 这一集出自哪只桶(只进编不出时的兜底句;逐条坐标由先跑的受理判 `rulePolicyBucketsRefusal` 给)。 */
|
|
81
|
+
export declare function createRuleListPolicy(rules: readonly SourcedRule[], behavior: PermissionRule["behavior"], table: string, pathBases: PathRuleBases | undefined): ToolPolicy;
|
|
82
|
+
/**
|
|
83
|
+
* deny / ask 桶的一个条目:**纯串** = 请求自己写的规则(今天的形,读在任务根);**带层条目** `{ rule, layer }` = 端从某个设置层读来的规则,`layer` 是
|
|
84
|
+
* 契约包 `SETTINGS_SOURCES` 的来源词(= 参照实现的设置来源词)。层只决定这条规则的 `/` 读在哪个根({@link layerRuleRoot}),**不决定准入档**
|
|
85
|
+
* (准入只由凭据推,`http/caller-trust.ts`):调用方自报任何层,挪动的只是它自己那条收紧读在哪,与它自己写 `//abs` 等价。
|
|
86
|
+
*
|
|
87
|
+
* 为什么是「条目带层」而不是一只新键:本版之前的每一个读者(回滚、滚动发布中的旧副本)对一只没见过的键是**静默忽略**(续跑重放走宽容层),
|
|
88
|
+
* 带层的收紧会整批消失;对一个不是串的条目则在桶门上 `bad_entry` —— 新提交 400、续跑 409,响亮拒。条数帽也因此仍是「一桶一张表」。
|
|
89
|
+
*/
|
|
90
|
+
export type RuleListEntry = string | LayeredRule;
|
|
91
|
+
/** 带层条目(恰两键;受理判 {@link ruleListEntryOk})。 */
|
|
92
|
+
export interface LayeredRule {
|
|
93
|
+
readonly rule: string;
|
|
94
|
+
readonly layer: SettingsSource;
|
|
95
|
+
}
|
|
96
|
+
/** 一条进编译口的规则与它的出处串(core 规则行逐条带 `source`,问 / 拒句点名它)。 */
|
|
97
|
+
export interface SourcedRule {
|
|
98
|
+
readonly rule: string;
|
|
99
|
+
readonly source: string;
|
|
100
|
+
}
|
|
101
|
+
/** 一个条目合不合 deny / ask 桶的条目律:非空串;或恰 `rule`(非空串)+ `layer`(`SETTINGS_SOURCES` 里的词)两键的纯对象。别的形一律不合(多键、空串、未知层、数组、原型对象)。 */
|
|
102
|
+
export declare function ruleListEntryOk(x: unknown): x is RuleListEntry;
|
|
103
|
+
/** 条目的规则原文(纯串即它自己)。 */
|
|
104
|
+
export declare function ruleTextOf(entry: RuleListEntry): string;
|
|
105
|
+
/** 条目进编译口的出处串:纯串 = 桶坐标(今天的字面);带层 = 桶坐标 + 层(`settings.permissions.deny, layer userSettings`)。 */
|
|
106
|
+
export declare function ruleSourceOf(table: string, entry: RuleListEntry): string;
|
|
107
|
+
/** 规则的 `/` 读在哪个根(闭集,也是一桶里各集的编译序与判序):`task` = 任务根;`configuration` = 执行环境的配置根(`execution-config-root.ts`)。 */
|
|
108
|
+
export declare const RULE_ROOTS: readonly ["task", "configuration"];
|
|
109
|
+
export type RuleRoot = (typeof RULE_ROOTS)[number];
|
|
110
|
+
/**
|
|
111
|
+
* 一个设置层的 `/` 读在哪个根 —— 参照实现(CC 2.1.309)的表照录(`cli309.js:258818` `zBt` → `:252700` `D8o` / `swn`:user 层 ⇒ 配置目录 `Le()`〔`:251921`〕;
|
|
112
|
+
* project / local / policy ⇒ 工作目录;flag ⇒ flag 文件的目录,内联 ⇒ 工作目录)。本服务的两处取舍:`flagSettings` 读任务根(flag 文件的目录是端那台机的事实,本服务手里没有;
|
|
113
|
+
* 要固定位置写 `//abs`);`foreignProjectSettings`(仓库里写给别的工具的项目文件)与项目层同根。契约包加一个来源词 ⇒ 这里编译红。
|
|
114
|
+
*/
|
|
115
|
+
export declare function layerRuleRoot(layer: SettingsSource): RuleRoot;
|
|
116
|
+
/**
|
|
117
|
+
* 一个条目编进哪一集:**只有 `/` 随层变** —— 带层条目的层根是配置根 ∧ 这条规则要 `root`(core 的必需基分类 `ruleBasesNeeded`)⇒ `configuration`;
|
|
118
|
+
* 其余一律 `task`(纯串、别的层、`~/` / 相对形、`//abs` 与 subpath 这类分类为空的规则 —— 后者在 Windows 族上仍随声明的根选盘,必须留在今天那一集)。
|
|
119
|
+
* 文法读不出的串 ⇒ `task`(受理判在前面就拒它,这里不另说一句)。
|
|
120
|
+
*
|
|
121
|
+
* 为什么按根分集、不按层分集:共根各层给出逐字相同的锚,合一集判决不变;而一只声明了 `root` 却没声明 `cwd` 的集把相对形读在声明的根上(core
|
|
122
|
+
* `policyAnchors`)—— 「一层一集」在任务根读不出的腿(未登记工作目录的 host 腿、每一条沙箱腿)上会把 user 层的相对规则读到配置根下。配置根那一集只装要
|
|
123
|
+
* `root` 的规则,`cwd` 永远不被问。
|
|
124
|
+
*/
|
|
125
|
+
export declare function ruleRootOf(entry: RuleListEntry, behavior: PermissionRule["behavior"]): RuleRoot;
|
|
126
|
+
/** 一桶的条目按根分集(受理判与编译读同一只:判的就是要编的那一集);集内保持原序。 */
|
|
127
|
+
export declare function ruleSetsOf(entries: readonly RuleListEntry[], behavior: PermissionRule["behavior"]): Readonly<Record<RuleRoot, readonly RuleListEntry[]>>;
|
|
79
128
|
//# sourceMappingURL=permission-rule-list.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createPermissionRulePolicy } from "@sema-agent/core";
|
|
2
|
-
import { stripControlAndBidi } from "@sema-agent/settings-schema";
|
|
1
|
+
import { createPermissionRulePolicy, parseRuleText, ruleBasesNeeded } from "@sema-agent/core";
|
|
2
|
+
import { SETTINGS_SOURCES, stripControlAndBidi } from "@sema-agent/settings-schema";
|
|
3
3
|
export const MAX_PERMISSION_RULES_PER_LIST = 1_000;
|
|
4
4
|
export function buildRuleListJudgeOptions(pathBases) {
|
|
5
5
|
return {
|
|
@@ -7,13 +7,60 @@ export function buildRuleListJudgeOptions(pathBases) {
|
|
|
7
7
|
...(pathBases !== undefined ? { pathBases } : {}),
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
-
export function createRuleListPolicy(
|
|
10
|
+
export function createRuleListPolicy(rules, behavior, table, pathBases) {
|
|
11
11
|
try {
|
|
12
|
-
return createPermissionRulePolicy(
|
|
12
|
+
return createPermissionRulePolicy(rules.map(({ rule, source }) => ({ rule, behavior, source })), { defaultAction: "allow", ...buildRuleListJudgeOptions(pathBases) });
|
|
13
13
|
}
|
|
14
14
|
catch (e) {
|
|
15
15
|
const coreMsg = stripControlAndBidi(e instanceof Error ? e.message : String(e));
|
|
16
|
-
throw new Error(`${
|
|
16
|
+
throw new Error(`${table.replace("settings.", "")} has rule(s) core cannot compile — ${coreMsg}`, { cause: e });
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
const SETTINGS_SOURCE_SET = new Set(SETTINGS_SOURCES);
|
|
20
|
+
export function ruleListEntryOk(x) {
|
|
21
|
+
if (typeof x === "string")
|
|
22
|
+
return x.length > 0;
|
|
23
|
+
if (x === null || typeof x !== "object" || Array.isArray(x) || Object.getPrototypeOf(x) !== Object.prototype)
|
|
24
|
+
return false;
|
|
25
|
+
const keys = Object.keys(x);
|
|
26
|
+
if (keys.length !== 2 || !Object.hasOwn(x, "rule") || !Object.hasOwn(x, "layer"))
|
|
27
|
+
return false;
|
|
28
|
+
const { rule, layer } = x;
|
|
29
|
+
return typeof rule === "string" && rule.length > 0 && typeof layer === "string" && SETTINGS_SOURCE_SET.has(layer);
|
|
30
|
+
}
|
|
31
|
+
export function ruleTextOf(entry) {
|
|
32
|
+
return typeof entry === "string" ? entry : entry.rule;
|
|
33
|
+
}
|
|
34
|
+
export function ruleSourceOf(table, entry) {
|
|
35
|
+
return typeof entry === "string" ? table : `${table}, layer ${entry.layer}`;
|
|
36
|
+
}
|
|
37
|
+
export const RULE_ROOTS = ["task", "configuration"];
|
|
38
|
+
export function layerRuleRoot(layer) {
|
|
39
|
+
switch (layer) {
|
|
40
|
+
case "userSettings":
|
|
41
|
+
return "configuration";
|
|
42
|
+
case "projectSettings":
|
|
43
|
+
case "localSettings":
|
|
44
|
+
case "flagSettings":
|
|
45
|
+
case "policySettings":
|
|
46
|
+
case "foreignProjectSettings":
|
|
47
|
+
return "task";
|
|
48
|
+
default: {
|
|
49
|
+
const unhandled = layer;
|
|
50
|
+
return unhandled;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function ruleRootOf(entry, behavior) {
|
|
55
|
+
if (typeof entry === "string" || layerRuleRoot(entry.layer) === "task")
|
|
56
|
+
return "task";
|
|
57
|
+
const parsed = parseRuleText(entry.rule, behavior);
|
|
58
|
+
return "rule" in parsed && ruleBasesNeeded(parsed.rule).includes("root") ? "configuration" : "task";
|
|
59
|
+
}
|
|
60
|
+
export function ruleSetsOf(entries, behavior) {
|
|
61
|
+
const sets = { task: [], configuration: [] };
|
|
62
|
+
for (const entry of entries)
|
|
63
|
+
sets[ruleRootOf(entry, behavior)].push(entry);
|
|
64
|
+
return sets;
|
|
65
|
+
}
|
|
19
66
|
//# sourceMappingURL=permission-rule-list.js.map
|
|
@@ -142,6 +142,12 @@ export declare class CachingSessionStore implements SessionStore {
|
|
|
142
142
|
* 只读快照而非 getter:声明是店的构造期常量(会变的声明本身就是缺陷)。
|
|
143
143
|
*/
|
|
144
144
|
readonly placements?: SessionStore["placements"];
|
|
145
|
+
/**
|
|
146
|
+
* 内层店的**会话耐久声明**(core `SessionStore.durability`,说的是未 placed 的会话)转发 —— 与 {@link retention} / {@link placements} 同一族教训:
|
|
147
|
+
* 生产默认把真店包进本类,core 的接线清单(`wiring_manifest.session.store`)与停驻自检读的就是包装层;不转发 = 一只声明了 `durable` 的店在默认部署上
|
|
148
|
+
* 读成 `process_local`(core 对缺席的 fail-closed 读法),没有任何一处报错。只读快照;缺席则不设。
|
|
149
|
+
*/
|
|
150
|
+
readonly durability?: SessionStore["durability"];
|
|
145
151
|
/** [ref]:placed 分区的两只读面(reap 分区腿 / 准入探针)——同款转发理由:reaper 与准入判据拿到的
|
|
146
152
|
* 是包装层,不转发 = 分区腿静默不可用、暖命中的 claim 关门静默失效。缺席则不设(诚实降级)。 */
|
|
147
153
|
readonly listPlaced?: (kind: "subagent", opts?: {
|
|
@@ -33,6 +33,7 @@ export class CachingSessionStore {
|
|
|
33
33
|
replaceEntries;
|
|
34
34
|
retention;
|
|
35
35
|
placements;
|
|
36
|
+
durability;
|
|
36
37
|
listPlaced;
|
|
37
38
|
placementOf;
|
|
38
39
|
constructor(inner, opts) {
|
|
@@ -43,6 +44,8 @@ export class CachingSessionStore {
|
|
|
43
44
|
const declared = inner.retention;
|
|
44
45
|
if (declared !== undefined)
|
|
45
46
|
this.retention = declared;
|
|
47
|
+
if (inner.durability !== undefined)
|
|
48
|
+
this.durability = inner.durability;
|
|
46
49
|
if (inner.placements !== undefined)
|
|
47
50
|
this.placements = inner.placements;
|
|
48
51
|
if (typeof inner.listPlaced === "function")
|