@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/coding-tools/coding-tool-support.d.ts +16 -0
  3. package/dist/coding-tools/coding-tool-support.js +12 -0
  4. package/dist/command-safety/rule-scorer.d.ts +1 -1
  5. package/dist/command-safety/rule-scorer.js +11 -113
  6. package/dist/config/schemas-sandbox.d.ts +1 -0
  7. package/dist/config/schemas-sandbox.js +9 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +2 -2
  10. package/dist/internal/process-kill.d.ts +6 -2
  11. package/dist/internal/process-kill.js +6 -2
  12. package/dist/internal.d.ts +1 -0
  13. package/dist/internal.js +1 -0
  14. package/dist/mcp/connect.js +15 -1
  15. package/dist/mcp/types.d.ts +6 -0
  16. package/dist/native/auth.d.ts +2 -2
  17. package/dist/native/auth.js +4 -4
  18. package/dist/native/errors.js +7 -0
  19. package/dist/native/failed-round-trip-spend.d.ts +16 -0
  20. package/dist/native/failed-round-trip-spend.js +33 -0
  21. package/dist/native/session/partial-spend.d.ts +20 -0
  22. package/dist/native/session/partial-spend.js +14 -0
  23. package/dist/native/session/session.js +11 -4
  24. package/dist/native/session/turn-complete-step.js +23 -3
  25. package/dist/native/session-adapter.js +3 -2
  26. package/dist/permissions/bash-lex.js +36 -4
  27. package/dist/sandbox/claude-settings.d.ts +46 -0
  28. package/dist/sandbox/claude-settings.js +76 -0
  29. package/dist/sandbox/index.d.ts +2 -1
  30. package/dist/sandbox/index.js +1 -0
  31. package/dist/sandbox/launcher.js +4 -0
  32. package/dist/sandbox/messages.d.ts +1 -1
  33. package/dist/sandbox/messages.js +15 -3
  34. package/dist/sandbox/policy-builder.js +27 -2
  35. package/dist/sandbox/session-sandbox.d.ts +3 -0
  36. package/dist/sandbox/session-sandbox.js +9 -0
  37. package/dist/sandbox/srt-backend.d.ts +10 -1
  38. package/dist/sandbox/srt-backend.js +14 -4
  39. package/dist/sandbox/types.d.ts +23 -0
  40. package/dist/session/agent-session-options.js +1 -1
  41. package/dist/session/agent-session-types.d.ts +1 -1
  42. package/dist/session/native-backend-options.js +5 -3
  43. package/dist/session/session-backend.d.ts +9 -0
  44. package/dist/session/session-tool-support.d.ts +1 -1
  45. package/dist/session/session-tool-support.js +4 -2
  46. package/dist/session/session-types.d.ts +10 -0
  47. package/dist/tools/bash.d.ts +7 -1
  48. package/dist/tools/bash.js +18 -3
  49. package/dist/tools/denial-redirect.d.ts +22 -7
  50. package/dist/tools/denial-redirect.js +29 -13
  51. package/dist/tools/git-flags/locate.d.ts +47 -0
  52. package/dist/tools/git-flags/locate.js +361 -0
  53. package/dist/tools/index.d.ts +1 -0
  54. package/dist/tools/index.js +1 -0
  55. package/dist/tools/nax-dirs.d.ts +22 -0
  56. package/dist/tools/nax-dirs.js +89 -0
  57. package/dist/tools/nax-evasion.d.ts +19 -0
  58. package/dist/tools/nax-evasion.js +88 -0
  59. package/dist/tools/nax-revert.d.ts +34 -0
  60. package/dist/tools/nax-revert.js +233 -0
  61. package/dist/tools/nax-stash-restore.d.ts +7 -0
  62. package/dist/tools/nax-stash-restore.js +92 -0
  63. package/dist/tools/nax-state-guard.d.ts +18 -0
  64. package/dist/tools/nax-state-guard.js +478 -0
  65. package/dist/tools/policy-bash-raw.d.ts +3 -0
  66. package/dist/tools/policy-bash-raw.js +40 -12
  67. package/dist/tools/policy-bash.d.ts +3 -0
  68. package/dist/tools/policy-bash.js +38 -9
  69. package/dist/tools/policy-command-branch.d.ts +3 -0
  70. package/dist/tools/policy-command-branch.js +3 -1
  71. package/dist/tools/policy.js +13 -0
  72. package/dist/tools/provider-advertise.d.ts +13 -0
  73. package/dist/tools/provider-advertise.js +41 -1
  74. package/dist/tools/provider-instructions.d.ts +33 -0
  75. package/dist/tools/provider-instructions.js +63 -0
  76. package/dist/tools/provider-types.d.ts +12 -0
  77. package/dist/tools/run-command.d.ts +15 -0
  78. package/dist/tools/run-command.js +16 -1
  79. package/dist/tools/tool-audit.d.ts +1 -1
  80. 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") {
@@ -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 { tools, grants: expandProviderGrants(entries), failures, skipped, withheld, providerIdByTool, entries };
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.87.1",
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.87.1",
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": "latest"
57
+ "tag": "canary"
58
58
  }
59
59
  }