@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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to `@nathapp/nax-agent` are recorded here. The format follow
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). While the version is `0.x`, a minor
|
|
5
5
|
release may change the public API.
|
|
6
6
|
|
|
7
|
+
## [0.88.0-canary.2] - 2026-10-15
|
|
8
|
+
|
|
9
|
+
- No changes in this package. Released in lockstep with the other nax packages.
|
|
10
|
+
|
|
11
|
+
## [0.88.0-canary.1] - 2026-10-13
|
|
12
|
+
|
|
13
|
+
- Bash guard for nax run state (#2490): `git update-index --skip-worktree` / `--assume-unchanged` (and the `--no-` forms) on a `.nax` path, `git stash` / `push` / `pop` / `apply` in a repo that holds a `.nax/` directory (unless the pathspec excludes `.nax`), and `git checkout` / `restore` of a `.nax` path are now refused in every segment of a compound command, in gated and raw mode, also behind `git -C`/`-c`, env assignments, `env`/`command`/`nice`/`timeout`/`xargs` wrappers and `bash -c`/`eval`, plus a coarse text backstop for wrappers it cannot enumerate. `RULE_SET_VERSION` is now 4 (git global options, `-c alias.*` and more wrappers are resolved when locating the subcommand).
|
|
14
|
+
- Bash guard gaps closed (#2493): whole-tree reverts (`git reset --hard`, `git checkout .`, `git restore [--source=REV] .`, `:/`, `*`, the root, exclusion-only pathspecs) are refused unless a full `.nax` exclusion follows `--`; the `.nax` presence check also finds a `.nax/` up to three levels below the root (cached per compiled policy); `\git`, `GIT_WORK_TREE` / `GIT_DIR` / `GIT_CONFIG_*` assignments, `git config alias.X <verb>`, `!` aliases and aliases that begin with global options, unknown git global options (fail closed to the text backstop; `--attr-source` is now a known separate-value global), `python|node|perl|ruby -c|-e` code and verbs built by shell expansion are all read; `git checkout feature/.nax` (a branch name) is no longer a false positive. `RULE_SET_VERSION` is now 6 (`locateGit` reads `\git`, re-skips globals after an alias expansion, follows alias chains of any length and never expands an alias over a git builtin).
|
|
15
|
+
- Bash guard, final pass (#2493): a shell name or `-c` cluster anywhere in a segment triggers the quoted-string scan again (`if sh -c "..."`, `find -exec sh -c`, `su -c`, `setsid bash -c` were let through by round 2); `git clean` counts as a dry run only for an `n` before any `e` in a short cluster (`-fdenx` is `-e nx`, a real clean); `--literal-pathspecs` / `GIT_LITERAL_PATHSPECS` with an exclusion pathspec on checkout, restore, stash or clean is refused (it turns every exclusion into a literal path). The command guard stops here; detect-and-repair of nax run state is tracked in #2497.
|
|
16
|
+
- Bash guard, review round 2 (#2493): the covering exclusion must be the top-anchored `/**` form (real git: a glob without the `/**` matches only the `.nax` directory entry, not the files in it, and a relative glob is taken from the working directory); a `:(...)` pathspec with an `attr:` element, or whose magic is not exactly `exclude`, is no exclusion; `checkout-index`, `read-tree` and `clean` operands that name `.nax` are refused; long options are matched by any prefix git accepts (`--k`, `--f`, `--a`); the gated screen accepts the exact covering pathspec instead of rejecting its quoted `*`; the quoted-string scan covers only shells, interpreters and exec-style wrappers (`grep "git stash"`, `gh ... --body "..."`, `printf`, `sed` are no longer refused); `git clean -n` / `--dry-run` is allowed. Not covered, documented: git subcommands that execute a string (`rebase -x`, `submodule foreach`, `bisect run`, `difftool -x`, `fetch --upload-pack=`), executing environment variables (`GIT_EXTERNAL_DIFF`, `GIT_PAGER`, `GIT_EDITOR`, `GIT_SSH_COMMAND`), executing config keys beyond the listed ones (`remote.*.uploadpack`, `uploadpack.packObjectsHook`, `core.alternateRefsCommand`, `merge.*.driver`, `core.gitProxy`, `protocol.ext.allow` with `ext::` URLs), `deno eval` in raw mode, and commands built by string arithmetic in an interpreter.
|
|
17
|
+
- Bash guard, review round 1 (#2493): alias chains have no depth limit (a cycle is refused); backslash-built words (`g\it`, `st\ash`) are read; `restore --staged` is index-only only when every flag is known (`--wor`, `-SW` count as worktree); more interpreters and any quoted string next to a guarded verb are scanned; git config keys that execute commands (`core.fsmonitor`, `core.hooksPath`, `diff.external`, `include.path`, ...) are refused through `-c`, `--config-env` and `git config`; an alias over a git builtin is not expanded; the covering exclusion is now `':(top,exclude,glob)**/.nax/**'` (the literal `:!.nax` only counts while the root `.nax` is the only one); `checkout -f`, `switch -f`, `reset --keep|--merge`, `clean`, `checkout-index -a -f` and `read-tree -u --reset` are refused while a `.nax/` exists; an expansion head needs a `git` token in the segment. A `.nax/` below the root, for example a test fixture within three levels, makes `git stash` refused for the whole repository. The nested scan caches a found `.nax` but re-scans an empty result after one second.
|
|
18
|
+
- Native errors: a `quota` kind from `@nathapp/nax-ai` (usage-limit and token-plan 429s) now maps to
|
|
19
|
+
`fail-quota` instead of a transient rate limit.
|
|
20
|
+
|
|
7
21
|
## [0.87.1] - 2026-10-12
|
|
8
22
|
|
|
9
23
|
- No changes in this package. Released in lockstep with the other nax packages.
|
|
@@ -32,6 +32,16 @@ export interface CodingToolSupport {
|
|
|
32
32
|
readonly tools: readonly CodingTool[];
|
|
33
33
|
readonly surface: ToolSurfaceDraft;
|
|
34
34
|
};
|
|
35
|
+
/** nax#2500: the dispatch launcher's sandbox as data, attached by the nax resolver for ACP opens. */
|
|
36
|
+
readonly acpSandbox?: import("#src/sandbox/types").SessionSandbox;
|
|
37
|
+
/**
|
|
38
|
+
* The rendered `## MCP servers` section (nax#2517), built by the nax resolver
|
|
39
|
+
* with `renderProviderInstructions` from this hop's resolved provider
|
|
40
|
+
* instructions. The session open folds it into the session's `system` field
|
|
41
|
+
* (native) or hands it to the ACP tool host. ABSENT when no attached provider
|
|
42
|
+
* contributed instructions, so "no section" and "an empty one" stay distinct.
|
|
43
|
+
*/
|
|
44
|
+
readonly providerInstructions?: string;
|
|
35
45
|
}
|
|
36
46
|
export declare function buildCodingToolSupport(args: {
|
|
37
47
|
root?: string;
|
|
@@ -134,6 +144,12 @@ export declare function buildCodingToolSupport(args: {
|
|
|
134
144
|
launcher?: CommandLauncher;
|
|
135
145
|
/** US-001: forwarded into ToolRunContext so Bash/Exec can SIGKILL on cancel. */
|
|
136
146
|
abortSignal?: AbortSignal;
|
|
147
|
+
/**
|
|
148
|
+
* The rendered `## MCP servers` section for this hop (nax#2517), produced by
|
|
149
|
+
* the resolver from `ResolvedProviderTools.instructions`; returned on the
|
|
150
|
+
* support unchanged.
|
|
151
|
+
*/
|
|
152
|
+
providerInstructions?: string;
|
|
137
153
|
}): CodingToolSupport | undefined;
|
|
138
154
|
/**
|
|
139
155
|
* Ledger session name.
|
|
@@ -144,8 +144,20 @@ export function buildCodingToolSupport(args) {
|
|
|
144
144
|
auditSink: sink,
|
|
145
145
|
declaredTools: args.declared,
|
|
146
146
|
...(args.toolSurface !== undefined ? { toolSurface: args.toolSurface } : {}),
|
|
147
|
+
...providerInstructionsField(args),
|
|
147
148
|
};
|
|
148
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* The support's optional `providerInstructions` (#2517) as a spreadable field —
|
|
152
|
+
* ABSENT when unset, so `"providerInstructions" in support` answers "is there
|
|
153
|
+
* an MCP section?". A helper rather than an inline conditional because
|
|
154
|
+
* `buildCodingToolSupport` sits at its complexity baseline.
|
|
155
|
+
*/
|
|
156
|
+
function providerInstructionsField(args) {
|
|
157
|
+
if (args.providerInstructions === undefined)
|
|
158
|
+
return {};
|
|
159
|
+
return { providerInstructions: args.providerInstructions };
|
|
160
|
+
}
|
|
149
161
|
/** The audit sink a support resolution records through; no dir ⇒ a non-recording sink. */
|
|
150
162
|
function supportSink(args) {
|
|
151
163
|
if (args.auditDir === undefined)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RuleResult } from "./types.ts";
|
|
2
|
-
export declare const RULE_SET_VERSION =
|
|
2
|
+
export declare const RULE_SET_VERSION = 6;
|
|
3
3
|
/** Context the scorer may use; every field is optional and absent means v1 behaviour. */
|
|
4
4
|
export interface RuleContext {
|
|
5
5
|
/** The project root the command runs against (the Bash call's cwd). */
|
|
@@ -27,14 +27,24 @@
|
|
|
27
27
|
* hit. The existing eleven regex families stay; the new patterns live in a
|
|
28
28
|
* per-segment pass.
|
|
29
29
|
*
|
|
30
|
+
* v5: `locateGit` (shared with the nax-state guard, #2493) reads a
|
|
31
|
+
* backslash-prefixed `\git`, runs the global-option skip again over an
|
|
32
|
+
* expanded alias, and follows alias chains, so a discard behind any of those
|
|
33
|
+
* is now located by the v3 per-segment pass.
|
|
34
|
+
*
|
|
35
|
+
* v6: `locateGit` never expands an alias that shadows a git builtin (git ignores it) and follows alias
|
|
36
|
+
* chains of any length (a cycle is reported, not looped on), so `git -c alias.checkout=status checkout -f`
|
|
37
|
+
* is the discard it really is.
|
|
38
|
+
*
|
|
30
39
|
* Frozen before the red-team corpus was written. Any pattern or behaviour
|
|
31
40
|
* change bumps RULE_SET_VERSION.
|
|
32
41
|
*/
|
|
33
42
|
import { posix } from "node:path";
|
|
34
43
|
import { errorMessage } from "#src/infra/errors";
|
|
35
44
|
import { lexBashCommand } from "#src/permissions/index";
|
|
45
|
+
import { locateGit } from "#src/tools/git-flags/locate";
|
|
36
46
|
import { QUESTION_IDS } from "./types.js";
|
|
37
|
-
export const RULE_SET_VERSION =
|
|
47
|
+
export const RULE_SET_VERSION = 6;
|
|
38
48
|
const RULES = {
|
|
39
49
|
deletes_data: [
|
|
40
50
|
/\brm\s+(?:\S+\s+)*?(?:-[a-zA-Z]*[rRf][a-zA-Z]*|--recursive|--force)\b/,
|
|
@@ -140,118 +150,6 @@ const FORCE_FLAGS = new Set(["-f", "--force", "--discard-changes"]);
|
|
|
140
150
|
const STAGED_FLAGS = new Set(["--staged", "-S"]);
|
|
141
151
|
/** True when `word` is a worktree-restore flag — its own pattern handles these. */
|
|
142
152
|
const WORKTREE_FLAGS = new Set(["--worktree", "-W"]);
|
|
143
|
-
/** `git`'s global options that take no value — pass-through, then look for
|
|
144
|
-
* the subcommand. */
|
|
145
|
-
const GIT_BOOLEAN_GLOBALS = new Set([
|
|
146
|
-
"--no-pager",
|
|
147
|
-
"--no-replace-objects",
|
|
148
|
-
"--bare",
|
|
149
|
-
"--literal-pathspecs",
|
|
150
|
-
"--glob-pathspecs",
|
|
151
|
-
"--noglob-pathspecs",
|
|
152
|
-
"--icase-pathspecs",
|
|
153
|
-
]);
|
|
154
|
-
/** `git`'s global options that take a separate value (the next token). */
|
|
155
|
-
const GIT_VALUE_GLOBALS = new Set(["-c", "-C"]);
|
|
156
|
-
/** `git`'s global long options that embed their value (`--key=value`). */
|
|
157
|
-
const GIT_KEY_VALUE_GLOBALS = new Set([
|
|
158
|
-
"--git-dir=",
|
|
159
|
-
"--exec-path=",
|
|
160
|
-
"--work-tree=",
|
|
161
|
-
"--namespace=",
|
|
162
|
-
"--super-prefix=",
|
|
163
|
-
]);
|
|
164
|
-
/** `sudo` options whose values occupy the next shell word. */
|
|
165
|
-
const SUDO_VALUE_OPTIONS = new Set([
|
|
166
|
-
"-u",
|
|
167
|
-
"-g",
|
|
168
|
-
"-h",
|
|
169
|
-
"-D",
|
|
170
|
-
"-C",
|
|
171
|
-
"-p",
|
|
172
|
-
"-r",
|
|
173
|
-
"-t",
|
|
174
|
-
"-U",
|
|
175
|
-
"--user",
|
|
176
|
-
"--group",
|
|
177
|
-
"--host",
|
|
178
|
-
"--chdir",
|
|
179
|
-
"--close-from",
|
|
180
|
-
"--prompt",
|
|
181
|
-
"--role",
|
|
182
|
-
"--type",
|
|
183
|
-
"--other-user",
|
|
184
|
-
]);
|
|
185
|
-
/** Skip a sudo/doas prefix, including options with a separate value. */
|
|
186
|
-
function afterPrivilegeWrapper(words) {
|
|
187
|
-
if (words[0] !== "sudo" && words[0] !== "doas")
|
|
188
|
-
return 0;
|
|
189
|
-
let i = 1;
|
|
190
|
-
while (words[i]?.startsWith("-")) {
|
|
191
|
-
const option = words[i++];
|
|
192
|
-
if (option === "--")
|
|
193
|
-
break;
|
|
194
|
-
if (option !== undefined && SUDO_VALUE_OPTIONS.has(option))
|
|
195
|
-
i += 1;
|
|
196
|
-
}
|
|
197
|
-
return i;
|
|
198
|
-
}
|
|
199
|
-
/** Skip an env prefix and its assignments and options. */
|
|
200
|
-
function afterEnvWrapper(words, start) {
|
|
201
|
-
if (words[start] !== "env")
|
|
202
|
-
return start;
|
|
203
|
-
let i = start + 1;
|
|
204
|
-
while (i < words.length) {
|
|
205
|
-
const word = words[i];
|
|
206
|
-
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(word) || word === "-i" || word === "--ignore-environment") {
|
|
207
|
-
i += 1;
|
|
208
|
-
}
|
|
209
|
-
else if (word === "-u" || word === "--unset") {
|
|
210
|
-
i += 2;
|
|
211
|
-
}
|
|
212
|
-
else
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
return i;
|
|
216
|
-
}
|
|
217
|
-
/** Find `git` in the executable position after supported shell wrappers. */
|
|
218
|
-
function gitExecutableIndex(words) {
|
|
219
|
-
let i = afterEnvWrapper(words, afterPrivilegeWrapper(words));
|
|
220
|
-
if (words[i] === "command" || words[i] === "exec")
|
|
221
|
-
i += 1;
|
|
222
|
-
return words[i] === "git" ? i : undefined;
|
|
223
|
-
}
|
|
224
|
-
/** Locate executable `git` in `words` and advance past its global options. Returns the
|
|
225
|
-
subcommand and the index of the first argument (or `undefined` when `git`
|
|
226
|
-
isn't there or has no subcommand). Handles `sudo git`, `env VAR=x git`,
|
|
227
|
-
`git --no-pager checkout`, etc. */
|
|
228
|
-
function locateGit(words) {
|
|
229
|
-
const gitIdx = gitExecutableIndex(words);
|
|
230
|
-
if (gitIdx === undefined)
|
|
231
|
-
return undefined;
|
|
232
|
-
let i = gitIdx + 1;
|
|
233
|
-
while (i < words.length) {
|
|
234
|
-
const w = words[i];
|
|
235
|
-
if (GIT_BOOLEAN_GLOBALS.has(w)) {
|
|
236
|
-
i += 1;
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (GIT_VALUE_GLOBALS.has(w)) {
|
|
240
|
-
// The value is a separate token; skip it.
|
|
241
|
-
i += 2;
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
if ([...GIT_KEY_VALUE_GLOBALS].some((prefix) => w.startsWith(prefix))) {
|
|
245
|
-
i += 1;
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
const subcommand = words[i];
|
|
251
|
-
if (subcommand === undefined)
|
|
252
|
-
return undefined;
|
|
253
|
-
return { subcommand, args: words.slice(i + 1) };
|
|
254
|
-
}
|
|
255
153
|
/** True when `token` is a short-flag bundle that includes `f` (so `-f`,
|
|
256
154
|
* `-fq`, `-fb`, `-fqH`, ...). Stops at `--` so a long flag like
|
|
257
155
|
* `--force-something` is not misread as a bundle. */
|
|
@@ -24,6 +24,7 @@ export declare const SandboxConfigSchema: z.ZodObject<{
|
|
|
24
24
|
}, z.core.$strip>>;
|
|
25
25
|
network: z.ZodPrefault<z.ZodObject<{
|
|
26
26
|
allowedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
|
+
allowLocalBinding: z.ZodOptional<z.ZodBoolean>;
|
|
27
28
|
}, z.core.$strip>>;
|
|
28
29
|
}, z.core.$strip>;
|
|
29
30
|
export type SandboxConfig = z.infer<typeof SandboxConfigSchema>;
|
|
@@ -38,6 +38,15 @@ export const SandboxConfigSchema = z.object({
|
|
|
38
38
|
.object({
|
|
39
39
|
/** absent = unrestricted (spec S2); [] = no network; a list = allow-list. */
|
|
40
40
|
allowedDomains: z.array(z.string()).optional(),
|
|
41
|
+
/**
|
|
42
|
+
* US-002 (#2538): let a sandboxed command reach loopback ports.
|
|
43
|
+
*
|
|
44
|
+
* Off when absent, and it only changes anything alongside an
|
|
45
|
+
* `allowedDomains` list: with an open network srt's macOS profile already
|
|
46
|
+
* allows all traffic. srt reads it once per process, from the config given
|
|
47
|
+
* to `SandboxManager.initialize`.
|
|
48
|
+
*/
|
|
49
|
+
allowLocalBinding: z.boolean().optional(),
|
|
41
50
|
})
|
|
42
51
|
.prefault({}),
|
|
43
52
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type { AfterResponsePatch, AfterResponsePayload, AfterToolPatch, AfterToo
|
|
|
26
26
|
export { displayToolInput, TOOL_CALL_INPUT_BYTES, TOOL_RESULT_PREVIEW_BYTES, toolResultPreview, } from "#src/native/session/turn-event-emitter";
|
|
27
27
|
export { type ApprovalAuditRow, type ApprovalEntry, type ApprovalsFile, type ApprovalsFileRead, type ApprovalsFileState, type ApprovalsTaint, ASK_CANCELLED_REASON, ASK_DENIED_REASON, ASK_NO_CHANNEL_REASON, ASK_PROFILE_REASON, ASK_TIMEOUT_REASON, ASK_UNAVAILABLE_REASON, ASK_UNSHOWABLE_REASON, type AskControl, type AskDecidedBy, type AskDecision, type AskLink, type AskLinkOutcome, type AskRequest, type AskResolver, type AskVerdict, appendApproval, appendApprovalAudit, approvalId, approvalsPath, type BashLexResult, type BashRedirect, type BashSegment, type BashSegmentSeparator, type BashToken, type ClearTaintOutcome, chainAskLinks, clearApprovalsTaint, createApprovalsLink, findApproval, findSecretSpans, headlessAskResolver, isForgeCapable, lexBashCommand, type MaskResult, maskForPrompt, type PrepareApprovalsStoreOptions, parseRuleList, parseToolExpression, prepareApprovalsStore, type RemovalDecision, type RemovalResult, type ResolvedPermissions, readApprovals, readApprovalsFile, readApprovalsFileDetailed, redactForRow, redactRowStrings, removeApprovals, type SecretSpan, taintApprovals, writeApprovalsFile, } from "#src/permissions/index";
|
|
28
28
|
export { type AgentGlobOptions, type AgentRuntime, type AgentSpawnOptions, type AgentSpawnResult, type AgentSpawnStdin, getAgentRuntime, nodeRuntime, setAgentRuntime, } from "#src/runtime/index";
|
|
29
|
-
export { buildSandboxPolicy, type CommandLauncher, type CommandLauncherOptions, createCommandLauncher, createSrtBackend, DISABLED_SANDBOX_STATE, defaultTempRoots, denialHintLine, type GitLayout, type LaunchRequest, type LaunchResult, type LaunchSpec, LIKELY_SANDBOX_DENIAL, listCredentialFiles, listGitGuardFiles, listNaxEntries, type ProbeResult, probeSandbox, probeSandboxOnce, quoteArgvForShell, rawBashRefusalReason, resetSandboxBackend, resolveGitLayout, runTempRoots, runTmpRoot, type SandboxBackend, type SandboxBackendName, type SandboxNetworkPolicy, type SandboxPolicy, type SandboxPolicyInput, type SandboxRecord, type SandboxState, type SandboxWrapRequest, sandboxBackendFor, sandboxSentence, sessionTmpDir, sessionTmpDirUnder, strayCommonDirTripwire, unsandboxedSentence, warnSandboxUnavailableOnce, } from "#src/sandbox/index";
|
|
29
|
+
export { buildSandboxPolicy, type CommandLauncher, type CommandLauncherOptions, createCommandLauncher, createSrtBackend, DISABLED_SANDBOX_STATE, defaultTempRoots, denialHintLine, type GitLayout, type LaunchRequest, type LaunchResult, type LaunchSpec, LIKELY_SANDBOX_DENIAL, listCredentialFiles, listGitGuardFiles, listNaxEntries, type ProbeResult, probeSandbox, probeSandboxOnce, quoteArgvForShell, rawBashRefusalReason, resetSandboxBackend, resolveGitLayout, runTempRoots, runTmpRoot, type SandboxBackend, type SandboxBackendName, type SandboxNetworkPolicy, type SandboxPolicy, type SandboxPolicyInput, type SandboxRecord, type SandboxState, type SandboxWrapRequest, type SessionSandbox, sessionSandboxFrom, sandboxBackendFor, sandboxSentence, sessionTmpDir, sessionTmpDirUnder, strayCommonDirTripwire, unsandboxedSentence, warnSandboxUnavailableOnce, } from "#src/sandbox/index";
|
|
30
30
|
export type { AdapterFailure } from "#src/session/adapter-failure";
|
|
31
31
|
export { createAgentSession, resumeAgentSession } from "#src/session/agent-session";
|
|
32
32
|
export { AgentSessionError, type AgentSessionErrorCode } from "#src/session/agent-session-errors";
|
|
@@ -42,5 +42,5 @@ export { attachTurnSpend, type FailedTurnSpend, readTurnSpend } from "#src/sessi
|
|
|
42
42
|
export type { JSONSchema, ToolDescriptor, } from "#src/session/tool-descriptor";
|
|
43
43
|
export { createTurnDeadline, type TurnDeadline, } from "#src/session/turn-deadline";
|
|
44
44
|
export type { TurnEvent, TurnEventSink } from "#src/session/turn-event";
|
|
45
|
-
export { adaptProviderTool, advertisedSchemaBytes, applyModelTruncationPolicy, BASH_TIMEOUT_MS, BASH_TOOL_NAME, buildCommitArgvs, buildGitArgv, buildGrepArgv, type CodingTool, type CodingToolName, type CodingToolOutcome, type CodingToolRuntime, classifyExec, compileToolPolicy, createBashTool, createCodingToolRuntime, createNoOpToolAuditSink, createRunCommandTool, createToolAuditSink, cutBufferToByteCap, DEFAULT_BASH_SHELL, DEFAULT_LOG_FORMAT, DEFAULT_LOG_MAX_COUNT, DEFAULT_TOOL_MAX_BYTES, DEFAULT_TOOL_MAX_FILE_BYTES, type DeclaredCommandRequest, type DeclaredCommandResult, type DeclaredCommandRunner, deleteTool, EXEC_TOOL_NAME, type ExecTarget, editTool, expandMcpRuleGrants, expandProviderGrants, flushOpenToolAuditSinks, GIT_ESCAPE_FLAGS, GIT_READ_VERBS, getCodingTool, gitCommitTool, gitExcludePathspecsOf, gitIgnorePatternsOf, gitTool, globTool, grepTool, isKnownManager, listCodingTools, MAX_PROVIDER_DESCRIPTION_BYTES, MAX_PROVIDER_SCHEMA_BYTES, MCP_RULE_TOOL, MODEL_MAX_BYTES, MODEL_MAX_LINE_CHARS, MODEL_MAX_LINES, type ModelTruncationOptions, mcpRuleAdmits, type NormalizeInput, type NormalizeResult, namespacedToolName, narrowGrants, normalizeExec, normalizeManagerBinary, type PolicyVerdict, PROVIDER_ID_RE, type ProtectedPathsPolicy, type ProviderGrantEntry, type ProviderKind, type ProviderTool, partitionMcpRules, partitionNaxOwnedPaths, providerAttachesTo, READ_CEILING, RESERVED_TOOL_NAMES, type ReadFileSliceOptions, type ReadFileSliceResult, type RegisteredSink, type ResolvedProviderTools, readFileSlice, readTool, registerBuiltinCodingTools, registerBuiltinTool, registerCodingTool, registerToolAuditSink, requestCapabilityTool, resolveProviderTools, resolveWithin, SCRATCHPAD_DIR, SPILL_DIR, type SpillRequest, sanitizeProviderTools, scratchpadListTool, scratchpadReadTool, scratchpadWriteTool, spillRelativePath, substituteCommand, substituteCommandSpec, type ToolAuditHeader, type ToolAuditSink, type ToolCallContext, type ToolCallRecord, type ToolGrant, type ToolPatternNarrowing, type ToolPolicy, type ToolPolicyOptions, type ToolProvider, type ToolResult, type ToolRunContext, type ToolScope, type ToolSurfaceDraft, type ToolSurfaceRecord, type TruncateForModelOptions, type TruncationDirection, type TruncationResult, truncateForModel, truncationDirectionFor, unregisterToolAuditSink, validateProviderId, writeSpill, writeTool, } from "#src/tools/index";
|
|
45
|
+
export { adaptProviderTool, advertisedSchemaBytes, applyModelTruncationPolicy, BASH_TIMEOUT_MS, BASH_TOOL_NAME, buildCommitArgvs, buildGitArgv, buildGrepArgv, type CodingTool, type CodingToolName, type CodingToolOutcome, type CodingToolRuntime, classifyExec, compileToolPolicy, createBashTool, createCodingToolRuntime, createNoOpToolAuditSink, createRunCommandTool, createToolAuditSink, cutBufferToByteCap, DEFAULT_BASH_SHELL, DEFAULT_LOG_FORMAT, DEFAULT_LOG_MAX_COUNT, DEFAULT_TOOL_MAX_BYTES, DEFAULT_TOOL_MAX_FILE_BYTES, type DeclaredCommandRequest, type DeclaredCommandResult, type DeclaredCommandRunner, deleteTool, EXEC_TOOL_NAME, type ExecTarget, editTool, expandMcpRuleGrants, expandProviderGrants, flushOpenToolAuditSinks, GIT_ESCAPE_FLAGS, GIT_READ_VERBS, getCodingTool, gitCommitTool, gitExcludePathspecsOf, gitIgnorePatternsOf, gitTool, globTool, grepTool, isKnownManager, listCodingTools, MAX_PROVIDER_DESCRIPTION_BYTES, MAX_PROVIDER_SCHEMA_BYTES, MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER, MCP_INSTRUCTIONS_MAX_CHARS_TOTAL, MCP_RULE_TOOL, MODEL_MAX_BYTES, MODEL_MAX_LINE_CHARS, MODEL_MAX_LINES, type ModelTruncationOptions, mcpRuleAdmits, type NormalizeInput, type NormalizeResult, namespacedToolName, narrowGrants, normalizeExec, normalizeManagerBinary, type PolicyVerdict, PROVIDER_ID_RE, type ProtectedPathsPolicy, type ProviderGrantEntry, type ProviderKind, type ProviderTool, partitionMcpRules, partitionNaxOwnedPaths, providerAttachesTo, READ_CEILING, RESERVED_TOOL_NAMES, type ReadFileSliceOptions, type ReadFileSliceResult, type RegisteredSink, type ResolvedProviderTools, readFileSlice, readTool, registerBuiltinCodingTools, registerBuiltinTool, registerCodingTool, registerToolAuditSink, renderProviderInstructions, requestCapabilityTool, resolveProviderTools, resolveWithin, SCRATCHPAD_DIR, SPILL_DIR, type SpillRequest, sanitizeProviderTools, scratchpadListTool, scratchpadReadTool, scratchpadWriteTool, spillRelativePath, substituteCommand, substituteCommandSpec, type ToolAuditHeader, type ToolAuditSink, type ToolCallContext, type ToolCallRecord, type ToolGrant, type ToolPatternNarrowing, type ToolPolicy, type ToolPolicyOptions, type ToolProvider, type ToolResult, type ToolRunContext, type ToolScope, type ToolSurfaceDraft, type ToolSurfaceRecord, type TruncateForModelOptions, type TruncationDirection, type TruncationResult, truncateForModel, truncationDirectionFor, unregisterToolAuditSink, validateProviderId, writeSpill, writeTool, } from "#src/tools/index";
|
|
46
46
|
export { EMPTY_OWNED_PATHS_POLICY, type OwnedBashCandidate, type OwnedPathsPolicy, } from "#src/tools/owned-paths";
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ export { AuthCancelledError, ambientShadows, anyAmbientCredential, authImportOut
|
|
|
21
21
|
export { displayToolInput, TOOL_CALL_INPUT_BYTES, TOOL_RESULT_PREVIEW_BYTES, toolResultPreview, } from "#src/native/session/turn-event-emitter";
|
|
22
22
|
export { ASK_CANCELLED_REASON, ASK_DENIED_REASON, ASK_NO_CHANNEL_REASON, ASK_PROFILE_REASON, ASK_TIMEOUT_REASON, ASK_UNAVAILABLE_REASON, ASK_UNSHOWABLE_REASON, appendApproval, appendApprovalAudit, approvalId, approvalsPath, chainAskLinks, clearApprovalsTaint, createApprovalsLink, findApproval, findSecretSpans, headlessAskResolver, isForgeCapable, lexBashCommand, maskForPrompt, parseRuleList, parseToolExpression, prepareApprovalsStore, readApprovals, readApprovalsFile, readApprovalsFileDetailed, redactForRow, redactRowStrings, removeApprovals, taintApprovals, writeApprovalsFile, } from "#src/permissions/index";
|
|
23
23
|
export { getAgentRuntime, nodeRuntime, setAgentRuntime, } from "#src/runtime/index";
|
|
24
|
-
export { buildSandboxPolicy, createCommandLauncher, createSrtBackend, DISABLED_SANDBOX_STATE, defaultTempRoots, denialHintLine, LIKELY_SANDBOX_DENIAL, listCredentialFiles, listGitGuardFiles, listNaxEntries, probeSandbox, probeSandboxOnce, quoteArgvForShell, rawBashRefusalReason, resetSandboxBackend, resolveGitLayout, runTempRoots, runTmpRoot, sandboxBackendFor, sandboxSentence, sessionTmpDir, sessionTmpDirUnder, strayCommonDirTripwire, unsandboxedSentence, warnSandboxUnavailableOnce, } from "#src/sandbox/index";
|
|
24
|
+
export { buildSandboxPolicy, createCommandLauncher, createSrtBackend, DISABLED_SANDBOX_STATE, defaultTempRoots, denialHintLine, LIKELY_SANDBOX_DENIAL, listCredentialFiles, listGitGuardFiles, listNaxEntries, probeSandbox, probeSandboxOnce, quoteArgvForShell, rawBashRefusalReason, resetSandboxBackend, resolveGitLayout, runTempRoots, runTmpRoot, sessionSandboxFrom, sandboxBackendFor, sandboxSentence, sessionTmpDir, sessionTmpDirUnder, strayCommonDirTripwire, unsandboxedSentence, warnSandboxUnavailableOnce, } from "#src/sandbox/index";
|
|
25
25
|
export { createAgentSession, resumeAgentSession } from "#src/session/agent-session";
|
|
26
26
|
export { AgentSessionError } from "#src/session/agent-session-errors";
|
|
27
27
|
export { NO_OP_INTERACTION_HANDLER, } from "#src/session/interaction-handler";
|
|
@@ -29,5 +29,5 @@ export { nativeBackend } from "#src/session/native-backend";
|
|
|
29
29
|
export { SessionTurnError, } from "#src/session/session-types";
|
|
30
30
|
export { attachTurnSpend, readTurnSpend } from "#src/session/turn-spend";
|
|
31
31
|
export { createTurnDeadline, } from "#src/session/turn-deadline";
|
|
32
|
-
export { adaptProviderTool, advertisedSchemaBytes, applyModelTruncationPolicy, BASH_TIMEOUT_MS, BASH_TOOL_NAME, buildCommitArgvs, buildGitArgv, buildGrepArgv, classifyExec, compileToolPolicy, createBashTool, createCodingToolRuntime, createNoOpToolAuditSink, createRunCommandTool, createToolAuditSink, cutBufferToByteCap, DEFAULT_BASH_SHELL, DEFAULT_LOG_FORMAT, DEFAULT_LOG_MAX_COUNT, DEFAULT_TOOL_MAX_BYTES, DEFAULT_TOOL_MAX_FILE_BYTES, deleteTool, EXEC_TOOL_NAME, editTool, expandMcpRuleGrants, expandProviderGrants, flushOpenToolAuditSinks, GIT_ESCAPE_FLAGS, GIT_READ_VERBS, getCodingTool, gitCommitTool, gitExcludePathspecsOf, gitIgnorePatternsOf, gitTool, globTool, grepTool, isKnownManager, listCodingTools, MAX_PROVIDER_DESCRIPTION_BYTES, MAX_PROVIDER_SCHEMA_BYTES, MCP_RULE_TOOL, MODEL_MAX_BYTES, MODEL_MAX_LINE_CHARS, MODEL_MAX_LINES, mcpRuleAdmits, namespacedToolName, narrowGrants, normalizeExec, normalizeManagerBinary, PROVIDER_ID_RE, partitionMcpRules, partitionNaxOwnedPaths, providerAttachesTo, READ_CEILING, RESERVED_TOOL_NAMES, readFileSlice, readTool, registerBuiltinCodingTools, registerBuiltinTool, registerCodingTool, registerToolAuditSink, requestCapabilityTool, resolveProviderTools, resolveWithin, SCRATCHPAD_DIR, SPILL_DIR, sanitizeProviderTools, scratchpadListTool, scratchpadReadTool, scratchpadWriteTool, spillRelativePath, substituteCommand, substituteCommandSpec, truncateForModel, truncationDirectionFor, unregisterToolAuditSink, validateProviderId, writeSpill, writeTool, } from "#src/tools/index";
|
|
32
|
+
export { adaptProviderTool, advertisedSchemaBytes, applyModelTruncationPolicy, BASH_TIMEOUT_MS, BASH_TOOL_NAME, buildCommitArgvs, buildGitArgv, buildGrepArgv, classifyExec, compileToolPolicy, createBashTool, createCodingToolRuntime, createNoOpToolAuditSink, createRunCommandTool, createToolAuditSink, cutBufferToByteCap, DEFAULT_BASH_SHELL, DEFAULT_LOG_FORMAT, DEFAULT_LOG_MAX_COUNT, DEFAULT_TOOL_MAX_BYTES, DEFAULT_TOOL_MAX_FILE_BYTES, deleteTool, EXEC_TOOL_NAME, editTool, expandMcpRuleGrants, expandProviderGrants, flushOpenToolAuditSinks, GIT_ESCAPE_FLAGS, GIT_READ_VERBS, getCodingTool, gitCommitTool, gitExcludePathspecsOf, gitIgnorePatternsOf, gitTool, globTool, grepTool, isKnownManager, listCodingTools, MAX_PROVIDER_DESCRIPTION_BYTES, MAX_PROVIDER_SCHEMA_BYTES, MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER, MCP_INSTRUCTIONS_MAX_CHARS_TOTAL, MCP_RULE_TOOL, MODEL_MAX_BYTES, MODEL_MAX_LINE_CHARS, MODEL_MAX_LINES, mcpRuleAdmits, namespacedToolName, narrowGrants, normalizeExec, normalizeManagerBinary, PROVIDER_ID_RE, partitionMcpRules, partitionNaxOwnedPaths, providerAttachesTo, READ_CEILING, RESERVED_TOOL_NAMES, readFileSlice, readTool, registerBuiltinCodingTools, registerBuiltinTool, registerCodingTool, registerToolAuditSink, renderProviderInstructions, requestCapabilityTool, resolveProviderTools, resolveWithin, SCRATCHPAD_DIR, SPILL_DIR, sanitizeProviderTools, scratchpadListTool, scratchpadReadTool, scratchpadWriteTool, spillRelativePath, substituteCommand, substituteCommandSpec, truncateForModel, truncationDirectionFor, unregisterToolAuditSink, validateProviderId, writeSpill, writeTool, } from "#src/tools/index";
|
|
33
33
|
export { EMPTY_OWNED_PATHS_POLICY, } from "#src/tools/owned-paths";
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
* 2. Falls back to killing the single process if group kill fails
|
|
13
13
|
*
|
|
14
14
|
* @param pid - Process ID (positive, not negative)
|
|
15
|
-
* @param signal - Signal to send (SIGTERM, SIGKILL, etc.)
|
|
15
|
+
* @param signal - Signal to send (SIGTERM, SIGKILL, etc.); `0` probes whether the group still has a member
|
|
16
|
+
* @param opts - `singleProcessFallback: false` skips step 2. Pass it once the group leader is known to have
|
|
17
|
+
* exited: the bare pid may have been reused by an unrelated process by then.
|
|
16
18
|
* @returns true if kill was sent successfully, false if process already exited
|
|
17
19
|
*
|
|
18
20
|
* @example
|
|
@@ -24,4 +26,6 @@
|
|
|
24
26
|
* }
|
|
25
27
|
* ```
|
|
26
28
|
*/
|
|
27
|
-
export declare function killProcessGroup(pid: number, signal: NodeJS.Signals | number
|
|
29
|
+
export declare function killProcessGroup(pid: number, signal: NodeJS.Signals | number, opts?: {
|
|
30
|
+
readonly singleProcessFallback?: boolean;
|
|
31
|
+
}): boolean;
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
* 2. Falls back to killing the single process if group kill fails
|
|
13
13
|
*
|
|
14
14
|
* @param pid - Process ID (positive, not negative)
|
|
15
|
-
* @param signal - Signal to send (SIGTERM, SIGKILL, etc.)
|
|
15
|
+
* @param signal - Signal to send (SIGTERM, SIGKILL, etc.); `0` probes whether the group still has a member
|
|
16
|
+
* @param opts - `singleProcessFallback: false` skips step 2. Pass it once the group leader is known to have
|
|
17
|
+
* exited: the bare pid may have been reused by an unrelated process by then.
|
|
16
18
|
* @returns true if kill was sent successfully, false if process already exited
|
|
17
19
|
*
|
|
18
20
|
* @example
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
* }
|
|
25
27
|
* ```
|
|
26
28
|
*/
|
|
27
|
-
export function killProcessGroup(pid, signal) {
|
|
29
|
+
export function killProcessGroup(pid, signal, opts = {}) {
|
|
28
30
|
// A pid that is not a real child reaches other processes: 0 is our own group,
|
|
29
31
|
// 1 (as -1) is every process we may signal, and a negative pid is init or a
|
|
30
32
|
// foreign group. A spawn that failed has no pid, so refuse rather than signal.
|
|
@@ -39,6 +41,8 @@ export function killProcessGroup(pid, signal) {
|
|
|
39
41
|
const err = error;
|
|
40
42
|
// ESRCH = no such process — process group doesn't exist, try single process
|
|
41
43
|
if (err.code === "ESRCH") {
|
|
44
|
+
if (opts.singleProcessFallback === false)
|
|
45
|
+
return false;
|
|
42
46
|
try {
|
|
43
47
|
process.kill(pid, signal);
|
|
44
48
|
return true;
|
package/dist/internal.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export * from "#src/tools/git-commit";
|
|
|
93
93
|
export * from "#src/tools/grep";
|
|
94
94
|
export * from "#src/tools/index";
|
|
95
95
|
export { _instructionAccessDeps } from "#src/tools/instruction-access";
|
|
96
|
+
export { _naxStateGuardDeps } from "#src/tools/nax-stash-restore";
|
|
96
97
|
export * from "#src/tools/owned-paths";
|
|
97
98
|
export * from "#src/tools/package-managers";
|
|
98
99
|
export * from "#src/tools/policy";
|
package/dist/internal.js
CHANGED
|
@@ -93,6 +93,7 @@ export * from "#src/tools/git-commit";
|
|
|
93
93
|
export * from "#src/tools/grep";
|
|
94
94
|
export * from "#src/tools/index";
|
|
95
95
|
export { _instructionAccessDeps } from "#src/tools/instruction-access";
|
|
96
|
+
export { _naxStateGuardDeps } from "#src/tools/nax-stash-restore";
|
|
96
97
|
// S3-2: the port (nax supplies the policy behind it from its own module).
|
|
97
98
|
export * from "#src/tools/owned-paths";
|
|
98
99
|
export * from "#src/tools/package-managers";
|
package/dist/mcp/connect.js
CHANGED
|
@@ -83,6 +83,15 @@ function createHandle(config) {
|
|
|
83
83
|
throw new McpConnectError(`MCP connect failed: ${messageOf(error)}`);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The server's `initialize` instructions (#2517), or `undefined` for anything
|
|
88
|
+
* that is not a non-empty string: an empty one would render an empty prompt
|
|
89
|
+
* section, and a `getInstructions()` that returns nothing is the same silence.
|
|
90
|
+
*/
|
|
91
|
+
function serverInstructions(client) {
|
|
92
|
+
const text = client.getInstructions();
|
|
93
|
+
return typeof text === "string" && text !== "" ? text : undefined;
|
|
94
|
+
}
|
|
86
95
|
export async function connectMcp(config, opts) {
|
|
87
96
|
const handle = createHandle(config);
|
|
88
97
|
const client = new Client({ name: opts.clientInfo.name, version: opts.clientInfo.version });
|
|
@@ -95,7 +104,11 @@ export async function connectMcp(config, opts) {
|
|
|
95
104
|
throw new McpConnectError("MCP connect failed: aborted");
|
|
96
105
|
await client.connect(handle.transport, { signal, timeout: opts.timeoutMs });
|
|
97
106
|
const tools = await listAll(client, signal, opts.timeoutMs);
|
|
98
|
-
return liveConnection(config, client, handle, tools, {
|
|
107
|
+
return liveConnection(config, client, handle, tools, {
|
|
108
|
+
graceMs,
|
|
109
|
+
life,
|
|
110
|
+
instructions: serverInstructions(client),
|
|
111
|
+
});
|
|
99
112
|
}
|
|
100
113
|
catch (error) {
|
|
101
114
|
await closeTransport(client, handle, graceMs, life);
|
|
@@ -127,6 +140,7 @@ function liveConnection(config, client, handle, tools, state) {
|
|
|
127
140
|
return handle.pid();
|
|
128
141
|
},
|
|
129
142
|
tools,
|
|
143
|
+
...(state.instructions !== undefined ? { instructions: state.instructions } : {}),
|
|
130
144
|
call(name, input, opts) {
|
|
131
145
|
if (closing !== undefined || life.exited)
|
|
132
146
|
return Promise.reject(new McpCallError(`MCP call ${name} failed: the connection is closed`));
|
package/dist/mcp/types.d.ts
CHANGED
|
@@ -31,6 +31,12 @@ export interface McpConnection {
|
|
|
31
31
|
/** The stdio child process pid; null for HTTP. Read before close() — the SDK may clear it after. */
|
|
32
32
|
readonly pid: number | null;
|
|
33
33
|
readonly tools: readonly McpToolInfo[];
|
|
34
|
+
/**
|
|
35
|
+
* The server's own `initialize` instructions — how to use it, in its words
|
|
36
|
+
* (#2517). Absent when it sent none (or an empty string): an MCP server that
|
|
37
|
+
* says nothing must not produce an empty prompt section.
|
|
38
|
+
*/
|
|
39
|
+
readonly instructions?: string;
|
|
34
40
|
/** Server-side tool errors are `isError` results; transport failures throw McpCallError. */
|
|
35
41
|
call(name: string, input: unknown, opts: McpCallOptions): Promise<McpCallResult>;
|
|
36
42
|
/** stdio only: called once when the process exits without close() having been called. */
|
package/dist/native/auth.d.ts
CHANGED
|
@@ -29,8 +29,8 @@ export declare const _authDeps: {
|
|
|
29
29
|
ambientAuthAvailable: typeof ambientAuthAvailable;
|
|
30
30
|
providerIds: () => Promise<string[]>;
|
|
31
31
|
/**
|
|
32
|
-
* Injectable timer pair — lets tests drive
|
|
33
|
-
* expiry
|
|
32
|
+
* Injectable timer pair — lets tests drive ambient credential probes'
|
|
33
|
+
* expiry branches off a virtual clock instead of waiting the full 2s
|
|
34
34
|
* production timeout. Mirrors `_heartbeatDeps` / `_idleWatchdogDeps`.
|
|
35
35
|
*
|
|
36
36
|
* @internal
|
package/dist/native/auth.js
CHANGED
|
@@ -41,8 +41,8 @@ export const _authDeps = {
|
|
|
41
41
|
ambientAuthAvailable,
|
|
42
42
|
providerIds: async () => (await defaultProviders()).map((provider) => provider.id),
|
|
43
43
|
/**
|
|
44
|
-
* Injectable timer pair — lets tests drive
|
|
45
|
-
* expiry
|
|
44
|
+
* Injectable timer pair — lets tests drive ambient credential probes'
|
|
45
|
+
* expiry branches off a virtual clock instead of waiting the full 2s
|
|
46
46
|
* production timeout. Mirrors `_heartbeatDeps` / `_idleWatchdogDeps`.
|
|
47
47
|
*
|
|
48
48
|
* @internal
|
|
@@ -301,14 +301,14 @@ export async function providersWithoutCredentials(providerIds) {
|
|
|
301
301
|
})).then((missing) => missing.filter((id) => id !== undefined));
|
|
302
302
|
let timer;
|
|
303
303
|
const expiry = new Promise((resolve) => {
|
|
304
|
-
timer = setTimeout(() => resolve([]), AMBIENT_PROBE_TIMEOUT_MS);
|
|
304
|
+
timer = _authDeps.setTimeout(() => resolve([]), AMBIENT_PROBE_TIMEOUT_MS);
|
|
305
305
|
});
|
|
306
306
|
try {
|
|
307
307
|
return await Promise.race([sweep, expiry]);
|
|
308
308
|
}
|
|
309
309
|
finally {
|
|
310
310
|
if (timer !== undefined)
|
|
311
|
-
clearTimeout(timer);
|
|
311
|
+
_authDeps.clearTimeout(timer);
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
/**
|
package/dist/native/errors.js
CHANGED
|
@@ -17,6 +17,13 @@ const FAILURES = Object.freeze({
|
|
|
17
17
|
outcome: "fail-rate-limit",
|
|
18
18
|
retriable: true,
|
|
19
19
|
},
|
|
20
|
+
// The account or plan is exhausted: waiting does not help, so not retriable.
|
|
21
|
+
quota: {
|
|
22
|
+
message: "nax-ai account or plan quota exhausted",
|
|
23
|
+
category: "availability",
|
|
24
|
+
outcome: "fail-quota",
|
|
25
|
+
retriable: false,
|
|
26
|
+
},
|
|
20
27
|
auth: {
|
|
21
28
|
message: "nax-ai authentication failed",
|
|
22
29
|
category: "availability",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prices the tokens a failed or aborted round trip was billed for and attaches
|
|
3
|
+
* them to the thrown error (`session/partial-spend.ts`), where the turn loop's
|
|
4
|
+
* `completeWithRecovery` folds them into the turn's accumulated usage.
|
|
5
|
+
*
|
|
6
|
+
* nax-ai's `collectStream` hands the partial usage over on the error
|
|
7
|
+
* (`partialUsageOf`); only the adapter holds the rate card, so pricing happens
|
|
8
|
+
* here, once, with the same `priceCall` the success path uses.
|
|
9
|
+
*/
|
|
10
|
+
import type { Pricing } from "#src/cost/standard-types";
|
|
11
|
+
/**
|
|
12
|
+
* Runs one round trip; if it throws, prices whatever it was billed first and
|
|
13
|
+
* attaches that to the error, which is rethrown untouched. A wrapper (rather
|
|
14
|
+
* than a catch at the call site) keeps `sendTurn` under the complexity ratchet.
|
|
15
|
+
*/
|
|
16
|
+
export declare function billedOnFailure<T>(rates: Pricing, roundTrip: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prices the tokens a failed or aborted round trip was billed for and attaches
|
|
3
|
+
* them to the thrown error (`session/partial-spend.ts`), where the turn loop's
|
|
4
|
+
* `completeWithRecovery` folds them into the turn's accumulated usage.
|
|
5
|
+
*
|
|
6
|
+
* nax-ai's `collectStream` hands the partial usage over on the error
|
|
7
|
+
* (`partialUsageOf`); only the adapter holds the rate card, so pricing happens
|
|
8
|
+
* here, once, with the same `priceCall` the success path uses.
|
|
9
|
+
*/
|
|
10
|
+
import { partialUsageOf } from "@nathapp/nax-ai";
|
|
11
|
+
import { priceCall } from "#src/cost/core/index";
|
|
12
|
+
import { attachPartialSpend } from "./session/partial-spend.js";
|
|
13
|
+
function recordFailedRoundTripSpend(err, rates) {
|
|
14
|
+
const usage = partialUsageOf(err);
|
|
15
|
+
if (usage === undefined)
|
|
16
|
+
return;
|
|
17
|
+
const { costUsd, resolvedRates } = priceCall(usage, rates);
|
|
18
|
+
attachPartialSpend(err, { usage, costUsd, rates: resolvedRates });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Runs one round trip; if it throws, prices whatever it was billed first and
|
|
22
|
+
* attaches that to the error, which is rethrown untouched. A wrapper (rather
|
|
23
|
+
* than a catch at the call site) keeps `sendTurn` under the complexity ratchet.
|
|
24
|
+
*/
|
|
25
|
+
export async function billedOnFailure(rates, roundTrip) {
|
|
26
|
+
try {
|
|
27
|
+
return await roundTrip();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
recordFailedRoundTripSpend(err, rates);
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend billed by a round trip that then failed.
|
|
3
|
+
*
|
|
4
|
+
* A failed or aborted request still bills the tokens it streamed (nax-ai's
|
|
5
|
+
* `partialUsageOf`). The adapter's `complete` closure is the only place that
|
|
6
|
+
* can price them, but it can only throw, so the priced spend rides on the
|
|
7
|
+
* thrown error's identity (a WeakMap, like `turn-spend.ts`) and the turn loop's
|
|
8
|
+
* `completeWithRecovery` folds it into the turn accumulator. `take` removes the
|
|
9
|
+
* entry, so an error reaching the fold twice cannot count twice.
|
|
10
|
+
*/
|
|
11
|
+
import type { PricingRates, TokenUsage } from "#src/cost/standard-types";
|
|
12
|
+
export interface PartialSpend {
|
|
13
|
+
readonly usage: TokenUsage;
|
|
14
|
+
readonly costUsd: number;
|
|
15
|
+
readonly rates?: PricingRates;
|
|
16
|
+
}
|
|
17
|
+
/** Attaches the priced partial spend of a failed round trip to the error it threw. */
|
|
18
|
+
export declare function attachPartialSpend(err: unknown, spend: PartialSpend): void;
|
|
19
|
+
/** Removes and returns the partial spend attached to `err`, if any. */
|
|
20
|
+
export declare function takePartialSpend(err: unknown): PartialSpend | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const partialByError = new WeakMap();
|
|
2
|
+
/** Attaches the priced partial spend of a failed round trip to the error it threw. */
|
|
3
|
+
export function attachPartialSpend(err, spend) {
|
|
4
|
+
if (typeof err === "object" && err !== null)
|
|
5
|
+
partialByError.set(err, spend);
|
|
6
|
+
}
|
|
7
|
+
/** Removes and returns the partial spend attached to `err`, if any. */
|
|
8
|
+
export function takePartialSpend(err) {
|
|
9
|
+
if (typeof err !== "object" || err === null)
|
|
10
|
+
return undefined;
|
|
11
|
+
const spend = partialByError.get(err);
|
|
12
|
+
partialByError.delete(err);
|
|
13
|
+
return spend;
|
|
14
|
+
}
|
|
@@ -83,12 +83,19 @@ function openTranscriptStore(name, opts) {
|
|
|
83
83
|
/**
|
|
84
84
|
* Records, or clears, the session's system prompt at open. A helper so that
|
|
85
85
|
* openNativeSession gains no branch.
|
|
86
|
+
*
|
|
87
|
+
* `providerInstructions` (the rendered `## MCP servers` section, #2517) is
|
|
88
|
+
* folded in HERE rather than kept in a map of its own: the model sees one
|
|
89
|
+
* `system` string, and every consumer of `state.systemPrompts`
|
|
90
|
+
* (`systemFieldFor`, the adapter's context-budget setup) keeps working
|
|
91
|
+
* unchanged.
|
|
86
92
|
*/
|
|
87
|
-
function recordSystemPrompt(state, name,
|
|
88
|
-
|
|
93
|
+
function recordSystemPrompt(state, name, prompts) {
|
|
94
|
+
const parts = [prompts.systemPrompt, prompts.providerInstructions].filter((part) => part !== undefined && part !== "");
|
|
95
|
+
if (parts.length === 0)
|
|
89
96
|
state.systemPrompts.delete(name);
|
|
90
97
|
else
|
|
91
|
-
state.systemPrompts.set(name,
|
|
98
|
+
state.systemPrompts.set(name, parts.join("\n\n"));
|
|
92
99
|
}
|
|
93
100
|
/** The request field for the session's system prompt: `{ system }`, or `{}` when it has none. */
|
|
94
101
|
export function systemFieldFor(state, sessionName) {
|
|
@@ -119,7 +126,7 @@ export async function openNativeSession(state, name, opts) {
|
|
|
119
126
|
state.carryHistoryAcrossModels.add(name);
|
|
120
127
|
else
|
|
121
128
|
state.carryHistoryAcrossModels.delete(name);
|
|
122
|
-
recordSystemPrompt(state, name, opts
|
|
129
|
+
recordSystemPrompt(state, name, opts);
|
|
123
130
|
const instructions = await openRepositoryInstructions(name, opts, store);
|
|
124
131
|
state.repositoryInstructions.set(name, instructions);
|
|
125
132
|
state.transcripts.set(name, {
|