@sema-agent/core 7.56.0 → 7.57.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 +541 -0
- package/LICENSE +1 -1
- package/dist/agents/org-delivery-gate.js +3 -4
- package/dist/agents/peer-held-queue.d.ts +69 -49
- package/dist/agents/peer-held-queue.js +255 -106
- package/dist/agents/peer-session-drain.js +63 -43
- package/dist/agents/send-message-tool.js +3 -2
- package/dist/agents/subagent.js +8 -8
- package/dist/brain/anthropic.js +16 -8
- package/dist/brain/open-responses.js +10 -8
- package/dist/brain/openai.js +11 -9
- package/dist/brain/provider-dialect.d.ts +3 -2
- package/dist/brain/provider-dialect.js +2 -2
- package/dist/brain/request-params.d.ts +6 -0
- package/dist/brain/request-params.js +14 -1
- package/dist/brain/stream-engine.d.ts +9 -0
- package/dist/brain/stream-engine.js +24 -10
- package/dist/brain/stream-shared.d.ts +5 -4
- package/dist/brain/stream-shared.js +4 -4
- package/dist/core/allow-entry.d.ts +3 -10
- package/dist/core/allow-entry.js +17 -25
- package/dist/core/approval-object.d.ts +21 -5
- package/dist/core/approval-object.js +44 -17
- package/dist/core/ask-origin.d.ts +2 -2
- package/dist/core/auto-compaction.d.ts +71 -6
- package/dist/core/auto-compaction.js +42 -26
- package/dist/core/background-agent-store.d.ts +6 -2
- package/dist/core/background-agent-store.js +1 -1
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +17 -29
- package/dist/core/checkpoint-store.js +19 -11
- package/dist/core/cross-boundary-taint.js +4 -2
- package/dist/core/engine-notice.d.ts +3 -2
- package/dist/core/engine-notice.js +6 -0
- package/dist/core/file-history-store.d.ts +14 -8
- package/dist/core/file-history-store.js +14 -4
- package/dist/core/gate-lanes.js +2 -3
- package/dist/core/hooks.d.ts +2 -2
- package/dist/core/mailbox-store.d.ts +62 -1
- package/dist/core/mailbox-store.js +59 -0
- package/dist/core/mcp-name-judge.d.ts +46 -0
- package/dist/core/mcp-name-judge.js +61 -0
- package/dist/core/mcp-probe.d.ts +1 -1
- package/dist/core/mcp-probe.js +2 -5
- package/dist/core/mcp.js +10 -30
- package/dist/core/memory-engine/consolidation-driver.js +4 -3
- package/dist/core/memory-engine/distiller.js +26 -17
- package/dist/core/memory-engine/layout.js +5 -2
- package/dist/core/park-selfcheck.js +2 -0
- package/dist/core/permission-rule-consent.d.ts +5 -3
- package/dist/core/permission-rule-consent.js +20 -15
- package/dist/core/permission-rule-model.d.ts +43 -2
- package/dist/core/permission-rule-model.js +52 -8
- package/dist/core/permission-rule-org.js +1 -1
- package/dist/core/protocol-http-auth.d.ts +16 -2
- package/dist/core/protocol-http-auth.js +42 -11
- package/dist/core/protocol-naming.d.ts +20 -0
- package/dist/core/protocol-naming.js +20 -3
- package/dist/core/read-only-shell.d.ts +20 -5
- package/dist/core/read-only-shell.js +29 -14
- package/dist/core/remote-env.d.ts +37 -1
- package/dist/core/remote-env.js +11 -0
- package/dist/core/resume-startup-cause.d.ts +24 -2
- package/dist/core/resume-startup-cause.js +20 -1
- package/dist/core/roles.js +50 -7
- package/dist/core/runner/compact-session.d.ts +10 -0
- package/dist/core/runner/compact-session.js +202 -0
- package/dist/core/runner/compaction-call-options.d.ts +2 -2
- package/dist/core/runner/compaction-seams.d.ts +21 -1
- package/dist/core/runner/compaction-seams.js +11 -8
- package/dist/core/runner/contracts.d.ts +23 -16
- package/dist/core/runner/derived-route-fallback.d.ts +13 -0
- package/dist/core/runner/derived-route-fallback.js +6 -0
- package/dist/core/runner/env-capability-census.d.ts +3 -2
- package/dist/core/runner/env-capability-census.js +9 -3
- package/dist/core/runner/permission-rule-lanes.d.ts +7 -7
- package/dist/core/runner/permission-rule-lanes.js +17 -11
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
- package/dist/core/runner/prepare-config-doors.d.ts +6 -0
- package/dist/core/runner/prepare-config-doors.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -0
- package/dist/core/runner/prepare-park-ask.d.ts +2 -0
- package/dist/core/runner/prepare-park-ask.js +2 -2
- package/dist/core/runner/prepare-policy-chain.js +2 -4
- package/dist/core/runner/prepare-task.d.ts +4 -1
- package/dist/core/runner/prepare-task.js +23 -27
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +13 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +13 -7
- package/dist/core/runner/rewind-files.d.ts +2 -5
- package/dist/core/runner/rewind-files.js +9 -51
- package/dist/core/runner/run-leg.js +10 -21
- package/dist/core/runner/run-notification-lane.d.ts +2 -0
- package/dist/core/runner/run-notification-lane.js +2 -2
- package/dist/core/runner/run-recovery-lanes.js +3 -4
- package/dist/core/runner/run-settle-and-teardown.js +5 -11
- package/dist/core/runner/run-terminal-adoption.js +3 -4
- package/dist/core/runner/run-turn-boundary.js +3 -5
- package/dist/core/runner/runtask.d.ts +8 -1
- package/dist/core/runner/runtask.js +28 -34
- package/dist/core/runner/send-message-continuation.js +1 -1
- package/dist/core/runner/session-verb-doors.d.ts +28 -0
- package/dist/core/runner/session-verb-doors.js +48 -0
- package/dist/core/runner/session-write-ledger.d.ts +30 -0
- package/dist/core/runner/session-write-ledger.js +101 -0
- package/dist/core/runner/stream-settle-backstop.js +11 -14
- package/dist/core/runner/sub-agent-deny-lists.d.ts +25 -3
- package/dist/core/runner/sub-agent-deny-lists.js +21 -10
- package/dist/core/runner/teardown-bounded.d.ts +1 -0
- package/dist/core/runner/teardown-bounded.js +1 -0
- package/dist/core/runner-deps.d.ts +2 -2
- package/dist/core/secret-env.d.ts +4 -0
- package/dist/core/secret-env.js +3 -0
- package/dist/core/session-policy-store.d.ts +7 -2
- package/dist/core/session-policy-store.js +1 -1
- package/dist/core/shell-wrapper-projection.d.ts +1 -1
- package/dist/core/store-contracts/background-agent-store-contract.js +1 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/store-contracts/file-history-store-contract.js +9 -0
- package/dist/core/store-contracts/mailbox-store-contract.js +49 -1
- package/dist/core/task-registry-agent.d.ts +8 -4
- package/dist/core/task-registry-agent.js +14 -8
- package/dist/core/task-registry-shared.d.ts +35 -6
- package/dist/core/task-registry-shared.js +11 -0
- package/dist/core/task-registry.d.ts +1 -1
- package/dist/core/task-registry.js +1 -1
- package/dist/core/tool-catalog-entries.d.ts +6 -0
- package/dist/core/tool-catalog-entries.js +1 -0
- package/dist/core/tool-name-aliases.d.ts +32 -2
- package/dist/core/tool-name-aliases.js +23 -0
- package/dist/core/tool-name-readings.d.ts +4 -0
- package/dist/core/tool-name-readings.js +1 -0
- package/dist/core/tool-policy.d.ts +10 -6
- package/dist/core/tool-policy.js +4 -7
- package/dist/core/trace.d.ts +4 -1
- package/dist/core/trace.js +2 -1
- package/dist/core/untrusted-egress.d.ts +20 -0
- package/dist/core/untrusted-egress.js +77 -3
- package/dist/engine/harness/agent-harness.d.ts +7 -5
- package/dist/engine/harness/agent-harness.js +11 -2
- package/dist/engine/loop/agent-loop.d.ts +1 -4
- package/dist/engine/loop/agent-loop.js +26 -4
- package/dist/index.d.ts +12 -7
- package/dist/index.js +7 -4
- package/dist/internal/harness.d.ts +2 -1
- package/dist/internal/harness.js +1 -0
- package/dist/primitives/credential-ref.d.ts +89 -0
- package/dist/primitives/credential-ref.js +125 -0
- package/dist/primitives/index.d.ts +2 -0
- package/dist/primitives/index.js +1 -0
- package/dist/stores/file/checkpoint-store.d.ts +0 -4
- package/dist/stores/file/checkpoint-store.js +0 -1
- package/dist/stores/file/file-history-store.js +12 -4
- package/dist/stores/file/mailbox-store.d.ts +2 -1
- package/dist/stores/file/mailbox-store.js +26 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +15 -19
- package/dist/tools/fs/bash-readonly-classifier.js +5 -10
- package/dist/tools/fs/fs-bash.js +10 -3
- package/dist/tools/fs/pattern-kill-guard.d.ts +3 -2
- package/dist/tools/fs/pattern-kill-guard.js +68 -36
- package/dist/tools/fs/shell-read-check.d.ts +10 -0
- package/dist/tools/fs/shell-read-check.js +7 -1
- package/dist/tools/fs/shell-read-identity.d.ts +12 -9
- package/dist/tools/fs/shell-read-identity.js +9 -4
- package/dist/tools/web.js +9 -7
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +79 -3
- package/wire-changes/core-stores-sql@7.57.0.json +29 -0
- package/wire-changes/core@7.57.0.json +1487 -0
- package/wire-changes/mods-kernel@7.57.0.json +29 -0
- package/wire-changes/primitives@7.57.0.json +245 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { errorResult } from "../../core/tools.js";
|
|
4
|
-
import {
|
|
4
|
+
import { processTableOf } from "../../core/remote-env.js";
|
|
5
5
|
import { inheritedHostEnv } from "../../core/secret-env.js";
|
|
6
6
|
import { inlineUntrusted } from "../../core/untrusted-text.js";
|
|
7
7
|
import { planShellReads } from "./shell-read-plan.js";
|
|
8
8
|
import { programArgs } from "./shell-read-write-targets.js";
|
|
9
9
|
const PROBE_TIMEOUT_MS = 5_000;
|
|
10
10
|
const ANCESTOR_HOPS_MAX = 128;
|
|
11
|
-
const
|
|
11
|
+
const SHELL_PID_SPELLING = String.raw `\$(?:\$|\{\$\})`;
|
|
12
|
+
const SHELL_PID_WORD = new RegExp(`^${SHELL_PID_SPELLING}$`);
|
|
13
|
+
const SHELL_PID_EXPANSION = new RegExp(SHELL_PID_SPELLING, "g");
|
|
12
14
|
const NAME_SELECTION_KEEPS_ANCESTORS = process.platform === "linux" ? [] : ["-a"];
|
|
15
|
+
const OPTIONS_END_AT_FIRST_OPERAND = process.platform !== "linux";
|
|
13
16
|
const SIGNAL_ONLY_FIRST_ARGUMENT = process.platform !== "linux";
|
|
14
17
|
const HANDLER_SELECTS_BY_SIGNAL = process.platform === "linux";
|
|
15
18
|
const LONG_OPTIONS = new Map([
|
|
@@ -54,21 +57,28 @@ const isSignalWord = (w) => {
|
|
|
54
57
|
const m = SIGNAL_WORD.exec(w);
|
|
55
58
|
return m !== null && (m[1] === undefined || SIGNAL_NAMES.has(m[1].toUpperCase()));
|
|
56
59
|
};
|
|
57
|
-
function
|
|
60
|
+
function readSelection(allWords, killer) {
|
|
58
61
|
const signalAt = killer !== "pkill" ? -1 : SIGNAL_ONLY_FIRST_ARGUMENT ? (isSignalWord(allWords[0] ?? "") ? 0 : -1) : allWords.findIndex(isSignalWord);
|
|
59
62
|
const signalWord = signalAt < 0 ? undefined : allWords[signalAt].slice(1);
|
|
60
63
|
const words = signalAt < 0 ? allWords : allWords.filter((_, i) => i !== signalAt);
|
|
61
64
|
const out = [];
|
|
65
|
+
const parentLists = [];
|
|
62
66
|
let ended = false;
|
|
63
67
|
let requireHandler = false;
|
|
68
|
+
let negated = false;
|
|
64
69
|
let signalOption;
|
|
65
70
|
let valueOf;
|
|
71
|
+
const valueGiven = (key, value) => {
|
|
72
|
+
if (key === "P")
|
|
73
|
+
parentLists.push(value.split(","));
|
|
74
|
+
};
|
|
66
75
|
for (const w of words) {
|
|
67
76
|
if (valueOf !== undefined) {
|
|
68
|
-
if (valueOf === "ask")
|
|
77
|
+
if (valueOf.fate === "ask")
|
|
69
78
|
out.push(w);
|
|
70
|
-
else if (valueOf === "signal")
|
|
79
|
+
else if (valueOf.fate === "signal")
|
|
71
80
|
signalOption = w;
|
|
81
|
+
valueGiven(valueOf.key, w);
|
|
72
82
|
valueOf = undefined;
|
|
73
83
|
continue;
|
|
74
84
|
}
|
|
@@ -82,60 +92,75 @@ function selectorWords(allWords, killer) {
|
|
|
82
92
|
if (key !== undefined && (takesValueKey(key) || longValue === undefined)) {
|
|
83
93
|
if (key === "H")
|
|
84
94
|
requireHandler = true;
|
|
95
|
+
if (key === "v")
|
|
96
|
+
negated = true;
|
|
85
97
|
const fate = fateOfKey(key);
|
|
86
98
|
if (fate === "ask")
|
|
87
99
|
out.push(w);
|
|
88
100
|
if (takesValueKey(key) && longValue === undefined)
|
|
89
|
-
valueOf = fate;
|
|
90
|
-
else if (
|
|
91
|
-
|
|
101
|
+
valueOf = { fate, key };
|
|
102
|
+
else if (longValue !== undefined) {
|
|
103
|
+
if (fate === "signal")
|
|
104
|
+
signalOption = longValue;
|
|
105
|
+
valueGiven(key, longValue);
|
|
106
|
+
}
|
|
92
107
|
continue;
|
|
93
108
|
}
|
|
94
109
|
const letters = bundledLetters(w);
|
|
95
110
|
if (letters === undefined) {
|
|
96
111
|
out.push(w);
|
|
112
|
+
if (OPTIONS_END_AT_FIRST_OPERAND && (w === "-" || !w.startsWith("-")))
|
|
113
|
+
ended = true;
|
|
97
114
|
continue;
|
|
98
115
|
}
|
|
99
116
|
if (letters.includes("H"))
|
|
100
117
|
requireHandler = true;
|
|
118
|
+
if (letters.includes("v"))
|
|
119
|
+
negated = true;
|
|
101
120
|
const last = letters[letters.length - 1];
|
|
102
121
|
const fate = fateOfKey(last);
|
|
103
122
|
const kept = letters.filter((ch) => fateOfKey(ch) === "ask").join("");
|
|
104
123
|
const attached = VALUED_LETTERS.has(last) ? w.slice(1 + letters.length) : "";
|
|
105
124
|
if (kept !== "")
|
|
106
125
|
out.push(`-${kept}${fate === "ask" ? attached : ""}`);
|
|
107
|
-
if (VALUED_LETTERS.has(last)
|
|
108
|
-
|
|
126
|
+
if (VALUED_LETTERS.has(last)) {
|
|
127
|
+
if (attached === "")
|
|
128
|
+
valueOf = { fate, key: last };
|
|
129
|
+
else
|
|
130
|
+
valueGiven(last, attached);
|
|
131
|
+
}
|
|
109
132
|
}
|
|
110
133
|
const signal = signalOption ?? signalWord;
|
|
111
|
-
return HANDLER_SELECTS_BY_SIGNAL && requireHandler && signal !== undefined ? ["--signal", signal, ...out] : out;
|
|
134
|
+
return { words: HANDLER_SELECTS_BY_SIGNAL && requireHandler && signal !== undefined ? ["--signal", signal, ...out] : out, parentLists, negated };
|
|
112
135
|
}
|
|
113
|
-
const
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return attached !== null && SHELL_PID_WORD.test(attached[1]);
|
|
120
|
-
});
|
|
136
|
+
const LAST_PARENT_LIST_WINS = process.platform === "linux";
|
|
137
|
+
function boundedToShellChildren(read, unread) {
|
|
138
|
+
if (unread !== undefined)
|
|
139
|
+
return false;
|
|
140
|
+
const standing = LAST_PARENT_LIST_WINS ? read.parentLists.slice(-1) : read.parentLists;
|
|
141
|
+
return standing.length > 0 && standing.every((list) => list.every((pid) => SHELL_PID_WORD.test(pid)));
|
|
121
142
|
}
|
|
122
143
|
const literalName = (name) => name.replace(/[\\.[\]{}()*+?^$|]/g, "\\$&");
|
|
123
144
|
function wordsOf(segment) {
|
|
124
145
|
const args = programArgs(segment);
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
146
|
+
const expands = (a) => `\`${inlineUntrusted(a.raw, 60)}\` is expanded by the shell when the command runs`;
|
|
147
|
+
const stream = segment.reentry === "argv" ? "its arguments arrive on a stream" : undefined;
|
|
148
|
+
const opaque = args.find((a) => a.rewritten === true && /[$`*?[~{\\]/.test(a.raw.replace(SHELL_PID_EXPANSION, "0")));
|
|
149
|
+
const first = args.find((a) => a.rewritten === true);
|
|
150
|
+
const unread = stream ?? (opaque === undefined ? undefined : expands(opaque));
|
|
151
|
+
const computed = unread ?? (first === undefined ? undefined : expands(first));
|
|
152
|
+
return { words: args.map((a) => a.spelled), ...(computed !== undefined ? { computed } : {}), ...(unread !== undefined ? { unread } : {}) };
|
|
128
153
|
}
|
|
129
154
|
const shown = (segment) => inlineUntrusted(segment.text, 200);
|
|
130
155
|
function readPkill(segment) {
|
|
131
|
-
const { words, computed } = wordsOf(segment);
|
|
132
|
-
|
|
133
|
-
|
|
156
|
+
const { words, computed, unread } = wordsOf(segment);
|
|
157
|
+
const read = readSelection(words, "pkill");
|
|
158
|
+
if (boundedToShellChildren(read, unread)) {
|
|
159
|
+
return read.negated ? { text: shown(segment), probes: [], computed: "the selection is negated (`-v` / `--inverse`), so `-P` of the shell's pid no longer limits it to the shell's children" } : undefined;
|
|
134
160
|
}
|
|
135
161
|
if (computed !== undefined)
|
|
136
162
|
return { text: shown(segment), probes: [], computed };
|
|
137
|
-
|
|
138
|
-
return selectors.length === 0 ? undefined : { text: shown(segment), probes: [selectors] };
|
|
163
|
+
return read.words.length === 0 ? undefined : { text: shown(segment), probes: [read.words] };
|
|
139
164
|
}
|
|
140
165
|
const OPT_FLAG = { does: "flag" };
|
|
141
166
|
const OPT_FILTER = { does: "filter" };
|
|
@@ -157,7 +182,6 @@ const KILLALL_OPTIONS = new Map([
|
|
|
157
182
|
["-v", OPT_FLAG], ["--verbose", OPT_FLAG], ["-V", OPT_FLAG], ["--version", OPT_FLAG], ["-w", OPT_FLAG], ["--wait", OPT_FLAG], ["-d", OPT_FLAG], ["-z", OPT_FLAG], ["--help", OPT_FLAG],
|
|
158
183
|
]);
|
|
159
184
|
const takesValue = (opt) => opt !== undefined && (opt.does === "select" || opt.does === "context" || opt.does === "filter" || opt.does === "valued");
|
|
160
|
-
const OPTIONS_END_AT_FIRST_NAME = process.platform !== "linux";
|
|
161
185
|
const KILLALL_VALUED_LETTERS = new Set([...KILLALL_OPTIONS].filter(([spelling, opt]) => !spelling.startsWith("--") && takesValue(opt)).map(([spelling]) => spelling.slice(1)));
|
|
162
186
|
function optionsOf(w) {
|
|
163
187
|
if (w.startsWith("--")) {
|
|
@@ -185,7 +209,7 @@ function readKillallWords(words) {
|
|
|
185
209
|
}
|
|
186
210
|
if (!w.startsWith("-") || w === "-") {
|
|
187
211
|
names.push(w);
|
|
188
|
-
if (
|
|
212
|
+
if (OPTIONS_END_AT_FIRST_OPERAND) {
|
|
189
213
|
names.push(...words.slice(i + 1));
|
|
190
214
|
break;
|
|
191
215
|
}
|
|
@@ -238,7 +262,7 @@ function readSelector(segment) {
|
|
|
238
262
|
const names = read.words.filter((w, i) => !w.startsWith("-") && read.words[i - 1] !== "-o");
|
|
239
263
|
return { text: shown(segment), probes: names.map((n) => [...NAME_SELECTION_KEEPS_ANCESTORS, "-x", literalName(n)]) };
|
|
240
264
|
}
|
|
241
|
-
return { text: shown(segment), probes: [
|
|
265
|
+
return { text: shown(segment), probes: [readSelection(read.words, "pgrep").words] };
|
|
242
266
|
}
|
|
243
267
|
const SELECTORS = new Set(["pgrep", "pidof"]);
|
|
244
268
|
const EVERY_PROCESS_PID = /^-0*1$/;
|
|
@@ -358,12 +382,8 @@ const SENTENCE = {
|
|
|
358
382
|
computed: (f) => `Error (Bash): refusing to run \`${f.text}\` — which processes it selects is decided when the command runs (${f.why}), so it cannot first be checked against this agent's own process and the processes above it. Write the pattern into the command as a literal, ${WAY_OUT}.`,
|
|
359
383
|
unavailable: (f) => `Error (Bash): refusing to run \`${f.text}\` — the check that its selection holds neither this agent's own process nor a process above it could not run (${f.why}). Stop the process by its PID, or target your own children with \`pkill -P $$ ...\`.`,
|
|
360
384
|
};
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
return undefined;
|
|
364
|
-
const readings = patternKillReadings(command, cwd);
|
|
365
|
-
if (readings.length === 0)
|
|
366
|
-
return undefined;
|
|
385
|
+
const UNDECLARED_TABLE = "the execution environment does not declare whether its commands run on this agent's process table — `capabilities.sharesProcessTable` is missing or not a boolean";
|
|
386
|
+
async function findingsOnThisTable(readings, signal) {
|
|
367
387
|
const found = [];
|
|
368
388
|
const asked = await Promise.all(readings.map(async (r) => ({ r, answers: await Promise.all(r.probes.map((p) => selected(p, signal))) })));
|
|
369
389
|
let ancestry;
|
|
@@ -389,6 +409,18 @@ export async function patternKillRefusal(command, env, cwd, signal) {
|
|
|
389
409
|
found.push({ kind: "hit", text: r.text, pid, own: pid === process.pid });
|
|
390
410
|
}
|
|
391
411
|
}
|
|
412
|
+
return found;
|
|
413
|
+
}
|
|
414
|
+
export async function patternKillRefusal(command, env, cwd, signal) {
|
|
415
|
+
if (process.platform === "win32")
|
|
416
|
+
return undefined;
|
|
417
|
+
const readings = patternKillReadings(command, cwd).filter((r) => r.probes.length > 0 || r.computed !== undefined || r.everyProcess === true);
|
|
418
|
+
if (readings.length === 0)
|
|
419
|
+
return undefined;
|
|
420
|
+
const table = processTableOf(env);
|
|
421
|
+
if (table === "other")
|
|
422
|
+
return undefined;
|
|
423
|
+
const found = table === "this" ? await findingsOnThisTable(readings, signal) : [{ kind: "unavailable", text: readings[0].text, why: UNDECLARED_TABLE }];
|
|
392
424
|
const f = found.find((x) => x.kind === "hit") ?? found.find((x) => x.kind === "computed") ?? found[0];
|
|
393
425
|
if (f === undefined || signal?.aborted === true)
|
|
394
426
|
return undefined;
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
21
21
|
import { violationDetails } from "./safety.js";
|
|
22
22
|
import { type ShellReadPlan } from "./shell-read-plan.js";
|
|
23
|
+
import { type ReadLanding } from "./shell-read-identity.js";
|
|
23
24
|
import { type HomeViewSource } from "./read-deny-home.js";
|
|
24
25
|
import { type EstablishedRead, type ShellReadBoundary, type ShellReadDisposition, type ShellReadFinding, type ShellReadSeat } from "./shell-read-judge.js";
|
|
25
26
|
import type { ReadRootGrantCandidate } from "../../core/tool-spec.js";
|
|
@@ -169,9 +170,18 @@ export type ShellReadAdmission = (toolName: string, toolCallId: string | undefin
|
|
|
169
170
|
isError: true;
|
|
170
171
|
details?: unknown;
|
|
171
172
|
} | undefined>;
|
|
173
|
+
/** Where the station's last non-refused judgment of a call found its reads land (`readLandingsOf`): the judgment its
|
|
174
|
+
* admission holds for the call id — the one the spawn re-establishes — read without consuming it. `undefined` ⇒ the
|
|
175
|
+
* station holds none (a refused judgment records nothing). Its readers take it right where the latest judgment is a
|
|
176
|
+
* non-refused one of the bytes they hold: the park when the filed bytes are the judged ones, the resumed leg's execution
|
|
177
|
+
* station after its belt. A judgment that settles after a newer one replaced it is what the spawn would re-establish too:
|
|
178
|
+
* the spawn then refuses bytes the entry does not describe, and a row bound to it vetoes the resume wherever the two
|
|
179
|
+
* commands' reads differ (fail-closed either way). */
|
|
180
|
+
export type ShellReadLandings = (toolCallId: string) => readonly ReadLanding[] | undefined;
|
|
172
181
|
export interface ShellReadStation {
|
|
173
182
|
readonly check: ShellReadCheck;
|
|
174
183
|
readonly admit: ShellReadAdmission;
|
|
184
|
+
readonly landings: ShellReadLandings;
|
|
175
185
|
}
|
|
176
186
|
export interface ShellReadStationOptions {
|
|
177
187
|
readonly env: ExecutionEnv;
|
|
@@ -7,6 +7,7 @@ import { NOT_AUTO_ALLOWED, declinedProgramSentence } from "./bash-program-positi
|
|
|
7
7
|
import { bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
|
|
8
8
|
import { planShellReads } from "./shell-read-plan.js";
|
|
9
9
|
import { createShellReadEstablisher, establishShellReadsLexically } from "./shell-read-establish.js";
|
|
10
|
+
import { readLandingsOf } from "./shell-read-identity.js";
|
|
10
11
|
import { declaredReadDenyHomes, homeViewsAt } from "./read-deny-home.js";
|
|
11
12
|
import { anyOptionWord, programArgs } from "./shell-read-write-targets.js";
|
|
12
13
|
import { shellReadFaceWords } from "./shell-read-face.js";
|
|
@@ -395,6 +396,7 @@ export function createShellReadStation(opts) {
|
|
|
395
396
|
};
|
|
396
397
|
const admissions = new Map();
|
|
397
398
|
const remember = (toolCallId, a) => {
|
|
399
|
+
admissions.delete(toolCallId);
|
|
398
400
|
if (admissions.size >= ADMISSION_MAX) {
|
|
399
401
|
const oldest = admissions.keys().next().value;
|
|
400
402
|
if (oldest !== undefined)
|
|
@@ -478,5 +480,9 @@ export function createShellReadStation(opts) {
|
|
|
478
480
|
const r = refusalOf(toolName, identity, a.boundary, []);
|
|
479
481
|
return refused(errorResult(r.sentence, containmentDetails(changed.map((c) => c.target).filter((t) => t !== undefined))));
|
|
480
482
|
};
|
|
481
|
-
|
|
483
|
+
const landings = (toolCallId) => {
|
|
484
|
+
const a = admissions.get(toolCallId);
|
|
485
|
+
return a !== undefined ? readLandingsOf(a.established) : undefined;
|
|
486
|
+
};
|
|
487
|
+
return { check, admit, landings };
|
|
482
488
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The pre-spawn identity comparison: which established reads of one plan resolve somewhere else now.
|
|
3
|
-
*
|
|
4
|
-
* Compared PER CANDIDATE, never as one pooled set of targets: a candidate is keyed by where it stands in the plan
|
|
5
|
-
* (segment, nested plan) and by what it is (deny-only or a read, provenance, spelling, expansion, raw word), and its
|
|
6
|
-
* set of established targets is compared with the same candidate's set from the re-establishment. A pooled set let
|
|
7
|
-
* one candidate's targets hide another's move — `head linkx` retargeted onto a path some other word's pattern already
|
|
8
|
-
* matched, or onto a home shadow's target, left the pool unchanged and the call was admitted unjudged.
|
|
9
|
-
*/
|
|
10
1
|
import type { EstablishedPlan } from "./shell-read-establish.js";
|
|
11
2
|
import type { ObservedEnvironment } from "./shell-read-environment.js";
|
|
12
3
|
import type { EstablishedRead } from "./shell-read-judge.js";
|
|
13
4
|
import { type HomeViewsCarrier } from "./read-deny-home.js";
|
|
14
5
|
/** Every read of either establishment whose candidate's settled target set differs between the two. */
|
|
15
6
|
export declare function movedReads(previous: EstablishedPlan, again: EstablishedPlan): EstablishedRead[];
|
|
7
|
+
/** One read candidate's landing: its key and where it settled, each as a sha256 hex digest (the key's raw tuple joins on
|
|
8
|
+
* NUL and follows the command's length; a pattern lands on up to thousands of entries), with the raw word and the
|
|
9
|
+
* settled targets kept for the sentence that names a moved one. */
|
|
10
|
+
export interface ReadLanding {
|
|
11
|
+
readonly key: string;
|
|
12
|
+
readonly landing: string;
|
|
13
|
+
readonly raw: string;
|
|
14
|
+
readonly targets: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
/** Every read candidate of the establishment (nested plans included) with where it landed, in plan order. A candidate
|
|
17
|
+
* that settled nowhere (a lexical or unestablishable read) lands on the empty set — placed later, that is a move. */
|
|
18
|
+
export declare function readLandingsOf(e: EstablishedPlan): ReadLanding[];
|
|
16
19
|
/**
|
|
17
20
|
* The establishment with every read that landed carrying its HOME VIEW (read-deny-home.ts) among its spellings, nested
|
|
18
21
|
* plans included: the deny judge then places a home-anchored row wherever the target IS below the home, whatever it is
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { homeViewsOf } from "./read-deny-home.js";
|
|
2
3
|
export function movedReads(previous, again) {
|
|
3
4
|
const before = targetsByCandidate(previous);
|
|
@@ -6,12 +7,19 @@ export function movedReads(previous, again) {
|
|
|
6
7
|
for (const key of new Set([...before.keys(), ...after.keys()])) {
|
|
7
8
|
const b = before.get(key);
|
|
8
9
|
const a = after.get(key);
|
|
9
|
-
if (b !== undefined && a !== undefined &&
|
|
10
|
+
if (b !== undefined && a !== undefined && landingOf(b.targets) === landingOf(a.targets))
|
|
10
11
|
continue;
|
|
11
12
|
changed.push(...(b?.reads ?? []), ...(a?.reads ?? []));
|
|
12
13
|
}
|
|
13
14
|
return changed.filter((r) => r.target !== undefined);
|
|
14
15
|
}
|
|
16
|
+
export function readLandingsOf(e) {
|
|
17
|
+
return [...targetsByCandidate(e)].map(([key, l]) => ({ key: digest(key), landing: landingOf(l.targets), raw: l.reads[0]?.candidate.raw ?? "", targets: [...l.targets].sort() }));
|
|
18
|
+
}
|
|
19
|
+
function landingOf(targets) {
|
|
20
|
+
return digest(JSON.stringify([...targets].sort()));
|
|
21
|
+
}
|
|
22
|
+
const digest = (s) => createHash("sha256").update(s, "utf8").digest("hex");
|
|
15
23
|
function targetsByCandidate(e) {
|
|
16
24
|
const out = new Map();
|
|
17
25
|
const walk = (p, at) => {
|
|
@@ -35,9 +43,6 @@ function targetsByCandidate(e) {
|
|
|
35
43
|
walk(e, "");
|
|
36
44
|
return out;
|
|
37
45
|
}
|
|
38
|
-
function sameTargets(a, b) {
|
|
39
|
-
return a.size === b.size && [...a].every((t) => b.has(t));
|
|
40
|
-
}
|
|
41
46
|
export async function withHomeViews(door, placed, established, signal) {
|
|
42
47
|
const views = homeViewsOf(door.fs, placed, signal);
|
|
43
48
|
const unread = [];
|
package/dist/tools/web.js
CHANGED
|
@@ -3,6 +3,7 @@ import { defineTool, errorResult } from "../core/tools.js";
|
|
|
3
3
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
4
4
|
import { discloseReminderShaped, observeReminderMarkEcho } from "../core/reminder-disclosure.js";
|
|
5
5
|
import { egressThrownText } from "../core/untrusted-egress.js";
|
|
6
|
+
import { remoteWords } from "../core/protocol-http-auth.js";
|
|
6
7
|
import { fetchFollowing, hopRefusal, HOST_FLOOR_REFUSED_CODE } from "../core/host-floor.js";
|
|
7
8
|
import { hostOfUrl } from "../core/host-target.js";
|
|
8
9
|
import { assertNoRetiredKeys } from "../core/retired-keys.js";
|
|
@@ -210,12 +211,13 @@ function isPermittedRedirectHop(from, to) {
|
|
|
210
211
|
const there = hostOfUrl(to.toString());
|
|
211
212
|
return "host" in here && "host" in there && here.host === there.host;
|
|
212
213
|
}
|
|
214
|
+
const reasonPhrase = (res) => remoteWords(res.statusText, []);
|
|
213
215
|
async function fetchUnderFloor(doFetch, start, floor, userAgent, signal, onResponse) {
|
|
214
216
|
const out = await fetchFollowing(doFetch, start, { method: "GET", headers: { Accept: WEBFETCH_ACCEPT, "User-Agent": userAgent }, ...(signal !== undefined ? { signal } : {}) }, {
|
|
215
217
|
refusal: (url) => hopRefusal(url, floor, true),
|
|
216
218
|
follows: isPermittedRedirectHop,
|
|
217
219
|
maxRedirects: MAX_REDIRECTS,
|
|
218
|
-
onResponse: (res) => onResponse?.(res.status, res
|
|
220
|
+
onResponse: (res) => onResponse?.(res.status, reasonPhrase(res)),
|
|
219
221
|
});
|
|
220
222
|
switch (out.kind) {
|
|
221
223
|
case "answered":
|
|
@@ -328,7 +330,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
328
330
|
return failure(`Error (WebFetch): ${fr.error}`, connectedMeta ? { code: connectedMeta.status, codeText: connectedMeta.statusText } : {});
|
|
329
331
|
}
|
|
330
332
|
res = fr.res;
|
|
331
|
-
connectedMeta = { status: res.status, statusText: res
|
|
333
|
+
connectedMeta = { status: res.status, statusText: reasonPhrase(res) };
|
|
332
334
|
finalResponse = true;
|
|
333
335
|
const stream = res.body;
|
|
334
336
|
if (stream && typeof stream.getReader === "function") {
|
|
@@ -449,7 +451,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
449
451
|
: {}),
|
|
450
452
|
};
|
|
451
453
|
if (!res.ok) {
|
|
452
|
-
const headline = `Error (WebFetch): HTTP ${res.status} ${res
|
|
454
|
+
const headline = `Error (WebFetch): HTTP ${res.status} ${reasonPhrase(res)} for ${parsed.toString()}`;
|
|
453
455
|
const looksBinary = bodyBytes !== undefined && sniffBinarySignature(bodyBytes) !== undefined;
|
|
454
456
|
const errText = looksBinary
|
|
455
457
|
? ""
|
|
@@ -476,7 +478,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
476
478
|
type: "web-fetch",
|
|
477
479
|
url: parsed.toString(),
|
|
478
480
|
code: res.status,
|
|
479
|
-
codeText: res
|
|
481
|
+
codeText: reasonPhrase(res),
|
|
480
482
|
bytes: bodyBytes ? bodyBytes.length : raw.length,
|
|
481
483
|
durationMs: Date.now() - startedAt,
|
|
482
484
|
...transferStateDetails,
|
|
@@ -508,7 +510,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
508
510
|
type: "web-fetch",
|
|
509
511
|
url: parsed.toString(),
|
|
510
512
|
code: res.status,
|
|
511
|
-
codeText: res
|
|
513
|
+
codeText: reasonPhrase(res),
|
|
512
514
|
bytes: bodyBytes.length,
|
|
513
515
|
durationMs: Date.now() - startedAt,
|
|
514
516
|
...transferStateDetails,
|
|
@@ -634,7 +636,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
634
636
|
type: "web-fetch",
|
|
635
637
|
url: parsed.toString(),
|
|
636
638
|
code: res.status,
|
|
637
|
-
codeText: res
|
|
639
|
+
codeText: reasonPhrase(res),
|
|
638
640
|
bytes: bodyBytes ? bodyBytes.length : raw.length,
|
|
639
641
|
result: out.length > RESULT_PREVIEW_CHARS ? `${out.slice(0, RESULT_PREVIEW_CHARS)}\n…[${out.length - RESULT_PREVIEW_CHARS} chars truncated — full text in the tool output]` : out,
|
|
640
642
|
durationMs: Date.now() - startedAt,
|
|
@@ -990,7 +992,7 @@ export function createSearxngSearchBackend(baseUrl, options = {}) {
|
|
|
990
992
|
throw new Error(`SearXNG was redirected more than ${SEARXNG_MAX_REDIRECTS} times`);
|
|
991
993
|
const res = followed.res;
|
|
992
994
|
if (!res.ok)
|
|
993
|
-
throw new Error(`SearXNG ${res.status} ${res
|
|
995
|
+
throw new Error(`SearXNG ${res.status} ${reasonPhrase(res)} from ${url.origin}${url.pathname}`);
|
|
994
996
|
const body = (await res.json());
|
|
995
997
|
if (!Array.isArray(body.results))
|
|
996
998
|
throw new Error(`SearXNG returned no results array from ${url.origin}${url.pathname} — is format=json enabled on this instance?`);
|