@nathapp/nax-agent 0.91.7-canary.2 → 0.91.7-canary.4

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 CHANGED
@@ -4,6 +4,14 @@ 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.91.7-canary.4] - 2026-10-27
8
+
9
+ - No changes in this package. Released in lockstep with the other nax packages.
10
+
11
+ ## [0.91.7-canary.3] - 2026-10-27
12
+
13
+ - No changes in this package. Released in lockstep with the other nax packages.
14
+
7
15
  ## [0.91.7-canary.2] - 2026-10-26
8
16
 
9
17
  - No changes in this package. Released in lockstep with the other nax packages.
@@ -67,11 +67,13 @@ export declare function unsandboxedToolOptions(args: {
67
67
  */
68
68
  export declare function unsandboxedRuntimeOption(args: {
69
69
  readonly unsandboxedCommands?: boolean;
70
+ readonly askOnUnsandboxed?: boolean;
70
71
  readonly bashApproval: BashApprovalMode;
71
72
  }): {
72
73
  readonly unsandboxed?: {
73
74
  readonly enabled: boolean;
74
75
  readonly bashApproval: BashApprovalMode;
76
+ readonly alwaysAsk?: boolean;
75
77
  };
76
78
  };
77
79
  export declare function buildDeclaredCommandTools(args: DeclaredCommandToolsArgs): CodingTool[];
@@ -23,7 +23,15 @@ export function unsandboxedToolOptions(args) {
23
23
  * raise it.
24
24
  */
25
25
  export function unsandboxedRuntimeOption(args) {
26
- return args.unsandboxedCommands === true ? { unsandboxed: { enabled: true, bashApproval: args.bashApproval } } : {};
26
+ if (args.unsandboxedCommands !== true)
27
+ return {};
28
+ return {
29
+ unsandboxed: {
30
+ enabled: true,
31
+ bashApproval: args.bashApproval,
32
+ ...(args.askOnUnsandboxed === true ? { alwaysAsk: true } : {}),
33
+ },
34
+ };
27
35
  }
28
36
  export function buildDeclaredCommandTools(args) {
29
37
  return [
@@ -148,6 +148,7 @@ export declare function buildCodingToolSupport(args: {
148
148
  * the runtime's own reading of the field is a later story.
149
149
  */
150
150
  unsandboxedCommands?: boolean;
151
+ askOnUnsandboxed?: boolean;
151
152
  /** US-001: forwarded into ToolRunContext so Bash/Exec can SIGKILL on cancel. */
152
153
  abortSignal?: AbortSignal;
153
154
  /**
@@ -127,7 +127,11 @@ export function buildCodingToolSupport(args) {
127
127
  // schema — with the locally resolved mode, so a caller that left
128
128
  // `bashApproval` unset cannot enable an escape under a mode it did not
129
129
  // choose.
130
- ...unsandboxedRuntimeOption({ unsandboxedCommands: args.unsandboxedCommands, bashApproval }),
130
+ ...unsandboxedRuntimeOption({
131
+ unsandboxedCommands: args.unsandboxedCommands,
132
+ askOnUnsandboxed: args.askOnUnsandboxed,
133
+ bashApproval,
134
+ }),
131
135
  });
132
136
  const tools = runtime.advertised(advertised);
133
137
  // The fixed per-hop cost of advertising provider tools: their schemas enter
@@ -67,6 +67,11 @@ function exitStatus(code, signal) {
67
67
  return code;
68
68
  return 128 + (signal !== null ? (osConstants.signals[signal] ?? 0) : 0);
69
69
  }
70
+ /**
71
+ * `Readable.toWeb` is declared against `node:stream/web`'s `ReadableStream`, while
72
+ * `AgentSpawnResult` carries the global one. They are the same class at runtime and differ
73
+ * only in how their `getReader` overloads are written, so the bridge has to go via `unknown`.
74
+ */
70
75
  function toWeb(stream) {
71
76
  return stream === null ? emptyStream() : Readable.toWeb(stream);
72
77
  }
@@ -4,6 +4,7 @@
4
4
  * @nathapp/nax-agent-acp backend (S4) reuses these types unchanged.
5
5
  */
6
6
  import type { CommandInterceptor } from "#src/command-interceptor/index";
7
+ import type { CommandShadow } from "#src/command-safety/types";
7
8
  import type { TokenUsage } from "#src/cost/standard-types";
8
9
  import type { TranscriptStore } from "#src/native/session/transcript-types";
9
10
  import type { ProtectedPathsPolicy } from "#src/tools/protected-paths";
@@ -43,6 +44,7 @@ export interface AgentSessionHostPorts {
43
44
  /** Paths the sandbox and the read tools deny. Default: the configured credentials directory, if any. */
44
45
  readonly protectedPaths?: ProtectedPathsPolicy;
45
46
  readonly commandInterceptor?: CommandInterceptor;
47
+ readonly commandShadow?: CommandShadow;
46
48
  }
47
49
  export interface CreateAgentSessionOptions {
48
50
  /** The driver: `nativeBackend({ model })`, or an ACP backend. Native-only options live on the backend. */
@@ -1,4 +1,12 @@
1
+ /**
2
+ * nativeBackend's options (S4 spec 5.2): the native half of the facade's
3
+ * createAgentSession options, validated here rather than by the facade. zod
4
+ * checks the shape; the caller's own object is kept as `raw` (zod returns
5
+ * copies, which would break identity for handlers the caller owns).
6
+ */
7
+ import { z } from "zod";
1
8
  import type { BashApprovalMode } from "#src/config/bash-approval";
9
+ import { SandboxConfigSchema } from "#src/config/schemas-sandbox";
2
10
  import type { NativeCatalogOverrides } from "#src/native/client";
3
11
  import type { CredentialSource } from "#src/native/credentials/session-source";
4
12
  import type { LoopHandlerSet } from "#src/native/session/loop-events/types";
@@ -17,6 +25,17 @@ export interface NativeBackendOptions {
17
25
  readonly loopHandlerRole?: string;
18
26
  readonly hostPorts?: AgentSessionHostPorts;
19
27
  readonly bashApproval?: BashApprovalMode;
28
+ /** Ask before a per-call sandbox escape, including under raw; requires allowUnsandboxedCommands. */
29
+ readonly askOnUnsandboxed?: boolean;
30
+ /** Gate Bash when the sandbox is disabled or unavailable at session open. */
31
+ readonly askWhenSandboxDown?: boolean;
32
+ /** Keep provider-designated mutating tool asks outside the ask profile too. */
33
+ readonly askDiscoveredTools?: boolean;
34
+ /** Rule overlays appended to the profile's own permission rules. */
35
+ readonly askRules?: readonly string[];
36
+ readonly denyRules?: readonly string[];
37
+ /** Partial sandbox input, validated here and default-filled at posture resolution. */
38
+ readonly sandbox?: z.input<typeof SandboxConfigSchema>;
20
39
  readonly allowUnsandboxed?: boolean;
21
40
  /** Narrow the declared tools to these names (built-in names, `<provider>__<tool>`, or `<provider>__*`); scratchpad tools are always kept. */
22
41
  readonly allowedTools?: readonly string[];
@@ -5,6 +5,7 @@
5
5
  * copies, which would break identity for handlers the caller owns).
6
6
  */
7
7
  import { z } from "zod";
8
+ import { SandboxConfigSchema } from "#src/config/schemas-sandbox";
8
9
  import { parseNativeModel } from "#src/native/models";
9
10
  import { compactionSettingsSchema } from "#src/native/session/compaction-settings";
10
11
  import { isInstructionFileName } from "#src/native/session/instruction-file-name";
@@ -41,10 +42,19 @@ const OptionsSchema = z.strictObject({
41
42
  protectedPaths: z
42
43
  .custom((value) => isRecord(value) && isStringArray(value.gitExcludePathspecs) && isStringArray(value.gitIgnorePatterns), "must be a protected-paths policy with gitExcludePathspecs and gitIgnorePatterns string arrays")
43
44
  .optional(),
45
+ commandShadow: z
46
+ .custom(hasMethods(["observe", "settle", "drain"]), "must implement observe, settle and drain")
47
+ .optional(),
44
48
  commandInterceptor: z.custom(hasMethods(["intercept"]), "must implement intercept").optional(),
45
49
  })
46
50
  .optional(),
47
51
  bashApproval: z.enum(["raw", "gated", "escalate"]).optional(),
52
+ askOnUnsandboxed: z.boolean().optional(),
53
+ askWhenSandboxDown: z.boolean().optional(),
54
+ askDiscoveredTools: z.boolean().optional(),
55
+ askRules: z.array(z.string().min(1)).optional(),
56
+ denyRules: z.array(z.string().min(1)).optional(),
57
+ sandbox: SandboxConfigSchema.optional(),
48
58
  allowUnsandboxed: z.boolean().optional(),
49
59
  allowedTools: z.array(z.string().min(1)).optional(),
50
60
  allowUnsandboxedCommands: z.boolean().optional(),
@@ -14,6 +14,7 @@ import { NativeSessionAdapter } from "#src/native/session-adapter";
14
14
  import { resolveProviderTools } from "#src/tools/provider-advertise";
15
15
  import { AgentSessionError } from "./agent-session-errors.js";
16
16
  import { nativeProfileRules, parseNativeBackendOptions, } from "./native-backend-options.js";
17
+ import { effectiveBashApproval, nativePostureRules } from "./native-posture-rules.js";
17
18
  import { createSessionAskLink, createSessionAskResolver } from "./session-ask-link.js";
18
19
  import { createSessionInteractionHandler, embedderToolDescriptor } from "./session-interaction.js";
19
20
  import { buildSessionToolSupport, defaultProtectedPaths, providerAdmits, resolveSessionLauncher, SESSION_STAGE, } from "./session-tool-support.js";
@@ -41,7 +42,8 @@ async function openNative(resolved, ctx) {
41
42
  const raw = resolved.raw;
42
43
  if (ctx.resume !== undefined && raw.carryHistoryAcrossModels !== true)
43
44
  checkResumeModel(ctx.resume.doc, ctx.sessionId, raw.model);
44
- const { bashApproval, allowUnsandboxed, allowUnsandboxedCommands } = nativeProfileRules(ctx.profile, raw);
45
+ const { bashApproval: profileMode, allowUnsandboxed, allowUnsandboxedCommands, } = nativeProfileRules(ctx.profile, raw);
46
+ const posture = nativePostureRules(ctx.profile, raw);
45
47
  const protectedPaths = {
46
48
  ...defaultProtectedPaths(raw.credentials !== undefined),
47
49
  ...raw.hostPorts?.protectedPaths,
@@ -50,9 +52,11 @@ async function openNative(resolved, ctx) {
50
52
  profile: ctx.profile,
51
53
  root: ctx.workdir,
52
54
  protectedPaths,
53
- bashApproval,
55
+ bashApproval: profileMode,
54
56
  allowUnsandboxed,
57
+ config: posture.sandbox,
55
58
  });
59
+ const bashApproval = effectiveBashApproval(profileMode, posture.askWhenSandboxDown, sandbox.kind);
56
60
  const providerTools = ctx.profile === "none" || raw.toolProviders === undefined
57
61
  ? undefined
58
62
  : await resolveProviderTools(raw.toolProviders, SESSION_STAGE, ctx.workdir, raw.allowedTools !== undefined ? { admits: providerAdmits(raw.allowedTools) } : undefined);
@@ -76,6 +80,11 @@ async function openNative(resolved, ctx) {
76
80
  launcher,
77
81
  askResolver: createSessionAskResolver(createSessionAskLink({ port: ctx.asks, currentCallId: () => callId })),
78
82
  interceptor: raw.hostPorts?.commandInterceptor,
83
+ askRules: posture.askRules,
84
+ denyRules: posture.denyRules,
85
+ askOnUnsandboxed: posture.askOnUnsandboxed,
86
+ askDiscoveredTools: posture.askDiscoveredTools,
87
+ commandShadow: raw.hostPorts?.commandShadow,
79
88
  // US-003: the profile's validated opt-in, so an interactive "full" session
80
89
  // can offer Bash's per-call escape without turning the sandbox off.
81
90
  ...(allowUnsandboxedCommands ? { unsandboxedCommands: true } : {}),
@@ -83,6 +92,7 @@ async function openNative(resolved, ctx) {
83
92
  const interactionHandler = createSessionInteractionHandler({
84
93
  sessionId: ctx.sessionId,
85
94
  runtime: support.runtime,
95
+ unavailableTools: new Set((support.declaredTools ?? []).filter((name) => !support.tools.some((tool) => tool.name === name))),
86
96
  embedderTools: new Map(ctx.tools.map((tool) => [tool.name, tool])),
87
97
  asks: ctx.asks,
88
98
  turnSignal: ctx.turnSignal,
@@ -0,0 +1,18 @@
1
+ /** Optional native-session overlays, separate from the existing profile defaults. */
2
+ import type { BashApprovalMode } from "#src/config/bash-approval";
3
+ import { type SandboxConfig } from "#src/config/schemas-sandbox";
4
+ import type { SandboxState } from "#src/sandbox/index";
5
+ import type { ToolGrant } from "#src/tools/types";
6
+ import type { AgentSessionProfile } from "./agent-session-types.ts";
7
+ import type { NativeBackendOptions } from "./native-backend-options.ts";
8
+ export interface ResolvedPostureRules {
9
+ readonly askRules: readonly ToolGrant[];
10
+ readonly denyRules: readonly ToolGrant[];
11
+ readonly sandbox: SandboxConfig;
12
+ readonly askOnUnsandboxed: boolean;
13
+ readonly askWhenSandboxDown: boolean;
14
+ readonly askDiscoveredTools: boolean;
15
+ }
16
+ export declare function nativePostureRules(profile: AgentSessionProfile, raw: NativeBackendOptions): ResolvedPostureRules;
17
+ /** Snapshot the sandbox posture at open; a running session never silently changes modes. */
18
+ export declare function effectiveBashApproval(profileMode: BashApprovalMode, askWhenSandboxDown: boolean, sandboxKind: SandboxState["kind"]): BashApprovalMode;
@@ -0,0 +1,33 @@
1
+ import { DEFAULT_SANDBOX_CONFIG, SandboxConfigSchema } from "#src/config/schemas-sandbox";
2
+ import { parseRuleList } from "#src/permissions/grammar";
3
+ import { AgentSessionError } from "./agent-session-errors.js";
4
+ export function nativePostureRules(profile, raw) {
5
+ const toolOnlyOptions = [
6
+ "askRules",
7
+ "denyRules",
8
+ "askOnUnsandboxed",
9
+ "askWhenSandboxDown",
10
+ "askDiscoveredTools",
11
+ ];
12
+ for (const name of toolOnlyOptions) {
13
+ if ((profile === "none" || profile === "read") && raw[name] !== undefined) {
14
+ const path = `backend.${name}`;
15
+ throw new AgentSessionError(`Invalid agent session options: ${path} applies to profiles "ask", "full" and "review" only`, "AGENT_SESSION_INVALID_OPTIONS", { path });
16
+ }
17
+ }
18
+ if (raw.askOnUnsandboxed === true && raw.allowUnsandboxedCommands !== true) {
19
+ throw new AgentSessionError("Invalid agent session options: backend.askOnUnsandboxed requires backend.allowUnsandboxedCommands: true", "AGENT_SESSION_INVALID_OPTIONS", { path: "backend.askOnUnsandboxed" });
20
+ }
21
+ return {
22
+ askOnUnsandboxed: raw.askOnUnsandboxed === true,
23
+ askWhenSandboxDown: raw.askWhenSandboxDown === true,
24
+ askDiscoveredTools: raw.askDiscoveredTools === true,
25
+ askRules: parseRuleList(raw.askRules ?? []),
26
+ denyRules: parseRuleList(raw.denyRules ?? []),
27
+ sandbox: raw.sandbox === undefined ? DEFAULT_SANDBOX_CONFIG : SandboxConfigSchema.parse(raw.sandbox),
28
+ };
29
+ }
30
+ /** Snapshot the sandbox posture at open; a running session never silently changes modes. */
31
+ export function effectiveBashApproval(profileMode, askWhenSandboxDown, sandboxKind) {
32
+ return askWhenSandboxDown && sandboxKind !== "available" ? "gated" : profileMode;
33
+ }
@@ -9,6 +9,8 @@ export interface SessionInteractionDeps {
9
9
  readonly sessionId: string;
10
10
  readonly runtime: CodingToolRuntime;
11
11
  readonly embedderTools: ReadonlyMap<string, EmbedderTool>;
12
+ /** Declared coding tools withheld by the runtime's advertisement policy. Never execute these calls. */
13
+ readonly unavailableTools?: ReadonlySet<string>;
12
14
  readonly asks: SessionAskPort;
13
15
  /** The running turn's signal (a never-aborting one between turns). */
14
16
  readonly turnSignal: () => AbortSignal;
@@ -168,8 +168,15 @@ export function createSessionInteractionHandler(deps) {
168
168
  async onInteraction(request) {
169
169
  if (request.kind === "question")
170
170
  return answerQuestion(deps, request.text);
171
- if (request.kind === "context-tool")
171
+ if (request.kind === "context-tool") {
172
+ // A model may still call a denied tool it remembers from earlier turns.
173
+ // Keep it hidden, but distinguish a policy refusal from an unknown name.
174
+ if (deps.unavailableTools?.has(request.name)) {
175
+ const reason = `Tool "${request.name}" is not available under the session policy`;
176
+ return { answer: `Denied: ${reason}`, denied: { reason, breach: false } };
177
+ }
172
178
  throw toolError(`Unknown tool "${request.name}"`, request.name);
179
+ }
173
180
  const tool = deps.embedderTools.get(request.name);
174
181
  return tool !== undefined ? runEmbedderTool(deps, tool, request) : runCodingTool(deps, request);
175
182
  },
@@ -1,6 +1,8 @@
1
1
  import { type CodingToolSupport } from "#src/coding-tools/coding-tool-support";
2
2
  import type { CommandInterceptor } from "#src/command-interceptor/index";
3
+ import type { CommandShadow } from "#src/command-safety/types";
3
4
  import type { BashApprovalMode } from "#src/config/bash-approval";
5
+ import { type SandboxConfig } from "#src/config/schemas-sandbox";
4
6
  import type { AskResolver } from "#src/permissions/index";
5
7
  import type { CommandLauncher, SandboxState } from "#src/sandbox/index";
6
8
  import type { ProtectedPathsPolicy } from "#src/tools/protected-paths";
@@ -35,6 +37,7 @@ export interface SessionLauncherArgs {
35
37
  */
36
38
  readonly bashApproval: BashApprovalMode;
37
39
  readonly allowUnsandboxed: boolean;
40
+ readonly config?: SandboxConfig;
38
41
  }
39
42
  /** What the floor decided: the launcher to run under (if any), and the state to report. */
40
43
  export interface ResolvedSessionLauncher {
@@ -58,6 +61,11 @@ export interface SessionToolSupportArgs {
58
61
  readonly launcher: CommandLauncher | undefined;
59
62
  readonly askResolver: AskResolver;
60
63
  readonly interceptor: CommandInterceptor | undefined;
64
+ readonly askRules?: readonly ToolGrant[];
65
+ readonly denyRules?: readonly ToolGrant[];
66
+ readonly askOnUnsandboxed?: boolean;
67
+ readonly askDiscoveredTools?: boolean;
68
+ readonly commandShadow?: CommandShadow;
61
69
  /**
62
70
  * US-003: offer Bash's per-call `unsandboxed` argument for this session
63
71
  * (from `nativeProfileRules(...).allowUnsandboxedCommands`). Optional and
@@ -73,7 +73,7 @@ export async function resolveSessionLauncher(args) {
73
73
  return { launcher: undefined, sandbox: { kind: "disabled" } };
74
74
  }
75
75
  const launcher = await resolveSessionSandbox({
76
- config: DEFAULT_SANDBOX_CONFIG,
76
+ config: args.config ?? DEFAULT_SANDBOX_CONFIG,
77
77
  root: args.root,
78
78
  needsLauncher: true,
79
79
  protectedPaths: args.protectedPaths,
@@ -101,6 +101,14 @@ function narrowBuiltIns(profileTools, allowedTools) {
101
101
  const allowed = new Set(allowedTools);
102
102
  return profileTools.filter((tool) => UNIVERSAL_CODING_TOOLS.includes(tool) || allowed.has(tool));
103
103
  }
104
+ /** Optional overlays share one forwarding point for future posture knobs. */
105
+ function postureToolOptions(args) {
106
+ return {
107
+ ...(args.denyRules !== undefined ? { denyRules: args.denyRules } : {}),
108
+ ...(args.askOnUnsandboxed !== undefined ? { askOnUnsandboxed: args.askOnUnsandboxed } : {}),
109
+ ...(args.commandShadow !== undefined ? { commandShadow: args.commandShadow } : {}),
110
+ };
111
+ }
104
112
  export function buildSessionToolSupport(args) {
105
113
  const profileTools = narrowBuiltIns(declaredToolsFor(args.profile, args.protectedPaths), args.allowedTools);
106
114
  // CodingToolName enumerates built-ins, but the runtime also accepts provider names.
@@ -108,13 +116,14 @@ export function buildSessionToolSupport(args) {
108
116
  ...new Set([...profileTools, ...(args.providerTools?.tools.map((tool) => tool.name) ?? [])]),
109
117
  ];
110
118
  const grants = [...grantsFor(profileTools), ...(args.providerTools?.grants ?? [])];
111
- // Only the "ask" profile puts a discovered, non-read-only tool to the person; "full" never asks.
119
+ // Provider asks remain opt-in outside "ask"; configured rules follow them.
112
120
  const askRules = [
113
121
  ...askRulesFor(profileTools, args.bashApproval, args.profile),
114
- ...(args.profile === "ask" ? (args.providerTools?.askTools ?? []) : []).map((tool) => ({
122
+ ...(args.profile === "ask" || args.askDiscoveredTools ? (args.providerTools?.askTools ?? []) : []).map((tool) => ({
115
123
  tool: tool,
116
124
  patterns: ["*"],
117
125
  })),
126
+ ...(args.askRules ?? []),
118
127
  ];
119
128
  const providerInstructions = renderProviderInstructions(args.providerTools?.instructions ?? []);
120
129
  const support = buildCodingToolSupport({
@@ -139,6 +148,7 @@ export function buildSessionToolSupport(args) {
139
148
  ...(args.interceptor !== undefined ? { interceptor: args.interceptor } : {}),
140
149
  ...(args.launcher !== undefined ? { launcher: args.launcher } : {}),
141
150
  ...unsandboxedToolOptions(args),
151
+ ...postureToolOptions(args),
142
152
  });
143
153
  // Unreachable while the scratchpad trio is always declared and granted; kept for the type.
144
154
  if (support === undefined) {
@@ -161,7 +161,10 @@ export function acquireSessionLock(path, deps, isAlive) {
161
161
  rmSync(temp, { force: true });
162
162
  }
163
163
  }, LOCK_HEARTBEAT_MS);
164
- timer.unref();
164
+ // Bun returns a number and Node a Timeout; only the latter can be unref'd, and only it
165
+ // needs to be (a number handle never holds the loop open on its own).
166
+ if (typeof timer !== "number")
167
+ timer.unref();
165
168
  return async () => {
166
169
  try {
167
170
  // The heartbeat stays live while release waits out contention.
@@ -117,6 +117,7 @@ export declare function resolveUnsandboxedCall(p: {
117
117
  option: {
118
118
  readonly enabled: boolean;
119
119
  readonly bashApproval: BashApprovalMode;
120
+ readonly alwaysAsk?: boolean;
120
121
  } | undefined;
121
122
  }): UnsandboxedCall | undefined;
122
123
  /**
@@ -81,7 +81,7 @@ export function resolveUnsandboxedCall(p) {
81
81
  return {
82
82
  kind: "unsandboxed",
83
83
  suffix: unsandboxedReasonSuffix(p.input),
84
- asksOnAllow: p.option.bashApproval !== "raw",
84
+ asksOnAllow: p.option.alwaysAsk === true || p.option.bashApproval !== "raw",
85
85
  };
86
86
  }
87
87
  /** The reason a converted allow carries (spec US-006), before the suffix. */
@@ -159,5 +159,6 @@ export declare function createCodingToolRuntime(opts: {
159
159
  unsandboxed?: {
160
160
  readonly enabled: boolean;
161
161
  readonly bashApproval: BashApprovalMode;
162
+ readonly alwaysAsk?: boolean;
162
163
  };
163
164
  }): CodingToolRuntime;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax-agent",
3
- "version": "0.91.7-canary.2",
3
+ "version": "0.91.7-canary.4",
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,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "@anthropic-ai/sandbox-runtime": "0.0.77",
49
49
  "@modelcontextprotocol/sdk": "^1.32.1",
50
- "@nathapp/nax-ai": "0.91.7-canary.2",
50
+ "@nathapp/nax-ai": "0.91.7-canary.4",
51
51
  "zod": "^4.3.6"
52
52
  },
53
53
  "publishConfig": {