@sema-agent/core 7.23.1 → 7.23.3
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 +40 -0
- package/dist/agents/agent-transcript-tool.js +2 -1
- package/dist/agents/cascade.js +2 -1
- package/dist/agents/list-agents-tool.js +2 -1
- package/dist/agents/observer.js +3 -2
- package/dist/agents/peer-session-drain.js +2 -1
- package/dist/agents/retain-ledger.js +2 -1
- package/dist/agents/send-message-tool.js +11 -10
- package/dist/agents/subagent.js +18 -17
- package/dist/agents/teacher.js +5 -4
- package/dist/brain/circuit-breaker.js +2 -1
- package/dist/brain/degrading.js +2 -1
- package/dist/brain/errors.js +24 -15
- package/dist/brain/failover.js +2 -1
- package/dist/brain/stream-engine.js +2 -1
- package/dist/core/a2a.js +4 -3
- package/dist/core/artifact-host.js +2 -1
- package/dist/core/ask-question.js +3 -2
- package/dist/core/auto-promote.js +3 -2
- package/dist/core/gate-bounded-await.js +2 -1
- package/dist/core/gate-fold.js +4 -1
- package/dist/core/gate-lanes.js +4 -2
- package/dist/core/git-worktree-env.js +6 -5
- package/dist/core/hooks.js +4 -4
- package/dist/core/lsp-session.js +3 -2
- package/dist/core/mcp.js +4 -3
- package/dist/core/memory-admission.js +2 -1
- package/dist/core/memory-engine/consolidation-driver.js +5 -4
- package/dist/core/memory-engine/consolidation.js +2 -1
- package/dist/core/memory-engine/delegation-settlement.js +4 -3
- package/dist/core/memory-engine/distiller.js +8 -4
- package/dist/core/memory-engine/engine.js +63 -55
- package/dist/core/memory-engine/file-backend.js +11 -10
- package/dist/core/memory-engine/layout.js +5 -4
- package/dist/core/memory-engine/session-incarnation.js +2 -1
- package/dist/core/memory.js +2 -1
- package/dist/core/oracle-isolation.js +3 -2
- package/dist/core/permission-rule-store.js +2 -1
- package/dist/core/runner/announce-once-ledger.js +2 -1
- package/dist/core/runner/compaction-seams.js +3 -2
- package/dist/core/runner/gate-exit.d.ts +5 -0
- package/dist/core/runner/git-leg-delivery.js +4 -3
- package/dist/core/runner/git-status-frame.js +2 -1
- package/dist/core/runner/mcp-redial.js +2 -1
- package/dist/core/runner/memory-consolidation-driver.js +2 -1
- package/dist/core/runner/permission-rule-lanes.js +3 -2
- package/dist/core/runner/prepare-artifact.js +2 -1
- package/dist/core/runner/prepare-ask-lane.js +2 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
- package/dist/core/runner/prepare-inherited-gate.js +2 -1
- package/dist/core/runner/prepare-memory-engine-session.js +2 -1
- package/dist/core/runner/prepare-memory.js +5 -4
- package/dist/core/runner/prepare-policy-chain.js +3 -2
- package/dist/core/runner/prepare-project-context.js +3 -2
- package/dist/core/runner/prepare-protocol-tools.js +3 -2
- package/dist/core/runner/prepare-suspend-saga.js +2 -1
- package/dist/core/runner/prepare-task.js +6 -5
- package/dist/core/runner/prepare-turn-wiring.js +2 -1
- package/dist/core/runner/resume-apply.js +5 -4
- package/dist/core/runner/resume-preflight.js +3 -2
- package/dist/core/runner/run-git-lane.js +4 -3
- package/dist/core/runner/run-leg.js +4 -3
- package/dist/core/runner/run-recovery-lanes.js +3 -2
- package/dist/core/runner/run-settle-and-teardown.js +2 -1
- package/dist/core/runner/run-stop-and-final-verify.js +2 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.js +2 -1
- package/dist/core/runner/run-terminal-adoption.js +6 -5
- package/dist/core/runner/run-turn-boundary.js +5 -4
- package/dist/core/runner/runtask.js +2 -1
- package/dist/core/runner/stream-settle-backstop.js +5 -4
- package/dist/core/runner/stream-steer-verb.js +2 -1
- package/dist/core/runner/strict-output-schema.js +2 -1
- package/dist/core/runner/teardown-bounded.js +2 -1
- package/dist/core/safe-notify.js +2 -1
- package/dist/core/skills-directory.js +2 -1
- package/dist/core/strategy-store.js +2 -1
- package/dist/core/task-registry-agent.js +2 -1
- package/dist/core/task-registry-monitor.js +2 -1
- package/dist/core/task-registry-workflow.js +2 -1
- package/dist/core/task-registry.js +3 -2
- package/dist/core/thrown-value.d.ts +79 -0
- package/dist/core/thrown-value.js +56 -0
- package/dist/core/tool-errors.d.ts +0 -6
- package/dist/core/tool-errors.js +36 -14
- package/dist/core/tool-model-gate.js +2 -1
- package/dist/core/tool-policy.js +6 -13
- package/dist/core/tool-result-store.js +2 -1
- package/dist/core/tool-spec.d.ts +12 -0
- package/dist/core/write-protect.js +3 -2
- package/dist/engine/compaction/compaction.js +3 -4
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.js +8 -7
- package/dist/engine/loop/agent-loop.js +7 -6
- package/dist/engine/lsp/node-lsp-manager.js +5 -4
- package/dist/engine/lsp/stdio-lsp-transport.js +12 -5
- package/dist/orchestration/dev-vm-script-runner.js +2 -1
- package/dist/orchestration/run-workflow-tool.js +7 -6
- package/dist/orchestration/workflow-governance.js +2 -1
- package/dist/orchestration/workflow-sandbox-conformance.js +3 -2
- package/dist/orchestration/workflow-script-store.js +2 -1
- package/dist/orchestration/workflow.js +15 -14
- package/dist/server/http.js +3 -2
- package/dist/stores/file/adoption/adopt.js +3 -2
- package/dist/stores/file/fs-atomic.js +2 -1
- package/dist/stores/file/permission-rule-adopt.js +2 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/stores/file/session-store.js +4 -3
- package/dist/stores/file/strategy-store.js +7 -6
- package/dist/tools/artifact/artifact-tool.js +3 -2
- package/dist/tools/artifact/conformance.js +4 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +20 -9
- package/dist/tools/fs/bash-readonly-classifier.js +7 -3
- package/dist/tools/fs/fs-bash.d.ts +16 -10
- package/dist/tools/fs/fs-bash.js +84 -35
- package/dist/tools/fs/fs-write.js +3 -2
- package/dist/tools/fs/safety.js +2 -1
- package/dist/tools/fs/search.js +3 -2
- package/dist/tools/monitor.js +3 -2
- package/dist/tools/sql.js +2 -1
- package/dist/tools/web.js +5 -15
- package/dist/tools/worktree.js +12 -9
- package/package.json +2 -1
- package/wire-changes/core@7.23.2.json +42 -0
- package/wire-changes/core@7.23.3.json +25 -0
package/dist/tools/fs/fs-bash.js
CHANGED
|
@@ -8,7 +8,7 @@ import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_MISSING_ID_MESS
|
|
|
8
8
|
import { hasBackgroundShell } from "../../core/background-shell.js";
|
|
9
9
|
import { delimitUntrusted } from "../../core/untrusted-text.js";
|
|
10
10
|
import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js";
|
|
11
|
-
import { imageMagicMatches, normalizeAbsPathLexically, withinAnyRoot } from "./safety.js";
|
|
11
|
+
import { imageMagicMatches, normalizeAbsPathLexically, violationDetails, withinAnyRoot } from "./safety.js";
|
|
12
12
|
import { boundaryIsTheSandbox, canonicalPathIsAuthoritative, hasDestroy, isIsolated } from "../../core/remote-env.js";
|
|
13
13
|
import { ghRateLimitHint } from "./gh-rate-limit.js";
|
|
14
14
|
import { resolveBashTimeoutCaps, bashTimeoutCapsSec, bashTimeoutArgRefusal, bashTimeoutParamDescription, envErrorDetail, bashMaxOutputChars, clipShellOutput, writeShellOverflowFile, createShellOverflowSpoolFence, shellRecoveryHint, CWD_SENTINEL, stripCwdSentinel, BASH_READONLY_CONFINEMENT_NOTE, } from "./fs-shared.js";
|
|
@@ -16,6 +16,7 @@ import { PROBE_CAUSE_PATH_MAX, inlineUntrusted } from "../../core/untrusted-text
|
|
|
16
16
|
import { bashClassifyDefaultAllow, BASH_READONLY_DEFAULT_ALLOW, coarseReadonlyCheck, classifyBoundedReadonlyPollLoopDetailed, classifyCompoundReadonlyDetailed, classifyOutOfRootReadGate, refusedCommandMayRead, classifySimpleCommandReadBoundary, } from "./bash-readonly-classifier.js";
|
|
17
17
|
import { NOT_AUTO_ALLOWED, SHELL_SCAN_DECLINES } from "./bash-program-position.js";
|
|
18
18
|
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
19
|
+
import { thrownText } from "../../core/thrown-value.js";
|
|
19
20
|
const RECURSIVE_CAUSE_MAX_PATHS = 3;
|
|
20
21
|
const RECURSIVE_READ_CAUSE_CODE = "shell.recursive_read_unbounded";
|
|
21
22
|
function operandFamily(paths) {
|
|
@@ -47,7 +48,8 @@ export function bashReversibilityProbe(allow, boundary) {
|
|
|
47
48
|
if (loop.reason === undefined)
|
|
48
49
|
return { reversible: true };
|
|
49
50
|
const seatOffer = (verdict) => (offers ? readRootCandidateOf(verdict) : {});
|
|
50
|
-
|
|
51
|
+
const loopScan = refusedCommandScan(command, allowSet, resolved, loop, CLASSIFY_SEAT_DECLINE_EVIDENCE);
|
|
52
|
+
if (boundaryDeclared(loop) || unjudgedRead(command, allowSet, resolved, loop, loopScan))
|
|
51
53
|
return { reversible: false, mandated: true, ...seatOffer(loop) };
|
|
52
54
|
const refusedCause = recursiveReadCause(detailed);
|
|
53
55
|
return { reversible: false, ...boundaryGate(detailed), ...seatOffer(detailed), ...(refusedCause !== undefined ? { cause: refusedCause } : {}) };
|
|
@@ -92,20 +94,29 @@ export const CLASSIFY_SEAT_DECLINE_EVIDENCE = new Set();
|
|
|
92
94
|
function readBoundaryMandate(command, allowSet, resolved, verdict, declineEvidence) {
|
|
93
95
|
if (resolved === undefined)
|
|
94
96
|
return {};
|
|
95
|
-
|
|
97
|
+
const scan = refusedCommandScan(command, allowSet, resolved, verdict, declineEvidence);
|
|
98
|
+
if (denyJudgeSpoke(verdict) || unjudgedRead(command, allowSet, resolved, verdict, scan))
|
|
96
99
|
return { mandated: true };
|
|
97
100
|
const named = classifyOutOfRootReadGate(command, allowSet, resolved);
|
|
98
|
-
|
|
101
|
+
if (named.gated || named.denied === true)
|
|
102
|
+
return { mandated: true };
|
|
103
|
+
return declinedRung(scan);
|
|
104
|
+
}
|
|
105
|
+
function declinedRung(scan) {
|
|
106
|
+
return scan.declined ? { boundaryDeclined: true } : {};
|
|
107
|
+
}
|
|
108
|
+
function refusedCommandScan(command, allowSet, resolved, verdict, declineEvidence) {
|
|
109
|
+
return verdict.refusedWhole === true ? refusedCommandMayRead(command, allowSet, resolved, declineEvidence) : { mayRead: false, declined: false };
|
|
99
110
|
}
|
|
100
111
|
function boundaryUnjudged(verdict) {
|
|
101
112
|
return verdict.refusedWhole === true || (verdict.unresolvedOperands !== undefined && verdict.unresolvedOperands.length > 0);
|
|
102
113
|
}
|
|
103
|
-
function unjudgedRead(command, allowSet, resolved, verdict,
|
|
114
|
+
function unjudgedRead(command, allowSet, resolved, verdict, scan) {
|
|
104
115
|
if (verdict.unresolvedOperands !== undefined && verdict.unresolvedOperands.length > 0)
|
|
105
116
|
return true;
|
|
106
117
|
if (verdict.refusedWhole !== true)
|
|
107
118
|
return false;
|
|
108
|
-
if (
|
|
119
|
+
if (scan.mayRead)
|
|
109
120
|
return true;
|
|
110
121
|
if (resolved === undefined)
|
|
111
122
|
return false;
|
|
@@ -128,7 +139,7 @@ export function bashReadBoundaryProbe(boundary) {
|
|
|
128
139
|
const allowSet = new Set(bashClassifyDefaultAllow());
|
|
129
140
|
const judge = (command, resolved, offers) => {
|
|
130
141
|
const seatVerdict = (verdict) => {
|
|
131
|
-
if (boundaryDeclared(verdict) || unjudgedRead(command, allowSet, resolved, verdict, BOUNDARY_SEAT_DECLINE_EVIDENCE)) {
|
|
142
|
+
if (boundaryDeclared(verdict) || unjudgedRead(command, allowSet, resolved, verdict, refusedCommandScan(command, allowSet, resolved, verdict, BOUNDARY_SEAT_DECLINE_EVIDENCE))) {
|
|
132
143
|
const cause = recursiveReadCause(verdict);
|
|
133
144
|
return { reversible: false, mandated: true, ...(cause !== undefined ? { cause } : {}) };
|
|
134
145
|
}
|
|
@@ -726,7 +737,7 @@ async function adoptBackgroundShell(req) {
|
|
|
726
737
|
return { ok: true, taskId, outputFile };
|
|
727
738
|
}
|
|
728
739
|
catch (e) {
|
|
729
|
-
const reason =
|
|
740
|
+
const reason = thrownText(e);
|
|
730
741
|
let kill;
|
|
731
742
|
try {
|
|
732
743
|
kill = await env.killBackground(shellId);
|
|
@@ -761,7 +772,10 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
|
|
|
761
772
|
};
|
|
762
773
|
const canonicalEscape = async (command, signal) => {
|
|
763
774
|
const resolvedBoundary = typeof taskOpts.readBoundary === "function" ? taskOpts.readBoundary() : taskOpts.readBoundary;
|
|
764
|
-
if (resolvedBoundary === undefined
|
|
775
|
+
if (resolvedBoundary === undefined)
|
|
776
|
+
return undefined;
|
|
777
|
+
const ask = { deny: resolvedBoundary.denyMatch, containment: resolvedBoundary.face !== "open" };
|
|
778
|
+
if (ask.deny === undefined && !ask.containment)
|
|
765
779
|
return undefined;
|
|
766
780
|
const faces = [
|
|
767
781
|
classifyCompoundReadonlyDetailed(command, recheckReaders, resolvedBoundary),
|
|
@@ -770,7 +784,7 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
|
|
|
770
784
|
const literal = [...new Set(faces.flatMap((v) => [...(v.checkedPaths ?? []), ...(v.checkedSpellings ?? [])]))];
|
|
771
785
|
const asked = new Set(faces.flatMap((v) => v.outOfRootPaths ?? []));
|
|
772
786
|
const patterns = [...new Set(faces.flatMap((v) => [...(v.undecidedPaths ?? []), ...(v.undecidedSpellings ?? [])]))].filter((p) => !asked.has(normalizeAbsPathLexically(p)));
|
|
773
|
-
return await canonicalEscapeRefusal(canonicalBoundaryFor(resolvedBoundary.roots), { literal, patterns }, resolvedBoundary.roots, FULL_SHELL_FACE_ESCAPE_PROSE, signal);
|
|
787
|
+
return await canonicalEscapeRefusal(canonicalBoundaryFor(resolvedBoundary.roots), { literal, patterns }, resolvedBoundary.roots, FULL_SHELL_FACE_ESCAPE_PROSE, ask, signal);
|
|
774
788
|
};
|
|
775
789
|
return defineTool({
|
|
776
790
|
name: "Bash",
|
|
@@ -1178,6 +1192,7 @@ function createCanonicalReadBoundary(env, readRoots) {
|
|
|
1178
1192
|
let frontier = [p.slice(0, segs[first].start).replace(/[\\/]+$/, "") || "/"];
|
|
1179
1193
|
let dirsUsed = 0;
|
|
1180
1194
|
let matches = 0;
|
|
1195
|
+
const canonicalOfMatch = new Map();
|
|
1181
1196
|
for (let j = first; j < segs.length; j++) {
|
|
1182
1197
|
const seg = segs[j].text;
|
|
1183
1198
|
if (!GLOB_METACHAR_RE.test(seg)) {
|
|
@@ -1211,40 +1226,48 @@ function createCanonicalReadBoundary(env, readRoots) {
|
|
|
1211
1226
|
if (re.test(dot))
|
|
1212
1227
|
next.push(joinCandidate(dir, dot));
|
|
1213
1228
|
}
|
|
1214
|
-
|
|
1215
|
-
if (isLast) {
|
|
1216
|
-
if (canonDir !== undefined && !withinAnyRoot(roots, canonDir.value)) {
|
|
1217
|
-
next.push(canonDir.value);
|
|
1218
|
-
continue;
|
|
1219
|
-
}
|
|
1220
|
-
dirInside = canonDir !== undefined;
|
|
1221
|
-
}
|
|
1229
|
+
const canonDirHere = isLast ? canonDir?.value : undefined;
|
|
1222
1230
|
for (const entry of listed.value) {
|
|
1223
1231
|
if (!globSegmentMatches(re, seg, entry.name))
|
|
1224
1232
|
continue;
|
|
1225
1233
|
if (++matches > MAX_GLOB_MATCHES) {
|
|
1226
1234
|
return { unverifiable: `"${p}" expands to more than ${MAX_GLOB_MATCHES} names, too many to enumerate to verify` };
|
|
1227
1235
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1236
|
+
const candidate = joinCandidate(dir, entry.name);
|
|
1237
|
+
if (canonDirHere !== undefined && entry.kind !== "symlink")
|
|
1238
|
+
canonicalOfMatch.set(candidate, joinCandidate(canonDirHere, entry.name));
|
|
1239
|
+
next.push(candidate);
|
|
1231
1240
|
}
|
|
1232
1241
|
}
|
|
1233
1242
|
frontier = next;
|
|
1234
1243
|
if (frontier.length === 0)
|
|
1235
|
-
return { paths: [] };
|
|
1244
|
+
return { paths: [], canonical: canonicalOfMatch };
|
|
1236
1245
|
}
|
|
1237
|
-
return { paths: frontier };
|
|
1246
|
+
return { paths: frontier, canonical: canonicalOfMatch };
|
|
1238
1247
|
};
|
|
1239
1248
|
return {
|
|
1240
|
-
escapesAfterResolution: async (paths, signal) => {
|
|
1249
|
+
escapesAfterResolution: async (paths, ask, signal) => {
|
|
1250
|
+
const deny = ask.deny;
|
|
1241
1251
|
if (lexicalOnly || (paths.literal.length === 0 && paths.patterns.length === 0))
|
|
1242
1252
|
return { escaping: [] };
|
|
1243
1253
|
const roots = await canonicalRoots(signal);
|
|
1244
1254
|
const escaping = [];
|
|
1245
1255
|
let unresolvable;
|
|
1246
|
-
|
|
1247
|
-
|
|
1256
|
+
let denied;
|
|
1257
|
+
const denyHit = (p) => {
|
|
1258
|
+
if (deny === undefined || denied !== undefined)
|
|
1259
|
+
return false;
|
|
1260
|
+
const folded = normalizeAbsPathLexically(p);
|
|
1261
|
+
const pattern = deny(folded);
|
|
1262
|
+
if (pattern === null)
|
|
1263
|
+
return false;
|
|
1264
|
+
denied = { path: folded, pattern };
|
|
1265
|
+
return true;
|
|
1266
|
+
};
|
|
1267
|
+
const judge = async (p, canonicalKnown) => {
|
|
1268
|
+
if (denyHit(p))
|
|
1269
|
+
return;
|
|
1270
|
+
const real = canonicalKnown !== undefined ? { value: canonicalKnown, exact: true } : await resolve(p, signal);
|
|
1248
1271
|
if (real === undefined) {
|
|
1249
1272
|
if (resolverIsAuthoritative)
|
|
1250
1273
|
unresolvable ??= `"${p}" cannot be resolved to verify where it reads`;
|
|
@@ -1254,18 +1277,36 @@ function createCanonicalReadBoundary(env, readRoots) {
|
|
|
1254
1277
|
unresolvable ??= `"${p}" cannot be resolved to verify where it reads`;
|
|
1255
1278
|
return;
|
|
1256
1279
|
}
|
|
1257
|
-
if (
|
|
1280
|
+
if (denyHit(real.value))
|
|
1281
|
+
return;
|
|
1282
|
+
if (ask.containment && !withinAnyRoot(roots, real.value) && !escaping.includes(real.value))
|
|
1258
1283
|
escaping.push(real.value);
|
|
1259
1284
|
};
|
|
1260
|
-
for (const p of paths.literal)
|
|
1285
|
+
for (const p of paths.literal) {
|
|
1286
|
+
if (denied !== undefined)
|
|
1287
|
+
break;
|
|
1261
1288
|
await judge(p);
|
|
1289
|
+
}
|
|
1262
1290
|
for (const p of paths.patterns) {
|
|
1291
|
+
if (denied !== undefined)
|
|
1292
|
+
break;
|
|
1263
1293
|
const expanded = await expandGlobCandidate(p, roots, signal);
|
|
1264
|
-
if (expanded !== undefined && "unverifiable" in expanded)
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1294
|
+
if (expanded !== undefined && "unverifiable" in expanded) {
|
|
1295
|
+
unresolvable ??= expanded.unverifiable;
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
if (expanded !== undefined && expanded.paths.length > 0) {
|
|
1299
|
+
for (const t of expanded.paths) {
|
|
1300
|
+
if (denied !== undefined)
|
|
1301
|
+
break;
|
|
1302
|
+
await judge(t, expanded.canonical.get(t));
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
else
|
|
1306
|
+
await judge(p);
|
|
1268
1307
|
}
|
|
1308
|
+
if (denied !== undefined)
|
|
1309
|
+
return { escaping, denied };
|
|
1269
1310
|
return unresolvable === undefined ? { escaping } : { escaping, unverifiable: unresolvable };
|
|
1270
1311
|
},
|
|
1271
1312
|
canonicalOf: async (path, signal) => {
|
|
@@ -1289,8 +1330,14 @@ function createCanonicalReadBoundary(env, readRoots) {
|
|
|
1289
1330
|
},
|
|
1290
1331
|
};
|
|
1291
1332
|
}
|
|
1292
|
-
async function canonicalEscapeRefusal(canonical, candidates, readRoots, face, signal) {
|
|
1293
|
-
const resolved = await canonical.escapesAfterResolution(candidates, signal);
|
|
1333
|
+
async function canonicalEscapeRefusal(canonical, candidates, readRoots, face, ask, signal) {
|
|
1334
|
+
const resolved = await canonical.escapesAfterResolution(candidates, ask, signal);
|
|
1335
|
+
if (resolved.denied !== undefined) {
|
|
1336
|
+
const content = `Error (Bash): reading "${resolved.denied.path}" is refused: the target matches the sensitive-path ` +
|
|
1337
|
+
`read deny list (pattern "${resolved.denied.pattern}"). This list guards credential-class paths and ` +
|
|
1338
|
+
`applies regardless of the containment roots. ${DENY_REFUSAL_NOTE}`;
|
|
1339
|
+
return errorResult(content, violationDetails({ code: "read_path_denied", message: content, target: resolved.denied.path, pattern: resolved.denied.pattern }));
|
|
1340
|
+
}
|
|
1294
1341
|
if (resolved.unverifiable !== undefined) {
|
|
1295
1342
|
return errorResult(`Error (Bash): ${resolved.unverifiable}, so this command cannot be confirmed to read only inside the allowed directories for this session: ${readRoots.join(", ")}. ` +
|
|
1296
1343
|
face.note, { type: "readonly_out_of_root", code: "readonly_out_of_root", paths: [] });
|
|
@@ -1302,6 +1349,8 @@ async function canonicalEscapeRefusal(canonical, candidates, readRoots, face, si
|
|
|
1302
1349
|
}
|
|
1303
1350
|
return undefined;
|
|
1304
1351
|
}
|
|
1352
|
+
const DENY_REFUSAL_NOTE = "The read boundary judged this command's written spelling before the call was approved; what the shell " +
|
|
1353
|
+
"would actually read was established only at execution, and a deny-listed read is refused here rather than escalated.";
|
|
1305
1354
|
const READONLY_FACE_ESCAPE_PROSE = {
|
|
1306
1355
|
subject: "a path this command reads",
|
|
1307
1356
|
verdictPhrase: ` ${NOT_AUTO_ALLOWED}`,
|
|
@@ -1365,7 +1414,7 @@ export function createBashReadonlyTool(env, rootCanonical, allow, opts) {
|
|
|
1365
1414
|
return errorResult(`Error (Bash): ${boundary.reason}. ${BASH_READONLY_CONFINEMENT_NOTE}`, { type: "readonly_out_of_root", code: "readonly_out_of_root", paths: boundary.outOfRootPaths ?? [] });
|
|
1366
1415
|
}
|
|
1367
1416
|
}
|
|
1368
|
-
const escape = await canonicalEscapeRefusal(canonicalBoundary, { literal: [...(boundary.checkedPaths ?? []), ...(boundary.checkedSpellings ?? [])], patterns: [...(boundary.undecidedPaths ?? []), ...(boundary.undecidedSpellings ?? [])] }, readRoots, READONLY_FACE_ESCAPE_PROSE, ctx.signal);
|
|
1417
|
+
const escape = await canonicalEscapeRefusal(canonicalBoundary, { literal: [...(boundary.checkedPaths ?? []), ...(boundary.checkedSpellings ?? [])], patterns: [...(boundary.undecidedPaths ?? []), ...(boundary.undecidedSpellings ?? [])] }, readRoots, READONLY_FACE_ESCAPE_PROSE, { deny: undefined, containment: true }, ctx.signal);
|
|
1369
1418
|
if (escape !== undefined)
|
|
1370
1419
|
return escape;
|
|
1371
1420
|
const readOnlyTick = opts?.onToolProgress === undefined ? undefined : (t) => describeSafely(opts.onToolProgress, { toolCallId: ctx.toolCallId, toolName: "Bash", ...t });
|
|
@@ -5,6 +5,7 @@ import { sha256, resolveKey, violationText, violationDetails, requireRead, check
|
|
|
5
5
|
import { decodeTextBytes, encodeTextForFile, normalizeEditText, normalizeFileText } from "./encoding.js";
|
|
6
6
|
import { MAX_EDIT_BYTES, decodeEditBytes, tooLargeToEditMessage, truncatedUtf16BodyMessage, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, ipynbRedirect, countLines, applyRecordedEdit, } from "./fs-shared.js";
|
|
7
7
|
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
8
|
+
import { thrownText } from "../../core/thrown-value.js";
|
|
8
9
|
async function envFinalWrite(env, key, content, signal, opts) {
|
|
9
10
|
const fireEdited = () => {
|
|
10
11
|
if (opts?.edited?.hook === undefined)
|
|
@@ -66,7 +67,7 @@ async function trackToolEdit(hook, tool, path, key, signal) {
|
|
|
66
67
|
res = await hook({ tool, path, key, ...(signal !== undefined ? { signal } : {}) });
|
|
67
68
|
}
|
|
68
69
|
catch (err) {
|
|
69
|
-
return { refusal: `Error (${tool}): the file-history first-touch backup failed (${clipHookText(
|
|
70
|
+
return { refusal: `Error (${tool}): the file-history first-touch backup failed (${clipHookText(thrownText(err))}) — nothing was written.` };
|
|
70
71
|
}
|
|
71
72
|
if (res.ok === false)
|
|
72
73
|
return { refusal: clipHookText(res.refusal) };
|
|
@@ -80,7 +81,7 @@ async function gateToolWrite(hook, tool, path, key, content) {
|
|
|
80
81
|
res = await hook({ tool, path, key, content });
|
|
81
82
|
}
|
|
82
83
|
catch (err) {
|
|
83
|
-
return `Error (${tool}): the write gate failed (${clipHookText(
|
|
84
|
+
return `Error (${tool}): the write gate failed (${clipHookText(thrownText(err))}) — nothing was written.`;
|
|
84
85
|
}
|
|
85
86
|
if (res !== undefined && res.ok === false)
|
|
86
87
|
return `Error (${tool}): write rejected by the write gate (${res.code}): ${clipHookText(res.reason)}`;
|
package/dist/tools/fs/safety.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
|
|
3
3
|
import { pathTargetValue, protectivePathTargetOf } from "../../core/tool-registry.js";
|
|
4
4
|
import { withGateTimeout, DEFAULT_GATE_AWAIT_TIMEOUT_MS } from "../../core/gate-bounded-await.js";
|
|
5
|
+
import { thrownText } from "../../core/thrown-value.js";
|
|
5
6
|
export function fileArgPath(args) {
|
|
6
7
|
const a = args;
|
|
7
8
|
const fp = a?.file_path;
|
|
@@ -524,7 +525,7 @@ export async function canonicalizeTarget(env, path, signal, family, baseCwd) {
|
|
|
524
525
|
catch (err) {
|
|
525
526
|
if (seenThrow && err === thrownByProbe)
|
|
526
527
|
throw err;
|
|
527
|
-
return { ok: false, message: `cannot resolve path "${path}": ${
|
|
528
|
+
return { ok: false, message: `cannot resolve path "${path}": ${thrownText(err)}` };
|
|
528
529
|
}
|
|
529
530
|
}
|
|
530
531
|
async function probeRealTarget(env, path, target, family, signal) {
|
package/dist/tools/fs/search.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { delimitUntrusted } from "../../core/untrusted-text.js";
|
|
2
2
|
import { MAX_EXEC_OUTPUT_BYTES } from "../../core/exec-output-tail.js";
|
|
3
3
|
import { hasBinaryExtension, isAbsoluteForFamily, isWinFormPath, joinForFamily, pathFamilyOf } from "./safety.js";
|
|
4
|
+
import { thrownText } from "../../core/thrown-value.js";
|
|
4
5
|
const WALK_MAX_FILES = 5000;
|
|
5
6
|
const WALK_MAX_DEPTH = 32;
|
|
6
7
|
const GREP_DEFAULT_CAP = 250;
|
|
@@ -772,7 +773,7 @@ export async function jsGrepDetailed(env, root, p, signal, guards, deny, denyOut
|
|
|
772
773
|
test = compilePattern(p);
|
|
773
774
|
}
|
|
774
775
|
catch (e) {
|
|
775
|
-
return { text: `Error (grep): invalid regex: ${
|
|
776
|
+
return { text: `Error (grep): invalid regex: ${thrownText(e)}` };
|
|
776
777
|
}
|
|
777
778
|
const ignore = await buildIgnore(env, root, signal);
|
|
778
779
|
const rootPrefix = root.replace(/[\\/]+$/, "") + (root.includes("\\") ? "\\" : "/");
|
|
@@ -912,7 +913,7 @@ export async function jsGrepDetailed(env, root, p, signal, guards, deny, denyOut
|
|
|
912
913
|
catch (e) {
|
|
913
914
|
if (e instanceof GrepBudgetExceeded)
|
|
914
915
|
return { text: budgetError(shownPath, monotonicNow() - fileMatchStart) };
|
|
915
|
-
return { text: `Error (grep): invalid regex: ${
|
|
916
|
+
return { text: `Error (grep): invalid regex: ${thrownText(e)}` };
|
|
916
917
|
}
|
|
917
918
|
fileCount = spans.length;
|
|
918
919
|
if (mode === "content") {
|
package/dist/tools/monitor.js
CHANGED
|
@@ -3,6 +3,7 @@ import { defineTool, errorResult } from "../core/tools.js";
|
|
|
3
3
|
import { hasBackgroundShell } from "../core/background-shell.js";
|
|
4
4
|
import { MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, } from "../core/task-registry.js";
|
|
5
5
|
import { toolFace } from "../core/tool-catalog-entries.js";
|
|
6
|
+
import { thrownText } from "../core/thrown-value.js";
|
|
6
7
|
const MONITOR_DESCRIPTION = "Run a shell command in the background and watch its stdout as a stream of events.\n" +
|
|
7
8
|
"\n" +
|
|
8
9
|
"- Each line the command writes to stdout becomes a notification event delivered to you between turns; " +
|
|
@@ -98,7 +99,7 @@ export function createMonitorTool(env, opts) {
|
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
catch (e) {
|
|
101
|
-
const why =
|
|
102
|
+
const why = thrownText(e);
|
|
102
103
|
let killDetail;
|
|
103
104
|
try {
|
|
104
105
|
const kill = await env.killBackground(spawn.value.shellId);
|
|
@@ -106,7 +107,7 @@ export function createMonitorTool(env, opts) {
|
|
|
106
107
|
killDetail = `${kill.error.code}: ${kill.error.message}`;
|
|
107
108
|
}
|
|
108
109
|
catch (killErr) {
|
|
109
|
-
killDetail =
|
|
110
|
+
killDetail = thrownText(killErr);
|
|
110
111
|
}
|
|
111
112
|
return errorResult(killDetail === undefined
|
|
112
113
|
? `Error (Monitor): the watch process was started but could not be registered (${why}); it has been terminated.`
|
package/dist/tools/sql.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
+
import { thrownText } from "../core/thrown-value.js";
|
|
2
3
|
const FORBIDDEN = /\b(insert|update|delete|drop|alter|create|truncate|grant|revoke|merge|replace|attach|detach|pragma|vacuum|exec|execute|call|copy|lock|reindex)\b/i;
|
|
3
4
|
class SqlValidationError extends Error {
|
|
4
5
|
}
|
|
@@ -82,7 +83,7 @@ export function createSqlTool(opts) {
|
|
|
82
83
|
validateReadOnlySql(sql, opts.allowTables);
|
|
83
84
|
}
|
|
84
85
|
catch (e) {
|
|
85
|
-
const msg =
|
|
86
|
+
const msg = thrownText(e);
|
|
86
87
|
return { content: `SQL rejected: ${msg}`, details: { error: msg }, terminate: false };
|
|
87
88
|
}
|
|
88
89
|
const rows = await opts.query(stripTrailingSemicolon(sql.trim()));
|
package/dist/tools/web.js
CHANGED
|
@@ -8,6 +8,7 @@ import { DEFAULT_CHARS_PER_TOKEN } from "../internal/harness.js";
|
|
|
8
8
|
import { binaryMagicFormat } from "./fs/safety.js";
|
|
9
9
|
import { pdfMagicMatches } from "./fs/pdf.js";
|
|
10
10
|
import { toolFace } from "../core/tool-catalog-entries.js";
|
|
11
|
+
import { thrownText } from "../core/thrown-value.js";
|
|
11
12
|
const MAX_REDIRECTS = 10;
|
|
12
13
|
const MAX_URL_LENGTH = 2048;
|
|
13
14
|
const WEBFETCH_ACCEPT = "text/markdown, text/html, */*";
|
|
@@ -447,7 +448,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
447
448
|
};
|
|
448
449
|
}
|
|
449
450
|
return {
|
|
450
|
-
content: `Error (WebFetch): request failed: ${
|
|
451
|
+
content: `Error (WebFetch): request failed: ${thrownText(e)}`,
|
|
451
452
|
details: errDetails,
|
|
452
453
|
isError: true,
|
|
453
454
|
};
|
|
@@ -594,7 +595,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
594
595
|
catch (e) {
|
|
595
596
|
out = text;
|
|
596
597
|
note =
|
|
597
|
-
`[note: summarization failed (${inlineUntrusted(
|
|
598
|
+
`[note: summarization failed (${inlineUntrusted(thrownText(e), 160)}) — ` +
|
|
598
599
|
`raw page content follows; the requested analysis ("${inlineUntrusted(prompt, 120)}") was NOT applied]`;
|
|
599
600
|
}
|
|
600
601
|
}
|
|
@@ -910,7 +911,7 @@ export function createWebSearchTool(config) {
|
|
|
910
911
|
return abortedFrame();
|
|
911
912
|
if (ac.signal.aborted)
|
|
912
913
|
return timedOutFrame();
|
|
913
|
-
const msg = redactSecrets(
|
|
914
|
+
const msg = redactSecrets(thrownText(e)).trim();
|
|
914
915
|
const headline = "Error (WebSearch): the search backend failed.";
|
|
915
916
|
const verdict = classifySearchFailure(msg);
|
|
916
917
|
const composed = (msg ? `${headline} The backend's error text follows:\n\n${delimitUntrusted("WebSearch backend error", msg, SEARCH_ERROR_EXCERPT_CHARS)}` : headline) + `\n\n${verdict.hint}`;
|
|
@@ -1029,18 +1030,7 @@ export async function probeSearchBackend(search, options) {
|
|
|
1029
1030
|
return { ok: true, results: results.length };
|
|
1030
1031
|
}
|
|
1031
1032
|
catch (e) {
|
|
1032
|
-
|
|
1033
|
-
if (e instanceof Error)
|
|
1034
|
-
text = e.message;
|
|
1035
|
-
else {
|
|
1036
|
-
try {
|
|
1037
|
-
text = String(e);
|
|
1038
|
-
}
|
|
1039
|
-
catch {
|
|
1040
|
-
text = Object.prototype.toString.call(e);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
return { ok: false, error: text };
|
|
1033
|
+
return { ok: false, error: thrownText(e) };
|
|
1044
1034
|
}
|
|
1045
1035
|
finally {
|
|
1046
1036
|
clearTimeout(timer);
|
package/dist/tools/worktree.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { defineTool, errorResult } from "../core/tools.js";
|
|
4
4
|
import { addWorktree, pruneWorktrees, WORKTREE_PARENT } from "../core/git-worktree-env.js";
|
|
5
5
|
import { toolFace } from "../core/tool-catalog-entries.js";
|
|
6
|
+
import { thrownText } from "../core/thrown-value.js";
|
|
6
7
|
const ENTER_DESCRIPTION = "Use this tool ONLY when explicitly instructed to work in a worktree — either by the user directly, or by " +
|
|
7
8
|
"project instructions. This tool creates an isolated git worktree and switches the current session into it.\n" +
|
|
8
9
|
"\n" +
|
|
@@ -67,14 +68,16 @@ const EXIT_DESCRIPTION = "Exit a worktree session created by EnterWorktree and r
|
|
|
67
68
|
"- Once exited, EnterWorktree can be called again to create a fresh worktree.";
|
|
68
69
|
const shq = (s) => `'${s.replace(/'/g, "'\\''")}'`;
|
|
69
70
|
function reasonOf(e) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
try {
|
|
72
|
+
if (typeof e === "object" && e !== null && !(e instanceof Error)) {
|
|
73
|
+
const m = e.message;
|
|
74
|
+
if (typeof m === "string" && m !== "")
|
|
75
|
+
return m;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
76
79
|
}
|
|
77
|
-
return
|
|
80
|
+
return thrownText(e);
|
|
78
81
|
}
|
|
79
82
|
async function resolveBaseCommit(env, repoRoot, baseRef) {
|
|
80
83
|
const rev = async (ref) => {
|
|
@@ -147,7 +150,7 @@ export function createWorktreeTools(env, opts) {
|
|
|
147
150
|
try {
|
|
148
151
|
const probe = await env.exec("git rev-parse --is-inside-work-tree", { cwd: repoRoot });
|
|
149
152
|
if (!probe.ok || probe.value.exitCode !== 0) {
|
|
150
|
-
const detail = probe.ok ? (probe.value.stderr || probe.value.stdout).trim().split("\n")[0] :
|
|
153
|
+
const detail = probe.ok ? (probe.value.stderr || probe.value.stdout).trim().split("\n")[0] : thrownText(probe.error);
|
|
151
154
|
return errorResult(`Error (EnterWorktree): the working directory ${repoRoot} is not a git repository` +
|
|
152
155
|
`${detail ? ` (${detail})` : ""}. This deployment has no VCS-agnostic worktree extension point, ` +
|
|
153
156
|
"so worktree isolation is unavailable here.");
|
|
@@ -214,7 +217,7 @@ export function createWorktreeTools(env, opts) {
|
|
|
214
217
|
createdDir = worktreeDir;
|
|
215
218
|
}
|
|
216
219
|
catch (e) {
|
|
217
|
-
return errorResult(`Error (EnterWorktree): ${
|
|
220
|
+
return errorResult(`Error (EnterWorktree): ${thrownText(e)}`);
|
|
218
221
|
}
|
|
219
222
|
session.current = {
|
|
220
223
|
worktreeDir: createdDir,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/core",
|
|
3
|
-
"version": "7.23.
|
|
3
|
+
"version": "7.23.3",
|
|
4
4
|
"description": "Stateless, task-oriented AI agent core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"gate:field-liveness": "node scripts/verify-field-liveness.mjs",
|
|
71
71
|
"gate:debts": "node scripts/verify-debts.mjs",
|
|
72
72
|
"handoff:diff": "node scripts/dist-handoff-diff.mjs",
|
|
73
|
+
"post-ship": "node scripts/post-ship.mjs",
|
|
73
74
|
"gate:debts:rc": "node scripts/verify-debts.mjs --rc-gate",
|
|
74
75
|
"gate:error-surface": "node scripts/verify-error-surface.mjs",
|
|
75
76
|
"gate:criteria": "node scripts/criteria-lint.mjs",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "core",
|
|
3
|
+
"version": "7.23.2",
|
|
4
|
+
"from": "e9bf8451c623a118a6589a6f860bd7b828c3aa3e",
|
|
5
|
+
"to": "v7.23.2",
|
|
6
|
+
"added": [],
|
|
7
|
+
"removed": [],
|
|
8
|
+
"changed": [],
|
|
9
|
+
"narrowed": [],
|
|
10
|
+
"closedSetMembers": [],
|
|
11
|
+
"closedSetsUnreadable": [],
|
|
12
|
+
"typeKeys": [
|
|
13
|
+
{
|
|
14
|
+
"type": "ReversibilityVerdict",
|
|
15
|
+
"at": [
|
|
16
|
+
"src/core/tool-spec.ts:50"
|
|
17
|
+
],
|
|
18
|
+
"added": [
|
|
19
|
+
"boundaryDeclined"
|
|
20
|
+
],
|
|
21
|
+
"removed": [],
|
|
22
|
+
"breaking": false,
|
|
23
|
+
"criteriaCovered": false
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"declarationMoves": [],
|
|
27
|
+
"mintMoves": [],
|
|
28
|
+
"criteriaObserves": [],
|
|
29
|
+
"consumersHint": {
|
|
30
|
+
"cli": [
|
|
31
|
+
"ReversibilityVerdict"
|
|
32
|
+
],
|
|
33
|
+
"server": [
|
|
34
|
+
"ReversibilityVerdict"
|
|
35
|
+
],
|
|
36
|
+
"sdk": [],
|
|
37
|
+
"note": "scanned cli@8dd52617 server@7d768b71 sdk@3b02abb — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
|
|
38
|
+
},
|
|
39
|
+
"breakingCount": 0,
|
|
40
|
+
"breakingItems": [],
|
|
41
|
+
"toResolvedAt": "cb5240a4a8289d28ed37ef84df8ee1bfb2139905"
|
|
42
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "core",
|
|
3
|
+
"version": "7.23.3",
|
|
4
|
+
"from": "b48d37a658cfaa46695e76339021dc1226a9ba57",
|
|
5
|
+
"to": "v7.23.3",
|
|
6
|
+
"added": [],
|
|
7
|
+
"removed": [],
|
|
8
|
+
"changed": [],
|
|
9
|
+
"narrowed": [],
|
|
10
|
+
"closedSetMembers": [],
|
|
11
|
+
"closedSetsUnreadable": [],
|
|
12
|
+
"typeKeys": [],
|
|
13
|
+
"declarationMoves": [],
|
|
14
|
+
"mintMoves": [],
|
|
15
|
+
"criteriaObserves": [],
|
|
16
|
+
"consumersHint": {
|
|
17
|
+
"cli": [],
|
|
18
|
+
"server": [],
|
|
19
|
+
"sdk": [],
|
|
20
|
+
"note": "scanned cli@bef85633 server@1da4e102 sdk@0a6aeb7 — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
|
|
21
|
+
},
|
|
22
|
+
"breakingCount": 0,
|
|
23
|
+
"breakingItems": [],
|
|
24
|
+
"toResolvedAt": "912d7792e95edd36fb9b6bb68866661ac36f7f6a"
|
|
25
|
+
}
|