@nathapp/nax-agent 0.87.1 → 0.88.0-canary.2
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 +14 -0
- package/dist/coding-tools/coding-tool-support.d.ts +16 -0
- package/dist/coding-tools/coding-tool-support.js +12 -0
- package/dist/command-safety/rule-scorer.d.ts +1 -1
- package/dist/command-safety/rule-scorer.js +11 -113
- package/dist/config/schemas-sandbox.d.ts +1 -0
- package/dist/config/schemas-sandbox.js +9 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/internal/process-kill.d.ts +6 -2
- package/dist/internal/process-kill.js +6 -2
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +1 -0
- package/dist/mcp/connect.js +15 -1
- package/dist/mcp/types.d.ts +6 -0
- package/dist/native/auth.d.ts +2 -2
- package/dist/native/auth.js +4 -4
- package/dist/native/errors.js +7 -0
- package/dist/native/failed-round-trip-spend.d.ts +16 -0
- package/dist/native/failed-round-trip-spend.js +33 -0
- package/dist/native/session/partial-spend.d.ts +20 -0
- package/dist/native/session/partial-spend.js +14 -0
- package/dist/native/session/session.js +11 -4
- package/dist/native/session/turn-complete-step.js +23 -3
- package/dist/native/session-adapter.js +3 -2
- package/dist/permissions/bash-lex.js +36 -4
- package/dist/sandbox/claude-settings.d.ts +46 -0
- package/dist/sandbox/claude-settings.js +76 -0
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/index.js +1 -0
- package/dist/sandbox/launcher.js +4 -0
- package/dist/sandbox/messages.d.ts +1 -1
- package/dist/sandbox/messages.js +15 -3
- package/dist/sandbox/policy-builder.js +27 -2
- package/dist/sandbox/session-sandbox.d.ts +3 -0
- package/dist/sandbox/session-sandbox.js +9 -0
- package/dist/sandbox/srt-backend.d.ts +10 -1
- package/dist/sandbox/srt-backend.js +14 -4
- package/dist/sandbox/types.d.ts +23 -0
- package/dist/session/agent-session-options.js +1 -1
- package/dist/session/agent-session-types.d.ts +1 -1
- package/dist/session/native-backend-options.js +5 -3
- package/dist/session/session-backend.d.ts +9 -0
- package/dist/session/session-tool-support.d.ts +1 -1
- package/dist/session/session-tool-support.js +4 -2
- package/dist/session/session-types.d.ts +10 -0
- package/dist/tools/bash.d.ts +7 -1
- package/dist/tools/bash.js +18 -3
- package/dist/tools/denial-redirect.d.ts +22 -7
- package/dist/tools/denial-redirect.js +29 -13
- package/dist/tools/git-flags/locate.d.ts +47 -0
- package/dist/tools/git-flags/locate.js +361 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/nax-dirs.d.ts +22 -0
- package/dist/tools/nax-dirs.js +89 -0
- package/dist/tools/nax-evasion.d.ts +19 -0
- package/dist/tools/nax-evasion.js +88 -0
- package/dist/tools/nax-revert.d.ts +34 -0
- package/dist/tools/nax-revert.js +233 -0
- package/dist/tools/nax-stash-restore.d.ts +7 -0
- package/dist/tools/nax-stash-restore.js +92 -0
- package/dist/tools/nax-state-guard.d.ts +18 -0
- package/dist/tools/nax-state-guard.js +478 -0
- package/dist/tools/policy-bash-raw.d.ts +3 -0
- package/dist/tools/policy-bash-raw.js +40 -12
- package/dist/tools/policy-bash.d.ts +3 -0
- package/dist/tools/policy-bash.js +38 -9
- package/dist/tools/policy-command-branch.d.ts +3 -0
- package/dist/tools/policy-command-branch.js +3 -1
- package/dist/tools/policy.js +13 -0
- package/dist/tools/provider-advertise.d.ts +13 -0
- package/dist/tools/provider-advertise.js +41 -1
- package/dist/tools/provider-instructions.d.ts +33 -0
- package/dist/tools/provider-instructions.js +63 -0
- package/dist/tools/provider-types.d.ts +12 -0
- package/dist/tools/run-command.d.ts +15 -0
- package/dist/tools/run-command.js +16 -1
- package/dist/tools/tool-audit.d.ts +1 -1
- package/package.json +3 -3
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* reaching `resolveWithin` back would close a cycle.
|
|
11
11
|
*/
|
|
12
12
|
import type { BashApprovalMode } from "#src/config/bash-approval";
|
|
13
|
+
import type { NaxDirIndex } from "./nax-dirs.ts";
|
|
13
14
|
import type { OwnedPathsPolicy } from "./owned-paths.ts";
|
|
14
15
|
import type { CompiledEntry } from "./policy-match.ts";
|
|
15
16
|
import type { PolicyVerdict, ToolScope } from "./types.ts";
|
|
@@ -26,6 +27,8 @@ export interface BashCommandBranchArgs {
|
|
|
26
27
|
/** S3-2: host-owned path rules; the `raw` screen hands it one candidate per live frame. */
|
|
27
28
|
readonly ownedPaths: OwnedPathsPolicy;
|
|
28
29
|
readonly resolvedRoot: string;
|
|
30
|
+
/** Per-policy cache of the `.nax` directories below the root, shared by every Bash call (#2493). */
|
|
31
|
+
readonly naxDirs?: NaxDirIndex;
|
|
29
32
|
readonly denyBy: ReadonlyMap<string, CompiledEntry>;
|
|
30
33
|
readonly askBy: ReadonlyMap<string, CompiledEntry>;
|
|
31
34
|
readonly resolvePath: (candidate: string, cwd: string) => string | null;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { checkBashCommand } from "./policy-bash.js";
|
|
13
13
|
import { screenRawBashCommand } from "./policy-bash-raw.js";
|
|
14
14
|
export function commandBranch(args) {
|
|
15
|
-
const { tool, scope, input, grant, bashApproval, rawBashRefusal, sandboxWrapped, ownedPaths, resolvedRoot, denyBy, askBy, resolvePath, deny, askVerdict, } = args;
|
|
15
|
+
const { tool, scope, input, grant, bashApproval, rawBashRefusal, sandboxWrapped, ownedPaths, resolvedRoot, naxDirs, denyBy, askBy, resolvePath, deny, askVerdict, } = args;
|
|
16
16
|
if (scope.commandField === undefined)
|
|
17
17
|
return undefined;
|
|
18
18
|
if (bashApproval === "raw") {
|
|
@@ -23,6 +23,7 @@ export function commandBranch(args) {
|
|
|
23
23
|
command: input[scope.commandField],
|
|
24
24
|
initialPath: resolvedRoot,
|
|
25
25
|
root: resolvedRoot,
|
|
26
|
+
...(naxDirs !== undefined ? { naxDirs } : {}),
|
|
26
27
|
sandboxWrapped: sandboxWrapped === true,
|
|
27
28
|
resolvePath,
|
|
28
29
|
ownedPaths,
|
|
@@ -40,6 +41,7 @@ export function commandBranch(args) {
|
|
|
40
41
|
...(denyEntry !== undefined ? { denyEntry } : {}),
|
|
41
42
|
...(askEntry !== undefined ? { askEntry } : {}),
|
|
42
43
|
initialPath: resolvedRoot,
|
|
44
|
+
...(naxDirs !== undefined ? { naxDirs } : {}),
|
|
43
45
|
resolvePath,
|
|
44
46
|
});
|
|
45
47
|
if (result.kind === "deny") {
|
package/dist/tools/policy.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { relative, resolve, sep } from "node:path";
|
|
17
17
|
import { realOrRaw } from "#src/internal/realpath";
|
|
18
|
+
import { claudeSettingsWriteRefusal } from "#src/sandbox/claude-settings";
|
|
18
19
|
import { validateArgv } from "./exec-guard.js";
|
|
20
|
+
import { createNaxDirIndex } from "./nax-dirs.js";
|
|
19
21
|
import { EMPTY_OWNED_PATHS_POLICY } from "./owned-paths.js";
|
|
20
22
|
import { commandBranch } from "./policy-command-branch.js";
|
|
21
23
|
import { compileArgvPattern, compileRuleMap, globToRegExp, matchedArgvSource, matchedGlobSource, matchedRulePatterns, matchesAny, matchesArgvGrant, } from "./policy-match.js";
|
|
@@ -35,6 +37,8 @@ function isFieldlessScope(scope) {
|
|
|
35
37
|
}
|
|
36
38
|
export function compileToolPolicy(grants, root, options) {
|
|
37
39
|
const resolvedRoot = realOrRaw(root);
|
|
40
|
+
// One downward `.nax` scan per compiled policy, shared by every Bash call it checks (#2493).
|
|
41
|
+
const naxDirs = createNaxDirIndex();
|
|
38
42
|
const owned = options?.ownedPaths ?? EMPTY_OWNED_PATHS_POLICY;
|
|
39
43
|
// nax#2115: the SAME transform `relativeTo` applies to every checked path, so
|
|
40
44
|
// the guard compares like with like. A path outside the root yields a
|
|
@@ -88,6 +92,14 @@ export function compileToolPolicy(grants, root, options) {
|
|
|
88
92
|
* denied and an ask on an ungranted call never becomes an approval prompt.
|
|
89
93
|
*/
|
|
90
94
|
function applyPathRules(tool, rel, state) {
|
|
95
|
+
// US-006 (branch-review B1): the session-root Claude Code settings files are
|
|
96
|
+
// refused to the same mutating path tools the OS sandbox denies writes to.
|
|
97
|
+
// The port's `writeRefusal` cannot carry this one -- an embedder that injects
|
|
98
|
+
// nothing still gets the sandbox deny, so it must get the tool deny too --
|
|
99
|
+
// and both guards read the ONE list in `#src/sandbox/claude-settings`.
|
|
100
|
+
const settingsRefusal = claudeSettingsWriteRefusal(tool, rel, resolvedRoot);
|
|
101
|
+
if (settingsRefusal !== undefined)
|
|
102
|
+
return deny(`${tool} may not modify ${settingsRefusal}`);
|
|
91
103
|
const ownedRefusal = owned.writeRefusal(tool, rel, { exemptRel: ownedWriteExemption, optIns: naxOptIns });
|
|
92
104
|
if (ownedRefusal !== undefined)
|
|
93
105
|
return deny(`${tool} may not modify ${ownedRefusal}`);
|
|
@@ -220,6 +232,7 @@ export function compileToolPolicy(grants, root, options) {
|
|
|
220
232
|
sandboxWrapped: options?.sandboxWrapped,
|
|
221
233
|
ownedPaths: owned,
|
|
222
234
|
resolvedRoot,
|
|
235
|
+
naxDirs,
|
|
223
236
|
denyBy,
|
|
224
237
|
askBy,
|
|
225
238
|
resolvePath: (candidate, cwd) => resolveWithin(resolvedRoot, resolve(cwd, candidate), owned),
|
|
@@ -40,6 +40,19 @@ export interface ResolvedProviderTools {
|
|
|
40
40
|
* back apart (src/tools/provider-adapt.ts's header).
|
|
41
41
|
*/
|
|
42
42
|
readonly entries: readonly ProviderGrantEntry[];
|
|
43
|
+
/**
|
|
44
|
+
* What each attached provider that still advertises a tool says about using
|
|
45
|
+
* it (#2517), in provider order — the raw texts, not the rendered section:
|
|
46
|
+
* rendering is a prompt concern (`provider-instructions.ts`), and a caller
|
|
47
|
+
* that reorders or drops entries must be able to (nax US-005 renders them).
|
|
48
|
+
*
|
|
49
|
+
* ABSENT when no provider contributed one, so "nobody spoke" and "everyone
|
|
50
|
+
* was silent" stay distinguishable rather than collapsing to `[]`.
|
|
51
|
+
*/
|
|
52
|
+
readonly instructions?: readonly {
|
|
53
|
+
providerId: string;
|
|
54
|
+
text: string;
|
|
55
|
+
}[];
|
|
43
56
|
}
|
|
44
57
|
export declare function resolveProviderTools(providers: readonly ToolProvider[], stage: string, workdir: string, options?: {
|
|
45
58
|
/**
|
|
@@ -13,6 +13,28 @@ import { adaptProviderTool } from "./provider-adapt.js";
|
|
|
13
13
|
import { expandProviderGrants } from "./provider-grants.js";
|
|
14
14
|
import { sanitizeProviderTools } from "./provider-sanitize.js";
|
|
15
15
|
import { providerAttachesTo } from "./provider-types.js";
|
|
16
|
+
/**
|
|
17
|
+
* One provider's instructions, or `undefined` when it has none to give.
|
|
18
|
+
*
|
|
19
|
+
* A provider that throws costs one warn and nothing else: its tools stay
|
|
20
|
+
* advertised, because instructions are an aid and a broken aid must never
|
|
21
|
+
* withdraw a working capability. Never rejects.
|
|
22
|
+
*/
|
|
23
|
+
async function instructionsFor(provider, workdir) {
|
|
24
|
+
if (provider.instructions === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
try {
|
|
27
|
+
const text = await provider.instructions(workdir);
|
|
28
|
+
return typeof text === "string" && text !== "" ? text : undefined;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
getSafeLogger()?.warn("mcp", `[provider] ${provider.id} instructions failed`, {
|
|
32
|
+
workdir,
|
|
33
|
+
reason: String(error),
|
|
34
|
+
});
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
16
38
|
export async function resolveProviderTools(providers, stage, workdir, options) {
|
|
17
39
|
const tools = [];
|
|
18
40
|
const entries = [];
|
|
@@ -20,6 +42,7 @@ export async function resolveProviderTools(providers, stage, workdir, options) {
|
|
|
20
42
|
const skipped = [];
|
|
21
43
|
const withheld = [];
|
|
22
44
|
const providerIdByTool = new Map();
|
|
45
|
+
const instructions = [];
|
|
23
46
|
for (const provider of providers) {
|
|
24
47
|
if (!providerAttachesTo(provider, stage)) {
|
|
25
48
|
skipped.push({ providerId: provider.id, reason: "stage-mismatch", stages: provider.stages });
|
|
@@ -40,12 +63,29 @@ export async function resolveProviderTools(providers, stage, workdir, options) {
|
|
|
40
63
|
localNames.push(tool.localName);
|
|
41
64
|
}
|
|
42
65
|
entries.push({ providerId: provider.id, localNames });
|
|
66
|
+
// Only a provider the model can actually use gets to speak: instructions
|
|
67
|
+
// for a capability `admits` withheld would describe tools that are not in
|
|
68
|
+
// the list, which is worse than saying nothing (#2517).
|
|
69
|
+
if (localNames.length > 0) {
|
|
70
|
+
const text = await instructionsFor(provider, workdir);
|
|
71
|
+
if (text !== undefined)
|
|
72
|
+
instructions.push({ providerId: provider.id, text });
|
|
73
|
+
}
|
|
43
74
|
}
|
|
44
75
|
catch (error) {
|
|
45
76
|
failures.push({ providerId: provider.id, reason: String(error) });
|
|
46
77
|
}
|
|
47
78
|
}
|
|
48
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
tools,
|
|
81
|
+
grants: expandProviderGrants(entries),
|
|
82
|
+
failures,
|
|
83
|
+
skipped,
|
|
84
|
+
withheld,
|
|
85
|
+
providerIdByTool,
|
|
86
|
+
entries,
|
|
87
|
+
...(instructions.length > 0 ? { instructions } : {}),
|
|
88
|
+
};
|
|
49
89
|
}
|
|
50
90
|
/**
|
|
51
91
|
* Bytes a tool list costs the prompt, paid on EVERY hop whether or not any
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `## MCP servers` section (#2517): what each MCP server said about itself
|
|
3
|
+
* in its `initialize` result, rendered as one block of system prompt text.
|
|
4
|
+
*
|
|
5
|
+
* Those instructions come from an external process, exactly like a `discovered`
|
|
6
|
+
* provider tool's description, so they are the same prompt-injection surface and
|
|
7
|
+
* get the same first treatment: control characters are stripped, not trusted
|
|
8
|
+
* (`provider-sanitize.ts`). Line breaks and tabs are kept, because instructions
|
|
9
|
+
* are prose and a tab or a list break is formatting the server meant to send.
|
|
10
|
+
*
|
|
11
|
+
* They are then capped twice. Per server, so one verbose server cannot crowd its
|
|
12
|
+
* neighbours out of the section. In total, so the section's context cost stays
|
|
13
|
+
* bounded on every hop — the tax nax#1991's context-burn report is about. A cut
|
|
14
|
+
* per-server text says so with `…(truncated)`; a server that does not fit the
|
|
15
|
+
* total is dropped whole and the section says how many were (a half-rendered
|
|
16
|
+
* set of instructions is worse than one that admits it is incomplete).
|
|
17
|
+
*/
|
|
18
|
+
/** One server's instructions as the provider reported them. */
|
|
19
|
+
type ProviderInstructionEntry = {
|
|
20
|
+
readonly providerId: string;
|
|
21
|
+
readonly text: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER = 2000;
|
|
24
|
+
export declare const MCP_INSTRUCTIONS_MAX_CHARS_TOTAL = 6000;
|
|
25
|
+
/**
|
|
26
|
+
* The section, or `undefined` when there is nothing to say — an empty section
|
|
27
|
+
* would spend prompt on a heading with no content under it.
|
|
28
|
+
*
|
|
29
|
+
* Per-server <= total, so the first server always fits and a non-empty input
|
|
30
|
+
* always renders at least one server.
|
|
31
|
+
*/
|
|
32
|
+
export declare function renderProviderInstructions(entries: readonly ProviderInstructionEntry[]): string | undefined;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `## MCP servers` section (#2517): what each MCP server said about itself
|
|
3
|
+
* in its `initialize` result, rendered as one block of system prompt text.
|
|
4
|
+
*
|
|
5
|
+
* Those instructions come from an external process, exactly like a `discovered`
|
|
6
|
+
* provider tool's description, so they are the same prompt-injection surface and
|
|
7
|
+
* get the same first treatment: control characters are stripped, not trusted
|
|
8
|
+
* (`provider-sanitize.ts`). Line breaks and tabs are kept, because instructions
|
|
9
|
+
* are prose and a tab or a list break is formatting the server meant to send.
|
|
10
|
+
*
|
|
11
|
+
* They are then capped twice. Per server, so one verbose server cannot crowd its
|
|
12
|
+
* neighbours out of the section. In total, so the section's context cost stays
|
|
13
|
+
* bounded on every hop — the tax nax#1991's context-burn report is about. A cut
|
|
14
|
+
* per-server text says so with `…(truncated)`; a server that does not fit the
|
|
15
|
+
* total is dropped whole and the section says how many were (a half-rendered
|
|
16
|
+
* set of instructions is worse than one that admits it is incomplete).
|
|
17
|
+
*/
|
|
18
|
+
export const MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER = 2000;
|
|
19
|
+
export const MCP_INSTRUCTIONS_MAX_CHARS_TOTAL = 6000;
|
|
20
|
+
const SECTION_HEADING = "## MCP servers";
|
|
21
|
+
const TRUNCATION_MARKER = "…(truncated)";
|
|
22
|
+
// C0 controls except tab (\u0009) and newline (\u000A), plus DEL. Carriage return
|
|
23
|
+
// (\u000D) is NOT exempt: it is a line-break-ish byte that no MCP server needs in
|
|
24
|
+
// prompt text, and keeping it would let a CRLF body smuggle a bare CR into the
|
|
25
|
+
// system prompt.
|
|
26
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping control bytes is the point of this regex
|
|
27
|
+
const CONTROL_CHARS = /[\u0000-\u0008\u000B-\u001F\u007F]/g;
|
|
28
|
+
/**
|
|
29
|
+
* Caps text at `max` code POINTS, so a cut never splits a surrogate pair the
|
|
30
|
+
* way `String.prototype.slice` would. The marker is added on top of the cap:
|
|
31
|
+
* a cut text is `max` characters of content plus the note that it was cut.
|
|
32
|
+
*/
|
|
33
|
+
function capText(text, max) {
|
|
34
|
+
const points = [...text];
|
|
35
|
+
return points.length <= max ? text : `${points.slice(0, max).join("")}${TRUNCATION_MARKER}`;
|
|
36
|
+
}
|
|
37
|
+
function serverBlock(entry) {
|
|
38
|
+
const text = capText(entry.text.replace(CONTROL_CHARS, ""), MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER);
|
|
39
|
+
return `### ${entry.providerId}\n${text}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The section, or `undefined` when there is nothing to say — an empty section
|
|
43
|
+
* would spend prompt on a heading with no content under it.
|
|
44
|
+
*
|
|
45
|
+
* Per-server <= total, so the first server always fits and a non-empty input
|
|
46
|
+
* always renders at least one server.
|
|
47
|
+
*/
|
|
48
|
+
export function renderProviderInstructions(entries) {
|
|
49
|
+
if (entries.length === 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
const blocks = [];
|
|
52
|
+
let total = 0;
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const block = serverBlock(entry);
|
|
55
|
+
if (total + block.length > MCP_INSTRUCTIONS_MAX_CHARS_TOTAL)
|
|
56
|
+
break;
|
|
57
|
+
total += block.length;
|
|
58
|
+
blocks.push(block);
|
|
59
|
+
}
|
|
60
|
+
const omitted = entries.length - blocks.length;
|
|
61
|
+
const tail = omitted === 0 ? "" : `\n\n(instructions omitted for ${omitted} more MCP server(s))`;
|
|
62
|
+
return [SECTION_HEADING, ...blocks].join("\n\n") + tail;
|
|
63
|
+
}
|
|
@@ -31,6 +31,18 @@ export interface ToolProvider {
|
|
|
31
31
|
* run executes stories in parallel worktrees.
|
|
32
32
|
*/
|
|
33
33
|
tools(workdir: string): Promise<readonly ProviderTool[]>;
|
|
34
|
+
/**
|
|
35
|
+
* What the provider says about USING it (#2517), with the same
|
|
36
|
+
* `workdir` contract as `tools` — an MCP server's own `initialize`
|
|
37
|
+
* instructions, plus whatever the host configured. Optional: a provider with
|
|
38
|
+
* nothing to say contributes no section.
|
|
39
|
+
*
|
|
40
|
+
* Called only for a provider that attached to this stage AND still has at
|
|
41
|
+
* least one advertised tool after `admits`, so instructions never describe a
|
|
42
|
+
* capability the model cannot see, and an empty or throwing answer costs the
|
|
43
|
+
* caller nothing (`provider-advertise.ts`).
|
|
44
|
+
*/
|
|
45
|
+
instructions?(workdir: string): Promise<string | undefined>;
|
|
34
46
|
}
|
|
35
47
|
/**
|
|
36
48
|
* No `__`: that sequence is the namespace separator, and an id containing it
|
|
@@ -104,4 +104,19 @@ export declare function substituteCommand(template: string, values: Record<strin
|
|
|
104
104
|
export declare function substituteCommandSpec(spec: QualityCommandSpec, values: Record<string, string | readonly string[]>): QualityCommandSpec | {
|
|
105
105
|
error: string;
|
|
106
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* US-006: the platform seam `execSandboxNote` reads to decide whether the
|
|
109
|
+
* description carries the macOS ps note.
|
|
110
|
+
*
|
|
111
|
+
* Kept out of `src/tools/index.ts` on purpose: `src/index.ts` takes named
|
|
112
|
+
* exports from that barrel, so a seam there would reach the package's public
|
|
113
|
+
* `.` entry, where `_` names are forbidden. It DOES reach `./internal`, because
|
|
114
|
+
* `src/internal.ts` star-exports this module — which is exactly where a
|
|
115
|
+
* `_*Deps` seam belongs (nax-only, outside semver), and why adding it here
|
|
116
|
+
* needed the one-line api/ snapshot update. Tests reach it through
|
|
117
|
+
* `#src/tools/run-command`.
|
|
118
|
+
*/
|
|
119
|
+
export declare const _runCommandToolDeps: {
|
|
120
|
+
platform: () => NodeJS.Platform;
|
|
121
|
+
};
|
|
107
122
|
export declare function createRunCommandTool(declared: ReadonlyMap<string, QualityCommandSpec>, opts?: RunCommandToolOptions): CodingTool;
|
|
@@ -194,10 +194,25 @@ function otherCommandsDeclaring(declared, key, currentCommand) {
|
|
|
194
194
|
}
|
|
195
195
|
return names;
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* US-006: the platform seam `execSandboxNote` reads to decide whether the
|
|
199
|
+
* description carries the macOS ps note.
|
|
200
|
+
*
|
|
201
|
+
* Kept out of `src/tools/index.ts` on purpose: `src/index.ts` takes named
|
|
202
|
+
* exports from that barrel, so a seam there would reach the package's public
|
|
203
|
+
* `.` entry, where `_` names are forbidden. It DOES reach `./internal`, because
|
|
204
|
+
* `src/internal.ts` star-exports this module — which is exactly where a
|
|
205
|
+
* `_*Deps` seam belongs (nax-only, outside semver), and why adding it here
|
|
206
|
+
* needed the one-line api/ snapshot update. Tests reach it through
|
|
207
|
+
* `#src/tools/run-command`.
|
|
208
|
+
*/
|
|
209
|
+
export const _runCommandToolDeps = {
|
|
210
|
+
platform: () => process.platform,
|
|
211
|
+
};
|
|
197
212
|
function execSandboxNote(launcher) {
|
|
198
213
|
const state = launcher?.state;
|
|
199
214
|
if (state?.kind === "available")
|
|
200
|
-
return ` An "argv" call runs ${sandboxSentence(state.network, state.sharedTmp !== false)}`;
|
|
215
|
+
return ` An "argv" call runs ${sandboxSentence(state.network, state.sharedTmp !== false, _runCommandToolDeps.platform())}`;
|
|
201
216
|
if (state?.kind === "unavailable")
|
|
202
217
|
return ` ${unsandboxedSentence(state.reason)}`;
|
|
203
218
|
return "";
|
|
@@ -202,7 +202,7 @@ export interface ToolSurfaceDraft {
|
|
|
202
202
|
/** A draft stamped with the transport (and, on ACP, the narrowed profile). */
|
|
203
203
|
export interface ToolSurfaceRecord extends ToolSurfaceDraft {
|
|
204
204
|
readonly transport: "native" | "acp";
|
|
205
|
-
readonly profile?: "full" | "read";
|
|
205
|
+
readonly profile?: "full" | "review" | "read";
|
|
206
206
|
}
|
|
207
207
|
export declare function createToolAuditSink(opts: {
|
|
208
208
|
dir: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nathapp/nax-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.88.0-canary.2",
|
|
4
4
|
"description": "nax's native coding agent: session contract, native loop, tools, permissions, sandbox, command-safety.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "William Khoo",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@anthropic-ai/sandbox-runtime": "0.0.77",
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
50
|
-
"@nathapp/nax-ai": "0.
|
|
50
|
+
"@nathapp/nax-ai": "0.88.0-canary.2",
|
|
51
51
|
"zod": "^4.3.6"
|
|
52
52
|
},
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public",
|
|
55
55
|
"registry": "https://registry.npmjs.org/",
|
|
56
56
|
"provenance": true,
|
|
57
|
-
"tag": "
|
|
57
|
+
"tag": "canary"
|
|
58
58
|
}
|
|
59
59
|
}
|