@sema-agent/core 7.17.7 → 7.19.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 +233 -13
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +5 -0
- package/dist/agents/teacher.js +1 -0
- package/dist/agents/verify.js +3 -0
- package/dist/brain/anthropic.js +6 -3
- package/dist/brain/open-responses.js +10 -4
- package/dist/brain/openai.js +5 -2
- package/dist/brain/reasoning.d.ts +13 -13
- package/dist/brain/reasoning.js +2 -2
- package/dist/brain/route-adjudicator.d.ts +18 -0
- package/dist/brain/route-adjudicator.js +16 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/brain/stream-shared.d.ts +3 -0
- package/dist/brain/stream-shared.js +1 -0
- package/dist/core/agent-definition.d.ts +10 -0
- package/dist/core/artifact-host.d.ts +41 -4
- package/dist/core/artifact-host.js +18 -2
- package/dist/core/ask-origin.d.ts +16 -0
- package/dist/core/ask-origin.js +8 -0
- package/dist/core/auto-mode-arming.d.ts +6 -0
- package/dist/core/auto-mode-arming.js +5 -5
- package/dist/core/auto-mode-defaults.d.ts +43 -15
- package/dist/core/auto-mode-defaults.js +4 -1
- package/dist/core/auto-mode.d.ts +184 -59
- package/dist/core/auto-mode.js +188 -13
- package/dist/core/checkpoint-store.d.ts +23 -2
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/engine-notice.d.ts +77 -1
- package/dist/core/engine-notice.js +7 -0
- package/dist/core/gate-fold.js +5 -1
- package/dist/core/gate-lanes.js +9 -8
- package/dist/core/gate-outcome.d.ts +42 -2
- package/dist/core/gate-outcome.js +23 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +14 -0
- package/dist/core/hook-seats.d.ts +66 -0
- package/dist/core/hook-seats.js +37 -0
- package/dist/core/hooks.d.ts +1 -1
- package/dist/core/read-root-candidate.d.ts +12 -0
- package/dist/core/read-root-candidate.js +24 -0
- package/dist/core/roles.d.ts +9 -1
- package/dist/core/roles.js +4 -0
- package/dist/core/runner/advertised-writable-dirs.d.ts +8 -3
- package/dist/core/runner/assemble-result.d.ts +2 -1
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/classifier-ladder.d.ts +227 -0
- package/dist/core/runner/classifier-ladder.js +214 -0
- package/dist/core/runner/contracts.d.ts +71 -13
- package/dist/core/runner/decide-continuation.d.ts +1 -3
- package/dist/core/runner/decide-continuation.js +1 -7
- package/dist/core/runner/deferral-census.d.ts +52 -0
- package/dist/core/runner/deferral-census.js +44 -0
- package/dist/core/runner/denial-limit-arms.js +2 -2
- package/dist/core/runner/derived-route-fallback.d.ts +8 -4
- package/dist/core/runner/derived-route-fallback.js +8 -3
- package/dist/core/runner/gate-exit.d.ts +12 -0
- package/dist/core/runner/gate-exit.js +6 -3
- package/dist/core/runner/prepare-artifact.d.ts +10 -2
- package/dist/core/runner/prepare-artifact.js +5 -4
- package/dist/core/runner/prepare-ask-lane.js +1 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +43 -55
- package/dist/core/runner/prepare-config-doors.d.ts +7 -1
- package/dist/core/runner/prepare-config-doors.js +13 -13
- package/dist/core/runner/prepare-defer-classify.d.ts +13 -19
- package/dist/core/runner/prepare-defer-classify.js +8 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +26 -0
- package/dist/core/runner/prepare-gate-stations.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +24 -7
- package/dist/core/runner/prepare-hands-readface.js +60 -24
- package/dist/core/runner/prepare-inherited-gate.d.ts +17 -4
- package/dist/core/runner/prepare-inherited-gate.js +10 -2
- package/dist/core/runner/prepare-lsp.d.ts +2 -2
- package/dist/core/runner/prepare-lsp.js +1 -1
- package/dist/core/runner/prepare-policy-chain.js +2 -0
- package/dist/core/runner/prepare-project-context.d.ts +5 -1
- package/dist/core/runner/prepare-project-context.js +7 -4
- package/dist/core/runner/prepare-prompt-inputs.d.ts +4 -2
- package/dist/core/runner/prepare-prompt-inputs.js +4 -1
- package/dist/core/runner/prepare-run-refs.d.ts +9 -1
- package/dist/core/runner/prepare-run-refs.js +3 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -0
- package/dist/core/runner/prepare-suspend-saga.js +5 -2
- package/dist/core/runner/prepare-task.js +19 -14
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +5 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +10 -2
- package/dist/core/runner/prepare-turn-wiring.d.ts +8 -4
- package/dist/core/runner/prepare-turn-wiring.js +3 -2
- package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -5
- package/dist/core/runner/prepare-wiring-manifest.js +5 -2
- package/dist/core/runner/resume-apply.js +3 -3
- package/dist/core/runner/resume-checkpoint-screen.js +7 -0
- package/dist/core/runner/run-harness-handlers.js +4 -1
- package/dist/core/runner/run-identity-wiring.js +6 -2
- package/dist/core/runner/run-leg.d.ts +2 -0
- package/dist/core/runner/run-leg.js +45 -45
- package/dist/core/runner/run-notification-lane.js +55 -27
- package/dist/core/runner/run-settle-and-teardown.js +1 -0
- package/dist/core/runner/run-terminal-adoption.js +16 -13
- package/dist/core/runner/run-turn-boundary.js +15 -5
- package/dist/core/runner/runtask.d.ts +6 -5
- package/dist/core/runner/runtask.js +26 -14
- package/dist/core/runner/skill-base-dir.d.ts +54 -0
- package/dist/core/runner/skill-base-dir.js +42 -0
- package/dist/core/runner/synthetic-tools.d.ts +83 -48
- package/dist/core/runner/synthetic-tools.js +69 -32
- package/dist/core/runner/tool-defer-gate.d.ts +22 -9
- package/dist/core/runner/tool-defer-gate.js +15 -5
- package/dist/core/runner/tool-disclosure.d.ts +5 -0
- package/dist/core/runner/tool-disclosure.js +2 -0
- package/dist/core/runner/tool-progress-lane.d.ts +42 -0
- package/dist/core/runner/tool-progress-lane.js +61 -0
- package/dist/core/runner/turn-attachments.d.ts +43 -12
- package/dist/core/runner/turn-attachments.js +58 -14
- package/dist/core/runner-deps.d.ts +48 -7
- package/dist/core/sensitive-path-policy.d.ts +58 -17
- package/dist/core/sensitive-path-policy.js +33 -5
- package/dist/core/skill-listing-budget.d.ts +112 -0
- package/dist/core/skill-listing-budget.js +58 -0
- package/dist/core/swappable-deps.d.ts +9 -7
- package/dist/core/swappable-deps.js +7 -3
- package/dist/core/task-event.d.ts +96 -29
- package/dist/core/task-notification.d.ts +142 -16
- package/dist/core/task-notification.js +106 -0
- package/dist/core/task-result.d.ts +9 -1
- package/dist/core/task-spec.d.ts +46 -3
- package/dist/core/tighten-task-spec.js +7 -5
- package/dist/core/tool-model-gate.d.ts +1 -1
- package/dist/core/tool-policy.d.ts +19 -4
- package/dist/core/tool-policy.js +6 -7
- package/dist/core/tool-search-setting.d.ts +78 -0
- package/dist/core/tool-search-setting.js +90 -0
- package/dist/core/tool-spec.d.ts +45 -4
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/wiring-manifest.d.ts +46 -0
- package/dist/core/wiring-manifest.js +9 -1
- package/dist/core/write-protect.d.ts +8 -3
- package/dist/core/write-protect.js +8 -5
- package/dist/engine/execution-env/node-execution-env.js +10 -4
- package/dist/engine/harness/agent-harness.d.ts +42 -0
- package/dist/engine/harness/agent-harness.js +61 -29
- package/dist/engine/loop/agent-loop.d.ts +68 -3
- package/dist/engine/loop/agent-loop.js +109 -16
- package/dist/index.d.ts +13 -8
- package/dist/index.js +11 -7
- package/dist/internal/harness.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +3 -0
- package/dist/orchestration/run-workflow-tool.js +6 -1
- package/dist/orchestration/workflow-governance.d.ts +7 -1
- package/dist/orchestration/workflow-governance.js +9 -5
- package/dist/orchestration/workflow-primitives.d.ts +3 -0
- package/dist/orchestration/workflow-primitives.js +6 -1
- package/dist/orchestration/workflow-types.d.ts +48 -13
- package/dist/orchestration/workflow-types.js +1 -1
- package/dist/orchestration/workflow.js +45 -17
- package/dist/prompt-assembly/epoch.js +2 -0
- package/dist/prompt-assembly/packs/sema-default.js +2 -2
- package/dist/prompt-assembly/section-usage.d.ts +26 -0
- package/dist/prompt-assembly/section-usage.js +10 -0
- package/dist/prompt-assembly/types.d.ts +8 -6
- package/dist/prompts/default.d.ts +13 -0
- package/dist/prompts/default.js +16 -8
- package/dist/tools/artifact/artifact-text.d.ts +10 -1
- package/dist/tools/artifact/artifact-text.js +6 -3
- package/dist/tools/artifact/artifact-tool.d.ts +12 -2
- package/dist/tools/artifact/artifact-tool.js +203 -70
- package/dist/tools/artifact/conformance.d.ts +56 -0
- package/dist/tools/artifact/conformance.js +349 -0
- package/dist/tools/artifact/local-stub.js +1 -1
- package/dist/tools/artifact/reject-breaker.d.ts +28 -0
- package/dist/tools/artifact/reject-breaker.js +42 -0
- package/dist/tools/fs/bash-lexer.d.ts +132 -0
- package/dist/tools/fs/bash-lexer.js +563 -0
- package/dist/tools/fs/bash-program-position.d.ts +142 -0
- package/dist/tools/fs/bash-program-position.js +125 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +81 -16
- package/dist/tools/fs/bash-readonly-classifier.js +180 -558
- package/dist/tools/fs/containment-face.d.ts +89 -0
- package/dist/tools/fs/containment-face.js +81 -0
- package/dist/tools/fs/fs-bash.d.ts +46 -1
- package/dist/tools/fs/fs-bash.js +165 -25
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-search-tools.d.ts +3 -3
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +24 -5
- package/dist/tools/fs/index.js +18 -6
- package/dist/tools/fs/read-deny.d.ts +4 -3
- package/dist/tools/fs/repo-map.d.ts +2 -2
- package/dist/tools/fs/safety.d.ts +18 -3
- package/dist/tools/fs/safety.js +11 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +99 -13
- package/wire-changes/core@7.17.7.json +49 -0
- package/wire-changes/core@7.18.0.json +1273 -0
- package/wire-changes/core@7.19.0.json +457 -0
- package/dist/tools/fs/read-face.d.ts +0 -68
- package/dist/tools/fs/read-face.js +0 -47
|
@@ -86,7 +86,7 @@ async function gateToolWrite(hook, tool, path, key, content) {
|
|
|
86
86
|
return `Error (${tool}): write rejected by the write gate (${res.code}): ${clipHookText(res.reason)}`;
|
|
87
87
|
return undefined;
|
|
88
88
|
}
|
|
89
|
-
export function createEditFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
89
|
+
export function createEditFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited, writeFace) {
|
|
90
90
|
return defineTool({
|
|
91
91
|
name: "Edit",
|
|
92
92
|
...toolFace("edit"),
|
|
@@ -111,7 +111,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
111
111
|
const path = fileArgPath(args);
|
|
112
112
|
if (path === undefined)
|
|
113
113
|
return { ok: true };
|
|
114
|
-
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
114
|
+
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots, undefined, undefined, writeFace);
|
|
115
115
|
if (!r.ok)
|
|
116
116
|
return { ok: true };
|
|
117
117
|
if (a.old_string === a.new_string)
|
|
@@ -149,7 +149,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
149
149
|
const path = fileArgPath(args);
|
|
150
150
|
if (path === undefined)
|
|
151
151
|
return errorResult(`Error (Edit): file_path is required.`);
|
|
152
|
-
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
152
|
+
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots, undefined, undefined, writeFace);
|
|
153
153
|
if (!r.ok)
|
|
154
154
|
return errorResult(violationText("Edit", r.violation), violationDetails(r.violation));
|
|
155
155
|
if (!batch && a.old_string === a.new_string) {
|
|
@@ -324,7 +324,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
324
324
|
},
|
|
325
325
|
});
|
|
326
326
|
}
|
|
327
|
-
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
327
|
+
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited, writeFace) {
|
|
328
328
|
return defineTool({
|
|
329
329
|
name: "Write",
|
|
330
330
|
...toolFace("write"),
|
|
@@ -343,7 +343,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
343
343
|
return { ok: true };
|
|
344
344
|
if (ipynbRedirect("Write", path))
|
|
345
345
|
return { ok: true };
|
|
346
|
-
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
346
|
+
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots, undefined, undefined, writeFace);
|
|
347
347
|
if (!r.ok)
|
|
348
348
|
return { ok: true };
|
|
349
349
|
const exists = await env.exists(r.key, ctx.signal);
|
|
@@ -362,7 +362,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
362
362
|
const ipynb = ipynbRedirect("Write", path);
|
|
363
363
|
if (ipynb)
|
|
364
364
|
return errorResult(ipynb);
|
|
365
|
-
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
365
|
+
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots, undefined, undefined, writeFace);
|
|
366
366
|
if (!r.ok)
|
|
367
367
|
return errorResult(violationText("Write", r.violation), violationDetails(r.violation));
|
|
368
368
|
const exists = await env.exists(r.key, ctx.signal);
|
|
@@ -423,7 +423,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
423
423
|
const notebookNotIpynbMessage = (notebookPath) => `Error (NotebookEdit): "${notebookPath}" is not a .ipynb file; use Edit for other file types.`;
|
|
424
424
|
const NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE = `Error (NotebookEdit): Cell type is required when using edit_mode=insert.`;
|
|
425
425
|
const NOTEBOOK_CELL_ID_REQUIRED_MESSAGE = `Error (NotebookEdit): cell_id is required for replace/delete.`;
|
|
426
|
-
export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
426
|
+
export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited, writeFace) {
|
|
427
427
|
return defineTool({
|
|
428
428
|
name: "NotebookEdit",
|
|
429
429
|
...toolFace("notebook-edit"),
|
|
@@ -457,7 +457,7 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
|
|
|
457
457
|
return { ok: false, code: "invalid", message: NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE };
|
|
458
458
|
if (mode !== "insert" && !a.cell_id)
|
|
459
459
|
return { ok: false, code: "invalid", message: NOTEBOOK_CELL_ID_REQUIRED_MESSAGE };
|
|
460
|
-
const r = await resolveKey(env, rootCanonical, a.notebook_path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
460
|
+
const r = await resolveKey(env, rootCanonical, a.notebook_path, ctx.signal, cwdRef?.current, additionalRoots, undefined, undefined, writeFace);
|
|
461
461
|
if (!r.ok)
|
|
462
462
|
return { ok: true };
|
|
463
463
|
const notRead = requireRead(state, r.key);
|
|
@@ -477,7 +477,7 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
|
|
|
477
477
|
return errorResult(NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE);
|
|
478
478
|
if (mode !== "insert" && !cell_id)
|
|
479
479
|
return errorResult(NOTEBOOK_CELL_ID_REQUIRED_MESSAGE);
|
|
480
|
-
const r = await resolveKey(env, rootCanonical, notebook_path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
480
|
+
const r = await resolveKey(env, rootCanonical, notebook_path, ctx.signal, cwdRef?.current, additionalRoots, undefined, undefined, writeFace);
|
|
481
481
|
if (!r.ok)
|
|
482
482
|
return errorResult(violationText("NotebookEdit", r.violation), violationDetails(r.violation));
|
|
483
483
|
const notRead = requireRead(state, r.key);
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ export * from "./read-deny.js";
|
|
|
14
14
|
import { type BashReadonlyRootBoundary } from "./bash-readonly-classifier.js";
|
|
15
15
|
export { BASH_CLASSIFY_DEFAULT_ALLOW } from "./bash-readonly-classifier.js";
|
|
16
16
|
import { type ReadDenyEntry } from "./read-deny.js";
|
|
17
|
-
|
|
17
|
+
import { type ContainmentFace } from "./containment-face.js";
|
|
18
|
+
export * from "./containment-face.js";
|
|
18
19
|
import { type CwdRef, type ReadImageDownsamplerOption } from "./fs-shared.js";
|
|
19
20
|
/** Options for {@link createHandsToolkit}. */
|
|
20
21
|
export interface HandsToolkitOptions {
|
|
@@ -108,6 +109,14 @@ export interface HandsToolkitOptions {
|
|
|
108
109
|
* by the Runner for every task and so cannot double as an opt-in signal. Default undefined ⇒ `false`
|
|
109
110
|
* downstream. */
|
|
110
111
|
autoBackgroundOnTimeout?: boolean;
|
|
112
|
+
/** The run's tool-progress sink — see createBashTool's taskOpts field of the same name. Runner-wired
|
|
113
|
+
* (a late-bound cell behind the closure), absent for a standalone toolkit build ⇒ the shell's execute
|
|
114
|
+
* leg opens no streaming callbacks at all and its `env.exec` options are byte-identical to before. */
|
|
115
|
+
onToolProgress?: (body: import("../../core/runner/contracts.js").ToolProgressBody) => void;
|
|
116
|
+
/** Is a PERSON's input waiting for this run's next turn boundary — see createBashTool's taskOpts field
|
|
117
|
+
* of the same name. Runner-wired (reads the harness's own accepted-but-unserved account); absent ⇒ the
|
|
118
|
+
* shell never auto-backgrounds a running command, exactly as before. */
|
|
119
|
+
queuedHumanInput?: () => boolean;
|
|
111
120
|
/** Read-image downsampler seam ({@link ReadImageDownsamplerOption}). Default undefined = auto-detect
|
|
112
121
|
* the optional sharp dependency once per process; `false` disables it (deterministic no-sharp behavior). */
|
|
113
122
|
readImageDownsampler?: ReadImageDownsamplerOption;
|
|
@@ -164,16 +173,26 @@ export interface HandsToolkitOptions {
|
|
|
164
173
|
* time (never silently ignored). Deployment seat ONLY, same clamp argument as the tiers key. */
|
|
165
174
|
readDenyBuiltinExclude?: readonly string[];
|
|
166
175
|
/** The READ-face containment state for the structured read faces
|
|
167
|
-
* ({@link import("./
|
|
168
|
-
* {@link import("./
|
|
176
|
+
* ({@link import("./containment-face.js").ContainmentFace}; resolved through the SAME
|
|
177
|
+
* {@link import("./containment-face.js").resolveContainmentFace} order prepare-task uses). Absent ⇒ the
|
|
169
178
|
* resolution order's default = "roots" (the engine never opens implicitly). "open" skips ONLY the
|
|
170
179
|
* roots containment judgment — the deny set, the UNC out-of-set refusal and the special-file type
|
|
171
180
|
* gates run in both faces. Beside `readOnly: true` the mount wins: this seat is deps-shaped (a
|
|
172
181
|
* deployment default, not a per-call assertion), so its "open" CLAMPS to "roots" and the clamp is
|
|
173
182
|
* ANNOUNCED (`config.read_face_deployment_clamped`, through `onNotice` / `console.warn`) — the loud
|
|
174
183
|
* refusal belongs to the TASK seat (`TaskSpec.readFace`), the one genuine per-call contradiction.
|
|
175
|
-
* Never affects the write faces. */
|
|
176
|
-
readFace?:
|
|
184
|
+
* Never affects the write faces — {@link writeFace} is a separate seat with its own default. */
|
|
185
|
+
readFace?: ContainmentFace;
|
|
186
|
+
/** The WRITE-face containment state for the structured write faces (Edit / Write / NotebookEdit)
|
|
187
|
+
* ({@link import("./containment-face.js").ContainmentFace}; resolved through the SAME
|
|
188
|
+
* {@link import("./containment-face.js").resolveContainmentFace} order — one body, one default,
|
|
189
|
+
* per seat). Absent ⇒ "roots" (today's behavior, byte-identical). "open" skips ONLY the roots
|
|
190
|
+
* containment judgment on the write band — the write-side sensitive-path policy, the blocked-device
|
|
191
|
+
* gate, the UNC out-of-set refusal and the read-before-edit requirement all still run, and the full
|
|
192
|
+
* `bash` face is unaffected either way (it was never fenced). Beside `readOnly: true` the mount
|
|
193
|
+
* wins the same way the read seat's does (that mount carries no write faces at all), announced as
|
|
194
|
+
* `config.write_face_deployment_clamped`. Never affects the read faces. */
|
|
195
|
+
writeFace?: ContainmentFace;
|
|
177
196
|
/** The session's reminder provenance mark: the band's engine-minted
|
|
178
197
|
* `<system-reminder>` open tags (the Read cyber/dedup/offset/empty reminders and the gh
|
|
179
198
|
* rate-limit hint) carry it as `mark="…"`, matching the system-prompt declaration the Runner
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -14,8 +14,8 @@ export * from "./read-deny.js";
|
|
|
14
14
|
import { BASH_READONLY_DEFAULT_ALLOW, } from "./bash-readonly-classifier.js";
|
|
15
15
|
export { BASH_CLASSIFY_DEFAULT_ALLOW } from "./bash-readonly-classifier.js";
|
|
16
16
|
import { compileReadDeny } from "./read-deny.js";
|
|
17
|
-
import {
|
|
18
|
-
export * from "./
|
|
17
|
+
import { deploymentFaceClampNotice, resolveContainmentFace } from "./containment-face.js";
|
|
18
|
+
export * from "./containment-face.js";
|
|
19
19
|
import {} from "./fs-shared.js";
|
|
20
20
|
import { createReadFileTool } from "./fs-read.js";
|
|
21
21
|
import { createEditFileTool, createWriteFileTool, createNotebookEditTool } from "./fs-write.js";
|
|
@@ -42,18 +42,27 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
42
42
|
...(opts.readDenyBuiltinTiers !== undefined ? { tiers: opts.readDenyBuiltinTiers } : {}),
|
|
43
43
|
...(opts.readDenyBuiltinExclude !== undefined ? { exclude: opts.readDenyBuiltinExclude } : {}),
|
|
44
44
|
});
|
|
45
|
-
const readFace =
|
|
46
|
-
|
|
45
|
+
const readFace = resolveContainmentFace({
|
|
46
|
+
kind: "read",
|
|
47
|
+
depsFace: opts.readFace,
|
|
47
48
|
readOnlyMount: readOnly,
|
|
48
49
|
orgGoverned: false,
|
|
49
50
|
fullShellReachable: includeShell && !readOnly,
|
|
50
|
-
onDeploymentClamp: () => deliverEngineNotice(opts.onNotice,
|
|
51
|
+
onDeploymentClamp: () => deliverEngineNotice(opts.onNotice, deploymentFaceClampNotice("read")),
|
|
52
|
+
});
|
|
53
|
+
const writeFace = resolveContainmentFace({
|
|
54
|
+
kind: "write",
|
|
55
|
+
depsFace: opts.writeFace,
|
|
56
|
+
readOnlyMount: readOnly,
|
|
57
|
+
orgGoverned: false,
|
|
58
|
+
fullShellReachable: includeShell && !readOnly,
|
|
59
|
+
onDeploymentClamp: () => deliverEngineNotice(opts.onNotice, deploymentFaceClampNotice("write")),
|
|
51
60
|
});
|
|
52
61
|
const tools = [
|
|
53
62
|
createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, opts.readCyberReminder, readDeny, readFace, opts.reminderMark, opts.reminderDisclosureCounts),
|
|
54
63
|
];
|
|
55
64
|
if (handsWriteHalfMounts({ readOnly })) {
|
|
56
|
-
tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited));
|
|
65
|
+
tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited, writeFace), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited, writeFace), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited, writeFace));
|
|
57
66
|
}
|
|
58
67
|
tools.push(createGrepTool(env, rootCanonical, readFaceRoots, readDeny, readFace, cwdRef), createGlobTool(env, rootCanonical, readFaceRoots, readDeny, readFace, cwdRef), createRepoMapTool(env, rootCanonical, readFaceRoots, readDeny, readFace));
|
|
59
68
|
if (includeShell) {
|
|
@@ -63,6 +72,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
63
72
|
...(opts.bashMaxTimeoutMs !== undefined ? { bashMaxTimeoutMs: opts.bashMaxTimeoutMs } : {}),
|
|
64
73
|
...(opts.onNotice !== undefined ? { onNotice: opts.onNotice } : {}),
|
|
65
74
|
...(readFaceRoots !== undefined ? { additionalRoots: readFaceRoots } : {}),
|
|
75
|
+
...(opts.onToolProgress !== undefined ? { onToolProgress: opts.onToolProgress } : {}),
|
|
66
76
|
})
|
|
67
77
|
: createBashTool(env, rootCanonical, commitCoAuthor, cwdRef, {
|
|
68
78
|
taskRegistry: opts.taskRegistry,
|
|
@@ -72,6 +82,8 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
72
82
|
onTaskNotification: opts.taskNotification,
|
|
73
83
|
detachHub: opts.detachHub,
|
|
74
84
|
...(opts.autoBackgroundOnTimeout !== undefined ? { autoBackgroundOnTimeout: opts.autoBackgroundOnTimeout } : {}),
|
|
85
|
+
...(opts.onToolProgress !== undefined ? { onToolProgress: opts.onToolProgress } : {}),
|
|
86
|
+
...(opts.queuedHumanInput !== undefined ? { queuedHumanInput: opts.queuedHumanInput } : {}),
|
|
75
87
|
...(opts.oneShot !== undefined ? { oneShot: opts.oneShot } : {}),
|
|
76
88
|
...(additionalRoots !== undefined ? { additionalRoots } : {}),
|
|
77
89
|
...(opts.shellReadBoundary !== undefined ? { readBoundary: opts.shellReadBoundary } : {}),
|
|
@@ -104,11 +104,12 @@ export interface ReadDenyBuiltinRow {
|
|
|
104
104
|
* `.sema`/`.claude` also hold agents, skills, hooks and (under the shell's layout,
|
|
105
105
|
* `~/.sema/engine-data`) the engine data root with the memory library — material the model reads
|
|
106
106
|
* and edits as ordinary work, and a directory-wide row would need a data-root passthrough (one more
|
|
107
|
-
* rule) to keep it readable.
|
|
108
|
-
* write there is self-authorization; a read of a non-credential file there is debugging. Token
|
|
107
|
+
* rule) to keep it readable. Token
|
|
109
108
|
* STORES of the same tooling (`.credentials.json`, an agent CLI's `auth.json`, …) are credentials rows, not
|
|
110
109
|
* agent-config rows: a deployment that switches this tier off to debug its agent configuration must
|
|
111
|
-
* not also expose the OAuth stores beside it.
|
|
110
|
+
* not also expose the OAuth stores beside it. These rows are ALSO the opt-in write deny set's
|
|
111
|
+
* agent-configuration block ({@link import("../../core/sensitive-path-policy.js").agentConfigDenyPatterns},
|
|
112
|
+
* which reads them from here; contract sentence there), so a row added here is judged by both faces.
|
|
112
113
|
*/
|
|
113
114
|
export declare const READ_FACE_BUILTIN_DENY_TABLE: readonly ReadDenyBuiltinRow[];
|
|
114
115
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentTool } from "../../internal/harness-types.js";
|
|
2
2
|
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
3
3
|
import type { ReadDenyMatcher } from "./read-deny.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { ContainmentFace } from "./containment-face.js";
|
|
5
5
|
/** Extract top-level symbol names (deduped, in first-seen order) from source text. A lightweight
|
|
6
6
|
* comment/docstring state machine skips column-0 declaration-like lines that are really inside a
|
|
7
7
|
* `/* … */` block comment or a Python `"""`/`'''` docstring — otherwise e.g. a module docstring
|
|
@@ -13,4 +13,4 @@ import type { ReadFace } from "./read-face.js";
|
|
|
13
13
|
* (src/core/auto-promote.ts) consumes it in ESCALATE-ONLY mode — a clean scan contributes nothing;
|
|
14
14
|
* it can only ever raise an escalation, never clear one. */
|
|
15
15
|
export declare function extractSymbols(text: string): string[];
|
|
16
|
-
export declare function createRepoMapTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?:
|
|
16
|
+
export declare function createRepoMapTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ContainmentFace): AgentTool;
|
|
@@ -258,6 +258,21 @@ export declare function isBinaryContent(sample: string): boolean;
|
|
|
258
258
|
* test with no verdict of its own.
|
|
259
259
|
*/
|
|
260
260
|
export declare function isUncPath(path: string): boolean;
|
|
261
|
+
/**
|
|
262
|
+
* True when a WIN-FORM spelling carries a component Win32's own normalization would trim — a trailing dot
|
|
263
|
+
* or space on anything but the `.`/`..` specials (the same client-side strip {@link uncCanonicalKey}
|
|
264
|
+
* folds, stated once and read here rather than re-derived).
|
|
265
|
+
*
|
|
266
|
+
* Such a spelling is an ALIAS: `x. ` and `x` name ONE directory to the OS and two different strings to a
|
|
267
|
+
* lexical comparator. A caller that must reason about a spelling byte-wise — a directory offered to a
|
|
268
|
+
* person as the thing to declare, a common-prefix cut between two operands — cannot pick one of the two
|
|
269
|
+
* readings on the person's behalf, so it withholds. POSIX paths are never aliased this way: a trailing dot
|
|
270
|
+
* or space there is an ordinary filename character.
|
|
271
|
+
*
|
|
272
|
+
* The UNC ANCHOR is exempt for the reason the key minter states: a host's trailing dot is DNS (the FQDN
|
|
273
|
+
* root) and share-name normalization is the server's business; neither is the client-side strip.
|
|
274
|
+
*/
|
|
275
|
+
export declare function hasWin32AliasedComponent(p: string): boolean;
|
|
261
276
|
/** The two spelling families a tree can be in. Read from the ROOT's spelling (the cwd's when a deployment
|
|
262
277
|
* supplies no root) — never from `process.platform`: the exec env may be remote and of the other family. */
|
|
263
278
|
export type PathFamily = "win" | "posix";
|
|
@@ -392,7 +407,7 @@ export declare function resolveKey(env: ExecutionEnv, rootCanonical: string, pat
|
|
|
392
407
|
pattern: string;
|
|
393
408
|
matchedView?: string;
|
|
394
409
|
} | null;
|
|
395
|
-
},
|
|
410
|
+
}, containmentFace?: "open" | "roots"): Promise<{
|
|
396
411
|
ok: true;
|
|
397
412
|
key: string;
|
|
398
413
|
} | {
|
|
@@ -458,7 +473,7 @@ export declare function withinAnyRoot(rootsCanonical: readonly string[], p: stri
|
|
|
458
473
|
*
|
|
459
474
|
* The card names the SANCTIONED way out and nothing else: widening the declaration
|
|
460
475
|
* (`additionalDirectories` / read-only `additionalReadDirectories`), or the deployment-level
|
|
461
|
-
* `readFace: "open"`
|
|
476
|
+
* `readFace: "open"` (reads) / `writeFace: "open"` (writes).
|
|
462
477
|
*
|
|
463
478
|
* It deliberately does NOT name the shell as unconfined by this fence, even though that is a true
|
|
464
479
|
* statement rather than a recommendation. A live retest settled the question: a refused agent read
|
|
@@ -469,7 +484,7 @@ export declare function withinAnyRoot(rootsCanonical: readonly string[], p: stri
|
|
|
469
484
|
* shell is deliberately unconfined by this fence, and every shell call still passes the deployment's
|
|
470
485
|
* approval policy); it is simply not handed to the model inside a denial.
|
|
471
486
|
*/
|
|
472
|
-
export declare const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories \u2014 or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.)";
|
|
487
|
+
export declare const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories \u2014 or additionalReadDirectories for read-only access; a deployment that wants reads or writes open everywhere can declare readFace / writeFace: \"open\" instead of listing directories.)";
|
|
473
488
|
/** inv 1 (read-before-edit): a file must have been read this task before it can be edited/overwritten.
|
|
474
489
|
* Message is CC 2.1.198 live-verbatim (all-tools-live-probe 2026-07-08 §2.1/§3.1/§5.1 — one message for
|
|
475
490
|
* Edit/Write/NotebookEdit: "before writing to it", not the old sema "before editing").
|
package/dist/tools/fs/safety.js
CHANGED
|
@@ -314,6 +314,13 @@ function foldUncLexically(p) {
|
|
|
314
314
|
const anchor = `\\\\${host}${share !== undefined ? `\\${share}` : ""}`;
|
|
315
315
|
return { key: out.length > 0 ? `${anchor}\\${out.join("\\")}` : anchor, climbedAboveShare: climbed };
|
|
316
316
|
}
|
|
317
|
+
export function hasWin32AliasedComponent(p) {
|
|
318
|
+
if (!isWinFormPath(p))
|
|
319
|
+
return false;
|
|
320
|
+
const parts = p.split(/[\\/]+/).filter((seg) => seg.length > 0);
|
|
321
|
+
const tail = p.startsWith("\\\\") || p.startsWith("//") ? parts.slice(2) : parts;
|
|
322
|
+
return tail.some((seg) => seg !== "." && seg !== ".." && /[. ]$/.test(seg));
|
|
323
|
+
}
|
|
317
324
|
export function pathFamilyOf(bases) {
|
|
318
325
|
const tree = bases.root ?? bases.cwd;
|
|
319
326
|
return tree === undefined ? undefined : isWinFormPath(tree) ? "win" : "posix";
|
|
@@ -443,7 +450,7 @@ function within(rootCanonical, p) {
|
|
|
443
450
|
return true;
|
|
444
451
|
return pN.identity.startsWith(rootN.identity.endsWith(SEP) ? rootN.identity : rootN.identity + SEP);
|
|
445
452
|
}
|
|
446
|
-
export async function resolveKey(env, rootCanonical, path, signal, baseCwd, additionalRootsCanonical, exactFileReadExemption, readDeny,
|
|
453
|
+
export async function resolveKey(env, rootCanonical, path, signal, baseCwd, additionalRootsCanonical, exactFileReadExemption, readDeny, containmentFace) {
|
|
447
454
|
const canon = await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootCanonical }), baseCwd);
|
|
448
455
|
if (!canon.ok)
|
|
449
456
|
return { ok: false, violation: { code: "invalid", message: canon.message } };
|
|
@@ -471,13 +478,13 @@ export async function resolveKey(env, rootCanonical, path, signal, baseCwd, addi
|
|
|
471
478
|
};
|
|
472
479
|
}
|
|
473
480
|
}
|
|
474
|
-
if (
|
|
481
|
+
if (containmentFace === "open") {
|
|
475
482
|
if (isUncPath(key) && isWinFormPath(key) && !within(rootCanonical, key) && !additionalRootsCanonical?.some((r) => within(r, key))) {
|
|
476
483
|
return {
|
|
477
484
|
ok: false,
|
|
478
485
|
violation: {
|
|
479
486
|
code: "invalid",
|
|
480
|
-
message: `path "${path}" names a network (UNC) location outside the declared roots; refused even under
|
|
487
|
+
message: `path "${path}" names a network (UNC) location outside the declared roots; refused even under an open containment face — an access against an undeclared share can hang on the SMB mount timeout and offers credentials to the remote host.`,
|
|
481
488
|
},
|
|
482
489
|
};
|
|
483
490
|
}
|
|
@@ -604,7 +611,7 @@ export function violationDetails(v) {
|
|
|
604
611
|
export function withinAnyRoot(rootsCanonical, p) {
|
|
605
612
|
return rootsCanonical.some((r) => within(r, p));
|
|
606
613
|
}
|
|
607
|
-
export const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories — or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.)";
|
|
614
|
+
export const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories — or additionalReadDirectories for read-only access; a deployment that wants reads or writes open everywhere can declare readFace / writeFace: \"open\" instead of listing directories.)";
|
|
608
615
|
export function requireRead(state, key) {
|
|
609
616
|
const entry = state.get(key);
|
|
610
617
|
if (entry === undefined || entry.isPartialView) {
|