@sema-agent/core 5.32.0 → 5.33.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/CHANGELOG.md +63 -0
- package/dist/agents/cascade.d.ts +49 -1
- package/dist/agents/cascade.js +2 -2
- package/dist/agents/verify.d.ts +70 -4
- package/dist/agents/verify.js +62 -16
- package/dist/core/checkpoint-store.d.ts +95 -0
- package/dist/core/checkpoint-store.js +40 -0
- package/dist/core/hooks.d.ts +14 -6
- package/dist/core/hooks.js +14 -3
- package/dist/core/memory-engine/file-backend.d.ts +172 -22
- package/dist/core/memory-engine/file-backend.js +877 -79
- package/dist/core/memory-engine/memory-backend-contract.js +33 -0
- package/dist/core/runner/assemble-result.d.ts +7 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +72 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +126 -0
- package/dist/core/runner/prepare-config-doors.d.ts +140 -0
- package/dist/core/runner/prepare-config-doors.js +250 -0
- package/dist/core/runner/prepare-safety-scan.d.ts +53 -0
- package/dist/core/runner/prepare-safety-scan.js +80 -0
- package/dist/core/runner/prepare-task.d.ts +27 -81
- package/dist/core/runner/prepare-task.js +83 -586
- package/dist/core/runner/prepare-workspace-restore.d.ts +102 -0
- package/dist/core/runner/prepare-workspace-restore.js +144 -0
- package/dist/core/runner/runtask.js +8 -2
- package/dist/core/tool-policy.d.ts +25 -0
- package/dist/core/types.d.ts +140 -13
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/orchestration/workflow-governance.d.ts +6 -4
- package/dist/tools/fs/bash-readonly-classifier.d.ts +9 -3
- package/dist/tools/fs/bash-readonly-classifier.js +4 -1
- package/dist/tools/fs/fs-bash.d.ts +19 -3
- package/dist/tools/fs/fs-bash.js +26 -1
- package/dist/tools/fs/index.d.ts +20 -4
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/read-deny.d.ts +66 -8
- package/dist/tools/fs/read-deny.js +75 -39
- package/dist/tools/fs/read-face.d.ts +3 -2
- package/dist/tools/fs/search.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { RemoteExecutionEnv, RemoteExecutionError, SnapshotId, VmLifecycleOptions } from "../remote-env.js";
|
|
2
|
+
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
3
|
+
import type { RemoteEnvFailureNote, RunnerDeps, TaskSpec } from "../types.js";
|
|
4
|
+
import type { PrepareResume, RunInternals } from "./prepare-task.js";
|
|
5
|
+
/**
|
|
6
|
+
* codex 1360 r2-r4 — rebase one checkpointed absolute path from the OLD workspace root onto the RESTORED
|
|
7
|
+
* one (divergent `resumeVM`). POSIX-ONLY by contract: every remote lane's `mountPath` is a container
|
|
8
|
+
* path (e2b/k8s/ssh/adb/local-docker are all Linux targets), so a backslash ANYWHERE in the inputs marks
|
|
9
|
+
* the value outside this function's domain and it returns `p` UNCHANGED — an un-rebased path is honestly
|
|
10
|
+
* observable (the divergence observation already fired) while a WRONGLY-rebased one silently corrupts
|
|
11
|
+
* cwd/read-state (r4: drive-root and cross-family recomposition are not implementable without a Windows
|
|
12
|
+
* path model no lane needs). Trailing slashes are tolerated (from="/app/" must not weld the suffix);
|
|
13
|
+
* bare "/" keeps filesystem-root semantics; a path outside `from` (incl. the prefix-sibling
|
|
14
|
+
* "/application" vs "/app") returns unchanged. Exported for direct unit pinning.
|
|
15
|
+
*/
|
|
16
|
+
export declare function rebaseWorkspacePath(p: string, fromRaw: string, toRaw: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* RB-439-a — project one failed remote lifecycle call onto the caller-facing note. ONE builder for all
|
|
19
|
+
* three ops so the `retryable` classification can never drift between the lane that reports a `suspendVM`
|
|
20
|
+
* refusal and the lane that reports a failed restore.
|
|
21
|
+
*/
|
|
22
|
+
export declare function remoteEnvFailureNote(op: RemoteEnvFailureNote["op"], error: RemoteExecutionError, attempts: number): RemoteEnvFailureNote;
|
|
23
|
+
/**
|
|
24
|
+
* RB-439-a — `resumeVM` with a bounded retry on the TRANSIENT codes, plus the attempt count for the
|
|
25
|
+
* caller-facing note.
|
|
26
|
+
*
|
|
27
|
+
* Why this op and no other: `resumeVM` is the one lifecycle call the seam documents as "idempotent +
|
|
28
|
+
* re-entrant" (a replica that crashed mid-suspend can be superseded by another resuming the SAME
|
|
29
|
+
* snapshot), which is exactly `withRetry`'s red line — retry idempotent establishment, never a
|
|
30
|
+
* side-effecting op. `suspendVM` TAKES a snapshot and `postResumeInit` re-injects secrets under an
|
|
31
|
+
* ordering red line; neither is retried here, they are disclosed with `retryable` instead so the caller
|
|
32
|
+
* decides. A code outside the retryable family (`unsupported`, `auth_failed`) returns on the first
|
|
33
|
+
* attempt — `withRetry` will not spend a second call on a permanent refusal.
|
|
34
|
+
*/
|
|
35
|
+
export declare function restoreWorkspaceWithRetry(env: RemoteExecutionEnv, snapshotId: SnapshotId, options: VmLifecycleOptions): Promise<{
|
|
36
|
+
outcome: Awaited<ReturnType<RemoteExecutionEnv["resumeVM"]>>;
|
|
37
|
+
attempts: number;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* RB-439-c — {@link rebaseWorkspacePath} over a SET of accepted spellings of the old root: the first
|
|
41
|
+
* prefix that actually matches wins, and a path under none of them is returned unchanged.
|
|
42
|
+
*
|
|
43
|
+
* The set exists because "the old root" has no single spelling. `WorkspaceHandle.mountPath` is whatever the
|
|
44
|
+
* adapter called the mount, while the persisted paths being migrated were spelled by whoever produced them
|
|
45
|
+
* (a `cd` the env canonicalized, a Read key resolved through realpath). On a target where the root has an
|
|
46
|
+
* equivalent alias (`/var` ↔ `/private/var`) those disagree while naming the same directory, and a
|
|
47
|
+
* single-prefix rebase then matched nothing and silently left the resumed shell in the pre-suspend tree —
|
|
48
|
+
* under a disclosure that said the task follows the restored root. Order is caller-chosen (the checkpointed
|
|
49
|
+
* spelling first, its canonical form second) and only matters if one prefix is a prefix of another, in
|
|
50
|
+
* which case the earlier — more specific — spelling is the intended one. Exported for direct unit pinning.
|
|
51
|
+
*/
|
|
52
|
+
export declare function rebaseWorkspacePathAcross(p: string, froms: readonly string[], to: string): string;
|
|
53
|
+
export interface PrepareWorkspaceRestoreInput {
|
|
54
|
+
/** borrowed-readonly (the ownership-ref PROTOCOL stays with the driver — D-8 案①): the env this
|
|
55
|
+
* task owns, when the factory minted one. This phase reads and drives it (`resumeVM`,
|
|
56
|
+
* `postResumeInit`, `canonicalPath`) but NEVER rebinds or destroys it — the detach guard and the
|
|
57
|
+
* prepare-throw catch are the exception-path owners, both in the driver, and their reference
|
|
58
|
+
* must stay authoritative (a phase-local rebind could never reach them). */
|
|
59
|
+
ownedEnv: ExecutionEnv | undefined;
|
|
60
|
+
/** borrowed-readonly — the two deployment seats this phase reads, as a Pick over the SAME `deps`
|
|
61
|
+
* object (相 API 规则件 R-4: receiver preserved for `deps.onError?.()`; a new deps read must
|
|
62
|
+
* widen this Pick). `executionEnv` feeds only the observation sink's remote predicate. */
|
|
63
|
+
deps: Pick<RunnerDeps, "onError" | "executionEnv">;
|
|
64
|
+
/** borrowed-readonly — `signal` only: the caller's cancel signal, composed into the restore's
|
|
65
|
+
* abortSignal so a cancel DURING the restore short-circuits it (the live listener is attached
|
|
66
|
+
* only after the harness exists). */
|
|
67
|
+
spec: Pick<TaskSpec, "signal">;
|
|
68
|
+
/** borrowed-readonly — the trusted spawn-side seats the observation sink reads: the sink itself
|
|
69
|
+
* and the isolation REQUEST (by this point past the driver's fail-closed enforcement, so
|
|
70
|
+
* `isolated: true` is a receipt, not a restatement). */
|
|
71
|
+
internals: Pick<RunInternals, "onWorkspaceResolved" | "isolation"> | undefined;
|
|
72
|
+
/** borrowed-readonly — the durable-resume seats this phase reads: the checkpointed
|
|
73
|
+
* `workspaceHandle` (restore target) and `executesApprovedAction` (the divergence fail-closed
|
|
74
|
+
* key — codex 1360 r6). Absent for a fresh/non-durable run: the phase is then root-passthrough. */
|
|
75
|
+
resume: Pick<PrepareResume, "workspaceHandle" | "executesApprovedAction"> | undefined;
|
|
76
|
+
/** owned (value) — the pre-restore task root (isolation-aware initial derivation, computed in the
|
|
77
|
+
* driver). Dead after this call by design (R-5): later phases read the returned
|
|
78
|
+
* `taskRootFinal`, never this spelling. */
|
|
79
|
+
taskRootInitial: string;
|
|
80
|
+
/** borrowed-readonly — prepare's abort seat; only `.signal` is read (kept as the object so the
|
|
81
|
+
* moved body's `abortController.signal` spelling stays verbatim). */
|
|
82
|
+
abortController: {
|
|
83
|
+
readonly signal: AbortSignal;
|
|
84
|
+
};
|
|
85
|
+
/** borrowed-readonly — the acquired session's id, for the observation arms' error tags. */
|
|
86
|
+
sessionId: string;
|
|
87
|
+
}
|
|
88
|
+
/** The phase's outputs (相 API 规则件 four-class form). */
|
|
89
|
+
export interface PrepareWorkspaceRestoreResult {
|
|
90
|
+
/** owned — the SETTLED task root (T6 FINAL semantics): the initial derivation, else the park-only
|
|
91
|
+
* handle's mountPath, else resumeVM's restored mountPath — settled before the observation sink
|
|
92
|
+
* fired. Bound to a NEW name by design (R-5): `taskRootPath` must not be re-spelled by later
|
|
93
|
+
* phases, so a consumer moved ahead of this call is a lexical error. */
|
|
94
|
+
taskRootFinal: string;
|
|
95
|
+
/** owned (pure function; closes over the phase-armed rebase state) — migrates one checkpointed
|
|
96
|
+
* absolute path from the OLD workspace root onto the restored one (RB-439-c: any accepted
|
|
97
|
+
* spelling of the old root); the identity function when no divergence was armed. Consumers (T7):
|
|
98
|
+
* the readFileState checkpoint seed, the handsCwd seed, the activeWorktree restore. */
|
|
99
|
+
rebaseRestoredPath: (p: string) => string;
|
|
100
|
+
}
|
|
101
|
+
/** The B-4 phase body — the workspace-restore slice, verbatim (see the module header for the contract). */
|
|
102
|
+
export declare function prepareWorkspaceRestore(input: PrepareWorkspaceRestoreInput): Promise<PrepareWorkspaceRestoreResult>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { isRemoteExecutionEnv, isRetryableRemoteErrorCode, missingRestoreSurface, RETRYABLE_REMOTE_ERROR_CODES } from "../remote-env.js";
|
|
2
|
+
import { withRetry } from "../with-retry.js";
|
|
3
|
+
export function rebaseWorkspacePath(p, fromRaw, toRaw) {
|
|
4
|
+
if (p.includes("\\") || fromRaw.includes("\\") || toRaw.includes("\\"))
|
|
5
|
+
return p;
|
|
6
|
+
const stripTrail = (s) => (s.length > 1 && s.endsWith("/") ? stripTrail(s.slice(0, -1)) : s);
|
|
7
|
+
const from = stripTrail(fromRaw);
|
|
8
|
+
const to = stripTrail(toRaw);
|
|
9
|
+
if (p === from || stripTrail(p) === from)
|
|
10
|
+
return to;
|
|
11
|
+
const fromPrefix = from === "/" ? from : `${from}/`;
|
|
12
|
+
if (!p.startsWith(fromPrefix))
|
|
13
|
+
return p;
|
|
14
|
+
const suffix = p.slice(fromPrefix.length).replace(/^\/+/, "");
|
|
15
|
+
return to === "/" ? `${to}${suffix}` : `${to}/${suffix}`;
|
|
16
|
+
}
|
|
17
|
+
export function remoteEnvFailureNote(op, error, attempts) {
|
|
18
|
+
return { op, code: error.code, retryable: isRetryableRemoteErrorCode(error.code), attempts, message: error.message };
|
|
19
|
+
}
|
|
20
|
+
const REMOTE_RESTORE_MAX_ATTEMPTS = 2;
|
|
21
|
+
const REMOTE_RESTORE_BACKOFF_MS = 200;
|
|
22
|
+
export async function restoreWorkspaceWithRetry(env, snapshotId, options) {
|
|
23
|
+
let attempts = 0;
|
|
24
|
+
const outcome = await withRetry(async (attempt) => {
|
|
25
|
+
attempts = attempt;
|
|
26
|
+
return env.resumeVM(snapshotId, options);
|
|
27
|
+
}, { retryableCodes: RETRYABLE_REMOTE_ERROR_CODES, maxAttempts: REMOTE_RESTORE_MAX_ATTEMPTS, backoffMs: () => REMOTE_RESTORE_BACKOFF_MS }, { ...(options.abortSignal !== undefined ? { signal: options.abortSignal } : {}) });
|
|
28
|
+
return { outcome, attempts };
|
|
29
|
+
}
|
|
30
|
+
export function rebaseWorkspacePathAcross(p, froms, to) {
|
|
31
|
+
for (const from of froms) {
|
|
32
|
+
const out = rebaseWorkspacePath(p, from, to);
|
|
33
|
+
if (out !== p)
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
return p;
|
|
37
|
+
}
|
|
38
|
+
export async function prepareWorkspaceRestore(input) {
|
|
39
|
+
const { ownedEnv, deps, spec, internals, resume, abortController, sessionId } = input;
|
|
40
|
+
let taskRootPath = input.taskRootInitial;
|
|
41
|
+
let restoredRootRebase;
|
|
42
|
+
const rebaseRestoredPath = (p) => restoredRootRebase === undefined ? p : rebaseWorkspacePathAcross(p, restoredRootRebase.from, restoredRootRebase.to);
|
|
43
|
+
if (resume?.workspaceHandle !== undefined) {
|
|
44
|
+
const failResume = (message, cause, note) => {
|
|
45
|
+
const e = new Error(message, cause ? { cause } : undefined);
|
|
46
|
+
e.code = "resume.env_failed";
|
|
47
|
+
if (note !== undefined)
|
|
48
|
+
e.remoteEnvFailure = note;
|
|
49
|
+
throw e;
|
|
50
|
+
};
|
|
51
|
+
const handle = resume.workspaceHandle;
|
|
52
|
+
if (ownedEnv === undefined || !isRemoteExecutionEnv(ownedEnv)) {
|
|
53
|
+
failResume("resume needs a RemoteExecutionEnv from executionEnvFactory to restore the workspace snapshot");
|
|
54
|
+
}
|
|
55
|
+
else if (handle.snapshotId === undefined) {
|
|
56
|
+
if (handle.restoreMode !== "park_only" && ownedEnv.capabilities.suspendable) {
|
|
57
|
+
failResume("checkpoint workspaceHandle has no snapshotId and is not a park_only handle, but the resumed env is suspendable — refusing to resume on a possibly-unrestored workspace (corrupt checkpoint?)");
|
|
58
|
+
}
|
|
59
|
+
if (handle.mountPath && handle.mountPath !== taskRootPath) {
|
|
60
|
+
taskRootPath = handle.mountPath;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const restoreSignal = spec.signal
|
|
65
|
+
? AbortSignal.any([abortController.signal, spec.signal])
|
|
66
|
+
: abortController.signal;
|
|
67
|
+
const missingHere = missingRestoreSurface(ownedEnv);
|
|
68
|
+
if (missingHere.length > 0) {
|
|
69
|
+
failResume(`the resumed execution env cannot restore a workspace snapshot: its adapter does not implement ${missingHere.join(" or ")}. The checkpoint holds snapshot "${handle.snapshotId}" — wire an adapter that implements the full RemoteExecutionEnv restore surface and re-resume.`);
|
|
70
|
+
}
|
|
71
|
+
const { outcome: restored, attempts: restoreAttempts } = await restoreWorkspaceWithRetry(ownedEnv, handle.snapshotId, {
|
|
72
|
+
abortSignal: restoreSignal,
|
|
73
|
+
priorHandle: handle,
|
|
74
|
+
});
|
|
75
|
+
if (!restored.ok) {
|
|
76
|
+
failResume(`resumeVM failed after ${restoreAttempts} attempt(s) (${restored.error.code}): ${restored.error.message}`, restored.error, remoteEnvFailureNote("resumeVM", restored.error, restoreAttempts));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const restoredEnv = ownedEnv;
|
|
80
|
+
const canonicalInEnv = async (p) => {
|
|
81
|
+
try {
|
|
82
|
+
const r = await restoredEnv.canonicalPath(p, restoreSignal);
|
|
83
|
+
return r.ok ? r.value : undefined;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
let checkpointedCanonical;
|
|
90
|
+
let sameRootUnderAlias = false;
|
|
91
|
+
if (restored.value.mountPath !== handle.mountPath) {
|
|
92
|
+
checkpointedCanonical = await canonicalInEnv(handle.mountPath);
|
|
93
|
+
const restoredCanonical = await canonicalInEnv(restored.value.mountPath);
|
|
94
|
+
sameRootUnderAlias =
|
|
95
|
+
checkpointedCanonical !== undefined && restoredCanonical !== undefined && checkpointedCanonical === restoredCanonical;
|
|
96
|
+
}
|
|
97
|
+
if (restored.value.mountPath !== handle.mountPath && !sameRootUnderAlias) {
|
|
98
|
+
if (resume.executesApprovedAction === true) {
|
|
99
|
+
failResume(`resumeVM workspace-root divergence with a pending approved action: checkpointed mountPath "${handle.mountPath}" but the restored handle reports "${restored.value.mountPath}" — the approved args are bound to the checkpointed root; refusing to execute them against a moved workspace (adapter should honor priorHandle)`);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
deps.onError?.(new Error(`resumeVM workspace-root divergence: checkpointed mountPath "${handle.mountPath}" but the restored handle reports "${restored.value.mountPath}" — the resumed task follows the restored root`), { phase: "config", sessionId });
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (restored.value.mountPath && restored.value.mountPath !== taskRootPath) {
|
|
108
|
+
taskRootPath = restored.value.mountPath;
|
|
109
|
+
}
|
|
110
|
+
if (restored.value.mountPath && restored.value.mountPath !== handle.mountPath && !sameRootUnderAlias) {
|
|
111
|
+
const from = checkpointedCanonical !== undefined && checkpointedCanonical !== handle.mountPath
|
|
112
|
+
? [handle.mountPath, checkpointedCanonical]
|
|
113
|
+
: [handle.mountPath];
|
|
114
|
+
restoredRootRebase = { from, to: restored.value.mountPath };
|
|
115
|
+
try {
|
|
116
|
+
deps.onError?.(new Error(from.length > 1
|
|
117
|
+
? `resumeVM workspace-root rebase accepts both spellings of the checkpointed root (${from.map((f) => `"${f}"`).join(" and ")}) when migrating persisted paths to "${restored.value.mountPath}"`
|
|
118
|
+
: `resumeVM workspace-root rebase is spelling-exact: it matches the checkpointed root "${handle.mountPath}" only, so persisted paths recorded under an equivalent alias of it are NOT migrated to "${restored.value.mountPath}" and stay as written`), { phase: "config", sessionId });
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const init = await ownedEnv.postResumeInit();
|
|
125
|
+
if (!init.ok) {
|
|
126
|
+
failResume(`postResumeInit failed (${init.error.code}): ${init.error.message}`, init.error, remoteEnvFailureNote("postResumeInit", init.error, 1));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (internals?.onWorkspaceResolved !== undefined) {
|
|
131
|
+
try {
|
|
132
|
+
const workspaceEnv = ownedEnv ?? deps.executionEnv;
|
|
133
|
+
internals.onWorkspaceResolved({
|
|
134
|
+
cwd: taskRootPath,
|
|
135
|
+
isolated: internals.isolation === "worktree",
|
|
136
|
+
remote: workspaceEnv !== undefined &&
|
|
137
|
+
(isRemoteExecutionEnv(workspaceEnv) || workspaceEnv.hostLocalPaths === false),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { taskRootFinal: taskRootPath, rebaseRestoredPath };
|
|
144
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js";
|
|
2
|
+
import { createSafeNotifier } from "../safe-notify.js";
|
|
2
3
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, uuidv7 } from "../../internal/harness.js";
|
|
3
4
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
4
5
|
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, validatePendingSteer, winnerFromOutcome, } from "../checkpoint-store.js";
|
|
@@ -989,6 +990,9 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
989
990
|
}
|
|
990
991
|
function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
991
992
|
const { spec, queue, internals, ident, parentToolCallId, subagentName, pushContent, emitCommitted, startedToolCallIds, toolStartAt, writeFamilyOf, toolLabels, postToolBatchHook, batchArgs } = deps;
|
|
993
|
+
const internalsNotifier = createSafeNotifier({
|
|
994
|
+
onError: (f) => console.warn(`[sema-core] ${f.site}: run-internals activity sink threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
995
|
+
});
|
|
992
996
|
let lastWorkspaceCwd = prepared.cwdRef?.current;
|
|
993
997
|
const announceWorkspaceMove = () => {
|
|
994
998
|
const cwdNow = prepared.cwdRef?.current;
|
|
@@ -1150,7 +1154,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1150
1154
|
rs.counters.groundingSignalPostR9 = true;
|
|
1151
1155
|
}
|
|
1152
1156
|
const activityArg = primaryActivityArg(event.args);
|
|
1153
|
-
internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) });
|
|
1157
|
+
internalsNotifier.notify(() => internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) }), "runtask.onActivity.start");
|
|
1154
1158
|
pushContent({
|
|
1155
1159
|
type: "tool_start",
|
|
1156
1160
|
toolCallId: event.toolCallId,
|
|
@@ -1199,7 +1203,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1199
1203
|
ok: !event.isError,
|
|
1200
1204
|
ts: toolNow,
|
|
1201
1205
|
}));
|
|
1202
|
-
internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow });
|
|
1206
|
+
internalsNotifier.notify(() => internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow }), "runtask.onActivity.end");
|
|
1203
1207
|
if (prepared.lspDiagnostics && !event.isError) {
|
|
1204
1208
|
const name = event.toolName;
|
|
1205
1209
|
if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
|
|
@@ -3114,6 +3118,8 @@ export class Runner {
|
|
|
3114
3118
|
rewindNotes: prepared.rewindNotes,
|
|
3115
3119
|
strandedHumanAnswers,
|
|
3116
3120
|
remoteEnvFailures: prepared.remoteEnvFailures,
|
|
3121
|
+
effectiveReadFace: prepared.effectiveReadFace,
|
|
3122
|
+
effectiveReadDenyPatterns: prepared.effectiveReadDenyPatterns,
|
|
3117
3123
|
retryAfterMs: rs.limits.platformTerminal?.retryAfterMs,
|
|
3118
3124
|
abortedForTimeout: timeout.fired,
|
|
3119
3125
|
abortedForTurns: rs.limits.turnsExceeded,
|
|
@@ -175,6 +175,17 @@ export type PermissionResult = {
|
|
|
175
175
|
* consumer (approval card, wire frame) can tell the person their rule is alive, just outranked.
|
|
176
176
|
* Absent ⇒ no rule matched, or the ask was cleared normally. */
|
|
177
177
|
persistedRuleShadowed?: string;
|
|
178
|
+
/** backlog #239 disclosure (additive): WHY the reversibility probe did not clear this call, in the
|
|
179
|
+
* probe's own words. ENGINE-STAMPED at the irreversibility tighten (which constructs this decision
|
|
180
|
+
* wholesale), already neutralized + capped there; a `"maybe"` tier whose probe supplied no cause,
|
|
181
|
+
* and the `"always"` tier (which runs no probe), leave it absent. Same DISPLAY-ONLY posture as
|
|
182
|
+
* {@link persistedRuleShadowed}: a policy that self-declares it can only put its own prose on its
|
|
183
|
+
* own card — nothing reads this member to decide anything, so it is not an authority channel. */
|
|
184
|
+
probeReason?: string;
|
|
185
|
+
/** backlog #239 disclosure (additive): the STRUCTURED cause, for a probe whose verdict this engine
|
|
186
|
+
* understands (see {@link import("./checkpoint-store.js").ProbeCause}). Engine-stamped and
|
|
187
|
+
* validated at the irreversibility tighten; same display-only posture as the prose sibling. */
|
|
188
|
+
probeCause?: import("./checkpoint-store.js").ProbeCause;
|
|
178
189
|
/** An EXPLICIT `ask` permission rule matched this call (design/127 DSL — the CC `alwaysAskRules`
|
|
179
190
|
* shape); carries the matched rule's text. Present ⇔ a rule someone WROTE says "ask about this",
|
|
180
191
|
* never for a `defaultAction:"ask"` fallback (an unmatched call is default-closed posture, not a
|
|
@@ -657,6 +668,20 @@ export interface AskRequest {
|
|
|
657
668
|
* {@link PermissionResult}'s ask arm). The matched rule text, so the approval card renders "your
|
|
658
669
|
* rule is alive, just outranked" instead of leaving the person to regex the message prose. */
|
|
659
670
|
persistedRuleShadowed?: string;
|
|
671
|
+
/** backlog #239: WHY the reversibility probe did not clear this call — the account of the tighten the
|
|
672
|
+
* card was showing WITHOUT until now (the gate knew the cause and dropped it at this seam, leaving the
|
|
673
|
+
* approver the action but not the reason it is being asked about). Present only for a `"maybe"`-tier
|
|
674
|
+
* tighten whose probe supplied a cause; the durable park route carries the same string as
|
|
675
|
+
* `RiskDescriptor.probeReason`. Neutralized + length-capped by the gate. UNTRUSTED-for-display,
|
|
676
|
+
* never adjudication input. */
|
|
677
|
+
readonly probeReason?: string;
|
|
678
|
+
/** backlog #239: the STRUCTURED cause behind the tighten — a machine-readable code plus the operand
|
|
679
|
+
* families as arrays with honest totals, so this surface renders its OWN sentence rather than one the
|
|
680
|
+
* engine had to write before the check's edge cases were known. See
|
|
681
|
+
* {@link import("./checkpoint-store.js").ProbeCause}, whose contract is that the ARRAY is the
|
|
682
|
+
* structure: render the entries as data, never re-derive structure by splitting or joining them.
|
|
683
|
+
* The durable park route carries the same value as `RiskDescriptor.probeCause`. */
|
|
684
|
+
readonly probeCause?: import("./checkpoint-store.js").ProbeCause;
|
|
660
685
|
toolCallId: string;
|
|
661
686
|
/** The (post-rewrite) args the tool would run with. */
|
|
662
687
|
args: unknown;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -87,6 +87,25 @@ export type ToolEffect = "read" | "write" | "idempotent";
|
|
|
87
87
|
/** design/178 §3 — the content-origin classes (see {@link ToolSpec.contentOrigin}). */
|
|
88
88
|
export type ToolContentOrigin = "external" | "execution" | "local";
|
|
89
89
|
/** A native tool the model can call. `parameters` is a typebox schema; `execute` does the work. */
|
|
90
|
+
/**
|
|
91
|
+
* design/77 §4 — what a {@link ToolSpec.reversibilityProbe} returns for ONE call.
|
|
92
|
+
*
|
|
93
|
+
* `reversible` alone decides: the gate tightens a surviving `allow` to `ask` unless it is exactly `true`,
|
|
94
|
+
* and neither optional member can widen that. They exist so the resulting approval card can say WHY, a
|
|
95
|
+
* thing the probe knows at the moment it decides and nothing downstream can re-derive.
|
|
96
|
+
*
|
|
97
|
+
* Supply `cause` when the reason has parts a consumer should render itself; `reason` when it is prose
|
|
98
|
+
* this engine cannot interpret. Both are optional and independent, so a probe written against the
|
|
99
|
+
* original bare `{ reversible }` shape remains valid.
|
|
100
|
+
*/
|
|
101
|
+
export interface ReversibilityVerdict {
|
|
102
|
+
reversible: boolean;
|
|
103
|
+
/** Free-text account, neutralized and length-capped by the gate before it reaches any card. */
|
|
104
|
+
reason?: string;
|
|
105
|
+
/** Structured account — see {@link import("./checkpoint-store.js").ProbeCause}. Validated by the gate;
|
|
106
|
+
* a malformed value costs the DISCLOSURE only, never the ask. */
|
|
107
|
+
cause?: import("./checkpoint-store.js").ProbeCause;
|
|
108
|
+
}
|
|
90
109
|
export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
91
110
|
/**
|
|
92
111
|
* Model-facing tool name. MUST NOT contain `__` — that separator is reserved by the PROTOCOL TABLE
|
|
@@ -254,12 +273,37 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
254
273
|
* approval timeout) and **fail-closed**: a non-`reversible` verdict, a timeout, or a throw all tighten to
|
|
255
274
|
* `ask`. Declaring this probe defaults `irreversibility` to `"maybe"`. It is read from the spec at
|
|
256
275
|
* prepare-time and captured in a closure — NOT a tool argument — so the model cannot monkey-patch it.
|
|
276
|
+
*
|
|
277
|
+
* **`reason` (optional, additive both ways)** — WHY this call was not proven reversible, in the probe's
|
|
278
|
+
* own words, minted in the same pass that reached the verdict (so nothing downstream re-derives it).
|
|
279
|
+
* Read ONLY on a tightening verdict: a `reversible: true` return ends the gate, and a timeout/throw
|
|
280
|
+
* produces no verdict at all, so the cause travels exactly when there is an approval card to put it on.
|
|
281
|
+
* The gate neutralizes and length-caps it, then carries it to both approval routes — the synchronous
|
|
282
|
+
* `AskRequest.probeReason` and the durable park's `RiskDescriptor.probeReason`. Compatible in BOTH
|
|
283
|
+
* directions by construction: a probe written against the older bare `{ reversible }` shape stays
|
|
284
|
+
* type-correct and simply supplies no cause, and an engine that does not know the member ignores it.
|
|
285
|
+
*
|
|
286
|
+
* **`cause` (optional)** — the STRUCTURED alternative, for a probe whose verdict is machine-describable:
|
|
287
|
+
* a `code` plus operand families as arrays with honest totals ({@link ProbeCause}), carried to
|
|
288
|
+
* `AskRequest.probeCause` / `RiskDescriptor.probeCause`. Prefer it wherever the cause has parts. Prose
|
|
289
|
+
* has to be written before the edge cases are known — a sentence about what a command DOES is false
|
|
290
|
+
* wherever the check over-fires, and a multi-part disclosure flattened into one capped string loses
|
|
291
|
+
* whichever part sorts last — whereas a code cannot be false and an array cannot be truncated into a
|
|
292
|
+
* lie. `reason` remains for deployments whose cause is genuinely just prose this engine cannot read.
|
|
293
|
+
* Both may be supplied; each is validated and carried independently.
|
|
294
|
+
*
|
|
295
|
+
* TRUST: the text is DEPLOYMENT-authored (and, for the built-in shell probe, derived from the model's
|
|
296
|
+
* own command), so it is DISPLAY/TRIAGE metadata only — never adjudication input, and never a channel
|
|
297
|
+
* that can widen a verdict. A probe cannot use it to auto-allow: `reversible` alone decides.
|
|
298
|
+
*
|
|
299
|
+
* That property is STRUCTURAL, and keeping it so constrains where the cause may be written. It is
|
|
300
|
+
* carried as a member and deliberately NOT folded into the ask's `message`, because the message is
|
|
301
|
+
* what an auto-mode classifier is handed as its `askMessage` — and that classifier may answer an ask
|
|
302
|
+
* with `allow`. Text on the prose channel is therefore steering input to a decider that can clear the
|
|
303
|
+
* very ask the probe raised, which is exactly the authority a probe must not have. Anything added
|
|
304
|
+
* later that renders this cause must keep it on display surfaces only.
|
|
257
305
|
*/
|
|
258
|
-
reversibilityProbe?: (args: unknown) =>
|
|
259
|
-
reversible: boolean;
|
|
260
|
-
} | Promise<{
|
|
261
|
-
reversible: boolean;
|
|
262
|
-
}>;
|
|
306
|
+
reversibilityProbe?: (args: unknown) => ReversibilityVerdict | Promise<ReversibilityVerdict>;
|
|
263
307
|
/**
|
|
264
308
|
* design/178 §3 — CONTENT-ORIGIN class of what this tool can bring into the session, a fourth axis
|
|
265
309
|
* orthogonal to {@link effect} (repeat-safety), {@link egress} (blast-radius) and
|
|
@@ -2128,10 +2172,13 @@ export interface TaskSpec {
|
|
|
2128
2172
|
* design/199 件B — TASK-layer ADDITIONS to the built-in sensitive-path READ deny set
|
|
2129
2173
|
* ({@link import("../tools/fs/read-deny.js").READ_FACE_DEFAULT_DENY_ENTRIES}). Judged by the
|
|
2130
2174
|
* structured read faces (Read/Grep/Glob/RepoMap, their traversals, the classify shell gate's
|
|
2131
|
-
* auto-allow probe and the compaction attachment reader) in BOTH containment modes. Add-only
|
|
2132
|
-
*
|
|
2133
|
-
* {@link RunnerDeps.readDenyPatterns}; `[]` ≡ absent (union identity);
|
|
2134
|
-
* built-in
|
|
2175
|
+
* auto-allow probe and the compaction attachment reader) in BOTH containment modes. Add-only at
|
|
2176
|
+
* the task layer: entries here UNION with the active built-ins and the deployment's
|
|
2177
|
+
* {@link RunnerDeps.readDenyPatterns}; `[]` ≡ absent (union identity); NOTHING at the task layer
|
|
2178
|
+
* can remove a built-in — the one removal channel is the DEPLOYMENT's built-in configuration
|
|
2179
|
+
* ({@link RunnerDeps.readDenyBuiltinTiers} / {@link RunnerDeps.readDenyBuiltinExclude}, #245
|
|
2180
|
+
* revision of D-4), which no TaskSpec key reaches. Bad entry shapes refuse loudly at prepare
|
|
2181
|
+
* (#123). The write faces are untouched.
|
|
2135
2182
|
*/
|
|
2136
2183
|
readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
|
|
2137
2184
|
/**
|
|
@@ -2877,6 +2924,54 @@ export interface TaskResult {
|
|
|
2877
2924
|
deliveryId: string;
|
|
2878
2925
|
toolCallId: string;
|
|
2879
2926
|
}>;
|
|
2927
|
+
/**
|
|
2928
|
+
* #240 (design/199 v1.1) — the READ-face this leg's read surfaces actually judged under, as an
|
|
2929
|
+
* engine-filled OBSERVATION (never a knob: writing it on a spec does nothing). It is the run's ONE
|
|
2930
|
+
* resolved face — the same value the hands toolkit enforced and the delegation carriers rode — with
|
|
2931
|
+
* the hands-less resume pin folded in (a row resumed on a hands-less worker still reports the
|
|
2932
|
+
* checkpoint-frozen `"roots"`).
|
|
2933
|
+
*
|
|
2934
|
+
* **In-presence condition (#242 widened):** present on every leg that completes prepare — hands
|
|
2935
|
+
* mounted (any run with the built-in fs band) AND every hands-less leg (resume legs fold the
|
|
2936
|
+
* checkpoint seed stricter-wins; non-resume legs report the live resolution). Absent only on
|
|
2937
|
+
* prepare-failure terminals (nothing ran). Consumers that keyed on absence = "no posture" must
|
|
2938
|
+
* re-key: absence now means only "prepare never completed". Rides every OTHER terminal, not just
|
|
2939
|
+
* `completed` — the posture is a fact about the leg that ran, whatever ended it.
|
|
2940
|
+
*
|
|
2941
|
+
* Purpose: a POST-COMPLETION follow-on leg spawned OUTSIDE the delegation tree (the verify gate's
|
|
2942
|
+
* verifier and fix legs are the canonical case) cannot see the checkpoint fold the completed leg ran
|
|
2943
|
+
* under — its spawner holds only the resume-side config, which never contained the frozen posture.
|
|
2944
|
+
* Folding this seat into the follow-on spec (stricter-wins: only `"roots"` ever tightens; an
|
|
2945
|
+
* `"open"` observation is never forwarded) closes that escape for ANY follow-on, not just the
|
|
2946
|
+
* verifier. See {@link effectiveReadDenyPatterns} for the deny-set half.
|
|
2947
|
+
*/
|
|
2948
|
+
effectiveReadFace?: import("../tools/fs/read-face.js").ReadFace;
|
|
2949
|
+
/**
|
|
2950
|
+
* #240 (design/199 v1.1) — the sensitive-path deny ADDITIONS in force on this leg beyond the
|
|
2951
|
+
* built-in table, normalized (deployment ∪ task ∪ checkpoint-frozen seed), as an engine-filled
|
|
2952
|
+
* OBSERVATION. "In force" = judged by this leg's own read surfaces where they mounted, and carried
|
|
2953
|
+
* to its delegation subtree either way (a hands-less resume leg reports the entries its children
|
|
2954
|
+
* judge under).
|
|
2955
|
+
* This seat is the only place a checkpoint-FROZEN deny entry becomes visible after the resumed leg
|
|
2956
|
+
* completes: the resume-side caller's own config never contained it. A follow-on leg's spawner
|
|
2957
|
+
* unions these into the child's `readDenyPatterns` (add-only; the child's compile folds exact
|
|
2958
|
+
* duplicates, so re-supplying deployment-shared entries is idempotent) — that is how the frozen
|
|
2959
|
+
* posture rides every leg that continues this work, not just the delegation subtree.
|
|
2960
|
+
*
|
|
2961
|
+
* **In-presence condition:** present iff non-empty. Built-in entries are never listed (they are in
|
|
2962
|
+
* force on every run and re-compile locally). Same terminal coverage as
|
|
2963
|
+
* {@link effectiveReadFace}: rides any terminal of a leg that ran; absent on prepare failures.
|
|
2964
|
+
*
|
|
2965
|
+
* On a DURABLE-PAUSE hand-back these seats are additionally the RE-SUPPLY hint: a checkpoint
|
|
2966
|
+
* minted by a leg with no face resolution (a hands-less non-resume continuation) carries no
|
|
2967
|
+
* frozen face section of its own, so a caller resuming that token must fold these entries back
|
|
2968
|
+
* into the resume-side config (`readDenyPatterns` / `readFace: "roots"`). The resumed leg unions
|
|
2969
|
+
* them into its own judgment and delegation carriers; the union is NOT serialized into any
|
|
2970
|
+
* further checkpoint that leg may mint (a hands-less RESUME mint re-freezes only its original seed; a non-resume hands-less pause mints its live-resolved posture, #242), so
|
|
2971
|
+
* fold them on EVERY resume — the standing resume contract. The seats describe the posture
|
|
2972
|
+
* governing the WORK, never the contents of any checkpoint row.
|
|
2973
|
+
*/
|
|
2974
|
+
effectiveReadDenyPatterns?: readonly import("../tools/fs/read-deny.js").NormalizedReadDenyEntry[];
|
|
2880
2975
|
/**
|
|
2881
2976
|
* `turns`/`tokens`/`costMicroUsd` are this task's OWN model usage. `nested` is the summed usage of any
|
|
2882
2977
|
* delegated sub-runs (sub-agents) it spawned — present only when it delegated. The true total
|
|
@@ -4239,7 +4334,13 @@ export interface EngineNotice {
|
|
|
4239
4334
|
* deps and `runTask` builds a Runner per call), unwired console arm once per process;
|
|
4240
4335
|
* a library-direct `createHandsToolkit` mount announces at toolkit creation (one per mount,
|
|
4241
4336
|
* through the band-local `HandsToolkitOptions.onNotice` seat, absent ⇒ `console.warn`);
|
|
4242
|
-
* `detail: { seat, declared, inForce, cause }`.
|
|
4337
|
+
* `detail: { seat, declared, inForce, cause }`.
|
|
4338
|
+
*
|
|
4339
|
+
* Deliberately NOT a notice family: brain retry/reconnect liveness (a rate limit, a 5xx, a
|
|
4340
|
+
* transient network failure being retried). Those are per-attempt liveness frames with their own
|
|
4341
|
+
* frequency semantics and ride the wire `status` channel ({@link BrainStatus}), whose sink the
|
|
4342
|
+
* Runner establishes unconditionally around every brain-driving call — no host seat to wire, no
|
|
4343
|
+
* console fallback to flood. A reader looking for retry disclosure should look there, not here. */
|
|
4243
4344
|
code: string;
|
|
4244
4345
|
/** The exact human-readable line the unwired build prints via `console.warn` — same words, one text. */
|
|
4245
4346
|
message: string;
|
|
@@ -4320,11 +4421,37 @@ export interface RunnerDeps {
|
|
|
4320
4421
|
/**
|
|
4321
4422
|
* design/199 件B — DEPLOYMENT-layer ADDITIONS to the built-in sensitive-path READ deny set
|
|
4322
4423
|
* ({@link import("../tools/fs/read-deny.js").READ_FACE_DEFAULT_DENY_ENTRIES}); see
|
|
4323
|
-
* {@link TaskSpec.readDenyPatterns} for the judged surfaces. Add-only
|
|
4324
|
-
*
|
|
4325
|
-
*
|
|
4424
|
+
* {@link TaskSpec.readDenyPatterns} for the judged surfaces. Add-only on THIS seat: unions with
|
|
4425
|
+
* the active built-ins and any task-layer additions, and `[]` ≡ absent — narrowing the built-in
|
|
4426
|
+
* set is the sibling knobs' job ({@link readDenyBuiltinTiers} / {@link readDenyBuiltinExclude},
|
|
4427
|
+
* #245 revision of D-4), not a replacement escape hatch here. Bad entry shapes refuse loudly at
|
|
4428
|
+
* prepare (#123).
|
|
4326
4429
|
*/
|
|
4327
4430
|
readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
|
|
4431
|
+
/**
|
|
4432
|
+
* #245 (revises the D-4 zero-shrink ruling) — the deployment's built-in deny-table TIER selection:
|
|
4433
|
+
* EXACTLY the listed tiers of
|
|
4434
|
+
* {@link import("../tools/fs/read-deny.js").READ_FACE_BUILTIN_DENY_TABLE} are active (`[]` = none —
|
|
4435
|
+
* explicit and legal); absent = the default selection
|
|
4436
|
+
* ({@link import("../tools/fs/read-deny.js").READ_DENY_DEFAULT_TIERS}: every tier except
|
|
4437
|
+
* `shell-history`, which is unrestricted by default). Composes with
|
|
4438
|
+
* {@link readDenyBuiltinExclude} (tiers first, then row removal); never touches the additions
|
|
4439
|
+
* seats or the write faces. Unknown tier names refuse loudly at prepare (#123). DEPLOYMENT seat
|
|
4440
|
+
* ONLY: deliberately not a TaskSpec key and not in the governed workflow whitelist — a task or
|
|
4441
|
+
* governed script can only ADD deny entries, never widen the built-in face below its deployment.
|
|
4442
|
+
* Not frozen into checkpoints: the checkpoint face section persists ADDITIONS only, so a resumed
|
|
4443
|
+
* row's built-in face follows the CURRENT deployment configuration (the operator's live
|
|
4444
|
+
* authority), while frozen additions stay stricter-wins.
|
|
4445
|
+
*/
|
|
4446
|
+
readDenyBuiltinTiers?: readonly string[];
|
|
4447
|
+
/**
|
|
4448
|
+
* #245 — per-row removal from the built-in deny table by STABLE NAME (= the row's canonical
|
|
4449
|
+
* pattern text, e.g. `".ssh"`, `".config/gcloud"`; the admin delete channel), applied after
|
|
4450
|
+
* {@link readDenyBuiltinTiers} selection. Naming a row of an inactive tier is a satisfied intent
|
|
4451
|
+
* (no-op); a name matching NO table row refuses loudly at prepare (#123 — never silently
|
|
4452
|
+
* ignored). Same deployment-only seat, clamp argument and checkpoint posture as the tiers key.
|
|
4453
|
+
*/
|
|
4454
|
+
readDenyBuiltinExclude?: readonly string[];
|
|
4328
4455
|
/**
|
|
4329
4456
|
* design/199 件A — the DEPLOYMENT's read-face declaration
|
|
4330
4457
|
* ({@link import("../tools/fs/read-face.js").ReadFace}; see {@link TaskSpec.readFace} for the
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { Runner, runTask } from "./core/runner/runtask.js";
|
|
9
9
|
export { resolveTaskLimits } from "./core/runner/prepare-task.js";
|
|
10
|
+
export type { RunInternals, ResolvedWorkspace } from "./core/runner/prepare-task.js";
|
|
10
11
|
export type { BudgetAxis } from "./core/runner/assemble-result.js";
|
|
11
12
|
export { SESSION_LOG_DIGEST_SCHEME, sessionEntryDigest, sessionLogDigest, sessionLogDigestsComparable, } from "./engine/session/log-digest.js";
|
|
12
13
|
export type { ResumeTaskConfig } from "./core/runner/runtask.js";
|
|
@@ -87,10 +88,10 @@ export type { InvariantKind, FunctionContract, Invariant, InvariantViolation, Ch
|
|
|
87
88
|
export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
|
|
88
89
|
export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
|
|
89
90
|
export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
|
|
90
|
-
export { READ_FACE_DEFAULT_DENY_ENTRIES, compileReadDeny, type ReadDenyEntry, type ReadDenyMatcher, type NormalizedReadDenyEntry, } from "./tools/fs/index.js";
|
|
91
|
+
export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, type ReadDenyEntry, type ReadDenyMatcher, type NormalizedReadDenyEntry, type ReadDenyBuiltinTier, type ReadDenyBuiltinRow, type ReadDenyBuiltinConfig, } from "./tools/fs/index.js";
|
|
91
92
|
export { deploymentReadFaceClampNotice, resolveReadFace, type ReadFace, type ReadFaceInputs } from "./tools/fs/index.js";
|
|
92
93
|
export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, type ToolResultDeletionReport, } from "./core/tool-result-store.js";
|
|
93
|
-
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
|
|
94
|
+
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type ProbeCause, type ProbeCauseOperands, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
|
|
94
95
|
export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, type UsageWindow, type UsageWindowStore, type UsageWindowReading, type UsageWindowRecord, type UsageSlot, type UsageBucketRow, } from "./core/usage-window-store.js";
|
|
95
96
|
export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
|
|
96
97
|
export { ENV_LIFETIME_SUSPEND_MARGIN_MS, USAGE_WINDOW_REAP_MARGIN_MS } from "./core/runner/prepare-task.js";
|
|
@@ -252,7 +253,7 @@ export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
|
|
|
252
253
|
export { type BrainTimeoutConfig } from "./brain/timeout.js";
|
|
253
254
|
export { createAssistantMessageEventStream } from "./internal/llm.js";
|
|
254
255
|
export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
|
|
255
|
-
export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, EngineNotice, RuntimeCaps, BackgroundChildEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
|
|
256
|
+
export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, EngineNotice, RuntimeCaps, BackgroundChildEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ReversibilityVerdict, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
|
|
256
257
|
export { Type } from "typebox";
|
|
257
258
|
export type { TSchema, Static } from "typebox";
|
|
258
259
|
export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";
|
package/dist/index.js
CHANGED
|
@@ -68,7 +68,7 @@ export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
|
|
|
68
68
|
export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
|
|
69
69
|
export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
|
|
70
70
|
export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
|
|
71
|
-
export { READ_FACE_DEFAULT_DENY_ENTRIES, compileReadDeny, } from "./tools/fs/index.js";
|
|
71
|
+
export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, } from "./tools/fs/index.js";
|
|
72
72
|
export { deploymentReadFaceClampNotice, resolveReadFace } from "./tools/fs/index.js";
|
|
73
73
|
export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
|
|
74
74
|
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
|
|
@@ -50,10 +50,12 @@ export interface WorkflowAgentSpec {
|
|
|
50
50
|
*/
|
|
51
51
|
readFace?: "roots";
|
|
52
52
|
/**
|
|
53
|
-
* design/199 件B, TIGHTEN-ONLY: additional read-deny entries. Add-only at every layer
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
53
|
+
* design/199 件B, TIGHTEN-ONLY: additional read-deny entries. Add-only at every layer a governed
|
|
54
|
+
* SCRIPT can reach: the baseline's entries and the deployment-ACTIVE built-in set are always in
|
|
55
|
+
* force and a script cannot remove or replace them (`compileReadDeny` treats additions as
|
|
56
|
+
* add-only; `tightenTaskSpec` unions). Which built-in tiers are active is a DEPLOYMENT seat
|
|
57
|
+
* (#245: `readDenyBuiltinTiers`/`readDenyBuiltinExclude` on RunnerDeps) that no script-side key
|
|
58
|
+
* reaches — so anything a script writes here can only ever narrow what the child may read.
|
|
57
59
|
*/
|
|
58
60
|
readDenyPatterns?: readonly ReadDenyEntry[];
|
|
59
61
|
}
|
|
@@ -95,9 +95,15 @@ export interface BashReadonlyRootBoundary {
|
|
|
95
95
|
* demotes the command (ask, never auto-allow), independently of the roots — in-root operands are
|
|
96
96
|
* judged too. Returns the matched pattern, or null. TWO named residuals, both inherited from this
|
|
97
97
|
* classifier's declared purity (synchronous, zero I/O — RB-448/RB-451 state the same scope for the
|
|
98
|
-
* containment half): ① operand TARGET matching only — no ancestor intersection
|
|
99
|
-
*
|
|
100
|
-
*
|
|
98
|
+
* containment half): ① operand TARGET matching only — no ancestor intersection: the judge sees
|
|
99
|
+
* the operand's own resolved spelling, never its subtree. The RECURSIVE-reach half of that
|
|
100
|
+
* residual has since been narrowed (backlog #222): a listed recursive/expanding verb's path
|
|
101
|
+
* operand under this wired seat rides {@link CompoundReadonlyVerdict.recursiveReadPaths}
|
|
102
|
+
* (⊂ undecidedPaths), so `grep -r x ~/` no longer auto-allows — it demotes to ask through the
|
|
103
|
+
* undecided contract. What REMAINS of ① is the form table's stated open set (see the KNOWN OPEN
|
|
104
|
+
* SET note at {@link RECURSIVE_READ_FORMS}: a traversal verb absent there that a deployment
|
|
105
|
+
* allowlists classifies by its ordinary form and can auto-run a traversal unjudged);
|
|
106
|
+
* ② LEXICAL only — an in-root symlink whose target is a
|
|
101
107
|
* guarded path reads as its innocent spelling here, exactly as it does for the containment half
|
|
102
108
|
* (the enforcing/canonicalizing recheck is the bash_readonly leg's job via checkedPaths; the
|
|
103
109
|
* classify auto-allow lane has no I/O seat by contract). The structured read faces judge BOTH
|