@nathapp/nax-agent 0.1.0
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 +24 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/coding-tools/coding-tool-bash.d.ts +46 -0
- package/dist/coding-tools/coding-tool-bash.js +44 -0
- package/dist/coding-tools/coding-tool-extras.d.ts +35 -0
- package/dist/coding-tools/coding-tool-extras.js +56 -0
- package/dist/coding-tools/coding-tool-sandbox.d.ts +74 -0
- package/dist/coding-tools/coding-tool-sandbox.js +215 -0
- package/dist/coding-tools/coding-tool-support.d.ts +132 -0
- package/dist/coding-tools/coding-tool-support.js +159 -0
- package/dist/coding-tools/universal-coding-tools.d.ts +11 -0
- package/dist/coding-tools/universal-coding-tools.js +5 -0
- package/dist/command-interceptor/index.d.ts +55 -0
- package/dist/command-interceptor/index.js +60 -0
- package/dist/command-interceptor/shell.d.ts +36 -0
- package/dist/command-interceptor/shell.js +137 -0
- package/dist/command-safety/build.d.ts +19 -0
- package/dist/command-safety/build.js +47 -0
- package/dist/command-safety/guard.d.ts +32 -0
- package/dist/command-safety/guard.js +179 -0
- package/dist/command-safety/identifiers.d.ts +24 -0
- package/dist/command-safety/identifiers.js +20 -0
- package/dist/command-safety/index.d.ts +17 -0
- package/dist/command-safety/index.js +11 -0
- package/dist/command-safety/questions.d.ts +19 -0
- package/dist/command-safety/questions.js +60 -0
- package/dist/command-safety/row.d.ts +2 -0
- package/dist/command-safety/row.js +14 -0
- package/dist/command-safety/rule-scorer.d.ts +9 -0
- package/dist/command-safety/rule-scorer.js +391 -0
- package/dist/command-safety/shadow.d.ts +33 -0
- package/dist/command-safety/shadow.js +188 -0
- package/dist/command-safety/systemone-client.d.ts +16 -0
- package/dist/command-safety/systemone-client.js +92 -0
- package/dist/command-safety/tap.d.ts +37 -0
- package/dist/command-safety/tap.js +77 -0
- package/dist/command-safety/temp-only.d.ts +6 -0
- package/dist/command-safety/temp-only.js +215 -0
- package/dist/command-safety/tmp-write.d.ts +2 -0
- package/dist/command-safety/tmp-write.js +112 -0
- package/dist/command-safety/types.d.ts +185 -0
- package/dist/command-safety/types.js +21 -0
- package/dist/config/bash-approval.d.ts +33 -0
- package/dist/config/bash-approval.js +30 -0
- package/dist/config/catalog-overrides.d.ts +94 -0
- package/dist/config/catalog-overrides.js +7 -0
- package/dist/config/native-agent/index.d.ts +8 -0
- package/dist/config/native-agent/index.js +8 -0
- package/dist/config/schemas-sandbox.d.ts +31 -0
- package/dist/config/schemas-sandbox.js +45 -0
- package/dist/cost/core/index.d.ts +8 -0
- package/dist/cost/core/index.js +7 -0
- package/dist/cost/estimate.d.ts +34 -0
- package/dist/cost/estimate.js +65 -0
- package/dist/cost/model-spec.d.ts +28 -0
- package/dist/cost/model-spec.js +29 -0
- package/dist/cost/standard-types.d.ts +9 -0
- package/dist/cost/standard-types.js +1 -0
- package/dist/cost/usage-math.d.ts +18 -0
- package/dist/cost/usage-math.js +46 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +21 -0
- package/dist/infra/agent-logger.d.ts +22 -0
- package/dist/infra/agent-logger.js +19 -0
- package/dist/infra/credentials-config.d.ts +20 -0
- package/dist/infra/credentials-config.js +15 -0
- package/dist/infra/errors.d.ts +9 -0
- package/dist/infra/errors.js +11 -0
- package/dist/infra/index.d.ts +7 -0
- package/dist/infra/index.js +7 -0
- package/dist/infra/nax-error.d.ts +10 -0
- package/dist/infra/nax-error.js +16 -0
- package/dist/infra/spin-breaker/hash.d.ts +2 -0
- package/dist/infra/spin-breaker/hash.js +5 -0
- package/dist/infra/spin-breaker/index.d.ts +120 -0
- package/dist/infra/spin-breaker/index.js +340 -0
- package/dist/internal/agent-output-env.d.ts +44 -0
- package/dist/internal/agent-output-env.js +57 -0
- package/dist/internal/argv-exec.d.ts +58 -0
- package/dist/internal/argv-exec.js +224 -0
- package/dist/internal/bounded-io.d.ts +24 -0
- package/dist/internal/bounded-io.js +51 -0
- package/dist/internal/command-spec/index.d.ts +29 -0
- package/dist/internal/command-spec/index.js +36 -0
- package/dist/internal/describe-value-type.d.ts +8 -0
- package/dist/internal/describe-value-type.js +14 -0
- package/dist/internal/exec-framing.d.ts +18 -0
- package/dist/internal/exec-framing.js +22 -0
- package/dist/internal/file-lock.d.ts +91 -0
- package/dist/internal/file-lock.js +212 -0
- package/dist/internal/file-size.d.ts +2 -0
- package/dist/internal/file-size.js +12 -0
- package/dist/internal/git-add.d.ts +43 -0
- package/dist/internal/git-add.js +59 -0
- package/dist/internal/git-env.d.ts +59 -0
- package/dist/internal/git-env.js +93 -0
- package/dist/internal/git-exec.d.ts +58 -0
- package/dist/internal/git-exec.js +115 -0
- package/dist/internal/path-file-lock.d.ts +23 -0
- package/dist/internal/path-file-lock.js +25 -0
- package/dist/internal/process-alive.d.ts +42 -0
- package/dist/internal/process-alive.js +56 -0
- package/dist/internal/process-kill.d.ts +27 -0
- package/dist/internal/process-kill.js +54 -0
- package/dist/internal/realpath.d.ts +29 -0
- package/dist/internal/realpath.js +52 -0
- package/dist/internal/redact.d.ts +40 -0
- package/dist/internal/redact.js +161 -0
- package/dist/internal/shell-quote.d.ts +2 -0
- package/dist/internal/shell-quote.js +4 -0
- package/dist/internal/sort.d.ts +20 -0
- package/dist/internal/sort.js +20 -0
- package/dist/internal/strip-control-chars.d.ts +19 -0
- package/dist/internal/strip-control-chars.js +38 -0
- package/dist/internal/thenable.d.ts +10 -0
- package/dist/internal/thenable.js +15 -0
- package/dist/internal.d.ts +91 -0
- package/dist/internal.js +91 -0
- package/dist/native/adapter-deps.d.ts +46 -0
- package/dist/native/adapter-deps.js +43 -0
- package/dist/native/auth-types.d.ts +70 -0
- package/dist/native/auth-types.js +13 -0
- package/dist/native/auth.d.ts +133 -0
- package/dist/native/auth.js +362 -0
- package/dist/native/client.d.ts +50 -0
- package/dist/native/client.js +174 -0
- package/dist/native/complete.d.ts +33 -0
- package/dist/native/complete.js +88 -0
- package/dist/native/credentials/chained-store.d.ts +30 -0
- package/dist/native/credentials/chained-store.js +68 -0
- package/dist/native/credentials/change-guard.d.ts +51 -0
- package/dist/native/credentials/change-guard.js +136 -0
- package/dist/native/credentials/exec-source.d.ts +50 -0
- package/dist/native/credentials/exec-source.js +222 -0
- package/dist/native/credentials/fingerprint.d.ts +26 -0
- package/dist/native/credentials/fingerprint.js +174 -0
- package/dist/native/credentials/helper-process.d.ts +79 -0
- package/dist/native/credentials/helper-process.js +259 -0
- package/dist/native/credentials/helper-protocol.d.ts +36 -0
- package/dist/native/credentials/helper-protocol.js +79 -0
- package/dist/native/credentials/index.d.ts +60 -0
- package/dist/native/credentials/index.js +184 -0
- package/dist/native/errors.d.ts +34 -0
- package/dist/native/errors.js +109 -0
- package/dist/native/index.d.ts +25 -0
- package/dist/native/index.js +23 -0
- package/dist/native/model-resolver.d.ts +57 -0
- package/dist/native/model-resolver.js +73 -0
- package/dist/native/models.d.ts +116 -0
- package/dist/native/models.js +183 -0
- package/dist/native/session/ask-human.d.ts +15 -0
- package/dist/native/session/ask-human.js +22 -0
- package/dist/native/session/compaction.d.ts +94 -0
- package/dist/native/session/compaction.js +170 -0
- package/dist/native/session/handle-invalid-tool-call.d.ts +111 -0
- package/dist/native/session/handle-invalid-tool-call.js +159 -0
- package/dist/native/session/loop-events/cache-boundary.d.ts +11 -0
- package/dist/native/session/loop-events/cache-boundary.js +29 -0
- package/dist/native/session/loop-events/external-handler.d.ts +38 -0
- package/dist/native/session/loop-events/external-handler.js +193 -0
- package/dist/native/session/loop-events/index.d.ts +16 -0
- package/dist/native/session/loop-events/index.js +15 -0
- package/dist/native/session/loop-events/payload-guard.d.ts +9 -0
- package/dist/native/session/loop-events/payload-guard.js +31 -0
- package/dist/native/session/loop-events/registry.d.ts +84 -0
- package/dist/native/session/loop-events/registry.js +305 -0
- package/dist/native/session/loop-events/types.d.ts +251 -0
- package/dist/native/session/loop-events/types.js +14 -0
- package/dist/native/session/loop-handlers.d.ts +73 -0
- package/dist/native/session/loop-handlers.js +137 -0
- package/dist/native/session/nudge.d.ts +29 -0
- package/dist/native/session/nudge.js +35 -0
- package/dist/native/session/rate-provenance.d.ts +17 -0
- package/dist/native/session/rate-provenance.js +39 -0
- package/dist/native/session/session.d.ts +139 -0
- package/dist/native/session/session.js +208 -0
- package/dist/native/session/tool-input-exemplar.d.ts +2 -0
- package/dist/native/session/tool-input-exemplar.js +66 -0
- package/dist/native/session/tool-input-validation.d.ts +15 -0
- package/dist/native/session/tool-input-validation.js +146 -0
- package/dist/native/session/tool-mapping.d.ts +18 -0
- package/dist/native/session/tool-mapping.js +27 -0
- package/dist/native/session/tool-result.d.ts +37 -0
- package/dist/native/session/tool-result.js +25 -0
- package/dist/native/session/transcript-store.d.ts +77 -0
- package/dist/native/session/transcript-store.js +200 -0
- package/dist/native/session/truncation-handler.d.ts +47 -0
- package/dist/native/session/truncation-handler.js +83 -0
- package/dist/native/session/turn-accumulator.d.ts +45 -0
- package/dist/native/session/turn-accumulator.js +67 -0
- package/dist/native/session/turn-ask-human.d.ts +23 -0
- package/dist/native/session/turn-ask-human.js +41 -0
- package/dist/native/session/turn-compaction-step.d.ts +74 -0
- package/dist/native/session/turn-compaction-step.js +128 -0
- package/dist/native/session/turn-complete-step.d.ts +77 -0
- package/dist/native/session/turn-complete-step.js +159 -0
- package/dist/native/session/turn-end-event.d.ts +3 -0
- package/dist/native/session/turn-end-event.js +17 -0
- package/dist/native/session/turn-events.d.ts +63 -0
- package/dist/native/session/turn-events.js +48 -0
- package/dist/native/session/turn-loop-round-trip.d.ts +116 -0
- package/dist/native/session/turn-loop-round-trip.js +318 -0
- package/dist/native/session/turn-loop.d.ts +23 -0
- package/dist/native/session/turn-loop.js +250 -0
- package/dist/native/session/turn-result.d.ts +39 -0
- package/dist/native/session/turn-result.js +64 -0
- package/dist/native/session/turn-retry.d.ts +120 -0
- package/dist/native/session/turn-retry.js +182 -0
- package/dist/native/session/turn-tool-batch.d.ts +61 -0
- package/dist/native/session/turn-tool-batch.js +259 -0
- package/dist/native/session/turn-types.d.ts +164 -0
- package/dist/native/session/turn-types.js +36 -0
- package/dist/native/session-adapter.d.ts +56 -0
- package/dist/native/session-adapter.js +369 -0
- package/dist/native/session-affinity.d.ts +36 -0
- package/dist/native/session-affinity.js +43 -0
- package/dist/native/tier-providers.d.ts +18 -0
- package/dist/native/tier-providers.js +20 -0
- package/dist/permissions/approval-audit.d.ts +10 -0
- package/dist/permissions/approval-audit.js +22 -0
- package/dist/permissions/approvals-link.d.ts +20 -0
- package/dist/permissions/approvals-link.js +62 -0
- package/dist/permissions/approvals-store.d.ts +168 -0
- package/dist/permissions/approvals-store.js +259 -0
- package/dist/permissions/approvals-taint.d.ts +33 -0
- package/dist/permissions/approvals-taint.js +138 -0
- package/dist/permissions/ask-chain.d.ts +69 -0
- package/dist/permissions/ask-chain.js +32 -0
- package/dist/permissions/ask.d.ts +23 -0
- package/dist/permissions/ask.js +25 -0
- package/dist/permissions/bash-lex.d.ts +59 -0
- package/dist/permissions/bash-lex.js +279 -0
- package/dist/permissions/grammar.d.ts +12 -0
- package/dist/permissions/grammar.js +24 -0
- package/dist/permissions/index.d.ts +11 -0
- package/dist/permissions/index.js +9 -0
- package/dist/permissions/secret-spans.d.ts +19 -0
- package/dist/permissions/secret-spans.js +88 -0
- package/dist/permissions/types.d.ts +85 -0
- package/dist/permissions/types.js +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/node-glob.d.ts +3 -0
- package/dist/runtime/node-glob.js +83 -0
- package/dist/runtime/node-runtime.d.ts +3 -0
- package/dist/runtime/node-runtime.js +126 -0
- package/dist/runtime/slot.d.ts +11 -0
- package/dist/runtime/slot.js +17 -0
- package/dist/runtime/types.d.ts +51 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/which.d.ts +8 -0
- package/dist/runtime/which.js +32 -0
- package/dist/sandbox/argv-quote.d.ts +11 -0
- package/dist/sandbox/argv-quote.js +13 -0
- package/dist/sandbox/defaults.d.ts +27 -0
- package/dist/sandbox/defaults.js +47 -0
- package/dist/sandbox/git-guards.d.ts +64 -0
- package/dist/sandbox/git-guards.js +161 -0
- package/dist/sandbox/index.d.ts +11 -0
- package/dist/sandbox/index.js +10 -0
- package/dist/sandbox/launcher.d.ts +37 -0
- package/dist/sandbox/launcher.js +160 -0
- package/dist/sandbox/messages.d.ts +17 -0
- package/dist/sandbox/messages.js +46 -0
- package/dist/sandbox/policy-builder.d.ts +44 -0
- package/dist/sandbox/policy-builder.js +149 -0
- package/dist/sandbox/policy-inputs.d.ts +53 -0
- package/dist/sandbox/policy-inputs.js +76 -0
- package/dist/sandbox/probe.d.ts +8 -0
- package/dist/sandbox/probe.js +66 -0
- package/dist/sandbox/registry.d.ts +17 -0
- package/dist/sandbox/registry.js +57 -0
- package/dist/sandbox/session-tmp.d.ts +20 -0
- package/dist/sandbox/session-tmp.js +95 -0
- package/dist/sandbox/srt-backend.d.ts +21 -0
- package/dist/sandbox/srt-backend.js +99 -0
- package/dist/sandbox/types.d.ts +102 -0
- package/dist/sandbox/types.js +1 -0
- package/dist/session/adapter-failure.d.ts +60 -0
- package/dist/session/adapter-failure.js +11 -0
- package/dist/session/agent-stream-events.d.ts +103 -0
- package/dist/session/agent-stream-events.js +6 -0
- package/dist/session/interaction-handler.d.ts +53 -0
- package/dist/session/interaction-handler.js +2 -0
- package/dist/session/no-op-interaction-handler.d.ts +9 -0
- package/dist/session/no-op-interaction-handler.js +11 -0
- package/dist/session/protocol-types.d.ts +12 -0
- package/dist/session/protocol-types.js +1 -0
- package/dist/session/session-types.d.ts +456 -0
- package/dist/session/session-types.js +64 -0
- package/dist/session/tool-descriptor.d.ts +23 -0
- package/dist/session/tool-descriptor.js +5 -0
- package/dist/session/turn-deadline.d.ts +17 -0
- package/dist/session/turn-deadline.js +23 -0
- package/dist/tools/ask-request.d.ts +35 -0
- package/dist/tools/ask-request.js +57 -0
- package/dist/tools/bash-cwd.d.ts +82 -0
- package/dist/tools/bash-cwd.js +55 -0
- package/dist/tools/bash.d.ts +69 -0
- package/dist/tools/bash.js +283 -0
- package/dist/tools/delete.d.ts +57 -0
- package/dist/tools/delete.js +154 -0
- package/dist/tools/denial-redirect.d.ts +42 -0
- package/dist/tools/denial-redirect.js +266 -0
- package/dist/tools/deny-paths.d.ts +35 -0
- package/dist/tools/deny-paths.js +76 -0
- package/dist/tools/edit-region.d.ts +50 -0
- package/dist/tools/edit-region.js +90 -0
- package/dist/tools/edit.d.ts +25 -0
- package/dist/tools/edit.js +114 -0
- package/dist/tools/exec-allowlist-text.d.ts +18 -0
- package/dist/tools/exec-allowlist-text.js +24 -0
- package/dist/tools/exec-guard.d.ts +69 -0
- package/dist/tools/exec-guard.js +151 -0
- package/dist/tools/git-commit.d.ts +59 -0
- package/dist/tools/git-commit.js +220 -0
- package/dist/tools/git-flags/index.d.ts +10 -0
- package/dist/tools/git-flags/index.js +10 -0
- package/dist/tools/git.d.ts +88 -0
- package/dist/tools/git.js +397 -0
- package/dist/tools/glob.d.ts +29 -0
- package/dist/tools/glob.js +153 -0
- package/dist/tools/grep.d.ts +21 -0
- package/dist/tools/grep.js +162 -0
- package/dist/tools/index.d.ts +36 -0
- package/dist/tools/index.js +28 -0
- package/dist/tools/narrow-grants.d.ts +19 -0
- package/dist/tools/narrow-grants.js +54 -0
- package/dist/tools/nax-owned-writes.d.ts +135 -0
- package/dist/tools/nax-owned-writes.js +259 -0
- package/dist/tools/package-managers-table.d.ts +146 -0
- package/dist/tools/package-managers-table.js +325 -0
- package/dist/tools/package-managers-types.d.ts +36 -0
- package/dist/tools/package-managers-types.js +6 -0
- package/dist/tools/package-managers.d.ts +85 -0
- package/dist/tools/package-managers.js +391 -0
- package/dist/tools/path-list.d.ts +38 -0
- package/dist/tools/path-list.js +62 -0
- package/dist/tools/policy-bash-raw.d.ts +19 -0
- package/dist/tools/policy-bash-raw.js +210 -0
- package/dist/tools/policy-bash.d.ts +64 -0
- package/dist/tools/policy-bash.js +232 -0
- package/dist/tools/policy-command-branch.d.ts +32 -0
- package/dist/tools/policy-command-branch.js +63 -0
- package/dist/tools/policy-input.d.ts +2 -0
- package/dist/tools/policy-input.js +10 -0
- package/dist/tools/policy-match.d.ts +75 -0
- package/dist/tools/policy-match.js +117 -0
- package/dist/tools/policy-paths-branch.d.ts +97 -0
- package/dist/tools/policy-paths-branch.js +314 -0
- package/dist/tools/policy.d.ts +65 -0
- package/dist/tools/policy.js +232 -0
- package/dist/tools/protected-paths.d.ts +26 -0
- package/dist/tools/protected-paths.js +8 -0
- package/dist/tools/provider-adapt.d.ts +4 -0
- package/dist/tools/provider-adapt.js +32 -0
- package/dist/tools/provider-advertise.d.ts +39 -0
- package/dist/tools/provider-advertise.js +50 -0
- package/dist/tools/provider-grants.d.ts +36 -0
- package/dist/tools/provider-grants.js +72 -0
- package/dist/tools/provider-sanitize.d.ts +19 -0
- package/dist/tools/provider-sanitize.js +53 -0
- package/dist/tools/provider-types.d.ts +41 -0
- package/dist/tools/provider-types.js +26 -0
- package/dist/tools/read-continuation.d.ts +147 -0
- package/dist/tools/read-continuation.js +176 -0
- package/dist/tools/read-file.d.ts +32 -0
- package/dist/tools/read-file.js +85 -0
- package/dist/tools/read.d.ts +25 -0
- package/dist/tools/read.js +182 -0
- package/dist/tools/registry.d.ts +125 -0
- package/dist/tools/registry.js +67 -0
- package/dist/tools/request-capability.d.ts +13 -0
- package/dist/tools/request-capability.js +23 -0
- package/dist/tools/run-command-exec.d.ts +14 -0
- package/dist/tools/run-command-exec.js +161 -0
- package/dist/tools/run-command.d.ts +107 -0
- package/dist/tools/run-command.js +398 -0
- package/dist/tools/runtime-calltool.d.ts +146 -0
- package/dist/tools/runtime-calltool.js +223 -0
- package/dist/tools/runtime.d.ts +149 -0
- package/dist/tools/runtime.js +345 -0
- package/dist/tools/scratchpad.d.ts +20 -0
- package/dist/tools/scratchpad.js +197 -0
- package/dist/tools/spill.d.ts +81 -0
- package/dist/tools/spill.js +251 -0
- package/dist/tools/tool-audit.d.ts +171 -0
- package/dist/tools/tool-audit.js +183 -0
- package/dist/tools/truncate.d.ts +98 -0
- package/dist/tools/truncate.js +297 -0
- package/dist/tools/types.d.ts +145 -0
- package/dist/tools/types.js +24 -0
- package/dist/tools/verb-denial-argv-hint.d.ts +35 -0
- package/dist/tools/verb-denial-argv-hint.js +40 -0
- package/dist/tools/write.d.ts +9 -0
- package/dist/tools/write.js +45 -0
- package/package.json +54 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@nathapp/nax-agent` are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). While the version is `0.x`, a minor
|
|
5
|
+
release may change the public API.
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-10-03
|
|
8
|
+
|
|
9
|
+
First published version. Extracted from nax, where it was the native agent.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- The session contract, the native session adapter and `nativeComplete`, the tool set, permission
|
|
14
|
+
resolution, the OS sandbox, command-safety and the cost core, behind `@nathapp/nax-agent`.
|
|
15
|
+
- Process-wide slots: `setAgentLogger`, `configureCredentials`, and `setAgentRuntime` with a Node
|
|
16
|
+
default (`nodeRuntime`).
|
|
17
|
+
- Host ports: `runDeclaredCommand`, `ProtectedPathsPolicy`, `commandInterceptor`.
|
|
18
|
+
- `@nathapp/nax-agent/internal`, nax-only and outside semver.
|
|
19
|
+
- `api/nax-agent.api.txt`: the built public API, checked in CI.
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
|
|
23
|
+
- Requires Node.js >= 22.19.0. No Bun APIs ship in the package.
|
|
24
|
+
- `.` exports no `_`-prefixed name; test seams and reset hooks are on `./internal` only.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 William Khoo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @nathapp/nax-agent
|
|
2
|
+
|
|
3
|
+
nax's native coding agent as a package: the session contract, the native session adapter and its turn loop over `@nathapp/nax-ai`, the tool set, permission resolution, the OS sandbox, command-safety and cost accounting.
|
|
4
|
+
|
|
5
|
+
> **Pre-1.0, not yet published.** `0.1.0` is the first planned version. Until `1.0` the API of `.` may change in any minor release. Pin an exact version.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @nathapp/nax-agent
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires **Node.js >= 22.19.0**. The package is ESM only and ships no Bun code: it runs on Node, and on Bun, with the default runtime.
|
|
14
|
+
|
|
15
|
+
## Entry points
|
|
16
|
+
|
|
17
|
+
- **`@nathapp/nax-agent`** is the supported entry. Every export is named, and none starts with `_`. Its exact names are pinned in [`api/nax-agent.api.txt`](api/nax-agent.api.txt), which CI compares with the built declarations.
|
|
18
|
+
- **`@nathapp/nax-agent/internal`** is **nax-only and outside semver.** nax bundles this package and reaches below the public entry for shared helpers, `NaxError`, deep modules and the `_*Deps` test seams. Names, shapes and behaviour there can change in any release, patch included, and a change there is not a breaking change. Do not import it from another project.
|
|
19
|
+
|
|
20
|
+
## Process-wide slots
|
|
21
|
+
|
|
22
|
+
The host installs these once, near startup. They are module-level, so they apply to everything in the process.
|
|
23
|
+
|
|
24
|
+
| Slot | Install | When unset |
|
|
25
|
+
|:-----|:--------|:-----------|
|
|
26
|
+
| Logger | `setAgentLogger(logger)` (`null` clears) | Logging is silent: `getLogger()` is a no-op logger and `getSafeLogger()` is `null`. |
|
|
27
|
+
| Credentials | `configureCredentials({ configDir, readAuthConfig })` | The first credential read throws `NaxError` with code `CREDENTIALS_NOT_CONFIGURED`. There is no default config directory. |
|
|
28
|
+
| Runtime | `setAgentRuntime(runtime)` (`null` clears) | `getAgentRuntime()` returns `nodeRuntime`, built on `node:child_process` and `node:fs`. |
|
|
29
|
+
|
|
30
|
+
`AgentRuntime` is the process and glob contract (`spawn`, `glob`, `globSync`). The Node default is complete; install your own only to change how the agent spawns processes or expands globs.
|
|
31
|
+
|
|
32
|
+
## Ports the host supplies
|
|
33
|
+
|
|
34
|
+
Three pieces of host knowledge are passed in as data or functions, because the package cannot know them. Their behaviour when you omit them differs, so check each one.
|
|
35
|
+
|
|
36
|
+
- **`runDeclaredCommand`** runs a command your project declared (a test or lint command), never one the model wrote. If you do not supply it, the `RunCommand` tool answers `exit 1` with "no declared-command runner is configured for this session" and starts no process. It fails closed.
|
|
37
|
+
- **`ProtectedPathsPolicy`** names the paths you own and want kept away from the agent: git pathspecs the Git tool excludes from its default view, gitignore patterns `GitCommit` refuses to stage, the project state directory, the credential directory and the trust-store file the sandbox protects. If you do not supply it, the Git tool excludes nothing from its default view, and `GitCommit` fails closed: it refuses every path and stages nothing until the policy supplies a non-empty `gitIgnorePatterns` (an empty list refuses the same way). Supply it whenever the agent works in a directory that holds files you own. Building a sandboxed session requires it.
|
|
38
|
+
- **`commandInterceptor`** may rewrite a command before it runs (for example to prefix a wrapper binary). Rewrites are validated: an argv rewrite may only prefix the original argv with the provider's own binary, and a shell rewrite goes through the same narrowing. If you do not supply one, commands run unchanged. An interceptor that throws, or returns a rewrite that fails validation, is treated as a decline, and the original command runs.
|
|
39
|
+
|
|
40
|
+
## Status and roadmap
|
|
41
|
+
|
|
42
|
+
The embedder-facing session API is still being designed, so `0.x` may reshape `.` with a minor bump. See [`CHANGELOG.md`](CHANGELOG.md).
|
|
43
|
+
|
|
44
|
+
Maintainers: see the [release procedure](https://github.com/nathapp-io/nax/blob/main/packages/nax-agent/RELEASING.md)
|
|
45
|
+
for the manual 0.1.0 publish and OTP step, trusted-publisher setup and subsequent tagged releases.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash-specific grant resolution for `buildCodingToolSupport`.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from coding-tool-support.ts, which sits at the 600-line source
|
|
5
|
+
* limit, so this stage's grant math -- and the explanatory comments that are
|
|
6
|
+
* load-bearing for reading the ADR-030 gating rule correctly -- have room to
|
|
7
|
+
* live without pushing the caller over the limit.
|
|
8
|
+
*
|
|
9
|
+
* Pure: no I/O, no `NaxError`. Decides nothing about the empty-root guard or
|
|
10
|
+
* the terminal `tools.length === 0` guard; both stay in coding-tool-support.ts,
|
|
11
|
+
* where the rest of the runtime is assembled.
|
|
12
|
+
*/
|
|
13
|
+
import type { BashApprovalMode } from "#src/config/bash-approval";
|
|
14
|
+
import { type CodingToolName, type ToolGrant, type ToolPatternNarrowing } from "#src/tools/index";
|
|
15
|
+
export interface BashSupportResolution {
|
|
16
|
+
/**
|
|
17
|
+
* The grants `compileToolPolicy` should compile -- narrowed, and with the
|
|
18
|
+
* ADR-030 synthetic `Bash(*)` grant appended under `raw` when applicable.
|
|
19
|
+
*/
|
|
20
|
+
readonly effectiveGrants: readonly ToolGrant[];
|
|
21
|
+
/** Whether the op should receive a Bash tool at all (the declaration ceiling). */
|
|
22
|
+
readonly allowBash: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The patterns the Bash tool's description should name -- the EFFECTIVE
|
|
25
|
+
* grant (post-synthetic), not the pre-synthetic human grant, so a `raw`
|
|
26
|
+
* stage with no human `Bash(...)` rule still names what it can actually
|
|
27
|
+
* run instead of the stale "no command forms are granted" (ADR-030 / F3).
|
|
28
|
+
* `createBashTool` ignores this entirely under `raw` (it has its own
|
|
29
|
+
* static raw description), so the distinction only matters for
|
|
30
|
+
* `gated`/`escalate`, where it is identical to the pre-synthetic grant --
|
|
31
|
+
* `raw` is the only mode that ever synthesizes one.
|
|
32
|
+
*/
|
|
33
|
+
readonly bashDescriptionPatterns: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the Bash-specific half of a stage's compiled grants.
|
|
37
|
+
*
|
|
38
|
+
* `grants` is the RAW (pre-narrow) grant list, matching what
|
|
39
|
+
* `buildCodingToolSupport` used to pass into `narrowGrants` inline.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveBashSupport(args: {
|
|
42
|
+
declared: readonly CodingToolName[];
|
|
43
|
+
grants: readonly ToolGrant[];
|
|
44
|
+
toolPatterns?: ToolPatternNarrowing;
|
|
45
|
+
bashApproval: BashApprovalMode;
|
|
46
|
+
}): BashSupportResolution;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash-specific grant resolution for `buildCodingToolSupport`.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from coding-tool-support.ts, which sits at the 600-line source
|
|
5
|
+
* limit, so this stage's grant math -- and the explanatory comments that are
|
|
6
|
+
* load-bearing for reading the ADR-030 gating rule correctly -- have room to
|
|
7
|
+
* live without pushing the caller over the limit.
|
|
8
|
+
*
|
|
9
|
+
* Pure: no I/O, no `NaxError`. Decides nothing about the empty-root guard or
|
|
10
|
+
* the terminal `tools.length === 0` guard; both stay in coding-tool-support.ts,
|
|
11
|
+
* where the rest of the runtime is assembled.
|
|
12
|
+
*/
|
|
13
|
+
import { BASH_TOOL_NAME, narrowGrants, } from "#src/tools/index";
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the Bash-specific half of a stage's compiled grants.
|
|
16
|
+
*
|
|
17
|
+
* `grants` is the RAW (pre-narrow) grant list, matching what
|
|
18
|
+
* `buildCodingToolSupport` used to pass into `narrowGrants` inline.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveBashSupport(args) {
|
|
21
|
+
// Narrowed, not raw: `narrowGrants` is what the POLICY compiles, so reading
|
|
22
|
+
// the raw list here would name forms in the tool's description that the
|
|
23
|
+
// policy then refuses -- the wasted turn the `patterns` option exists to
|
|
24
|
+
// prevent, inverted.
|
|
25
|
+
const narrowedGrants = narrowGrants(args.grants, args.toolPatterns);
|
|
26
|
+
const bashGrant = narrowedGrants.findLast((grant) => grant.tool === BASH_TOOL_NAME);
|
|
27
|
+
const allowBash = args.declared.includes(BASH_TOOL_NAME);
|
|
28
|
+
// ADR-030: `raw` changes GATING, not GRANTING — the Bash tool still has to be
|
|
29
|
+
// granted or `callTool` never reaches the policy. The grant is synthetic
|
|
30
|
+
// (no human wrote a `Bash(...)` rule) and is deliberately conditioned on the
|
|
31
|
+
// op having DECLARED Bash. That condition is what keeps review ops and the
|
|
32
|
+
// verifier shell-free under every mode: they declare no Bash, so no mode can
|
|
33
|
+
// hand them one. Never grant unconditionally here.
|
|
34
|
+
const syntheticGrant = args.bashApproval === "raw" && allowBash && bashGrant === undefined
|
|
35
|
+
? { tool: BASH_TOOL_NAME, patterns: ["*"] }
|
|
36
|
+
: undefined;
|
|
37
|
+
const effectiveGrants = syntheticGrant !== undefined ? [...narrowedGrants, syntheticGrant] : narrowedGrants;
|
|
38
|
+
const effectiveBashGrant = bashGrant ?? syntheticGrant;
|
|
39
|
+
return {
|
|
40
|
+
effectiveGrants,
|
|
41
|
+
allowBash,
|
|
42
|
+
bashDescriptionPatterns: effectiveBashGrant?.patterns ?? [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session-local tools an operation declares: RunCommand (its declared
|
|
3
|
+
* commands and, when `Exec` is declared, its argv branch) and Bash.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from coding-tool-support.ts to keep that file under its 600-line
|
|
6
|
+
* source limit ahead of the P4 sandbox wiring. A pure move: every comment that
|
|
7
|
+
* explains a field below travelled with it.
|
|
8
|
+
*/
|
|
9
|
+
import type { BashApprovalMode } from "#src/config/bash-approval";
|
|
10
|
+
import type { QualityCommandSpec } from "#src/internal/command-spec/index";
|
|
11
|
+
import type { CommandLauncher } from "#src/sandbox/index";
|
|
12
|
+
import { type CodingTool, type DeclaredCommandRunner, type ToolGrant } from "#src/tools/index";
|
|
13
|
+
export interface DeclaredCommandToolsArgs {
|
|
14
|
+
readonly declaredCommands: ReadonlyMap<string, QualityCommandSpec>;
|
|
15
|
+
readonly allowExec: boolean;
|
|
16
|
+
readonly execGrant: ToolGrant | undefined;
|
|
17
|
+
readonly allowBash: boolean;
|
|
18
|
+
readonly bashDescriptionPatterns: readonly string[];
|
|
19
|
+
readonly bashApproval: BashApprovalMode;
|
|
20
|
+
/** A human can answer an escalated Bash command (ADR-030, amended for P4). */
|
|
21
|
+
readonly humanApproval?: boolean;
|
|
22
|
+
readonly root: string;
|
|
23
|
+
readonly repoRoot?: string;
|
|
24
|
+
readonly packageWorkdir?: string;
|
|
25
|
+
readonly commandCwd?: string;
|
|
26
|
+
readonly allowScripts?: boolean;
|
|
27
|
+
readonly packageName?: string;
|
|
28
|
+
readonly stripEnvVars?: readonly string[];
|
|
29
|
+
readonly shell?: string;
|
|
30
|
+
/** Port 7: runs declared commands; nax supplies `runQualityCommand`. */
|
|
31
|
+
readonly runDeclaredCommand?: DeclaredCommandRunner;
|
|
32
|
+
/** P4: how Bash and Exec-branch commands run; absent = direct spawn (tests). */
|
|
33
|
+
readonly launcher?: CommandLauncher;
|
|
34
|
+
}
|
|
35
|
+
export declare function buildDeclaredCommandTools(args: DeclaredCommandToolsArgs): CodingTool[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createBashTool, createRunCommandTool, } from "#src/tools/index";
|
|
2
|
+
export function buildDeclaredCommandTools(args) {
|
|
3
|
+
return [
|
|
4
|
+
...(args.declaredCommands.size > 0 || args.allowExec
|
|
5
|
+
? [
|
|
6
|
+
createRunCommandTool(args.declaredCommands, {
|
|
7
|
+
stripEnvVars: args.stripEnvVars,
|
|
8
|
+
commandCwd: args.commandCwd ?? args.root,
|
|
9
|
+
runDeclaredCommand: args.runDeclaredCommand,
|
|
10
|
+
...(args.allowExec
|
|
11
|
+
? {
|
|
12
|
+
exec: {
|
|
13
|
+
repoRoot: args.repoRoot ?? args.root,
|
|
14
|
+
// Post-root-move: `args.root` is the repo root, so the
|
|
15
|
+
// fallback only matters for single-package repos where
|
|
16
|
+
// the two coincide (and for tests not threading
|
|
17
|
+
// `packageWorkdir`). Production always threads it via
|
|
18
|
+
// `commandCwd` plumbing in `resolveCodingToolSupport`
|
|
19
|
+
// (Task 10), which makes `effectiveTarget`'s
|
|
20
|
+
// `packageRelPath === ""` collapse impossible for a
|
|
21
|
+
// package story.
|
|
22
|
+
packageWorkdir: args.packageWorkdir ?? args.root,
|
|
23
|
+
allowScripts: args.allowScripts ?? false,
|
|
24
|
+
// The compiled grant, not BUILT_IN_EXEC_PATTERNS -- a
|
|
25
|
+
// project's own Exec(...) expression replaces that
|
|
26
|
+
// list rather than extending it (see the comment on
|
|
27
|
+
// BUILT_IN_EXEC_PATTERNS in src/config/permissions.ts).
|
|
28
|
+
// `allowExec` is true only when execGrant is defined,
|
|
29
|
+
// so this array is never actually empty at this call
|
|
30
|
+
// site; the fallback exists only for the type.
|
|
31
|
+
patterns: args.execGrant?.patterns ?? [],
|
|
32
|
+
...(args.packageName !== undefined ? { packageName: args.packageName } : {}),
|
|
33
|
+
...(args.launcher !== undefined ? { launcher: args.launcher } : {}),
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
: {}),
|
|
37
|
+
}),
|
|
38
|
+
]
|
|
39
|
+
: []),
|
|
40
|
+
...(args.allowBash
|
|
41
|
+
? [
|
|
42
|
+
createBashTool({
|
|
43
|
+
...(args.shell !== undefined ? { shell: args.shell } : {}),
|
|
44
|
+
...(args.stripEnvVars !== undefined ? { stripEnvVars: args.stripEnvVars } : {}),
|
|
45
|
+
// The EFFECTIVE grant, so the description names what THIS
|
|
46
|
+
// stage may actually run, incl. the synthetic grant under
|
|
47
|
+
// `raw` (ADR-030 / F3) -- ignored under `raw` regardless.
|
|
48
|
+
patterns: args.bashDescriptionPatterns,
|
|
49
|
+
bashApproval: args.bashApproval,
|
|
50
|
+
...(args.humanApproval === true ? { humanApproval: true } : {}),
|
|
51
|
+
...(args.launcher !== undefined ? { launcher: args.launcher } : {}),
|
|
52
|
+
}),
|
|
53
|
+
]
|
|
54
|
+
: []),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import type { SandboxConfig } from "#src/config/schemas-sandbox";
|
|
3
|
+
import { type CommandLauncher, defaultTempRoots, listCredentialFiles, listGitGuardFiles, listNaxEntries, probeSandboxOnce, resolveGitLayout, runTempRoots, sandboxBackendFor, strayCommonDirTripwire } from "#src/sandbox/index";
|
|
4
|
+
import type { ProtectedPathsPolicy } from "#src/tools/index";
|
|
5
|
+
export declare const _sessionSandboxDeps: {
|
|
6
|
+
backendFor: typeof sandboxBackendFor;
|
|
7
|
+
probe: typeof probeSandboxOnce;
|
|
8
|
+
gitLayout: typeof resolveGitLayout;
|
|
9
|
+
naxEntries: typeof listNaxEntries;
|
|
10
|
+
gitGuardFiles: typeof listGitGuardFiles;
|
|
11
|
+
commonDirTripwire: typeof strayCommonDirTripwire;
|
|
12
|
+
credentialFiles: typeof listCredentialFiles;
|
|
13
|
+
tempRoots: typeof defaultTempRoots;
|
|
14
|
+
homedir: typeof homedir;
|
|
15
|
+
platform: () => NodeJS.Platform;
|
|
16
|
+
/** US-002 — creates the session temp dir before the policy is built. */
|
|
17
|
+
mkdir: (path: string) => Promise<unknown>;
|
|
18
|
+
/** US-002 — the host temp dir, kept injectable so the policy choice is testable. */
|
|
19
|
+
tmpdir: () => string;
|
|
20
|
+
/** US-002 — the temp roots of one confined run, given its own root. */
|
|
21
|
+
runTempRoots: typeof runTempRoots;
|
|
22
|
+
};
|
|
23
|
+
export declare function resolveSessionSandbox(args: {
|
|
24
|
+
readonly config: SandboxConfig | undefined;
|
|
25
|
+
readonly root: string;
|
|
26
|
+
readonly outputDir?: string;
|
|
27
|
+
readonly needsLauncher: boolean;
|
|
28
|
+
readonly storyId?: string;
|
|
29
|
+
/** US-004 — per-session temp directory the launcher creates and exports as TMPDIR/TMP/TEMP. */
|
|
30
|
+
readonly tmpDir?: string;
|
|
31
|
+
/** US-002 — the run's own temp root, present whenever `tmpDir` is (dispatch-supplied). */
|
|
32
|
+
readonly runTmpRoot?: string;
|
|
33
|
+
/** Host-owned paths the policy denies (S1 spec port 6); nax passes `naxProtectedPaths()`. */
|
|
34
|
+
readonly protectedPaths: ProtectedPathsPolicy;
|
|
35
|
+
}): Promise<CommandLauncher>;
|
|
36
|
+
/** The compile-time policy refusal for `raw` (Task 8), or undefined. */
|
|
37
|
+
export declare function rawRefusalFor(launcher: CommandLauncher | undefined): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* US-004: whether the session's sandbox confines temp writes to this run's own
|
|
40
|
+
* temp root.
|
|
41
|
+
*
|
|
42
|
+
* True exactly when the launcher is available AND says `sharedTmp: false` —
|
|
43
|
+
* the only state under which the command guard may skip the classifier for a
|
|
44
|
+
* temp-only command (`src/agents/coding-tool-support.ts` → `tempConfined` →
|
|
45
|
+
* `src/command-safety/guard.ts:185`). An available launcher without
|
|
46
|
+
* `sharedTmp`, an unavailable or disabled one, and no launcher at all all read
|
|
47
|
+
* as NOT confined.
|
|
48
|
+
*
|
|
49
|
+
* That is the RESOLVE-time answer, fixed when the launcher is built, and it is
|
|
50
|
+
* deliberately NOT the answer the policy narrows on. `SandboxPolicyInput.confined`
|
|
51
|
+
* ANDs it with whether the per-command session-dir `mkdir` succeeded
|
|
52
|
+
* (`src/sandbox/launcher.ts:194`), because the `export TMPDIR=…` prefix rides on
|
|
53
|
+
* that same mkdir. So a session whose dir fails to re-create reports `true` here
|
|
54
|
+
* while the policy for that ONE command is the unconflined one, `/tmp/claude`
|
|
55
|
+
* included — which means the guard's temp-only exemption can be claimed for a
|
|
56
|
+
* command that is not in fact confined. Deliberate, and documented on
|
|
57
|
+
* `SandboxPolicyInput.confined` (`src/sandbox/policy-builder.ts:41-64`); do not
|
|
58
|
+
* reconcile the two by making either follow the other.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isTempConfined(launcher: CommandLauncher | undefined): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* US-002: the raw-screen options `compileToolPolicy` consumes, derived from the
|
|
63
|
+
* launcher's state.
|
|
64
|
+
*
|
|
65
|
+
* `rawBashRefusal` is exactly what `rawRefusalFor` returns (an unavailable
|
|
66
|
+
* launcher refuses every call). `sandboxWrapped: true` is added for an available
|
|
67
|
+
* launcher: the command really does run inside the OS sandbox, so the raw screen
|
|
68
|
+
* stops refusing a feature PRD the command only READS. A disabled launcher
|
|
69
|
+
* yields neither key.
|
|
70
|
+
*/
|
|
71
|
+
export declare function rawScreenOptionsFor(launcher: CommandLauncher | undefined): {
|
|
72
|
+
rawBashRefusal?: string;
|
|
73
|
+
sandboxWrapped?: true;
|
|
74
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The async half of P4's session wiring (spec 5.2, review finding 7).
|
|
3
|
+
*
|
|
4
|
+
* resolveCodingToolSupport is async and awaits this; buildCodingToolSupport is
|
|
5
|
+
* synchronous and on the hot dispatch path, so it only receives the result as
|
|
6
|
+
* data. Nothing here may be called from the sync path.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir } from "node:fs/promises";
|
|
9
|
+
import { homedir, tmpdir } from "node:os";
|
|
10
|
+
import { errorMessage } from "#src/infra/errors";
|
|
11
|
+
import { getSafeLogger, NaxError } from "#src/infra/index";
|
|
12
|
+
import { approvalsPath } from "#src/permissions/index";
|
|
13
|
+
import { buildSandboxPolicy, createCommandLauncher, DISABLED_SANDBOX_STATE, defaultTempRoots, listCredentialFiles, listGitGuardFiles, listNaxEntries, probeSandboxOnce, rawBashRefusalReason, resolveGitLayout, runTempRoots, sandboxBackendFor, strayCommonDirTripwire, warnSandboxUnavailableOnce, } from "#src/sandbox/index";
|
|
14
|
+
export const _sessionSandboxDeps = {
|
|
15
|
+
backendFor: sandboxBackendFor,
|
|
16
|
+
probe: probeSandboxOnce,
|
|
17
|
+
gitLayout: resolveGitLayout,
|
|
18
|
+
naxEntries: listNaxEntries,
|
|
19
|
+
gitGuardFiles: listGitGuardFiles,
|
|
20
|
+
commonDirTripwire: strayCommonDirTripwire,
|
|
21
|
+
credentialFiles: listCredentialFiles,
|
|
22
|
+
tempRoots: defaultTempRoots,
|
|
23
|
+
homedir,
|
|
24
|
+
platform: () => process.platform,
|
|
25
|
+
/** US-002 — creates the session temp dir before the policy is built. */
|
|
26
|
+
mkdir: (path) => mkdir(path, { recursive: true }),
|
|
27
|
+
/** US-002 — the host temp dir, kept injectable so the policy choice is testable. */
|
|
28
|
+
tmpdir: () => tmpdir(),
|
|
29
|
+
/** US-002 — the temp roots of one confined run, given its own root. */
|
|
30
|
+
runTempRoots,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* US-002: which temp roots a session's policy grants.
|
|
34
|
+
*
|
|
35
|
+
* Shared roots are today's behaviour and the fallback: the opt-out
|
|
36
|
+
* (`allowSharedTmp`), a session with no run root, and a failed directory
|
|
37
|
+
* creation all land there.
|
|
38
|
+
*
|
|
39
|
+
* Confinement needs both `runTmpRoot` and `tmpDir` — the session temp dir the
|
|
40
|
+
* launcher exports as TMPDIR is no more writable than `/tmp` is, so it has to
|
|
41
|
+
* be created here, BEFORE the policy that grants it. A creation failure fails
|
|
42
|
+
* OPEN to the shared roots: a session must never run with a TMPDIR its own
|
|
43
|
+
* sandbox cannot write.
|
|
44
|
+
*/
|
|
45
|
+
async function sessionTempRoots(args) {
|
|
46
|
+
const { runTmpRoot, tmpDir } = args;
|
|
47
|
+
const shared = () => ({ tempRoots: _sessionSandboxDeps.tempRoots(), confined: false });
|
|
48
|
+
if (args.config.filesystem.allowSharedTmp || runTmpRoot === undefined || tmpDir === undefined)
|
|
49
|
+
return shared();
|
|
50
|
+
try {
|
|
51
|
+
await _sessionSandboxDeps.mkdir(tmpDir);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
getSafeLogger()?.warn("sandbox", "could not create the session temp dir — granting the shared temp roots", {
|
|
55
|
+
storyId: args.storyId ?? "_dispatch",
|
|
56
|
+
tmpDir,
|
|
57
|
+
error: errorMessage(err),
|
|
58
|
+
});
|
|
59
|
+
return shared();
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
tempRoots: _sessionSandboxDeps.runTempRoots({ runTmpRoot, tmpdir: _sessionSandboxDeps.tmpdir() }),
|
|
63
|
+
confined: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export async function resolveSessionSandbox(args) {
|
|
67
|
+
const config = args.config;
|
|
68
|
+
if (config === undefined || !config.enabled || !args.needsLauncher) {
|
|
69
|
+
return createCommandLauncher({
|
|
70
|
+
state: DISABLED_SANDBOX_STATE,
|
|
71
|
+
...(args.tmpDir !== undefined ? { tmpDir: args.tmpDir } : {}),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const backend = _sessionSandboxDeps.backendFor(config);
|
|
75
|
+
const probe = await _sessionSandboxDeps.probe(backend, args.storyId);
|
|
76
|
+
if (!probe.available) {
|
|
77
|
+
warnSandboxUnavailableOnce(probe.reason, args.storyId);
|
|
78
|
+
return createCommandLauncher({
|
|
79
|
+
state: { kind: "unavailable", backend: backend.name, reason: probe.reason },
|
|
80
|
+
...(args.tmpDir !== undefined ? { tmpDir: args.tmpDir } : {}),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const git = await _sessionSandboxDeps.gitLayout(args.root);
|
|
84
|
+
const credentialFiles = await _sessionSandboxDeps.credentialFiles(args.protectedPaths.credentialDir);
|
|
85
|
+
const approvalsFile = args.outputDir !== undefined ? approvalsPath(args.outputDir) : undefined;
|
|
86
|
+
// Before the policy: the confined roots include the session temp dir itself.
|
|
87
|
+
const { tempRoots, confined } = await sessionTempRoots({
|
|
88
|
+
config,
|
|
89
|
+
storyId: args.storyId,
|
|
90
|
+
runTmpRoot: args.runTmpRoot,
|
|
91
|
+
tmpDir: args.tmpDir,
|
|
92
|
+
});
|
|
93
|
+
const policyFor = async (root, tmpDirInForce) => buildSandboxPolicy({
|
|
94
|
+
root,
|
|
95
|
+
git,
|
|
96
|
+
// Per build, like the .nax entries: a worktree added mid-run gets its denies too.
|
|
97
|
+
gitGuardFiles: await _sessionSandboxDeps.gitGuardFiles(git),
|
|
98
|
+
naxEntries: await _sessionSandboxDeps.naxEntries(root, args.protectedPaths.projectStateDir),
|
|
99
|
+
credentialFiles,
|
|
100
|
+
...(approvalsFile !== undefined ? { approvalsFile } : {}),
|
|
101
|
+
// US-006: a command inside the sandbox must not be able to rewrite the
|
|
102
|
+
// trust store that decides whether repository code runs.
|
|
103
|
+
trustStoreFile: args.protectedPaths.trustStoreFile,
|
|
104
|
+
home: _sessionSandboxDeps.homedir(),
|
|
105
|
+
tempRoots,
|
|
106
|
+
// #2301: the RESOLVED confinement, not `config.filesystem.allowSharedTmp`.
|
|
107
|
+
// A session confines only when that opt-out is off, a run root AND a
|
|
108
|
+
// session dir were both supplied, and the dir was actually created.
|
|
109
|
+
// `allowSharedTmp: false` is therefore no evidence of confinement — it also
|
|
110
|
+
// holds when the session dir could not be created and when no run root was
|
|
111
|
+
// supplied, and reading the flag would deny a darwin session that is
|
|
112
|
+
// running on the shared roots.
|
|
113
|
+
//
|
|
114
|
+
// Narrowed by whether THIS command will actually get the session's
|
|
115
|
+
// `TMPDIR`. `createCommandLauncher` recreates the session dir before every
|
|
116
|
+
// run and drops the `export TMPDIR=…` prefix when that creation fails
|
|
117
|
+
// (src/sandbox/launcher.ts), and then srt's own forced `TMPDIR`
|
|
118
|
+
// (`/tmp/claude`, sandbox-utils.js:630) is what the child gets — denying it
|
|
119
|
+
// would leave the session with a TMPDIR its own sandbox refuses to write,
|
|
120
|
+
// the one posture SPEC-tmp-confinement.md:132 rules out.
|
|
121
|
+
confined: confined && tmpDirInForce,
|
|
122
|
+
platform: _sessionSandboxDeps.platform(),
|
|
123
|
+
config,
|
|
124
|
+
});
|
|
125
|
+
// Review #17: literal() refuses a glob character in any policy path, and the
|
|
126
|
+
// probe builds its own policy, so a repo path like `re[x]po` passed the probe
|
|
127
|
+
// and then failed every command. Build the policy once here instead.
|
|
128
|
+
// The old residual (a glob-named feature dir created mid-run failing every
|
|
129
|
+
// command) is gone: listNaxEntries skips glob-named `.nax` entries (nax#2260).
|
|
130
|
+
const policyError = await literalPolicyError(policyFor, args.root, confined);
|
|
131
|
+
if (policyError !== undefined) {
|
|
132
|
+
warnSandboxUnavailableOnce(policyError, args.storyId);
|
|
133
|
+
return createCommandLauncher({ state: { kind: "unavailable", backend: backend.name, reason: policyError } });
|
|
134
|
+
}
|
|
135
|
+
const afterWrapped = await _sessionSandboxDeps.commonDirTripwire(git, args.storyId);
|
|
136
|
+
const network = config.network.allowedDomains ?? "open"; // absent = open (spec S2)
|
|
137
|
+
return createCommandLauncher({
|
|
138
|
+
// `sharedTmp` is present only when confined: absent means the shared temp
|
|
139
|
+
// roots are writable, which is what every pre-US-002 launcher says.
|
|
140
|
+
state: { kind: "available", backend: backend.name, network, ...(confined ? { sharedTmp: false } : {}) },
|
|
141
|
+
backend,
|
|
142
|
+
policyFor,
|
|
143
|
+
...(afterWrapped !== undefined ? { afterWrapped } : {}),
|
|
144
|
+
...(args.tmpDir !== undefined ? { tmpDir: args.tmpDir } : {}),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/** The compile-time policy refusal for `raw` (Task 8), or undefined. */
|
|
148
|
+
export function rawRefusalFor(launcher) {
|
|
149
|
+
return launcher?.state.kind === "unavailable" ? rawBashRefusalReason(launcher.state.reason) : undefined;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* US-004: whether the session's sandbox confines temp writes to this run's own
|
|
153
|
+
* temp root.
|
|
154
|
+
*
|
|
155
|
+
* True exactly when the launcher is available AND says `sharedTmp: false` —
|
|
156
|
+
* the only state under which the command guard may skip the classifier for a
|
|
157
|
+
* temp-only command (`src/agents/coding-tool-support.ts` → `tempConfined` →
|
|
158
|
+
* `src/command-safety/guard.ts:185`). An available launcher without
|
|
159
|
+
* `sharedTmp`, an unavailable or disabled one, and no launcher at all all read
|
|
160
|
+
* as NOT confined.
|
|
161
|
+
*
|
|
162
|
+
* That is the RESOLVE-time answer, fixed when the launcher is built, and it is
|
|
163
|
+
* deliberately NOT the answer the policy narrows on. `SandboxPolicyInput.confined`
|
|
164
|
+
* ANDs it with whether the per-command session-dir `mkdir` succeeded
|
|
165
|
+
* (`src/sandbox/launcher.ts:194`), because the `export TMPDIR=…` prefix rides on
|
|
166
|
+
* that same mkdir. So a session whose dir fails to re-create reports `true` here
|
|
167
|
+
* while the policy for that ONE command is the unconflined one, `/tmp/claude`
|
|
168
|
+
* included — which means the guard's temp-only exemption can be claimed for a
|
|
169
|
+
* command that is not in fact confined. Deliberate, and documented on
|
|
170
|
+
* `SandboxPolicyInput.confined` (`src/sandbox/policy-builder.ts:41-64`); do not
|
|
171
|
+
* reconcile the two by making either follow the other.
|
|
172
|
+
*/
|
|
173
|
+
export function isTempConfined(launcher) {
|
|
174
|
+
return launcher?.state.kind === "available" && launcher.state.sharedTmp === false;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* US-002: the raw-screen options `compileToolPolicy` consumes, derived from the
|
|
178
|
+
* launcher's state.
|
|
179
|
+
*
|
|
180
|
+
* `rawBashRefusal` is exactly what `rawRefusalFor` returns (an unavailable
|
|
181
|
+
* launcher refuses every call). `sandboxWrapped: true` is added for an available
|
|
182
|
+
* launcher: the command really does run inside the OS sandbox, so the raw screen
|
|
183
|
+
* stops refusing a feature PRD the command only READS. A disabled launcher
|
|
184
|
+
* yields neither key.
|
|
185
|
+
*/
|
|
186
|
+
export function rawScreenOptionsFor(launcher) {
|
|
187
|
+
if (launcher?.state.kind === "available")
|
|
188
|
+
return { sandboxWrapped: true };
|
|
189
|
+
const rawBashRefusal = rawRefusalFor(launcher);
|
|
190
|
+
return rawBashRefusal !== undefined ? { rawBashRefusal } : {};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* The compile-time policy refusal for `raw` (Task 8), or undefined.
|
|
194
|
+
*
|
|
195
|
+
* `resolvedConfinement` is the session's RESOLVE-time `confined`, not the
|
|
196
|
+
* per-launch `tmpDirInForce` this signature's second slot normally carries — a
|
|
197
|
+
* superset of it, since a launch can only narrow. That is deliberate: this probe
|
|
198
|
+
* exists to turn a glob refusal from `buildSandboxPolicy` into a session-level
|
|
199
|
+
* refusal, and the entries a narrower confinement differs by (#2301 — srt's
|
|
200
|
+
* `/tmp/claude` grant, present only when NOT confined) are fixed literals with no
|
|
201
|
+
* glob character. Passing the superset therefore exercises the strictest policy
|
|
202
|
+
* without risking a refusal a real launch would never hit.
|
|
203
|
+
*/
|
|
204
|
+
async function literalPolicyError(policyFor, root, resolvedConfinement) {
|
|
205
|
+
try {
|
|
206
|
+
await policyFor(root, resolvedConfinement);
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
if (err instanceof NaxError && err.code === "SANDBOX_POLICY_NOT_LITERAL") {
|
|
211
|
+
return `[sandbox] a path in the sandbox policy contains a glob character: ${String(err.context?.path ?? "")}`;
|
|
212
|
+
}
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
}
|