@sema-agent/server 7.112.0 → 7.114.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 +95 -0
- package/USAGE.md +4 -4
- package/dist/approval-ask-audit-store.d.ts +3 -4
- package/dist/approval-ask-audit-store.js +3 -3
- package/dist/approval-card.d.ts +4 -4
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/memory-boundary.d.ts +2 -1
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/resolve-spec.js +2 -2
- package/dist/boot/stores.js +4 -6
- package/dist/boot/workflow-orchestration.js +4 -4
- package/dist/config-catalog.js +6 -5
- package/dist/config-invariants.d.ts +27 -0
- package/dist/config-invariants.js +54 -39
- package/dist/config-types.d.ts +5 -11
- package/dist/config-types.js +0 -1
- package/dist/config.d.ts +0 -16
- package/dist/config.js +44 -69
- package/dist/env-facts.d.ts +3 -1
- package/dist/env-facts.js +5 -1
- package/dist/execution-lane-caps.d.ts +54 -30
- package/dist/execution-lane-caps.js +48 -20
- package/dist/hooks/hook-runner.d.ts +87 -7
- package/dist/hooks/hook-runner.js +113 -20
- package/dist/host-decision.d.ts +5 -3
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -4
- package/dist/http/request-guards.js +2 -2
- package/dist/http/resume-legs.d.ts +9 -10
- package/dist/http/resume-legs.js +64 -18
- package/dist/http/route-ctx.d.ts +38 -8
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/routes/background.js +2 -2
- package/dist/http/routes/capabilities.js +2 -2
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/notify-wake.js +3 -3
- package/dist/http/routes/runs.js +19 -13
- package/dist/http/routes/session-sync.js +6 -6
- package/dist/http/routes/sessions.js +20 -20
- package/dist/http/routes/tasks.js +1 -0
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +1 -1
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/local-data-root.d.ts +14 -0
- package/dist/local-data-root.js +5 -0
- package/dist/observability/corrupt-read-seat.d.ts +46 -66
- package/dist/observability/corrupt-read-seat.js +79 -63
- package/dist/observability/fail-open.d.ts +28 -0
- package/dist/observability/fail-open.js +28 -0
- package/dist/observability/run-terminal.js +39 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +1 -5
- package/dist/orchestration/workflow-agent-session-index.js +2 -2
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -2
- package/dist/plugins/approval-ask-store-file.d.ts +1 -4
- package/dist/plugins/approval-ask-store-file.js +2 -2
- package/dist/plugins/file-resume-anchor-store.d.ts +1 -6
- package/dist/plugins/file-resume-anchor-store.js +2 -4
- package/dist/plugins/file-run-store.d.ts +0 -4
- package/dist/plugins/file-run-store.js +1 -3
- package/dist/plugins/remote-env-host.d.ts +0 -35
- package/dist/plugins/remote-env-host.js +0 -8
- package/dist/plugins/remote-env-local-docker.d.ts +5 -13
- package/dist/plugins/remote-env-local-docker.js +2 -4
- package/dist/plugins/send-file-ledger.d.ts +1 -5
- package/dist/plugins/send-file-ledger.js +2 -2
- package/dist/plugins/store-backend.js +8 -9
- package/dist/plugins/user-input-ledger-file.d.ts +1 -5
- package/dist/plugins/user-input-ledger-file.js +2 -2
- package/dist/run-local.d.ts +1 -1
- package/dist/run-local.js +2 -2
- package/dist/runs.js +1 -0
- package/dist/spec-fields.js +0 -1
- package/dist/store-posture.d.ts +7 -7
- package/dist/store-posture.js +0 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +3 -0
- package/dist/tool-approval.d.ts +1 -1
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.js +2 -3
- package/dist/trace/redact.d.ts +18 -0
- package/dist/trace/redact.js +4 -0
- package/dist/unattended-permission-mode.d.ts +9 -2
- package/dist/unattended-permission-mode.js +5 -1
- package/dist/user-input-ledger.d.ts +24 -5
- package/dist/user-input-ledger.js +46 -11
- package/package.json +4 -3
- package/scripts/retired-mode-seed-drain-local.mjs +317 -0
|
@@ -177,12 +177,12 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
177
177
|
ledgerPath;
|
|
178
178
|
entries = new Map();
|
|
179
179
|
log;
|
|
180
|
-
constructor(root, fsyncEnabled = true
|
|
180
|
+
constructor(root, fsyncEnabled = true) {
|
|
181
181
|
this.fsyncEnabled = fsyncEnabled;
|
|
182
182
|
const dir = join(root, "workflow-notify");
|
|
183
183
|
ensureDir(dir);
|
|
184
184
|
this.ledgerPath = join(dir, "journal.jsonl");
|
|
185
|
-
for (const ev of replayJsonlSkipping(this.ledgerPath, "workflow-notify-journal", JOURNAL_ENTRY_SHAPE
|
|
185
|
+
for (const ev of replayJsonlSkipping(this.ledgerPath, "workflow-notify-journal", JOURNAL_ENTRY_SHAPE).records)
|
|
186
186
|
this.entries.set(ev.runId, ev);
|
|
187
187
|
}
|
|
188
188
|
commit(entry) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Logger } from "../observability/logger.js";
|
|
2
1
|
import type { AskState } from "../approval-ask-machine.js";
|
|
3
2
|
import type { AskCallCoordinate, AskRow, AskTerminalClaimIntent, AskTerminalClaimOutcome, AskTransitionPatch, ApprovalAskStore, BatchRow, BindGateInput, BindResult, DecideAskInput, DecideResult, EnsureAskResult, ExpireResult, NewAskRow } from "./approval-ask-store-sql.js";
|
|
4
3
|
/** 数据根下的目录名。**按店命名**,与兄弟 File 店同规(`approval-nonces` / `approval-ask-audit` /
|
|
@@ -121,9 +120,7 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
121
120
|
*
|
|
122
121
|
* @param root local 数据根(= `LocalBackend` 的 `FileStorageBackend.root`,零新 env)。
|
|
123
122
|
*/
|
|
124
|
-
constructor(root: string
|
|
125
|
-
warn?: Logger["warn"];
|
|
126
|
-
});
|
|
123
|
+
constructor(root: string);
|
|
127
124
|
/**
|
|
128
125
|
* 内核的钟。窗口外读 ⇒ **抛**(不静默回落 `Date.now()`):窗口外的读数按定义不在账本上,重放会得出
|
|
129
126
|
* 另一个时间戳 —— 那是一条**静默**的重放分岔,而本店的全部价值就是重放等价。今天内核的五个读钟点
|
|
@@ -54,7 +54,7 @@ export class FileApprovalAskStore {
|
|
|
54
54
|
replayRejectedCount = 0;
|
|
55
55
|
corruptLineCount = 0;
|
|
56
56
|
corruptQuarantinePath;
|
|
57
|
-
constructor(root
|
|
57
|
+
constructor(root) {
|
|
58
58
|
const dir = join(root, APPROVAL_ASK_DIR);
|
|
59
59
|
ensureDir(dir);
|
|
60
60
|
this.tmpDir = join(dir, ".tmp");
|
|
@@ -62,7 +62,7 @@ export class FileApprovalAskStore {
|
|
|
62
62
|
this.path = join(dir, APPROVAL_ASK_JOURNAL_FILE);
|
|
63
63
|
this.core = new InMemoryApprovalAskStore({ now: () => this.clockRead() });
|
|
64
64
|
const tornTail = existsSync(this.path) && !endsAtRecordBoundary(this.path);
|
|
65
|
-
const { records, skipped } = replayJsonlSkipping(this.path, "approval-ask-journal", ASK_JOURNAL_SHAPE
|
|
65
|
+
const { records, skipped } = replayJsonlSkipping(this.path, "approval-ask-journal", ASK_JOURNAL_SHAPE);
|
|
66
66
|
this.corruptLineCount = skipped;
|
|
67
67
|
for (const rec of records)
|
|
68
68
|
this.replayRecord(rec);
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import type { Logger } from "../observability/logger.js";
|
|
2
1
|
export declare class FileResumeAnchorStore {
|
|
3
2
|
private readonly dir;
|
|
4
3
|
/** sessionId → (eventId → {entryId, owner}) — the same in-memory shape MemoryResumeAnchorStore holds, hydrated on ctor. */
|
|
5
4
|
private readonly bySession;
|
|
6
5
|
/** sessionId → the open append log (one O_APPEND fd per session, lazily opened on first put, held for lifetime). */
|
|
7
6
|
private readonly logs;
|
|
8
|
-
|
|
9
|
-
/** @param opts.warn corrupt-read disclosure seat (S-582; absent ⇒ the replay owner falls back to `console.warn`). */
|
|
10
|
-
constructor(root: string, opts?: {
|
|
11
|
-
warn?: Logger["warn"];
|
|
12
|
-
});
|
|
7
|
+
constructor(root: string);
|
|
13
8
|
/** `<dir>/<sanitizePathComponent(sessionId)>.jsonl` — the per-session log path (sessionId is uuidv7, already safe). */
|
|
14
9
|
private pathFor;
|
|
15
10
|
/** Replay every session log into the in-memory index on boot (torn-tail-safe via the replay owner; last-wins upsert
|
|
@@ -16,10 +16,8 @@ export class FileResumeAnchorStore {
|
|
|
16
16
|
dir;
|
|
17
17
|
bySession = new Map();
|
|
18
18
|
logs = new Map();
|
|
19
|
-
|
|
20
|
-
constructor(root, opts) {
|
|
19
|
+
constructor(root) {
|
|
21
20
|
this.dir = join(root, "resume-anchors");
|
|
22
|
-
this.warn = opts?.warn;
|
|
23
21
|
ensureDir(this.dir);
|
|
24
22
|
this.hydrate();
|
|
25
23
|
}
|
|
@@ -38,7 +36,7 @@ export class FileResumeAnchorStore {
|
|
|
38
36
|
if (!name.endsWith(".jsonl"))
|
|
39
37
|
continue;
|
|
40
38
|
const key = name.slice(0, -".jsonl".length);
|
|
41
|
-
const { records } = replayJsonlSkipping(join(this.dir, name), "resume-anchor", ANCHOR_RECORD_SHAPE
|
|
39
|
+
const { records } = replayJsonlSkipping(join(this.dir, name), "resume-anchor", ANCHOR_RECORD_SHAPE);
|
|
42
40
|
if (!records.length)
|
|
43
41
|
continue;
|
|
44
42
|
const m = this.bySession.get(key) ?? new Map();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type UsageRow } from "../usage-analytics.js";
|
|
2
2
|
import type { TaskStatus } from "@sema-agent/core";
|
|
3
|
-
import type { Logger } from "../observability/logger.js";
|
|
4
3
|
import { StoreBootRefusalError, type RunRecord, type SessionSummary, type RunEvent, type PersistedTaskResult, type LatestRunRef } from "./store-contracts.js";
|
|
5
4
|
import type { RunStoreCheckpointProbe } from "./memory-run-store.js";
|
|
6
5
|
import type { LedgerEventType } from "../trace/ledger-events.js";
|
|
@@ -46,12 +45,9 @@ export declare class FileRunStore {
|
|
|
46
45
|
/** [ref] 件B(census 第 29 行终局):`RUN_STORE_STRICT_HYDRATE` 的**部署级**表态。
|
|
47
46
|
* `true` ⇒ hydrate 的 readdir 失败拒启;缺席/`false` ⇒ 件6 的记债腿,行为字节不变。 */
|
|
48
47
|
private readonly strictHydrate;
|
|
49
|
-
/** S-582:三个盘面坏行的腐读披露座(`LocalBackend` 接 `discloseWarn`;缺席 ⇒ 重放属主退回 `console.warn`)。 */
|
|
50
|
-
private readonly corruptReadWarn;
|
|
51
48
|
constructor(root: string, opts?: {
|
|
52
49
|
error?(msg: string, fields?: Record<string, unknown>): void;
|
|
53
50
|
strictHydrate?: boolean;
|
|
54
|
-
warn?: Logger["warn"];
|
|
55
51
|
});
|
|
56
52
|
private runDir;
|
|
57
53
|
private runJsonPath;
|
|
@@ -62,11 +62,9 @@ export class FileRunStore {
|
|
|
62
62
|
claimLocks = new Map();
|
|
63
63
|
logger;
|
|
64
64
|
strictHydrate;
|
|
65
|
-
corruptReadWarn;
|
|
66
65
|
constructor(root, opts) {
|
|
67
66
|
this.logger = opts;
|
|
68
67
|
this.strictHydrate = opts?.strictHydrate === true;
|
|
69
|
-
this.corruptReadWarn = opts?.warn;
|
|
70
68
|
this.runsDir = join(root, "runs");
|
|
71
69
|
this.activeDir = join(root, "runs", "active");
|
|
72
70
|
this.tmpDir = join(root, "tmp");
|
|
@@ -137,7 +135,7 @@ export class FileRunStore {
|
|
|
137
135
|
}
|
|
138
136
|
}
|
|
139
137
|
readRecords(path, shape) {
|
|
140
|
-
return replayJsonlSkipping(path, "run-store", shape
|
|
138
|
+
return replayJsonlSkipping(path, "run-store", shape);
|
|
141
139
|
}
|
|
142
140
|
refuseIfUnreadable(jsonPath, dir) {
|
|
143
141
|
try {
|
|
@@ -419,39 +419,4 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
419
419
|
*/
|
|
420
420
|
export declare function hostExecutionEnvFactory(config?: HostEnvConfig): ExecutionEnvFactory;
|
|
421
421
|
export { localDockerExecutionEnvFactory, type LocalDockerEnvConfig, type LocalDockerEnvDeps, RemoteLocalDockerExecutionEnv } from "./remote-env-local-docker.js";
|
|
422
|
-
import { type LocalDockerEnvConfig, type LocalDockerEnvDeps } from "./remote-env-local-docker.js";
|
|
423
|
-
/**
|
|
424
|
-
* Config for the `remote-docker` provider: run each task in a container on a REMOTE docker daemon
|
|
425
|
-
* (DUAL-MODE-DESIGN §5 — "a user-specified REMOTE host's docker"; registry-core `RemoteExecSpec`).
|
|
426
|
-
*
|
|
427
|
-
* It is {@link LocalDockerEnvConfig} with the endpoint made REQUIRED — that is the whole difference, by design:
|
|
428
|
-
* the container adapter is already daemon-agnostic (workspace `mkdir`'d INSIDE the container, never a host
|
|
429
|
-
* bind-mount; bytes over `docker cp` / `docker exec cat`, which stream through the daemon API), and the endpoint
|
|
430
|
-
* rides as `-H <dockerHost>` on every invocation. So every local-docker knob (memory/cpus/pidsLimit/dropAllCaps/
|
|
431
|
-
* network/timeouts/env/dockerPath) applies unchanged to the remote lane.
|
|
432
|
-
*/
|
|
433
|
-
export interface RemoteDockerEnvConfig extends Omit<LocalDockerEnvConfig, "dockerHost" | "providerLabel"> {
|
|
434
|
-
/** Remote docker daemon endpoint (e.g. `tcp://host:2376` or `ssh://user@host`). REQUIRED for remote-docker. */
|
|
435
|
-
dockerHost: string;
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* `ExecutionEnvFactory` for the TOC `remote-docker` backend (DUAL-MODE-DESIGN §5): a per-task container on
|
|
439
|
-
* SOMEONE ELSE'S docker daemon (offload — the operator's box stays free; isolation:true, suspendable:false).
|
|
440
|
-
* Same lifetime contract as local-docker: unconnected/lazy, one container per task, `destroy()` = `docker rm -f`.
|
|
441
|
-
*
|
|
442
|
-
* 🔴 **Endpoint credentials are the docker CLI's own** — this adapter deliberately carries none. The docker CLI
|
|
443
|
-
* reads `DOCKER_CERT_PATH` + `DOCKER_TLS_VERIFY` from the worker's environment (inherited by every spawn) for a
|
|
444
|
-
* `tcp://` + TLS daemon, and uses `ssh-agent` / `~/.ssh/config` for an `ssh://` one. That keeps key material out
|
|
445
|
-
* of this process's config objects entirely. Consequence to know: `tcp://…:2376` WITHOUT those env vars fails
|
|
446
|
-
* loud at connect (the daemon refuses plaintext) — it does not silently downgrade; a plaintext `tcp://…:2375`
|
|
447
|
-
* daemon is unauthenticated and remains the operator's decision, exactly as in the registry-core schema.
|
|
448
|
-
* (The registry-core `remote-docker` arm additionally models `tlsCertEnv`/`tlsKeyEnv`/`tlsCaEnv`/`sshKeyEnv` =
|
|
449
|
-
* env-NAMEs the control plane resolves to PEM BYTES. Materializing those bytes to short-lived 0600 files and
|
|
450
|
-
* passing `--tlsverify --tlscert/--tlskey/--tlscacert` is the remaining config-surface follow-up, together with
|
|
451
|
-
* the `provider:"remote-docker"` arm on `AppConfig.remoteExec` — see the note on that union in config-types.ts.)
|
|
452
|
-
*
|
|
453
|
-
* Throws at factory-BUILD time on a missing/blank endpoint: a "remote" lane with no endpoint would otherwise
|
|
454
|
-
* quietly become the LOCAL daemon — an isolation-topology surprise, so it fails loud instead.
|
|
455
|
-
*/
|
|
456
|
-
export declare function remoteDockerExecutionEnvFactory(config: RemoteDockerEnvConfig, deps?: LocalDockerEnvDeps): ExecutionEnvFactory;
|
|
457
422
|
//# sourceMappingURL=remote-env-host.d.ts.map
|
|
@@ -1373,12 +1373,4 @@ export function hostExecutionEnvFactory(config = {}) {
|
|
|
1373
1373
|
});
|
|
1374
1374
|
}
|
|
1375
1375
|
export { localDockerExecutionEnvFactory, RemoteLocalDockerExecutionEnv } from "./remote-env-local-docker.js";
|
|
1376
|
-
import { localDockerExecutionEnvFactory } from "./remote-env-local-docker.js";
|
|
1377
|
-
export function remoteDockerExecutionEnvFactory(config, deps = {}) {
|
|
1378
|
-
const dockerHost = config.dockerHost?.trim();
|
|
1379
|
-
if (!dockerHost) {
|
|
1380
|
-
throw new RemoteExecutionError("connect_failed", "remoteExec provider 'remote-docker' requires a non-empty dockerHost (the remote daemon endpoint, e.g. tcp://host:2376 or ssh://user@host) — use provider 'local-docker' for THIS machine's daemon");
|
|
1381
|
-
}
|
|
1382
|
-
return localDockerExecutionEnvFactory({ ...config, dockerHost, providerLabel: "remote-docker" }, deps);
|
|
1383
|
-
}
|
|
1384
1376
|
//# sourceMappingURL=remote-env-host.js.map
|
|
@@ -27,19 +27,13 @@ export interface LocalDockerEnvConfig {
|
|
|
27
27
|
* DOCKER_HOST override for the docker CLI (e.g. a non-default socket). Passed as `-H <value>` on EVERY docker
|
|
28
28
|
* invocation (not the process env) so it can't leak / drift. Absent = the daemon DOCKER_HOST points at.
|
|
29
29
|
*
|
|
30
|
-
* Pointing this OFF-BOX (`tcp://host:2376`, `ssh://user@host`) is
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* `mkdir`'d INSIDE the container (never a bind-mount of a
|
|
34
|
-
* `docker exec cat`, which stream over the daemon API.
|
|
30
|
+
* Pointing this OFF-BOX (`tcp://host:2376`, `ssh://user@host`) is how a REMOTE docker daemon is used — the
|
|
31
|
+
* lane stays `local-docker` (`REMOTE_EXEC=local-docker` + `DOCKER_HOST`; the settings-schema lane table lists
|
|
32
|
+
* `DOCKER_HOST` as this lane's optional coordinate, and there is no separate `remote-docker` lane since 7.0.0).
|
|
33
|
+
* Nothing about the class is host-local: the workspace is `mkdir`'d INSIDE the container (never a bind-mount of a
|
|
34
|
+
* host path) and bytes move by `docker cp` / `docker exec cat`, which stream over the daemon API.
|
|
35
35
|
*/
|
|
36
36
|
dockerHost?: string;
|
|
37
|
-
/**
|
|
38
|
-
* Provider NAME reported on the {@link WorkspaceHandle}. Default `"local-docker"`; the `remote-docker` arm
|
|
39
|
-
* passes `"remote-docker"` so a handle never mislabels which lane it is (the CLASS is shared — `remote-docker`
|
|
40
|
-
* IS this adapter aimed at a remote daemon). Cosmetic/observability only: it selects no behavior.
|
|
41
|
-
*/
|
|
42
|
-
providerLabel?: string;
|
|
43
37
|
/** Container memory limit (docker `--memory`, e.g. "2g"). Absent = no limit. */
|
|
44
38
|
memory?: string;
|
|
45
39
|
/** Container CPU limit (docker `--cpus`, e.g. 1.5). Absent = no limit. */
|
|
@@ -88,8 +82,6 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
|
|
|
88
82
|
private readonly spawnFn;
|
|
89
83
|
/** Stable container NAME (also our handle id). Created on connect; targeted by every docker exec/cp/rm. */
|
|
90
84
|
private readonly containerName;
|
|
91
|
-
/** Provider name put on the WorkspaceHandle ("local-docker" | "remote-docker"). Observability only. */
|
|
92
|
-
private readonly providerName;
|
|
93
85
|
/** Set once the container is up + the workspace dir created. */
|
|
94
86
|
private containerId?;
|
|
95
87
|
private handle?;
|
|
@@ -24,7 +24,6 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
24
24
|
cfg;
|
|
25
25
|
spawnFn;
|
|
26
26
|
containerName;
|
|
27
|
-
providerName;
|
|
28
27
|
containerId;
|
|
29
28
|
handle;
|
|
30
29
|
destroyed = false;
|
|
@@ -47,7 +46,6 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
47
46
|
const id = config.id ?? randomBytes(6).toString("hex");
|
|
48
47
|
const suffix = randomBytes(4).toString("hex");
|
|
49
48
|
this.containerName = `sema-docker-${sanitizeId(id)}-${suffix}`;
|
|
50
|
-
this.providerName = config.providerLabel?.trim() || PROVIDER;
|
|
51
49
|
}
|
|
52
50
|
resolve(p) {
|
|
53
51
|
return path.posix.isAbsolute(p) ? path.posix.normalize(p) : path.posix.normalize(path.posix.join(this.cwd, p));
|
|
@@ -113,7 +111,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
113
111
|
return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
|
|
114
112
|
}
|
|
115
113
|
this.containerId = run.value.stdout.trim() || this.containerName;
|
|
116
|
-
this.handle = { sandboxId: this.containerName, provider:
|
|
114
|
+
this.handle = { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
|
|
117
115
|
if (config?.secrets?.length) {
|
|
118
116
|
}
|
|
119
117
|
return ok(this.handle);
|
|
@@ -136,7 +134,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
136
134
|
const insp = await this.docker(["inspect", "-f", "{{.State.Running}}", this.containerName], { timeoutMs: this.cfg.controlTimeoutMs });
|
|
137
135
|
if (insp.ok && insp.value.exitCode === 0 && insp.value.stdout.trim() === "true") {
|
|
138
136
|
this.containerId = this.containerName;
|
|
139
|
-
this.handle ??= { sandboxId: this.containerName, provider:
|
|
137
|
+
this.handle ??= { sandboxId: this.containerName, provider: PROVIDER, mountPath: this.cfg.mountPath, sessionToken: this.containerName };
|
|
140
138
|
return ok(this.handle);
|
|
141
139
|
}
|
|
142
140
|
return { ok: false, error: new RemoteExecutionError("connect_failed", `container '${this.containerName}' is not running (gone or stopped) — no managed snapshot to resume`) };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Pool as MysqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
3
|
import type { IssuedFileLink } from "./send-user-file.js";
|
|
4
|
-
import type { Logger } from "../observability/logger.js";
|
|
5
4
|
/** Ledger scope sentinel for single-user issuance (no principal) — mirrors scopeSegment's "_" key segment. */
|
|
6
5
|
export declare const SENDFILE_SINGLE_USER_SCOPE = "_";
|
|
7
6
|
export interface SendFileLedgerEntry {
|
|
@@ -48,10 +47,7 @@ export declare class FileSendFileLedger implements SendFileLedger {
|
|
|
48
47
|
private readonly path;
|
|
49
48
|
private readonly mem;
|
|
50
49
|
private log;
|
|
51
|
-
|
|
52
|
-
constructor(root: string, opts?: {
|
|
53
|
-
warn?: Logger["warn"];
|
|
54
|
-
});
|
|
50
|
+
constructor(root: string);
|
|
55
51
|
record(entry: SendFileLedgerEntry): Promise<void>;
|
|
56
52
|
listByScope(scope: string, limit: number, beforeId?: string): Promise<SendFileLedgerEntry[]>;
|
|
57
53
|
/** Release the append-log fd (LocalBackend.close → graceful-restart re-open). */
|
|
@@ -80,11 +80,11 @@ export class FileSendFileLedger {
|
|
|
80
80
|
path;
|
|
81
81
|
mem = new MemorySendFileLedger();
|
|
82
82
|
log;
|
|
83
|
-
constructor(root
|
|
83
|
+
constructor(root) {
|
|
84
84
|
const dir = join(root, "sendfile-ledger");
|
|
85
85
|
ensureDir(dir);
|
|
86
86
|
this.path = join(dir, "links.jsonl");
|
|
87
|
-
for (const r of replayJsonlSkipping(this.path, "send-file-ledger", LEDGER_ROW_SHAPE
|
|
87
|
+
for (const r of replayJsonlSkipping(this.path, "send-file-ledger", LEDGER_ROW_SHAPE).records)
|
|
88
88
|
void this.mem.record(r);
|
|
89
89
|
}
|
|
90
90
|
async record(entry) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
|
-
import {
|
|
2
|
+
import { createCorruptReadFailOpenSeat, createDurableAbsenceSeat } from "../observability/corrupt-read-seat.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { AdoptionError, FileStorageBackend, FileSessionRepo } from "@sema-agent/core";
|
|
5
5
|
import { FileWorkflowJournalStore } from "@sema-agent/core";
|
|
@@ -230,7 +230,7 @@ class LocalBackend {
|
|
|
230
230
|
this.fileBackend = acquireLocalBackendWithWait(() => new FileStorageBackend({
|
|
231
231
|
root,
|
|
232
232
|
...(config.fileHistory !== undefined ? { fileHistory: config.fileHistory } : {}),
|
|
233
|
-
onCorruptRead:
|
|
233
|
+
onCorruptRead: createDurableAbsenceSeat((msg, fields) => this.discloseWarn(msg, fields), root, "file-history = a rewind boundary or scope read as missing"),
|
|
234
234
|
}), config.storeLockWaitMs, (fields) => this.onWarn?.("store_dir_lock_wait", { ...fields }));
|
|
235
235
|
}
|
|
236
236
|
catch (e) {
|
|
@@ -241,22 +241,21 @@ class LocalBackend {
|
|
|
241
241
|
(tail === undefined ? "" : ` ${tail}`));
|
|
242
242
|
}
|
|
243
243
|
this.sessionStore = new LocalSessionStore(new FileSessionRepo(this.fileBackend.root, {
|
|
244
|
-
onCorruptRead:
|
|
244
|
+
onCorruptRead: createCorruptReadFailOpenSeat("server.session-repo.corrupt-record-skipped", noteReplayInSessionOpenScope),
|
|
245
245
|
}), this.fileBackend.root);
|
|
246
246
|
const corruptReadWarn = (msg, fields) => this.discloseWarn(msg, fields);
|
|
247
247
|
this.runStore = new FileRunStore(this.fileBackend.root, {
|
|
248
248
|
error: (msg, fields) => void (this.onError ?? this.onWarn)?.(msg, fields),
|
|
249
249
|
strictHydrate: config.runStoreStrictHydrate,
|
|
250
|
-
warn: corruptReadWarn,
|
|
251
250
|
});
|
|
252
|
-
this.resumeAnchorStore = new FileResumeAnchorStore(this.fileBackend.root
|
|
251
|
+
this.resumeAnchorStore = new FileResumeAnchorStore(this.fileBackend.root);
|
|
253
252
|
this.approvalExemptionStore = new FileApprovalExemptionStore(this.fileBackend.root, { warn: corruptReadWarn });
|
|
254
253
|
this.approvalNonceStore = new FileApprovalNonceStore(this.fileBackend.root, { warn: corruptReadWarn });
|
|
255
|
-
this.approvalAskStore = new FileApprovalAskStore(this.fileBackend.root
|
|
256
|
-
this.sendFileLedgerStore = new FileSendFileLedger(this.fileBackend.root
|
|
257
|
-
this.userInputLedgerStore = new FileUserInputLedgerStore(this.fileBackend.root
|
|
254
|
+
this.approvalAskStore = new FileApprovalAskStore(this.fileBackend.root);
|
|
255
|
+
this.sendFileLedgerStore = new FileSendFileLedger(this.fileBackend.root);
|
|
256
|
+
this.userInputLedgerStore = new FileUserInputLedgerStore(this.fileBackend.root);
|
|
258
257
|
this.workflowJournalStore = new FileWorkflowJournalStore(this.fileBackend.root, undefined, {
|
|
259
|
-
onCorruptRead:
|
|
258
|
+
onCorruptRead: createCorruptReadFailOpenSeat("server.workflow-journal.corrupt-record-skipped"),
|
|
260
259
|
});
|
|
261
260
|
this.outcomeSinkInst = new FileOutcomeSink(this.fileBackend.root);
|
|
262
261
|
const ownControlDir = fileMemoryControlDirFor(config);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Logger } from "../observability/logger.js";
|
|
2
1
|
import { UserInputLedgerFold, type NewUserInputAcceptance, type OpenScanCursor, type OpenScanPage, type SettleHow, type UserInputAcceptance, type UserInputLedgerRecord, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
3
2
|
/** 账本文件名(取证按文件字节对账 —— 崩溃语义的真源是文件)。 */
|
|
4
3
|
export declare const USER_INPUT_LEDGER_FILE = "ledger.jsonl";
|
|
@@ -24,10 +23,7 @@ export declare class FileUserInputLedgerStore extends MemoryUserInputLedgerStore
|
|
|
24
23
|
/** 账本绝对路径。 */
|
|
25
24
|
readonly path: string;
|
|
26
25
|
private log;
|
|
27
|
-
|
|
28
|
-
constructor(root: string, opts?: {
|
|
29
|
-
warn?: Logger["warn"];
|
|
30
|
-
});
|
|
26
|
+
constructor(root: string);
|
|
31
27
|
protected persist(rec: UserInputLedgerRecord): void;
|
|
32
28
|
/** 收追加口(LocalBackend.close → 优雅重启重开)。core `AppendLog.close()` 自己吞 `closeSync` 的错,不抛。 */
|
|
33
29
|
dispose(): void;
|
|
@@ -88,12 +88,12 @@ const RECORD_SHAPE = {
|
|
|
88
88
|
export class FileUserInputLedgerStore extends MemoryUserInputLedgerStore {
|
|
89
89
|
path;
|
|
90
90
|
log;
|
|
91
|
-
constructor(root
|
|
91
|
+
constructor(root) {
|
|
92
92
|
super();
|
|
93
93
|
const dir = join(root, USER_INPUT_LEDGER_DIR);
|
|
94
94
|
ensureDir(dir);
|
|
95
95
|
this.path = join(dir, USER_INPUT_LEDGER_FILE);
|
|
96
|
-
for (const rec of replayJsonlSkipping(this.path, "user-input-ledger", RECORD_SHAPE
|
|
96
|
+
for (const rec of replayJsonlSkipping(this.path, "user-input-ledger", RECORD_SHAPE).records)
|
|
97
97
|
this.fold.apply(rec);
|
|
98
98
|
}
|
|
99
99
|
persist(rec) {
|
package/dist/run-local.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ export interface LocalApproverIo {
|
|
|
142
142
|
* · **人答 N**(TTY 臂)⇒ 裸 `false` —— core 契约里这就是「一个人拒了」(`human_refused` / `who.party:"person"`,模型读「用户不想继续,
|
|
143
143
|
* 停下来等他」,run 以 `haltedOnUserRejection` 停批);
|
|
144
144
|
* · **没有人被问**(无 TTY 臂 / 参数展示不出臂)⇒ 部署政策拒对象 {@link UnattendedPolicyRefusal}(经与协调器 deny 政策臂**同一只**构造
|
|
145
|
-
* `buildUnattendedPolicyRefusal`;core
|
|
145
|
+
* `buildUnattendedPolicyRefusal`;core `readApproverOutcomeObject` ⇒ `policy_refused` / `who.party:"policy"`(core 7.39.0 #1265 起;此前 `"none"`),模型读 core 的
|
|
146
146
|
* 策略句 + 本席的拒因,不停批)。修前两臂交裸 `false` ⇒ 机器的拒被记成人拒、模型被告知去等一个不存在的人。
|
|
147
147
|
* 两臂的拒都经 `io.noteRefusedWithoutPerson` 报一次 —— 「活没干完」(退 1)此前搭的是 core 人拒标记的便车,S-956 之后由本席的事实给。
|
|
148
148
|
*/
|
package/dist/run-local.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { DEFAULT_MODEL_REF, resolveTaskModel } from "./model-select.js";
|
|
3
3
|
import { defaultModelRefusal, modelUnavailableMessage } from "./model-entry-refusal.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createDurableAbsenceSeat } from "./observability/corrupt-read-seat.js";
|
|
5
5
|
import { outwardPlane } from "./terminal.js";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { realpathSync } from "node:fs";
|
|
@@ -351,7 +351,7 @@ export async function runLocal(argv, deps = {}) {
|
|
|
351
351
|
fileBackend = acquireLocalBackendWithWait(() => new FileStorageBackend({
|
|
352
352
|
root,
|
|
353
353
|
...(config.fileHistory !== undefined ? { fileHistory: config.fileHistory } : {}),
|
|
354
|
-
onCorruptRead:
|
|
354
|
+
onCorruptRead: createDurableAbsenceSeat(logger.warn.bind(logger), root, "file-snapshot = a snapshot or scope read as missing"),
|
|
355
355
|
}), config.storeLockWaitMs, (fields) => logger.warn("store_dir_lock_wait", { ...fields }));
|
|
356
356
|
}
|
|
357
357
|
catch (e) {
|
package/dist/runs.js
CHANGED
|
@@ -369,6 +369,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
369
369
|
causeAtDone = cancelCtrl.cause();
|
|
370
370
|
}
|
|
371
371
|
fleetPublisher?.onEvent(ev);
|
|
372
|
+
await userInputLedger?.observeLegEvent(stream, ev);
|
|
372
373
|
await flushPromptManifest();
|
|
373
374
|
switch (ev.type) {
|
|
374
375
|
case "text_delta":
|
package/dist/spec-fields.js
CHANGED
|
@@ -37,7 +37,6 @@ export function normalizeAttachments(v) {
|
|
|
37
37
|
const out = {
|
|
38
38
|
...(o.todoReminder === true ? { todoReminder: true } : {}),
|
|
39
39
|
...(changedFiles !== undefined ? { changedFiles } : {}),
|
|
40
|
-
...(o.planModeReminder === true ? { planModeReminder: true } : {}),
|
|
41
40
|
...(typeof o.backgroundTasks === "boolean" ? { backgroundTasks: o.backgroundTasks } : {}),
|
|
42
41
|
...(o.toolsDelta === true ? { toolsDelta: true } : {}),
|
|
43
42
|
...(o.todoReminderMode === "baseline" || o.todoReminderMode === "off" ? { todoReminderMode: o.todoReminderMode } : {}),
|
package/dist/store-posture.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 由调用方自己读出落差。`degradedReason` present-iff 降级臂走过(按后端族定词,{@link storeDegradedReasonFor})。
|
|
13
13
|
* 顶层 `sql` 并入 `store.sql`(硬 breaking,零别名;对象逐字同形)。
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import type { StoreBackend as StoreBackendWord } from "@sema-agent/settings-schema/execution-lanes";
|
|
16
16
|
import type { StoreBackend } from "./plugins/store-backend.js";
|
|
17
17
|
import type { SqlEngineFacts } from "./plugins/sql-driver.js";
|
|
18
18
|
import { type SqlEngineCapability } from "./sql-engine-posture.js";
|
|
@@ -25,23 +25,23 @@ import { type SqlEngineCapability } from "./sql-engine-posture.js";
|
|
|
25
25
|
export declare const STORE_DEGRADED_REASONS: readonly ["db_unreachable", "local_unwritable"];
|
|
26
26
|
export type StoreDegradedReason = (typeof STORE_DEGRADED_REASONS)[number];
|
|
27
27
|
/** 降级臂按后端族定词(穷举 switch:词表加员 ⇒ 编译红)。`memory` 从不尝试打开后端,走不到降级臂 —— 到了就是装配缺陷,响亮抛。 */
|
|
28
|
-
export declare function storeDegradedReasonFor(declared:
|
|
28
|
+
export declare function storeDegradedReasonFor(declared: StoreBackendWord): StoreDegradedReason;
|
|
29
29
|
/** 装配值(纯数据;`boot/stores.ts` 铸一次,gauge / `/health` / caps 三面同读)。 */
|
|
30
30
|
export interface StorePosture {
|
|
31
|
-
readonly declared:
|
|
32
|
-
readonly backend:
|
|
31
|
+
readonly declared: StoreBackendWord;
|
|
32
|
+
readonly backend: StoreBackendWord;
|
|
33
33
|
readonly degradedReason?: StoreDegradedReason;
|
|
34
34
|
}
|
|
35
35
|
/** `build*`(纯数据)。`assembled` = 装配出的 `StoreBackend.kind`;缺席 = 没装出后端 ⇒ `memory`。 */
|
|
36
36
|
export declare function buildStorePosture(a: {
|
|
37
|
-
readonly declared:
|
|
37
|
+
readonly declared: StoreBackendWord;
|
|
38
38
|
readonly assembled: StoreBackend["kind"] | undefined;
|
|
39
39
|
readonly degradedReason: StoreDegradedReason | undefined;
|
|
40
40
|
}): StorePosture;
|
|
41
41
|
/** caps `store` 的形。 */
|
|
42
42
|
export interface StoreCapability {
|
|
43
|
-
readonly declared:
|
|
44
|
-
readonly backend:
|
|
43
|
+
readonly declared: StoreBackendWord;
|
|
44
|
+
readonly backend: StoreBackendWord;
|
|
45
45
|
readonly durable: boolean;
|
|
46
46
|
readonly degradedReason?: StoreDegradedReason;
|
|
47
47
|
/** 7.105.0 及以前的顶层 `sql` 对象,原样搬入(`{engine, isolation, txnMode}`)。present-iff `backend ∈ {mysql, pg}` ∧ 驱动此刻给得出
|
package/dist/store-posture.js
CHANGED
package/dist/task-settings.d.ts
CHANGED
|
@@ -298,6 +298,19 @@ export declare const TASK_SETTINGS_UNSUPPORTED_PERMISSION_KEYS: readonly ["addit
|
|
|
298
298
|
* · `permissions: null` 是既有钉住的「容器缺席」形,照旧不咬。
|
|
299
299
|
*/
|
|
300
300
|
export declare function taskSettingsKeyIssue(raw: unknown): KeyClosureIssue | null;
|
|
301
|
+
/**
|
|
302
|
+
* 🔒 **`settings.hooks` 会不会被采纳** —— 全仓**唯一**的车道谓词(S-995,主会话裁):hook 命令跑在 WORKER HOST([ref] class ② 能力授予),所以只有单用户车道
|
|
303
|
+
* (`requirePrincipal !== true`,与 scheduler / backgroundShell / lspHost / MCP 注入同一姿势)采纳;多租户车道收到 hooks **整只忽略**(`task_hooks_ignored`),绝不执行。
|
|
304
|
+
* 三处读它,谁也不许内联 `requirePrincipal`(各写各的 = 迟早漂):
|
|
305
|
+
* · `boot/resolve-spec.ts` —— 装配:不采纳 ⇒ 不装 `TaskSpec.hooks` + 一条 `task_hooks_ignored`;
|
|
306
|
+
* · `http/routes/capabilities.ts` —— 能力位 `taskSettings.hooks`(「says yes ⟺ route works」);
|
|
307
|
+
* · `http/admission.ts` —— fresh 受理面的「别名 matcher 永不命中」判据的**求值深度**:采纳 ⇒ `"regex"`(运行期同一只匹配函数,带资源闸);不采纳 ⇒ `"literal"`
|
|
308
|
+
* (7.112.0 的字面判:零调用方正则执行 —— 受理面是所有车道,租户不能借一只灾难回溯的 matcher 占满事件循环)。
|
|
309
|
+
* `test/hooks-alias-matcher-s995.test.ts` R5g 钉「三处都调它、没有内联判据」。
|
|
310
|
+
*/
|
|
311
|
+
export declare function taskHooksHonored(config: {
|
|
312
|
+
readonly requirePrincipal?: boolean;
|
|
313
|
+
}): boolean;
|
|
301
314
|
/**
|
|
302
315
|
* Validate an untrusted `body.settings` (raw `unknown` off the wire) into the service-trusted {@link
|
|
303
316
|
* ParsedTaskSettings}. NEVER trusts the wire type — every field is shape-checked. `defaultMode` accepts the FIVE
|
package/dist/task-settings.js
CHANGED
|
@@ -158,6 +158,9 @@ export function taskSettingsKeyIssue(raw) {
|
|
|
158
158
|
unsupportedKeys: unsupported.slice(0, MAX_KEY_ECHO),
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
|
+
export function taskHooksHonored(config) {
|
|
162
|
+
return config.requirePrincipal !== true;
|
|
163
|
+
}
|
|
161
164
|
export function parseTaskSettings(raw) {
|
|
162
165
|
if (raw === null || raw === undefined || typeof raw !== "object")
|
|
163
166
|
return { deferred: [] };
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -821,7 +821,7 @@ export type RespondSettledBy = Extract<HostSettledBy, HostDecidedBy>;
|
|
|
821
821
|
export type UnattendedApprovalPolicy = "park" | "deny";
|
|
822
822
|
/**
|
|
823
823
|
* S-689 —— `deny` 政策下「无人可答」交给 core 的形:core `AskOutcome` 对象臂的**部署政策拒**
|
|
824
|
-
* (`settledBy:"policy"` ⇒ core 结算 `policy_refused`、`who.party:"none"
|
|
824
|
+
* (`settledBy:"policy"` ⇒ core 结算 `policy_refused`、`who.party:"policy"`(core 7.39.0 #1265 起;此前 `"none"`),模型读的是策略句)。
|
|
825
825
|
*
|
|
826
826
|
* 修前这里交**裸 `false`** —— 而在 core 的 onAsk 契约里裸 `false` 就是「一个人拒了」:结算记
|
|
827
827
|
* `human_refused` / `who.party:"person"`,模型被告知「用户不想继续,停下来等他」,run 以
|
|
@@ -146,7 +146,7 @@ type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueue
|
|
|
146
146
|
type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
|
|
147
147
|
type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
|
|
148
148
|
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" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory";
|
|
149
|
-
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
|
|
149
|
+
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "outputStyle";
|
|
150
150
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
151
151
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
152
152
|
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";
|
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, isModeReleaseReason, 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, isSettlementParty, 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";
|
|
@@ -221,11 +221,10 @@ export function screenedStoredEventData(type, data) {
|
|
|
221
221
|
}
|
|
222
222
|
return { ...d, gate };
|
|
223
223
|
}
|
|
224
|
-
const SETTLEMENT_PARTIES = new Set(["person", "engine", "host", "none"]);
|
|
225
224
|
function projectSettlement(st) {
|
|
226
225
|
const whoV = st.who;
|
|
227
226
|
const partyV = whoV === null || typeof whoV !== "object" ? undefined : whoV.party;
|
|
228
|
-
if (
|
|
227
|
+
if (!isSettlementParty(partyV))
|
|
229
228
|
return undefined;
|
|
230
229
|
const who = whoV;
|
|
231
230
|
return {
|
package/dist/trace/redact.d.ts
CHANGED
|
@@ -133,6 +133,24 @@ export declare function redactSecretsWithArms(text: string, opts?: {
|
|
|
133
133
|
text: string;
|
|
134
134
|
arms: ReadonlySet<RedactionArm>;
|
|
135
135
|
};
|
|
136
|
+
/**
|
|
137
|
+
* 🧪 **跨度出口 —— 只给测试**(车 UH 脱敏基线门 `test/redaction-baseline-gate.test.ts`:判「必隐字节**每一个**都被盖住」,
|
|
138
|
+
* 而不是「必隐串整串不在」—— 后者对截断式脱敏(值的前缀被洗、尾段留着)恒判已藏,gpt-6 腿 r1-F1 [high])。
|
|
139
|
+
*
|
|
140
|
+
* 同一只 {@link runRedaction}、同一遍扫描,原样交出池已经算好的 `kept` 跨度(S-497 那一份;坐标在**归一化视图**上),
|
|
141
|
+
* 不是第二只脱敏器、不改任何判据、不喂观察者(`observe:false`,与影子重扫同口);`opts.json` 与 {@link redactSecrets} 的同名选项
|
|
142
|
+
* 逐字同义(JSON 模式,`redactedPreview` 走的那一条)。生产代码不调用它:
|
|
143
|
+
* 问「有没有凭据形」的消费方走 {@link redactSecretsWithArms}。
|
|
144
|
+
*/
|
|
145
|
+
export declare function redactionSpansForTest(text: string, opts?: {
|
|
146
|
+
json?: boolean;
|
|
147
|
+
}): {
|
|
148
|
+
text: string;
|
|
149
|
+
spans: ReadonlyArray<{
|
|
150
|
+
readonly start: number;
|
|
151
|
+
readonly end: number;
|
|
152
|
+
}>;
|
|
153
|
+
};
|
|
136
154
|
/**
|
|
137
155
|
* 🔴 环 + 深度预算(2026-07-25 补)。原实现是纯递归、**既不记已访问节点也不限深度**,而它的调用位置是
|
|
138
156
|
* **最不可信的边界**:`toolStart`/`toolEnd` 把工具的 `args`/`output`/`structured` 直接喂进来。后果两条:
|
package/dist/trace/redact.js
CHANGED
|
@@ -1791,6 +1791,10 @@ export function redactSecretsWithArms(text, opts) {
|
|
|
1791
1791
|
const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text");
|
|
1792
1792
|
return { text: r.text, arms: new Set(r.hits.map((h) => h.arm)) };
|
|
1793
1793
|
}
|
|
1794
|
+
export function redactionSpansForTest(text, opts) {
|
|
1795
|
+
const r = runRedaction(text, false, opts?.json === true, false, "free-text");
|
|
1796
|
+
return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
|
|
1797
|
+
}
|
|
1794
1798
|
const REDACT_MAX_DEPTH = 64;
|
|
1795
1799
|
export function redactDeep(value) {
|
|
1796
1800
|
return redactDeepInner(value, 0, new WeakSet());
|