@sema-agent/core 7.53.0 → 7.54.1
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 +291 -1
- package/LICENSE +3 -1
- package/dist/agents/peer-session-drain.d.ts +0 -1
- package/dist/agents/peer-session-drain.js +0 -2
- package/dist/agents/send-message-tool.js +3 -4
- package/dist/agents/subagent.d.ts +3 -5
- package/dist/agents/subagent.js +18 -11
- package/dist/agents/tool-filter.d.ts +7 -4
- package/dist/agents/tool-filter.js +7 -3
- package/dist/core/a2a.js +4 -2
- package/dist/core/allow-entry.d.ts +1 -0
- package/dist/core/allow-entry.js +1 -0
- package/dist/core/approval-object.d.ts +87 -0
- package/dist/core/approval-object.js +85 -0
- package/dist/core/arg-summary.d.ts +3 -2
- package/dist/core/arg-summary.js +17 -11
- package/dist/core/ask-origin.d.ts +43 -27
- package/dist/core/ask-origin.js +20 -11
- package/dist/core/ask-question.d.ts +0 -1
- package/dist/core/ask-question.js +4 -67
- package/dist/core/ask-unresolvable-notice.d.ts +3 -2
- package/dist/core/auto-compaction.d.ts +49 -50
- package/dist/core/auto-compaction.js +41 -51
- package/dist/core/auto-mode-prompt.d.ts +4 -2
- package/dist/core/auto-mode-prompt.js +4 -3
- package/dist/core/canonical-json.d.ts +69 -0
- package/dist/core/canonical-json.js +130 -8
- package/dist/core/checkpoint-requirements.d.ts +13 -7
- package/dist/core/checkpoint-requirements.js +4 -1
- package/dist/core/checkpoint-store.d.ts +33 -18
- package/dist/core/checkpoint-store.js +5 -4
- package/dist/core/child-clamp-seats.d.ts +45 -6
- package/dist/core/child-clamp-seats.js +56 -4
- package/dist/core/consolidate-scope.d.ts +1 -1
- package/dist/core/cross-boundary-taint.d.ts +213 -0
- package/dist/core/cross-boundary-taint.js +196 -0
- package/dist/core/decision-receipt.d.ts +44 -1
- package/dist/core/decision-receipt.js +16 -0
- package/dist/core/effective-path-target.d.ts +3 -0
- package/dist/core/effective-path-target.js +4 -0
- package/dist/core/engine-notice.d.ts +34 -2
- package/dist/core/engine-notice.js +17 -1
- package/dist/core/engine-storage-backend.d.ts +73 -0
- package/dist/core/engine-storage-backend.js +1 -0
- package/dist/core/file-history-store.d.ts +6 -2
- package/dist/core/file-history-store.js +1 -0
- package/dist/core/gate-fold.js +10 -2
- package/dist/core/gate-lanes.js +30 -21
- package/dist/core/gate-outcome.d.ts +7 -3
- package/dist/core/governance-codes.d.ts +7 -2
- package/dist/core/governance-codes.js +6 -0
- package/dist/core/hooks.d.ts +14 -14
- package/dist/core/hooks.js +1 -2
- package/dist/core/locked-config.d.ts +28 -1
- package/dist/core/locked-config.js +30 -3
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/mcp-server-spec.d.ts +66 -9
- package/dist/core/mcp-server-spec.js +52 -19
- package/dist/core/mcp.js +5 -4
- package/dist/core/memory-engine/engine.js +2 -1
- package/dist/core/memory-engine/export-bundle.d.ts +1 -1
- package/dist/core/memory-engine/export-bundle.js +1 -1
- package/dist/core/memory-engine/file-backend.d.ts +2 -6
- package/dist/core/memory-engine/file-backend.js +1 -12
- package/dist/core/memory.d.ts +3 -3
- package/dist/core/mode-policy.d.ts +13 -8
- package/dist/core/mode-policy.js +3 -2
- package/dist/core/mods/kernel/receive.d.ts +2 -1
- package/dist/core/mods/kernel/receive.js +7 -2
- package/dist/core/parent-spec-seat.d.ts +10 -13
- package/dist/core/parent-spec-seat.js +14 -6
- package/dist/core/pause-registry.d.ts +35 -1
- package/dist/core/pause-registry.js +10 -0
- package/dist/core/permission-rule-consent.d.ts +18 -5
- package/dist/core/permission-rule-consent.js +13 -8
- package/dist/core/permission-rule-model.d.ts +29 -6
- package/dist/core/permission-rule-model.js +6 -1
- package/dist/core/permission-rule-org.d.ts +37 -0
- package/dist/core/permission-rule-org.js +34 -42
- package/dist/core/permission-rule-session.js +2 -2
- package/dist/core/permission-rule-store.d.ts +8 -0
- package/dist/core/permission-rule-store.js +1 -0
- package/dist/core/permission-rule-sync.js +3 -2
- package/dist/core/read-only-shell.d.ts +1 -1
- package/dist/core/read-only-shell.js +30 -9
- package/dist/core/remote-env.d.ts +41 -1
- package/dist/core/remote-env.js +18 -0
- package/dist/core/resume-startup-cause.d.ts +18 -0
- package/dist/core/resume-startup-cause.js +12 -0
- package/dist/core/runner/approval-feedback.d.ts +46 -0
- package/dist/core/runner/approval-feedback.js +41 -0
- package/dist/core/runner/compaction-call-options.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.js +2 -2
- package/dist/core/runner/contracts.d.ts +33 -41
- package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
- package/dist/core/runner/cross-boundary-lane.js +190 -0
- package/dist/core/runner/decide-continuation.d.ts +4 -4
- package/dist/core/runner/decide-continuation.js +3 -1
- package/dist/core/runner/gate-exit.d.ts +32 -12
- package/dist/core/runner/gate-exit.js +75 -33
- package/dist/core/runner/inherited-ask-grants.js +2 -2
- package/dist/core/runner/person-path-rows.d.ts +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +7 -9
- package/dist/core/runner/prepare-acquire-reconcile.js +8 -6
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
- package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
- package/dist/core/runner/prepare-config-doors.d.ts +12 -15
- package/dist/core/runner/prepare-config-doors.js +8 -3
- package/dist/core/runner/prepare-context-lane.js +6 -3
- package/dist/core/runner/prepare-delegation-surface.d.ts +3 -2
- package/dist/core/runner/prepare-delegation-surface.js +2 -3
- package/dist/core/runner/prepare-execution-env.d.ts +1 -2
- package/dist/core/runner/prepare-execution-env.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
- package/dist/core/runner/prepare-gate-stations.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +2 -2
- package/dist/core/runner/prepare-hands-readface.js +1 -1
- package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
- package/dist/core/runner/prepare-inherited-gate.js +1 -0
- package/dist/core/runner/prepare-park-ask.d.ts +1 -1
- package/dist/core/runner/prepare-park-ask.js +9 -3
- package/dist/core/runner/prepare-policy-chain.js +2 -7
- package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
- package/dist/core/runner/prepare-safety-scan.js +34 -27
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
- package/dist/core/runner/prepare-suspend-saga.js +6 -2
- package/dist/core/runner/prepare-task.js +13 -10
- package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
- package/dist/core/runner/prepare-turn-wiring.js +14 -49
- package/dist/core/runner/prepare-wiring-manifest.d.ts +2 -2
- package/dist/core/runner/prepare-wiring-manifest.js +2 -2
- package/dist/core/runner/prepare-workspace-restore.js +2 -2
- package/dist/core/runner/resume-admission.js +9 -9
- package/dist/core/runner/resume-apply.js +4 -4
- package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
- package/dist/core/runner/resume-policy-outcome.js +18 -15
- package/dist/core/runner/resume-preflight.js +2 -2
- package/dist/core/runner/resume-review-outcome.js +3 -3
- package/dist/core/runner/run-leg.js +57 -33
- package/dist/core/runner/run-settle-and-teardown.js +2 -0
- package/dist/core/runner/run-terminal-adoption.js +15 -4
- package/dist/core/runner/session-file-state-replay.d.ts +5 -2
- package/dist/core/runner/session-file-state-replay.js +3 -2
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/tool-face-overlay.d.ts +24 -6
- package/dist/core/runner/tool-face-overlay.js +24 -15
- package/dist/core/runner-deps.d.ts +9 -9
- package/dist/core/session-store.d.ts +35 -16
- package/dist/core/session-store.js +20 -12
- package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
- package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
- package/dist/core/shell-wrapper-table.d.ts +5 -3
- package/dist/core/shell-wrapper-table.js +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
- package/dist/core/store-contracts/session-store-contract.js +683 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
- package/dist/core/task-event.d.ts +6 -4
- package/dist/core/task-registry-agent.js +1 -0
- package/dist/core/task-registry-shared.d.ts +5 -0
- package/dist/core/task-registry.d.ts +7 -0
- package/dist/core/task-registry.js +17 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +6 -6
- package/dist/core/tighten-task-spec.d.ts +6 -4
- package/dist/core/tighten-task-spec.js +2 -2
- package/dist/core/tool-catalog-entries.d.ts +1 -1
- package/dist/core/tool-catalog-entries.js +52 -52
- package/dist/core/tool-face-reader.d.ts +49 -0
- package/dist/core/tool-face-reader.js +94 -0
- package/dist/core/tool-face.d.ts +17 -8
- package/dist/core/tool-face.js +1 -0
- package/dist/core/tool-policy.d.ts +64 -23
- package/dist/core/tool-policy.js +62 -56
- package/dist/core/tool-roster.d.ts +46 -28
- package/dist/core/tool-roster.js +91 -14
- package/dist/core/tool-spec-members.js +2 -2
- package/dist/core/tool-spec.d.ts +20 -4
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +24 -2
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +7 -0
- package/dist/core/untrusted-text.js +3 -8
- package/dist/core/user-attachment.d.ts +43 -0
- package/dist/core/user-attachment.js +92 -15
- package/dist/engine/compaction/compaction.d.ts +2 -2
- package/dist/engine/compaction/utils.d.ts +2 -2
- package/dist/engine/harness/types.d.ts +7 -4
- package/dist/engine/llm/types.d.ts +7 -2
- package/dist/engine/llm/types.js +2 -1
- package/dist/engine/session/parent-chain-guard.d.ts +10 -0
- package/dist/engine/session/parent-chain-guard.js +10 -0
- package/dist/engine/session/repo-utils.d.ts +5 -0
- package/dist/engine/session/repo-utils.js +9 -0
- package/dist/engine/session/storage-base.js +5 -2
- package/dist/index.d.ts +23 -9
- package/dist/index.js +19 -8
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/internal/llm.d.ts +1 -1
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +3 -0
- package/dist/orchestration/run-workflow-tool.js +1 -0
- package/dist/orchestration/workflow-governance.d.ts +1 -1
- package/dist/orchestration/workflow.d.ts +3 -0
- package/dist/orchestration/workflow.js +2 -1
- package/dist/prompt-assembly/catalog/schema.d.ts +3 -0
- package/dist/prompt-assembly/catalog/schema.js +3 -0
- package/dist/prompt-assembly/overlay.js +2 -2
- package/dist/prompt-assembly/select.js +2 -2
- package/dist/prompts/default.d.ts +1 -1
- package/dist/shell/lex.d.ts +87 -8
- package/dist/shell/lex.js +283 -49
- package/dist/stores/file/index.d.ts +8 -6
- package/dist/tools/artifact/artifact-tool.js +10 -0
- package/dist/tools/fs/bash-program-position.d.ts +3 -15
- package/dist/tools/fs/bash-program-position.js +3 -10
- package/dist/tools/fs/bash-readonly-classifier.js +2 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-bash.js +72 -101
- package/dist/tools/fs/fs-read.d.ts +12 -0
- package/dist/tools/fs/fs-read.js +10 -9
- package/dist/tools/fs/fs-shared.d.ts +27 -31
- package/dist/tools/fs/fs-shared.js +12 -15
- package/dist/tools/fs/fs-write.js +1 -1
- package/dist/tools/fs/index.js +2 -2
- package/dist/tools/fs/read-deny-home.d.ts +1 -1
- package/dist/tools/fs/read-deny-home.js +5 -4
- package/dist/tools/fs/read-deny.d.ts +16 -3
- package/dist/tools/fs/read-deny.js +12 -9
- package/dist/tools/fs/safety.d.ts +7 -6
- package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
- package/dist/tools/fs/shell-read-bindings.js +15 -16
- package/dist/tools/fs/shell-read-device.js +1 -1
- package/dist/tools/fs/shell-read-plan.js +36 -47
- package/dist/tools/fs/shell-read-word.d.ts +1 -6
- package/dist/tools/fs/shell-read-word.js +2 -20
- package/dist/tools/fs/shell-reentry-text.js +1 -1
- package/dist/tools/web.d.ts +2 -2
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +5654 -5520
- package/wire-changes/core@7.53.0.json +30 -8
- package/wire-changes/core@7.54.0.json +2870 -0
- package/wire-changes/core@7.54.1.json +37 -0
- package/dist/core/shell-scan.d.ts +0 -60
- package/dist/core/shell-scan.js +0 -183
|
@@ -14,21 +14,9 @@ import { SHELL_CONTROL_PREFIXES, type LexedWord } from "../../shell/lex.js";
|
|
|
14
14
|
export declare const NOT_AUTO_ALLOWED = "\u2014 not auto-allowed";
|
|
15
15
|
/** argv[0] as the program bash will resolve it: `/usr/bin/cat` and `cat` are one name. */
|
|
16
16
|
export declare function programBasename(word: string): string;
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
* command TEXT rather than its words (the activity summary) reads it here. It retires with it.
|
|
21
|
-
*/
|
|
22
|
-
export declare const ASSIGNMENT_PREFIX: RegExp;
|
|
23
|
-
/** The prefix a whitespace-split token BEGINS with, or `undefined` — the text readers' view of
|
|
24
|
-
* {@link ASSIGNMENT_PREFIX}; `name` is the variable before any subscript, `settlesWholeName` false for an
|
|
25
|
-
* append and for a subscript. */
|
|
26
|
-
export declare function writtenAssignmentPrefix(token: string): {
|
|
27
|
-
readonly prefix: string;
|
|
28
|
-
readonly name: string;
|
|
29
|
-
readonly appends: boolean;
|
|
30
|
-
readonly settlesWholeName: boolean;
|
|
31
|
-
} | undefined;
|
|
17
|
+
/** A launcher's own `NAME=value` operand (`env`, `sudo`): any `=`-bearing non-option word, read DEQUOTED — the launcher's
|
|
18
|
+
* grammar, not the shell's (`env 'F[1=2' cmd` sets `F[1`). The one spelling of it. */
|
|
19
|
+
export declare function isLauncherAssignmentOperand(folded: string): boolean;
|
|
32
20
|
/** The names a segment's COMMAND ENVIRONMENT sets for the program it starts: the assignment words in front of the
|
|
33
21
|
* program (the lexer's {@link LexedWord.assignment} fact, a launcher's `NAME=value` operand included) — or `all` where
|
|
34
22
|
* a launcher in front of it may set, clear or replace the environment (`env -u` / `-i`, `sudo`, `su`, …). */
|
|
@@ -5,15 +5,8 @@ export const NOT_AUTO_ALLOWED = "— not auto-allowed";
|
|
|
5
5
|
export function programBasename(word) {
|
|
6
6
|
return word.includes("/") ? (word.split("/").pop() ?? word) : word;
|
|
7
7
|
}
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
const prefix = ASSIGNMENT_PREFIX.exec(token)?.[0];
|
|
11
|
-
if (prefix === undefined)
|
|
12
|
-
return undefined;
|
|
13
|
-
const appends = prefix.endsWith("+=");
|
|
14
|
-
const written = prefix.slice(0, prefix.length - (appends ? 2 : 1));
|
|
15
|
-
const bracket = written.indexOf("[");
|
|
16
|
-
return { prefix, name: bracket < 0 ? written : written.slice(0, bracket), appends, settlesWholeName: !appends && bracket < 0 };
|
|
8
|
+
export function isLauncherAssignmentOperand(folded) {
|
|
9
|
+
return !folded.startsWith("-") && folded.includes("=");
|
|
17
10
|
}
|
|
18
11
|
const ENVIRONMENT_KEEPING_LAUNCHERS = new Set(["nice", "nohup", "time", "timeout", "stdbuf", "ionice", "chrt", "taskset", "command", "builtin", "caffeinate"]);
|
|
19
12
|
export function commandEnvironmentNames(argv) {
|
|
@@ -123,7 +116,7 @@ export function readProgramPosition(words) {
|
|
|
123
116
|
owedOperands -= 1;
|
|
124
117
|
continue;
|
|
125
118
|
}
|
|
126
|
-
if (lastRow?.assignmentOperands === true &&
|
|
119
|
+
if (lastRow?.assignmentOperands === true && isLauncherAssignmentOperand(t))
|
|
127
120
|
continue;
|
|
128
121
|
if (lastRow?.shellCommand !== undefined && !t.startsWith("-") && selectsShell(launchers[launchers.length - 1], j))
|
|
129
122
|
return chain(undefined, shellRefusal(launchers[launchers.length - 1].index, j));
|
|
@@ -41,6 +41,8 @@ function expandsBeyondText(w) {
|
|
|
41
41
|
const k = p.expansion?.kind;
|
|
42
42
|
if (k === "parameter" || k === "command" || k === "process" || k === "arithmetic")
|
|
43
43
|
return true;
|
|
44
|
+
if (p.expansion?.inner !== undefined)
|
|
45
|
+
return true;
|
|
44
46
|
if (k === "tilde" && p.expansion?.body !== "~")
|
|
45
47
|
return true;
|
|
46
48
|
if (p.literal !== undefined && (p.quotedBy === undefined || p.quotedBy === '"') && /[$`]/.test(p.literal))
|
|
@@ -36,9 +36,9 @@ export declare function bashReversibilityProbe(allow?: readonly string[], bounda
|
|
|
36
36
|
*/
|
|
37
37
|
export { FULL_SHELL_CONTRACT_ID } from "../../core/tool-catalog-entries.js";
|
|
38
38
|
/** The CC-verbatim exit-1 interpretation for `command`, or undefined when exit 1 means a real error.
|
|
39
|
-
*
|
|
40
|
-
* command
|
|
41
|
-
* (CC `cLp`). Exported for its unit tests. */
|
|
39
|
+
* Read off the lexer's segments: the last one (the last `;`/`&&`/`||`/newline statement's last `|` piece) → its
|
|
40
|
+
* written command word past the leading assignments bash strips, path prefix stripped;
|
|
41
|
+
* `git grep`/`git diff` special-cased (CC `cLp`). Exported for its unit tests. */
|
|
42
42
|
export declare function bashExitOneInterpretation(command: string): string | undefined;
|
|
43
43
|
export declare function canAutoBackground(command: string, depth?: number): boolean;
|
|
44
44
|
/** One observation of a running command, handed to {@link runShell}'s caller once per
|
package/dist/tools/fs/fs-bash.js
CHANGED
|
@@ -18,6 +18,8 @@ import { backgroundLifetimeNote } from "./fs-bash-background-text.js";
|
|
|
18
18
|
import { resolveBashTimeoutCaps, bashTimeoutCapsSec, bashTimeoutArgRefusal, bashTimeoutParamDescription, envErrorDetail, bashMaxOutputChars, clipShellOutput, writeShellOverflowFile, shellRecoveryHint, CWD_SENTINEL, stripCwdSentinel, } from "./fs-shared.js";
|
|
19
19
|
import { bashClassifyDefaultAllow, BASH_READONLY_DEFAULT_ALLOW, coarseReadonlyCheck, classifyBoundedReadonlyPollLoopDetailed, classifyCompoundReadonlyDetailed } from "./bash-readonly-classifier.js";
|
|
20
20
|
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
21
|
+
import { argvOf, compoundValueHidesRun, expansionHidesRun, expansionsInHeredocBody, lexShell } from "../../shell/lex.js";
|
|
22
|
+
import { arithmeticAssignedNames } from "./shell-read-bindings.js";
|
|
21
23
|
import { thrownText } from "../../core/thrown-value.js";
|
|
22
24
|
function shellCommandOf(args) {
|
|
23
25
|
const command = args?.command;
|
|
@@ -55,19 +57,15 @@ const EXIT1_INTERPRETATION = {
|
|
|
55
57
|
"[": "Condition is false",
|
|
56
58
|
};
|
|
57
59
|
export function bashExitOneInterpretation(command) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const lastSeg = lastStmt.split("|").at(-1)?.trim() ?? "";
|
|
67
|
-
const tokens = lastSeg.split(/\s+/).filter(Boolean);
|
|
60
|
+
const lexed = lexShell(command);
|
|
61
|
+
if ("parseFailure" in lexed)
|
|
62
|
+
return undefined;
|
|
63
|
+
const last = [...lexed.segments].reverse().find((s) => s.tokens.length > 0);
|
|
64
|
+
const argv = last === undefined ? [] : argvOf(last);
|
|
68
65
|
let i = 0;
|
|
69
|
-
while (i <
|
|
66
|
+
while (i < argv.length && argv[i].assignment?.bare === true && argv[i].atCommandPosition)
|
|
70
67
|
i++;
|
|
68
|
+
const tokens = argv.map((w) => w.folded);
|
|
71
69
|
const name = tokens[i]?.split("/").pop() ?? "";
|
|
72
70
|
if (name === "git") {
|
|
73
71
|
for (let j = i + 1; j < tokens.length; j++) {
|
|
@@ -103,31 +101,41 @@ function dataUriImageFromStdout(stdout) {
|
|
|
103
101
|
return undefined;
|
|
104
102
|
}
|
|
105
103
|
const AUTO_BACKGROUND_BLOCKED_FIRST_WORDS = new Set(["sleep"]);
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
104
|
+
function pastAutoBackgroundWrapperPrefixes(words) {
|
|
105
|
+
const folded = words.map((w) => w.folded);
|
|
106
|
+
let i = 0;
|
|
107
|
+
for (;;) {
|
|
108
|
+
const w = folded[i];
|
|
109
|
+
let next = i;
|
|
110
|
+
if (words[i]?.assignment?.bare === true && words[i].atCommandPosition && words[i].assignment.value.raw !== "")
|
|
111
|
+
next = i + 1;
|
|
112
|
+
else if (w === "time" || w === "nohup" || w === "command" || w === "builtin" || w === "noglob")
|
|
113
|
+
next = i + 1;
|
|
114
|
+
else if (w === "timeout") {
|
|
115
|
+
let j = i + 1;
|
|
116
|
+
while (/^-[A-Za-z]/.test(folded[j] ?? ""))
|
|
117
|
+
j++;
|
|
118
|
+
if (/^\d+(?:\.\d+)?[smhd]?$/.test(folded[j] ?? ""))
|
|
119
|
+
next = j + 1;
|
|
120
|
+
}
|
|
121
|
+
else if (w === "nice") {
|
|
122
|
+
next = i + 1;
|
|
123
|
+
if (folded[next] === "-n" && /^-?\d+$/.test(folded[next + 1] ?? ""))
|
|
124
|
+
next += 2;
|
|
125
|
+
else if (/^-n?-?\d+$/.test(folded[next] ?? ""))
|
|
126
|
+
next += 1;
|
|
127
|
+
}
|
|
128
|
+
else if (w === "stdbuf") {
|
|
129
|
+
let j = i + 1;
|
|
130
|
+
while (/^-[ioe]./.test(folded[j] ?? ""))
|
|
131
|
+
j++;
|
|
132
|
+
if (j > i + 1)
|
|
133
|
+
next = j;
|
|
128
134
|
}
|
|
135
|
+
if (next === i || next >= folded.length)
|
|
136
|
+
return words.slice(i);
|
|
137
|
+
i = next;
|
|
129
138
|
}
|
|
130
|
-
return s;
|
|
131
139
|
}
|
|
132
140
|
function commandBasename(token) {
|
|
133
141
|
const idx = token.lastIndexOf("/");
|
|
@@ -141,84 +149,47 @@ function commandStringPayload(tokens) {
|
|
|
141
149
|
if (!COMMAND_STRING_SHELLS.has(word))
|
|
142
150
|
return undefined;
|
|
143
151
|
const flagIdx = tokens.findIndex((t, i) => i > 0 && /^-[A-Za-z]*c[A-Za-z]*$/.test(t));
|
|
144
|
-
return flagIdx === -1 ? undefined : tokens
|
|
145
|
-
}
|
|
146
|
-
function unquotePayload(payload) {
|
|
147
|
-
const s = payload.trim();
|
|
148
|
-
const ansiC = /^\$'([\s\S]*)'$/.exec(s);
|
|
149
|
-
if (ansiC)
|
|
150
|
-
return (ansiC[1] ?? "").replace(/\\n/g, "\n").replace(/\\t/g, "\t");
|
|
151
|
-
const quoted = /^(['"])([\s\S]*)\1$/.exec(s);
|
|
152
|
-
return quoted ? (quoted[2] ?? "") : s;
|
|
152
|
+
return flagIdx === -1 ? undefined : tokens[flagIdx + 1];
|
|
153
153
|
}
|
|
154
154
|
const AUTO_BACKGROUND_MAX_WRAPPER_DEPTH = 3;
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
let cur = "";
|
|
158
|
-
let quote;
|
|
159
|
-
for (let i = 0; i < command.length; i++) {
|
|
160
|
-
const c = command[i];
|
|
161
|
-
if (quote !== undefined) {
|
|
162
|
-
cur += c;
|
|
163
|
-
if (c === quote)
|
|
164
|
-
quote = undefined;
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (c === '"' || c === "'") {
|
|
168
|
-
quote = c;
|
|
169
|
-
cur += c;
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
if (c === ";" || c === "\n" || c === "|" || (c === "&" && command[i + 1] === "&")) {
|
|
173
|
-
if ((c === "&" || c === "|") && command[i + 1] === c)
|
|
174
|
-
i++;
|
|
175
|
-
out.push(cur);
|
|
176
|
-
cur = "";
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
if (c === "\r" && command[i + 1] === "\n") {
|
|
180
|
-
out.push(cur);
|
|
181
|
-
cur = "";
|
|
182
|
-
i++;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
cur += c;
|
|
186
|
-
}
|
|
187
|
-
out.push(cur);
|
|
188
|
-
return out.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
155
|
+
function arithmeticInFront(text, depth) {
|
|
156
|
+
return arithmeticAssignedNames(text).length > 0 || (expansionsInHeredocBody(text)?.some((x) => x.unresolved !== undefined || x.expansion.kind === "command" || (x.expansion.kind === "arithmetic" ? depth >= AUTO_BACKGROUND_MAX_WRAPPER_DEPTH || arithmeticInFront(x.expansion.body ?? "", depth + 1) : expansionHidesRun(x.expansion))) ?? true);
|
|
189
157
|
}
|
|
190
158
|
export function canAutoBackground(command, depth = 0) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
159
|
+
const lexed = lexShell(command);
|
|
160
|
+
if ("parseFailure" in lexed)
|
|
161
|
+
return false;
|
|
162
|
+
const segments = lexed.segments;
|
|
163
|
+
const hidden = (e) => e !== undefined && e.kind !== "command" && e.kind !== "process" && e.kind !== "arithmetic" && expansionHidesRun(e);
|
|
164
|
+
const words = segments.flatMap((s) => s.tokens.flatMap((t) => (t.kind === "word" ? [t] : t.kind === "redirect" && t.target !== undefined ? [t.target] : [])));
|
|
165
|
+
if (words.some((w) => w.unresolved === "parameter_text" || compoundValueHidesRun(w) || w.expansions.some((p) => hidden(p.expansion))))
|
|
166
|
+
return false;
|
|
167
|
+
const heredocs = segments.flatMap((s) => s.tokens.flatMap((t) => (t.kind === "heredoc" && !t.quoted ? [expansionsInHeredocBody(t.body)] : [])));
|
|
168
|
+
if (heredocs.some((b) => b === undefined || b.some((x) => x.unresolved !== undefined || hidden(x.expansion))))
|
|
169
|
+
return false;
|
|
170
|
+
const arithmetic = [...words.flatMap((w) => w.expansions), ...heredocs.flatMap((b) => b ?? [])].flatMap((p) => (p.expansion?.kind === "arithmetic" ? [p.expansion.body ?? ""] : []));
|
|
171
|
+
const bodies = [...segments.flatMap((s) => s.substitutions), ...heredocs.flatMap((b) => (b ?? []).flatMap((x) => (x.expansion.kind === "command" ? [x.expansion.body ?? ""] : [])))].filter(((left) => (b) => { const k = left.indexOf(b); if (k < 0)
|
|
172
|
+
return true; left.splice(k, 1); return false; })([...arithmetic]));
|
|
173
|
+
if ((bodies.length > 0 && depth >= AUTO_BACKGROUND_MAX_WRAPPER_DEPTH) || bodies.some((b) => !canAutoBackground(b, depth + 1)) || arithmetic.some((b) => arithmeticInFront(b, depth + 1)))
|
|
174
|
+
return false;
|
|
175
|
+
const runs = segments.map((s) => pastAutoBackgroundWrapperPrefixes(argvOf(s)).map((w) => w.folded)).filter((t) => t.length > 0);
|
|
176
|
+
if (runs.length === 0)
|
|
177
|
+
return true;
|
|
178
|
+
for (const tokens of runs) {
|
|
179
|
+
const word = commandBasename(tokens[0]);
|
|
180
|
+
if (word === "git")
|
|
181
|
+
return false;
|
|
182
|
+
if (depth < AUTO_BACKGROUND_MAX_WRAPPER_DEPTH) {
|
|
194
183
|
for (let i = 0; i < tokens.length; i++) {
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
184
|
+
const payload = commandStringPayload(tokens.slice(i));
|
|
185
|
+
if (payload !== undefined && payload.trim() !== "" && !canAutoBackground(payload, depth + 1))
|
|
197
186
|
return false;
|
|
198
187
|
}
|
|
199
188
|
}
|
|
200
|
-
}
|
|
201
|
-
const segments = command
|
|
202
|
-
.split(/&&|\|\||;|\||\r?\n/)
|
|
203
|
-
.map((s) => s.trim())
|
|
204
|
-
.filter((s) => s.length > 0);
|
|
205
|
-
if (segments.length === 0)
|
|
206
|
-
return true;
|
|
207
|
-
for (const seg of segments) {
|
|
208
|
-
const tokens = stripAutoBackgroundWrapperPrefixes(seg).split(/\s+/);
|
|
209
|
-
const word = commandBasename(tokens[0] ?? "");
|
|
210
|
-
if (word === "git")
|
|
211
|
-
return false;
|
|
212
|
-
const payload = depth < AUTO_BACKGROUND_MAX_WRAPPER_DEPTH ? commandStringPayload(tokens) : undefined;
|
|
213
|
-
if (payload !== undefined && payload.trim() !== "" && !canAutoBackground(unquotePayload(payload), depth + 1))
|
|
214
|
-
return false;
|
|
215
189
|
if (word === "xargs" && tokens.slice(1).some((t) => commandBasename(t) === "git"))
|
|
216
190
|
return false;
|
|
217
191
|
}
|
|
218
|
-
|
|
219
|
-
if (AUTO_BACKGROUND_BLOCKED_FIRST_WORDS.has(firstWord))
|
|
220
|
-
return false;
|
|
221
|
-
return true;
|
|
192
|
+
return !AUTO_BACKGROUND_BLOCKED_FIRST_WORDS.has(commandBasename(runs[0][0]));
|
|
222
193
|
}
|
|
223
194
|
function msTimeoutToRequestedSec(timeoutMs) {
|
|
224
195
|
if (timeoutMs === undefined || !Number.isFinite(timeoutMs))
|
|
@@ -155,6 +155,18 @@ export interface ReadForModelOptions {
|
|
|
155
155
|
key: string;
|
|
156
156
|
object: BoundFileObject;
|
|
157
157
|
};
|
|
158
|
+
/** The read state the two dedup answers (unchanged since the last read, already in context) are judged against —
|
|
159
|
+
* absent ⇒ the reader's own live state. A carrier that delivers the read into a context other than the live one
|
|
160
|
+
* passes the state that context will hold (a compaction's restore: what the landed summary leaves); such a read is
|
|
161
|
+
* not held back by the disclosure throttle either (the disclosure the window remembers left with that context). */
|
|
162
|
+
dedupAgainst?: ReadonlyMap<string, ReadEntry>;
|
|
163
|
+
/** A carrier that presents the read as file data (an attachment frame) takes a PDF as a reference — its size and page
|
|
164
|
+
* count, no pipeline run — since the Read projection interleaves engine text with the file's. A bound read always does. */
|
|
165
|
+
pdfAsReference?: boolean;
|
|
166
|
+
/** The canonical key the carrier's judgment of this path landed on, for a carrier that judged it without holding the
|
|
167
|
+
* object (a bound read carries it as `bound.key`): a path whose final resolution is elsewhere is refused, the refusal
|
|
168
|
+
* marked `details.type: "target_moved"` so a frame says it in the attachment's own words. */
|
|
169
|
+
judgedKey?: string;
|
|
158
170
|
}
|
|
159
171
|
export interface ModelFileReaderOptions {
|
|
160
172
|
env: ExecutionEnv;
|
package/dist/tools/fs/fs-read.js
CHANGED
|
@@ -30,8 +30,8 @@ export function createModelFileReader(opts) {
|
|
|
30
30
|
refusal: envelope,
|
|
31
31
|
effects: NO_EFFECTS(),
|
|
32
32
|
});
|
|
33
|
-
const disclosureOf = (segments, outlet, key) => {
|
|
34
|
-
const plan = planReminderDisclosure({ segments, mark: reminderMark, outlet, defuseExactMark: false, throttle: { key, windows } });
|
|
33
|
+
const disclosureOf = (segments, outlet, key, throttled = true) => {
|
|
34
|
+
const plan = planReminderDisclosure({ segments, mark: reminderMark, outlet, defuseExactMark: false, ...(throttled ? { throttle: { key, windows } } : {}) });
|
|
35
35
|
const verdict = plan.verdict;
|
|
36
36
|
return { ...(verdict.reminder !== undefined || verdict.envelopeTags !== undefined ? { note: { kind: "disclosure", verdict } } : {}), effects: plan.effects };
|
|
37
37
|
};
|
|
@@ -57,8 +57,9 @@ export function createModelFileReader(opts) {
|
|
|
57
57
|
const r = await resolve(path, ctx);
|
|
58
58
|
if (!r.ok)
|
|
59
59
|
return refuse(errorResult(violationText("Read", r.violation), violationDetails(r.violation)));
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
const judgedKey = opts?.bound?.key ?? opts?.judgedKey;
|
|
61
|
+
if (judgedKey !== undefined && r.key !== judgedKey) {
|
|
62
|
+
return refuse(errorResult(`Error (Read): "${path.slice(0, 300)}" resolved to a different file while it was being checked, so it was not read.`, { type: "target_moved" }));
|
|
62
63
|
}
|
|
63
64
|
const bound = opts?.bound;
|
|
64
65
|
const readBytes = (key, laneCap) => bound === undefined ? env.readBinaryFile(key, ctx.signal) : bound.object.readBytes(laneCap, ctx.signal);
|
|
@@ -114,7 +115,7 @@ export function createModelFileReader(opts) {
|
|
|
114
115
|
};
|
|
115
116
|
}
|
|
116
117
|
const pdfOf = async (preRead) => {
|
|
117
|
-
if (bound !== undefined) {
|
|
118
|
+
if (bound !== undefined || opts?.pdfAsReference === true) {
|
|
118
119
|
if (!info.ok)
|
|
119
120
|
return refuse(errorResult(`Error (Read): cannot size PDF "${path.slice(0, 300)}": ${info.error.message.slice(0, 200)}`), "pdf");
|
|
120
121
|
let held = preRead;
|
|
@@ -232,7 +233,7 @@ export function createModelFileReader(opts) {
|
|
|
232
233
|
` cat "${path}" | jq '.cells | length' # Count total cells\n` +
|
|
233
234
|
` cat "${path}" | jq '.cells[] | select(.cell_type=="code") | .source' # All code sources`), "notebook");
|
|
234
235
|
}
|
|
235
|
-
const prevNb = state.get(r.key);
|
|
236
|
+
const prevNb = (opts?.dedupAgainst ?? state).get(r.key);
|
|
236
237
|
if (prevNb?.seededFromContext && !prevNb.isPartialView && prevNb.hash === hash) {
|
|
237
238
|
return unchanged({ kind: "already_in_context", key: r.key }, "notebook", "already-in-context", 1, total, total);
|
|
238
239
|
}
|
|
@@ -242,7 +243,7 @@ export function createModelFileReader(opts) {
|
|
|
242
243
|
const observation = { key: r.key, entry: { hash, totalLines: countLines(content), truncated: false, view: { start: 1, end: total } } };
|
|
243
244
|
if (rendered.blocks.length === 0)
|
|
244
245
|
notes.push({ kind: "notebook_no_cells" });
|
|
245
|
-
const disclosure = disclosureOf(rendered.blocks.filter((b) => b.type === "text").map((b) => b.text), "notebook", r.key);
|
|
246
|
+
const disclosure = disclosureOf(rendered.blocks.filter((b) => b.type === "text").map((b) => b.text), "notebook", r.key, opts?.dedupAgainst === undefined);
|
|
246
247
|
if (disclosure.note !== undefined)
|
|
247
248
|
notes.push(disclosure.note);
|
|
248
249
|
return {
|
|
@@ -292,7 +293,7 @@ export function createModelFileReader(opts) {
|
|
|
292
293
|
}
|
|
293
294
|
}
|
|
294
295
|
const truncated = start > 1 || end < total || partial !== undefined;
|
|
295
|
-
const prev = state.get(r.key);
|
|
296
|
+
const prev = (opts?.dedupAgainst ?? state).get(r.key);
|
|
296
297
|
if (total > 0 && prev?.seededFromContext && !prev.isPartialView && start === 1 && effLimit === undefined && prev.hash === hash) {
|
|
297
298
|
return unchanged({ kind: "already_in_context", key: r.key }, "text", "already-in-context", 1, total, total);
|
|
298
299
|
}
|
|
@@ -309,7 +310,7 @@ export function createModelFileReader(opts) {
|
|
|
309
310
|
notes.push(partial);
|
|
310
311
|
else if (truncated)
|
|
311
312
|
notes.push({ kind: "range", path, start, end, total });
|
|
312
|
-
const disclosure = disclosureOf([body], "read", r.key);
|
|
313
|
+
const disclosure = disclosureOf([body], "read", r.key, opts?.dedupAgainst === undefined);
|
|
313
314
|
if (disclosure.note !== undefined)
|
|
314
315
|
notes.push(disclosure.note);
|
|
315
316
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
3
|
-
import { type FsViolation, type ReadFileState } from "./safety.js";
|
|
3
|
+
import { type FsViolation, type ReadEntry, type ReadFileState } from "./safety.js";
|
|
4
4
|
import { type DecodedTextFile } from "./encoding.js";
|
|
5
5
|
import { type HomeViewsCarrier } from "./read-deny-home.js";
|
|
6
6
|
import { type ImageDownsampler } from "../../core/mcp.js";
|
|
@@ -426,8 +426,10 @@ export declare function seedReadFileStateFromContext(state: ReadFileState, key:
|
|
|
426
426
|
* the context, which a continuation (or a compaction inside it) cannot promise.
|
|
427
427
|
* - no `seededFromContext`: that flag means the text lives in the system-prompt lane and survives
|
|
428
428
|
* compaction. This text lives in the transcript and does not.
|
|
429
|
+
* A Read result's text carries that result's call id (`transmission`), as the live read's entry does, so a trim that
|
|
430
|
+
* drops the result retires the entry; a Write's post-condition rode no result body and carries none.
|
|
429
431
|
*/
|
|
430
|
-
export declare function seedReadFileStateFromTranscript(state: ReadFileState, key: string, content: string, lastReadAt: number): void;
|
|
432
|
+
export declare function seedReadFileStateFromTranscript(state: ReadFileState, key: string, content: string, lastReadAt: number, transmission?: string): void;
|
|
431
433
|
/**
|
|
432
434
|
* Copy a SESSION-inherited read-file state into a new task's map, under the new task's
|
|
433
435
|
* own containment judgment (CC parity: readFileState is session-scoped — minted once per REPL session,
|
|
@@ -459,30 +461,28 @@ export declare function inheritSessionReadFileState(target: ReadFileState, inher
|
|
|
459
461
|
* Two deliberate divergences from CC's bare clear(), each from sema's own structure:
|
|
460
462
|
* - SEEDED entries survive: their content lives in the system-prompt lane, which compaction never touches,
|
|
461
463
|
* so their dedup remains CORRECT (CC re-seeds the same family right after its clear — equivalent end state).
|
|
462
|
-
* -
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
* Read, and a next Read would miss the dedup stub and retransmit the whole file, recreating the exact
|
|
475
|
-
* waste this dedup exists to remove. `preserveKeys` is the caller's kept-tail set
|
|
476
|
-
* (see `maybeCompact`'s `candidateFiles` computation) — passed as keys already in `state`'s own
|
|
477
|
-
* coordinate (canonicalized the same way the Read/Edit/Write tools populate it), so a plain lookup
|
|
478
|
-
* suffices; no re-hash/re-register needed since the entry's content genuinely has not changed.
|
|
464
|
+
* - A file still visible in the compaction's KEPT tail (its read or its attachment frame is there) survives
|
|
465
|
+
* unmodified, for the same reason: the model's view of it did not change. Wiping it would make the
|
|
466
|
+
* read-before-edit gate reject an Edit the model can visibly justify, and the next Read retransmit the file.
|
|
467
|
+
* `preserveKeys` is that set, in `state`'s own coordinate.
|
|
468
|
+
* A file the compaction restores re-registers through its restore frame's own delivery (the reader's commit,
|
|
469
|
+
* after this clear), never here. The read-before-edit gate tightens as a consequence, matching CC: after a
|
|
470
|
+
* compaction the model must re-Read a summarized-away file before editing it.
|
|
471
|
+
*/
|
|
472
|
+
/**
|
|
473
|
+
* What the read state holds once a landed summary has taken its reads out of view — the ONE answer to "which entries
|
|
474
|
+
* survive a compaction": the seeded entries and `preserveKeys`. The summarized clear applies it to the live state; the
|
|
475
|
+
* restore's reads dedup against it (the context they deliver into is the one the summary leaves).
|
|
479
476
|
*/
|
|
477
|
+
export declare function readFileStateAfterSummary(state: ReadonlyMap<string, ReadEntry>, preserveKeys: ReadonlyArray<string>): ReadFileState;
|
|
480
478
|
/**
|
|
481
|
-
* The
|
|
479
|
+
* The OTHER ways a transmission leaves the model's view, and why this function is named for context
|
|
482
480
|
* SHRINK rather than compaction: the request-layer stale-result pass replaces an old tool result's
|
|
483
|
-
* content with a cleared marker
|
|
484
|
-
*
|
|
485
|
-
*
|
|
481
|
+
* content with a cleared marker, and the request guard's last-resort trim drops old messages — a Read's
|
|
482
|
+
* result or an attachment frame among them — from the request; no summary, no re-attachment, nothing changed in the
|
|
483
|
+
* session — and for a read the effect on the entry is identical: the body its dedup stub points at is no
|
|
484
|
+
* longer anywhere the model can see, so the entry must retire with it. A transmission is named by the
|
|
485
|
+
* tool call that carried it, or by the attachment frame (`attachmentTransmissionId`).
|
|
486
486
|
*
|
|
487
487
|
* Selection is by the TRANSMISSION, not by path — an entry a LATER Read re-transmitted carries that
|
|
488
488
|
* later call's id and so survives a clear of the earlier one, which a path-keyed retirement could not
|
|
@@ -494,19 +494,15 @@ export declare function inheritSessionReadFileState(target: ReadFileState, inher
|
|
|
494
494
|
* carries the id of the last Read that transmitted it, so if that result was cleared its id is in the list.
|
|
495
495
|
*/
|
|
496
496
|
export type ReadFileStateShrink =
|
|
497
|
-
/** A landed compaction:
|
|
497
|
+
/** A landed compaction: everything but {@link readFileStateAfterSummary} retires (`preserveKeys` stayed visible in the kept tail). */
|
|
498
498
|
{
|
|
499
499
|
kind: "summarized";
|
|
500
|
-
attachedComplete: ReadonlyArray<{
|
|
501
|
-
path: string;
|
|
502
|
-
content: string;
|
|
503
|
-
}>;
|
|
504
500
|
preserveKeys?: ReadonlyArray<string>;
|
|
505
501
|
}
|
|
506
|
-
/**
|
|
502
|
+
/** These transmissions left the request (a stale-result clear's markers, a trim's dropped messages). Nothing re-entered. */
|
|
507
503
|
| {
|
|
508
|
-
kind: "
|
|
509
|
-
|
|
504
|
+
kind: "transmissions-gone";
|
|
505
|
+
transmissions: ReadonlyArray<string>;
|
|
510
506
|
};
|
|
511
507
|
export declare function applyContextShrinkToReadFileState(state: ReadFileState, shrink: ReadFileStateShrink): void;
|
|
512
508
|
/** CC 2.1.198 verbatim skeleton: the ENOENT error body for a
|
|
@@ -320,9 +320,9 @@ export function isReadDedupStubResult(resultText) {
|
|
|
320
320
|
export function seedReadFileStateFromContext(state, key, content) {
|
|
321
321
|
state.set(key, { hash: sha256(normalizeFileText(content)), totalLines: countLines(content), truncated: false, lastReadAt: Date.now(), seededFromContext: true });
|
|
322
322
|
}
|
|
323
|
-
export function seedReadFileStateFromTranscript(state, key, content, lastReadAt) {
|
|
323
|
+
export function seedReadFileStateFromTranscript(state, key, content, lastReadAt, transmission) {
|
|
324
324
|
const text = normalizeFileText(content);
|
|
325
|
-
state.set(key, { hash: sha256(text), totalLines: countLines(text), truncated: false, lastReadAt });
|
|
325
|
+
state.set(key, { hash: sha256(text), totalLines: countLines(text), truncated: false, lastReadAt, ...(transmission !== undefined ? { transmittedByToolCallId: transmission } : {}) });
|
|
326
326
|
}
|
|
327
327
|
export async function inheritSessionReadFileState(target, inherited, judgeKey) {
|
|
328
328
|
for (const [key, entry] of inherited) {
|
|
@@ -333,28 +333,25 @@ export async function inheritSessionReadFileState(target, inherited, judgeKey) {
|
|
|
333
333
|
target.set(judged, entry);
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
|
+
export function readFileStateAfterSummary(state, preserveKeys) {
|
|
337
|
+
const preserve = new Set(preserveKeys);
|
|
338
|
+
return new Map([...state].filter(([k, v]) => v.seededFromContext === true || preserve.has(k)));
|
|
339
|
+
}
|
|
336
340
|
export function applyContextShrinkToReadFileState(state, shrink) {
|
|
337
|
-
if (shrink.kind === "
|
|
338
|
-
if (shrink.
|
|
341
|
+
if (shrink.kind === "transmissions-gone") {
|
|
342
|
+
if (shrink.transmissions.length === 0)
|
|
339
343
|
return;
|
|
340
|
-
const cleared = new Set(shrink.
|
|
344
|
+
const cleared = new Set(shrink.transmissions);
|
|
341
345
|
for (const [k, v] of [...state]) {
|
|
342
346
|
if (v.transmittedByToolCallId !== undefined && cleared.has(v.transmittedByToolCallId))
|
|
343
347
|
state.delete(k);
|
|
344
348
|
}
|
|
345
349
|
return;
|
|
346
350
|
}
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
if (v.seededFromContext !== true && !preserve.has(k))
|
|
351
|
+
const surviving = readFileStateAfterSummary(state, shrink.preserveKeys ?? []);
|
|
352
|
+
for (const k of [...state.keys()])
|
|
353
|
+
if (!surviving.has(k))
|
|
351
354
|
state.delete(k);
|
|
352
|
-
}
|
|
353
|
-
for (const f of attachedComplete) {
|
|
354
|
-
const totalLines = countLines(f.content);
|
|
355
|
-
const displayLines = f.content.length === 0 ? 0 : f.content.split("\n").length;
|
|
356
|
-
state.set(f.path, { hash: sha256(f.content), totalLines, truncated: false, view: { start: 1, end: displayLines }, lastReadAt: Date.now() });
|
|
357
|
-
}
|
|
358
355
|
}
|
|
359
356
|
export async function enoentMessage(env, key, cwd, signal, readDeny) {
|
|
360
357
|
let msg = `File does not exist. Note: your current working directory is ${cwd}.`;
|
|
@@ -40,7 +40,7 @@ async function envFinalWrite(env, key, content, signal, opts) {
|
|
|
40
40
|
fireEdited();
|
|
41
41
|
return r;
|
|
42
42
|
}
|
|
43
|
-
const NOTHING_WAS_WRITTEN = new Set(["already_exists", "precondition_failed"]);
|
|
43
|
+
const NOTHING_WAS_WRITTEN = new Set(["already_exists", "precondition_failed", "target_unavailable"]);
|
|
44
44
|
async function writeThroughEnv(env, key, content, signal, opts) {
|
|
45
45
|
if (env.writeFileGuarded !== undefined) {
|
|
46
46
|
const r = await env.writeFileGuarded(key, content, { canonicalPath: key, ...(opts?.exclusive === true ? { exclusive: true } : {}) }, signal);
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export * from "./read-deny.js";
|
|
|
14
14
|
export { readDenyHomesOf } from "./read-deny-home.js";
|
|
15
15
|
import { BASH_READONLY_DEFAULT_ALLOW } from "./bash-readonly-classifier.js";
|
|
16
16
|
export { BASH_CLASSIFY_EXTRA_ALLOW, bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
|
|
17
|
-
import { checkedReadDenyHomes, compileReadDeny, compileWriteDeny,
|
|
17
|
+
import { checkedReadDenyHomes, compileReadDeny, compileWriteDeny, readDenyBuiltinConfigOf, readDenyRowAnchor } from "./read-deny.js";
|
|
18
18
|
import { withGateTimeout } from "../../core/gate-bounded-await.js";
|
|
19
19
|
import { carriedReadFaceOf, carryHomeViews, homeViewsAt, placeReadDenyAtHome, placementViewsOf, readDenyPlacedLater, sayPlacementLost } from "./read-deny-home.js";
|
|
20
20
|
import { deploymentFaceClampNotice, resolveContainmentFace } from "./containment-face.js";
|
|
@@ -142,7 +142,7 @@ function bandOwnTables(env, opts) {
|
|
|
142
142
|
const carriedViews = carried ? placementViewsOf(opts.readDenyHomes) : undefined;
|
|
143
143
|
if (carried && carriedViews === undefined)
|
|
144
144
|
sayPlacementLost();
|
|
145
|
-
const placesNothing = !unplaced.entries.some((e) =>
|
|
145
|
+
const placesNothing = !unplaced.entries.some((e) => readDenyRowAnchor(e.pattern) !== "none");
|
|
146
146
|
const late = carried || placesNothing ? undefined : readDenyPlacedLater(carryHomeViews(unplaced, homeViewsAt(env, [])), () => placeReadDenyAtHome(env, compileAt, readDenyHomes));
|
|
147
147
|
const readDeny = late?.matcher ??
|
|
148
148
|
(carried
|
|
@@ -41,7 +41,7 @@ export declare function readDenyLinksOf(env: ExecutionEnv, entries: readonly {
|
|
|
41
41
|
/**
|
|
42
42
|
* Which rows one placement reads, and where their heads are spelled:
|
|
43
43
|
* · `home` — the home-anchored rows (`~/…`), under each home; the row's run anchors it there;
|
|
44
|
-
* · `root` — the ROOTED rows (`/app/config-lkg.json`, {@link
|
|
44
|
+
* · `root` — the ROOTED rows (`/app/config-lkg.json`, {@link readDenyRowAnchor} `root`), under the filesystem root; the run
|
|
45
45
|
* anchors it there. A root is no home: read whether or not a home answered;
|
|
46
46
|
* · `unanchored` — the rows that open with the configuration directory's name (`~/.sema/…`, {@link agentConfigDirTail})
|
|
47
47
|
* under each spelling of the root the host declares for that directory (`RunnerDeps.agentConfigDir`): the head BELOW
|
|
@@ -4,7 +4,7 @@ import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, withGateTimeout } from "../../core/gate-
|
|
|
4
4
|
import { thrownText } from "../../core/thrown-value.js";
|
|
5
5
|
import { agentConfigDirTail } from "../../core/agent-config-dir.js";
|
|
6
6
|
import { validatePath } from "../../core/effective-path-target.js";
|
|
7
|
-
import {
|
|
7
|
+
import { readDenyPatternRun, readDenyRowAnchor, readDenyRunHead } from "./read-deny.js";
|
|
8
8
|
export const AUXILIARY_HOME_PROBE_MS = 2_000;
|
|
9
9
|
const said = new Set();
|
|
10
10
|
function sayOnce(kind, line) {
|
|
@@ -126,13 +126,14 @@ function anchoredRowsOf(entries, anchor) {
|
|
|
126
126
|
return rows;
|
|
127
127
|
}
|
|
128
128
|
function rowHeadOf(pattern, caseSensitive, anchor) {
|
|
129
|
+
const rowAnchor = readDenyRowAnchor(pattern);
|
|
129
130
|
if (anchor.at === "root") {
|
|
130
|
-
const head =
|
|
131
|
+
const head = rowAnchor === "root" ? literalHeadOf(pattern) : "";
|
|
131
132
|
return head.length > 0 ? head : undefined;
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
-
if (!homeAnchored)
|
|
134
|
+
if (rowAnchor !== "home")
|
|
135
135
|
return undefined;
|
|
136
|
+
const { run } = readDenyPatternRun(pattern);
|
|
136
137
|
if (anchor.at === "unanchored") {
|
|
137
138
|
const tail = agentConfigDirTail(run, caseSensitive);
|
|
138
139
|
return tail === undefined ? undefined : literalHeadOf(tail);
|