@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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission-subsystem types (spec 2026-09-13 native-permission-subsystem).
|
|
3
|
+
*
|
|
4
|
+
* Rules reuse the ToolGrant shape — {tool, patterns} — with the EFFECT carried
|
|
5
|
+
* by which list a rule sits in (allow/deny/ask), never inside the rule itself.
|
|
6
|
+
* Precedence is deny > ask > allow (spec R6) and is enforced where rules are
|
|
7
|
+
* evaluated (src/tools/policy.ts), not where they are declared.
|
|
8
|
+
*/
|
|
9
|
+
import type { BashApprovalMode } from "#src/config/bash-approval";
|
|
10
|
+
import type { ToolGrant } from "#src/tools/index";
|
|
11
|
+
/** What a matched `ask` rule needs answered before the call may run. */
|
|
12
|
+
export interface AskRequest {
|
|
13
|
+
readonly tool: string;
|
|
14
|
+
/** PipelineStage at the time of the call; a plain string here to keep this
|
|
15
|
+
* module free of value imports from src/config. */
|
|
16
|
+
readonly stage: string;
|
|
17
|
+
/** The rule expression that matched, verbatim from config. */
|
|
18
|
+
readonly rule: string;
|
|
19
|
+
/**
|
|
20
|
+
* The ask rule that matched, verbatim. Set ONLY when an ask rule matched;
|
|
21
|
+
* absent when the ask is an `escalate`-mode grant miss, where `rule` falls
|
|
22
|
+
* back to the denial reason. A remembered approval derives its `origin`
|
|
23
|
+
* from this (#2249).
|
|
24
|
+
*/
|
|
25
|
+
readonly matchedRule?: string;
|
|
26
|
+
/** One human-readable line describing the attempted call. */
|
|
27
|
+
readonly summary: string;
|
|
28
|
+
/**
|
|
29
|
+
* The command string VERBATIM, never truncated. `summary` is capped at 200
|
|
30
|
+
* chars for logs; a human deciding whether to permit a command must see all
|
|
31
|
+
* of it, or they are approving a string they never read.
|
|
32
|
+
*/
|
|
33
|
+
readonly command?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Set when the call's arguments contain a secret whose masked form could
|
|
36
|
+
* hide shell syntax (review #9). The human link denies without prompting.
|
|
37
|
+
*/
|
|
38
|
+
readonly unshowable?: true;
|
|
39
|
+
/** The permitted root -- where the shell actually starts (src/tools/bash.ts:165). */
|
|
40
|
+
readonly root?: string;
|
|
41
|
+
/** The verdict's original reason, NOT rewritten as `matched ask rule "..."`. */
|
|
42
|
+
readonly reason?: string;
|
|
43
|
+
readonly storyId?: string;
|
|
44
|
+
readonly featureName?: string;
|
|
45
|
+
}
|
|
46
|
+
export type { ToolGrant };
|
|
47
|
+
export interface ResolvedPermissions {
|
|
48
|
+
/**
|
|
49
|
+
* ACP permission mode string — the only resolved value anything consumes
|
|
50
|
+
* (`agents/acp/adapter.ts`, `runtime/middleware/audit.ts`).
|
|
51
|
+
*/
|
|
52
|
+
mode: "approve-all" | "approve-reads" | "default";
|
|
53
|
+
/**
|
|
54
|
+
* Declarative grants — data, never matchers. Compiled into an enforceable
|
|
55
|
+
* policy by src/tools/, which keeps glob and filesystem semantics out of the
|
|
56
|
+
* config layer while the decision stays here, in the gated SSOT.
|
|
57
|
+
*/
|
|
58
|
+
toolGrants?: readonly ToolGrant[];
|
|
59
|
+
/**
|
|
60
|
+
* Deny rules for the stage (spec R6: deny > ask > allow). Same {tool, patterns}
|
|
61
|
+
* shape as `toolGrants`; compiled and enforced by src/tools/.
|
|
62
|
+
*/
|
|
63
|
+
denyRules?: readonly ToolGrant[];
|
|
64
|
+
/** Ask rules for the stage; resolved by an AskResolver at call time (spec R1). */
|
|
65
|
+
askRules?: readonly ToolGrant[];
|
|
66
|
+
/**
|
|
67
|
+
* How far provider (MCP) tools reach for this stage (spec R7).
|
|
68
|
+
*
|
|
69
|
+
* `all` — every attached provider, as `unrestricted` has always had it.
|
|
70
|
+
* `rules` — only what the stage's `Mcp(...)` rules admit (`scoped`).
|
|
71
|
+
* `none` — no provider tools at all (`safe`, and the fail-closed arm).
|
|
72
|
+
*
|
|
73
|
+
* Decided here rather than by the consumer because `scoped` and `safe` both
|
|
74
|
+
* resolve to the same MODE, so no consumer can tell them apart — and this is
|
|
75
|
+
* a permission decision, which lives in this file by rule.
|
|
76
|
+
*/
|
|
77
|
+
providerScope?: "all" | "rules" | "none";
|
|
78
|
+
/**
|
|
79
|
+
* How a model-authored bash command string is adjudicated for this stage
|
|
80
|
+
* (ADR-030). Always present: the global default is `raw`, and a per-stage
|
|
81
|
+
* `permissions.<stage>.bashApproval` overrides it. Compiled into the policy
|
|
82
|
+
* by src/tools/, like the rule lists above — the DECISION stays here.
|
|
83
|
+
*/
|
|
84
|
+
bashApproval: BashApprovalMode;
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Node glob normalized to the agent contract and Bun's symlink traversal. */
|
|
2
|
+
import { globSync, lstatSync, statSync } from "node:fs";
|
|
3
|
+
import { glob } from "node:fs/promises";
|
|
4
|
+
import { relative, resolve, sep } from "node:path";
|
|
5
|
+
function scanOptions(pattern, opts) {
|
|
6
|
+
const cwd = resolve(opts.cwd);
|
|
7
|
+
if (!statSync(cwd).isDirectory()) {
|
|
8
|
+
throw Object.assign(new Error(`ENOTDIR: not a directory, glob '${cwd}'`), { code: "ENOTDIR", path: cwd });
|
|
9
|
+
}
|
|
10
|
+
const prefix = [];
|
|
11
|
+
for (const part of pattern.split("/")) {
|
|
12
|
+
if (/[*?[{]/.test(part.replace(/\\./g, "")))
|
|
13
|
+
break;
|
|
14
|
+
prefix.push(part.replace(/\\(.)/g, "$1"));
|
|
15
|
+
}
|
|
16
|
+
const literalPrefix = relative(cwd, resolve(cwd, ...prefix));
|
|
17
|
+
const symlinks = new Map();
|
|
18
|
+
const blockedSymlink = (path) => {
|
|
19
|
+
if (literalPrefix === path || literalPrefix.startsWith(`${path}${sep}`))
|
|
20
|
+
return false;
|
|
21
|
+
if (!symlinks.has(path))
|
|
22
|
+
symlinks.set(path, lstatSync(resolve(cwd, path), { throwIfNoEntry: false })?.isSymbolicLink() ?? false);
|
|
23
|
+
return symlinks.get(path) === true;
|
|
24
|
+
};
|
|
25
|
+
const exclude = (entry) => {
|
|
26
|
+
const path = relative(cwd, resolve(cwd, entry.parentPath, entry.name));
|
|
27
|
+
if (path.split(sep).some((segment) => segment.startsWith(".")))
|
|
28
|
+
return true;
|
|
29
|
+
// Node can optimize brace/class segments without reporting their symlink
|
|
30
|
+
// entries. Check lexical ancestors too, allowing only the literal prefix.
|
|
31
|
+
const parts = path.split(sep);
|
|
32
|
+
for (let end = 1; end <= parts.length; end++) {
|
|
33
|
+
if (blockedSymlink(parts.slice(0, end).join(sep)))
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
return { cwd, withFileTypes: true, exclude };
|
|
39
|
+
}
|
|
40
|
+
function filePath(entry, cwd, absolute, pattern) {
|
|
41
|
+
const full = resolve(cwd, entry.parentPath, entry.name);
|
|
42
|
+
if (!entry.isFile() &&
|
|
43
|
+
!(!/[*?[{]/.test(pattern.replace(/\\./g, "")) &&
|
|
44
|
+
entry.isSymbolicLink() &&
|
|
45
|
+
statSync(full, { throwIfNoEntry: false })?.isFile()))
|
|
46
|
+
return null;
|
|
47
|
+
const path = relative(cwd, full);
|
|
48
|
+
if (path.split(sep).some((segment) => segment.startsWith(".")))
|
|
49
|
+
return null;
|
|
50
|
+
const dotPrefix = pattern.match(/^(?:\.\/)+/)?.[0] ?? "";
|
|
51
|
+
return absolute ? resolve(full) : `${dotPrefix}${path}`;
|
|
52
|
+
}
|
|
53
|
+
function nodePattern(pattern) {
|
|
54
|
+
// Node treats backslash escapes differently; character classes express the
|
|
55
|
+
// same literal metacharacters without changing the Bun-facing contract.
|
|
56
|
+
return pattern.replace(/\\([[\]*?{}])/g, (_escape, char) => `[${char}]`);
|
|
57
|
+
}
|
|
58
|
+
export async function* nodeGlob(pattern, opts) {
|
|
59
|
+
const options = scanOptions(pattern, opts);
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for await (const entry of glob(nodePattern(pattern), options)) {
|
|
62
|
+
if (options.exclude(entry))
|
|
63
|
+
continue;
|
|
64
|
+
const path = filePath(entry, options.cwd, opts.absolute, pattern);
|
|
65
|
+
if (path !== null && !seen.has(path)) {
|
|
66
|
+
seen.add(path);
|
|
67
|
+
yield path;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function* nodeGlobSync(pattern, opts) {
|
|
72
|
+
const options = scanOptions(pattern, opts);
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const entry of globSync(nodePattern(pattern), options)) {
|
|
75
|
+
if (options.exclude(entry))
|
|
76
|
+
continue;
|
|
77
|
+
const path = filePath(entry, options.cwd, opts.absolute, pattern);
|
|
78
|
+
if (path !== null && !seen.has(path)) {
|
|
79
|
+
seen.add(path);
|
|
80
|
+
yield path;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default runtime: node:child_process adapted to AgentSpawnResult, normalised
|
|
3
|
+
* to Bun.spawn's measured behaviour (the spawn cases in @nathapp/nax-test-kit).
|
|
4
|
+
*
|
|
5
|
+
* Known difference, kept on purpose: with `env` omitted the child inherits the
|
|
6
|
+
* live process.env, where Bun passes the environment the process started with.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn as spawnChild } from "node:child_process";
|
|
9
|
+
import { statSync } from "node:fs";
|
|
10
|
+
import { constants as osConstants } from "node:os";
|
|
11
|
+
import { resolve } from "node:path";
|
|
12
|
+
import { Readable } from "node:stream";
|
|
13
|
+
import { nodeGlob, nodeGlobSync } from "./node-glob.js";
|
|
14
|
+
import { which } from "./which.js";
|
|
15
|
+
/** The search path a child gets when `env` is given without PATH (libuv's and Bun's default). */
|
|
16
|
+
const DEFAULT_PATH = "/usr/bin:/bin";
|
|
17
|
+
const ERRNO = { ENOENT: -2, ENOTDIR: -20, EACCES: -13 };
|
|
18
|
+
function spawnError(code, message, path) {
|
|
19
|
+
return Object.assign(new Error(message), { code, errno: ERRNO[code], syscall: "posix_spawn", path });
|
|
20
|
+
}
|
|
21
|
+
/** Bun fails a spawn synchronously; child_process reports it later, so check first. */
|
|
22
|
+
function precheck(cmd, opts) {
|
|
23
|
+
const bin = cmd[0];
|
|
24
|
+
if (bin === undefined || bin === "")
|
|
25
|
+
throw new TypeError("spawn: empty argv");
|
|
26
|
+
if (opts.cwd !== undefined) {
|
|
27
|
+
let isDirectory;
|
|
28
|
+
try {
|
|
29
|
+
isDirectory = statSync(opts.cwd).isDirectory();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw spawnError("ENOENT", `ENOENT: no such file or directory, posix_spawn '${bin}'`, bin);
|
|
33
|
+
}
|
|
34
|
+
if (!isDirectory)
|
|
35
|
+
throw spawnError("ENOTDIR", `ENOTDIR: not a directory, posix_spawn '${bin}'`, bin);
|
|
36
|
+
}
|
|
37
|
+
// A child with no cwd inherits ours; "." resolves to it.
|
|
38
|
+
const cwd = opts.cwd ?? ".";
|
|
39
|
+
if (bin.includes("/")) {
|
|
40
|
+
try {
|
|
41
|
+
statSync(resolve(cwd, bin));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw spawnError("ENOENT", `ENOENT: no such file or directory, posix_spawn '${bin}'`, bin);
|
|
45
|
+
}
|
|
46
|
+
if (which(bin, undefined, cwd) === null) {
|
|
47
|
+
throw spawnError("EACCES", `EACCES: permission denied, posix_spawn '${bin}'`, bin);
|
|
48
|
+
}
|
|
49
|
+
return bin;
|
|
50
|
+
}
|
|
51
|
+
// The child searches its own PATH: env's when env is given (or the default), the parent's otherwise.
|
|
52
|
+
const pathEnv = opts.env !== undefined ? (opts.env.PATH ?? DEFAULT_PATH) : process.env.PATH;
|
|
53
|
+
if (which(bin, pathEnv, cwd) === null) {
|
|
54
|
+
throw Object.assign(new Error(`Executable not found in $PATH: "${bin}"`), { code: "ENOENT", errno: -2, path: bin });
|
|
55
|
+
}
|
|
56
|
+
return bin;
|
|
57
|
+
}
|
|
58
|
+
function emptyStream() {
|
|
59
|
+
return new ReadableStream({ start: (controller) => controller.close() });
|
|
60
|
+
}
|
|
61
|
+
function definedEnv(env) {
|
|
62
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined));
|
|
63
|
+
}
|
|
64
|
+
/** 128 + n for a signal exit, as Bun reports it. */
|
|
65
|
+
function exitStatus(code, signal) {
|
|
66
|
+
if (code !== null)
|
|
67
|
+
return code;
|
|
68
|
+
return 128 + (signal !== null ? (osConstants.signals[signal] ?? 0) : 0);
|
|
69
|
+
}
|
|
70
|
+
function toWeb(stream) {
|
|
71
|
+
return stream === null ? emptyStream() : Readable.toWeb(stream);
|
|
72
|
+
}
|
|
73
|
+
export function nodeSpawn(cmd, opts) {
|
|
74
|
+
const bin = precheck(cmd, opts);
|
|
75
|
+
const child = spawnChild(bin, cmd.slice(1), {
|
|
76
|
+
cwd: opts.cwd,
|
|
77
|
+
env: opts.env !== undefined ? definedEnv(opts.env) : process.env,
|
|
78
|
+
stdio: [opts.stdin ?? "ignore", "pipe", opts.stderr],
|
|
79
|
+
detached: opts.detached === true,
|
|
80
|
+
});
|
|
81
|
+
// A broken pipe on stdin is swallowed, as Bun does; an unhandled 'error' event would crash the process.
|
|
82
|
+
child.stdin?.on("error", () => { });
|
|
83
|
+
if (child.pid === undefined) {
|
|
84
|
+
// The OS refused the spawn after the precheck passed (e.g. a script whose
|
|
85
|
+
// interpreter is missing). Node reports it on a later 'error' event; Bun throws
|
|
86
|
+
// here. Throw, and never hand back a process without a pid: a caller's group
|
|
87
|
+
// kill of an invalid pid would reach other processes.
|
|
88
|
+
child.once("error", () => { });
|
|
89
|
+
throw spawnError("ENOENT", `ENOENT: no such file or directory, posix_spawn '${bin}'`, bin);
|
|
90
|
+
}
|
|
91
|
+
// 'exit', not 'close': a grandchild still holding a pipe must not delay it (Bun resolves at exit).
|
|
92
|
+
const exited = new Promise((resolve, reject) => {
|
|
93
|
+
child.once("error", reject);
|
|
94
|
+
child.once("exit", (code, signal) => resolve(exitStatus(code, signal)));
|
|
95
|
+
});
|
|
96
|
+
const stdin = opts.stdin === "pipe" && child.stdin !== null
|
|
97
|
+
? {
|
|
98
|
+
write: (data) => {
|
|
99
|
+
if (child.stdin !== null && !child.stdin.destroyed)
|
|
100
|
+
child.stdin.write(data);
|
|
101
|
+
return typeof data === "string" ? Buffer.byteLength(data) : data.byteLength;
|
|
102
|
+
},
|
|
103
|
+
end: () => {
|
|
104
|
+
child.stdin?.end();
|
|
105
|
+
},
|
|
106
|
+
flush: () => { },
|
|
107
|
+
}
|
|
108
|
+
: undefined;
|
|
109
|
+
return {
|
|
110
|
+
stdout: toWeb(child.stdout),
|
|
111
|
+
stderr: opts.stderr === "pipe" ? toWeb(child.stderr) : emptyStream(),
|
|
112
|
+
exited,
|
|
113
|
+
pid: child.pid,
|
|
114
|
+
stdin,
|
|
115
|
+
get exitCode() {
|
|
116
|
+
return child.exitCode;
|
|
117
|
+
},
|
|
118
|
+
get signalCode() {
|
|
119
|
+
return child.signalCode;
|
|
120
|
+
},
|
|
121
|
+
kill: (signal) => {
|
|
122
|
+
child.kill(signal);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export const nodeRuntime = { spawn: nodeSpawn, glob: nodeGlob, globSync: nodeGlobSync };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentRuntime } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Install (or, with `null`, clear) the process-wide runtime. nax installs its
|
|
4
|
+
* Bun runtime at startup; an embedder may install its own. Like the logger
|
|
5
|
+
* slot, it is module-level; S3 decides whether it becomes per-session.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setAgentRuntime(runtime: AgentRuntime | null): void;
|
|
8
|
+
/** The installed runtime, or the Node default. */
|
|
9
|
+
export declare function getAgentRuntime(): AgentRuntime;
|
|
10
|
+
/** Spawn through whatever runtime is installed at the moment of the call. */
|
|
11
|
+
export declare const runtimeSpawn: AgentRuntime["spawn"];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { nodeRuntime } from "./node-runtime.js";
|
|
2
|
+
let installed = null;
|
|
3
|
+
/**
|
|
4
|
+
* Install (or, with `null`, clear) the process-wide runtime. nax installs its
|
|
5
|
+
* Bun runtime at startup; an embedder may install its own. Like the logger
|
|
6
|
+
* slot, it is module-level; S3 decides whether it becomes per-session.
|
|
7
|
+
*/
|
|
8
|
+
export function setAgentRuntime(runtime) {
|
|
9
|
+
installed = runtime;
|
|
10
|
+
}
|
|
11
|
+
/** The installed runtime, or the Node default. */
|
|
12
|
+
export function getAgentRuntime() {
|
|
13
|
+
return installed ?? nodeRuntime;
|
|
14
|
+
}
|
|
15
|
+
/** Spawn through whatever runtime is installed at the moment of the call. */
|
|
16
|
+
// nax-git-env-allow: pass-through to the installed runtime; git callers pass hardenedGitEnv themselves
|
|
17
|
+
export const runtimeSpawn = (cmd, opts) => getAgentRuntime().spawn(cmd, opts);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The process-spawning contract nax-agent's tools run through (spec S2 §4.2).
|
|
3
|
+
* The shape records Bun.spawn's measured behaviour; the Node default
|
|
4
|
+
* (node-runtime.ts) normalises to it, and nax installs Bun.spawn itself.
|
|
5
|
+
*
|
|
6
|
+
* Distinct from nax's own `SpawnOptions`/`SpawnResult` (packages/nax/src/utils/
|
|
7
|
+
* bun-deps.ts), which describe nax's direct Bun.spawn wrapper.
|
|
8
|
+
*/
|
|
9
|
+
export interface AgentSpawnOptions {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
stdin?: "pipe" | "inherit";
|
|
12
|
+
stdout: "pipe";
|
|
13
|
+
stderr: "pipe" | "inherit";
|
|
14
|
+
/** Replaces the environment; `undefined` values are dropped. Omitted: inherit. */
|
|
15
|
+
env?: Record<string, string | undefined>;
|
|
16
|
+
/** setsid: the child becomes its own process-group leader, so a group kill reaches its descendants (ORPHAN-1). */
|
|
17
|
+
detached?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface AgentSpawnStdin {
|
|
20
|
+
/** Returns the number of bytes written. Never throws for a child that already exited. */
|
|
21
|
+
write(data: string | Uint8Array): number;
|
|
22
|
+
end(): void;
|
|
23
|
+
flush(): void;
|
|
24
|
+
}
|
|
25
|
+
export interface AgentSpawnResult {
|
|
26
|
+
readonly stdout: ReadableStream<Uint8Array>;
|
|
27
|
+
/** With `stderr: "inherit"` there is nothing to read; callers that inherit never read it. */
|
|
28
|
+
readonly stderr: ReadableStream<Uint8Array>;
|
|
29
|
+
/** The exit code, or 128 + the signal number for a signal exit. Resolves at exit, not when the pipes close. */
|
|
30
|
+
readonly exited: Promise<number>;
|
|
31
|
+
readonly pid: number;
|
|
32
|
+
readonly stdin?: AgentSpawnStdin;
|
|
33
|
+
/** `null` until exit, and after a signal exit. */
|
|
34
|
+
readonly exitCode: number | null;
|
|
35
|
+
readonly signalCode: string | null;
|
|
36
|
+
kill(signal?: number | NodeJS.Signals): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The slot's contract. `spawn` throws synchronously when the process cannot be
|
|
40
|
+
* started (missing binary, missing or non-directory cwd, not executable).
|
|
41
|
+
*/
|
|
42
|
+
export interface AgentGlobOptions {
|
|
43
|
+
cwd: string;
|
|
44
|
+
absolute: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface AgentRuntime {
|
|
47
|
+
spawn(cmd: readonly string[], opts: AgentSpawnOptions): AgentSpawnResult;
|
|
48
|
+
/** Files only, no hidden result-relative segments; cwd failures propagate. */
|
|
49
|
+
glob(pattern: string, opts: AgentGlobOptions): AsyncIterable<string>;
|
|
50
|
+
globSync(pattern: string, opts: AgentGlobOptions): Iterable<string>;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first executable regular file named `name` on `pathEnv` (POSIX; Windows is
|
|
3
|
+
* unsupported, as for nax). A name containing "/" is checked as a path. Relative
|
|
4
|
+
* paths and relative PATH entries resolve against `cwd`, as the spawned child sees them.
|
|
5
|
+
*/
|
|
6
|
+
export declare function which(name: string, pathEnv?: string | undefined,
|
|
7
|
+
/** Defaults to "." : relative to the current directory, which a child with no cwd inherits. */
|
|
8
|
+
cwd?: string): string | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { accessSync, constants, statSync } from "node:fs";
|
|
2
|
+
import { delimiter, join, resolve } from "node:path";
|
|
3
|
+
function isExecutableFile(path) {
|
|
4
|
+
try {
|
|
5
|
+
if (!statSync(path).isFile())
|
|
6
|
+
return false;
|
|
7
|
+
accessSync(path, constants.X_OK);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The first executable regular file named `name` on `pathEnv` (POSIX; Windows is
|
|
16
|
+
* unsupported, as for nax). A name containing "/" is checked as a path. Relative
|
|
17
|
+
* paths and relative PATH entries resolve against `cwd`, as the spawned child sees them.
|
|
18
|
+
*/
|
|
19
|
+
export function which(name, pathEnv = process.env.PATH,
|
|
20
|
+
/** Defaults to "." : relative to the current directory, which a child with no cwd inherits. */
|
|
21
|
+
cwd = ".") {
|
|
22
|
+
if (name.includes("/"))
|
|
23
|
+
return isExecutableFile(resolve(cwd, name)) ? name : null;
|
|
24
|
+
for (const dir of (pathEnv ?? "").split(delimiter)) {
|
|
25
|
+
if (dir === "")
|
|
26
|
+
continue;
|
|
27
|
+
const candidate = join(resolve(cwd, dir), name);
|
|
28
|
+
if (isExecutableFile(candidate))
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POSIX single-quote quoting for RunCommand's Exec branch under the sandbox.
|
|
3
|
+
*
|
|
4
|
+
* srt wraps a command STRING that runs under `sh -c`, so a sandboxed Exec
|
|
5
|
+
* call becomes `sh -c '<argv, each element quoted>'`. Inside single quotes
|
|
6
|
+
* nothing is special, and an embedded quote is closed, escaped and reopened
|
|
7
|
+
* ('\''), so the mapping is injective and no metacharacter is ever
|
|
8
|
+
* interpreted. Lives here, not in run-command-exec.ts, whose whole-file guard
|
|
9
|
+
* forbids a shell-quoting import.
|
|
10
|
+
*/
|
|
11
|
+
export declare function quoteArgvForShell(argv: readonly string[]): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POSIX single-quote quoting for RunCommand's Exec branch under the sandbox.
|
|
3
|
+
*
|
|
4
|
+
* srt wraps a command STRING that runs under `sh -c`, so a sandboxed Exec
|
|
5
|
+
* call becomes `sh -c '<argv, each element quoted>'`. Inside single quotes
|
|
6
|
+
* nothing is special, and an embedded quote is closed, escaped and reopened
|
|
7
|
+
* ('\''), so the mapping is injective and no metacharacter is ever
|
|
8
|
+
* interpreted. Lives here, not in run-command-exec.ts, whose whole-file guard
|
|
9
|
+
* forbids a shell-quoting import.
|
|
10
|
+
*/
|
|
11
|
+
export function quoteArgvForShell(argv) {
|
|
12
|
+
return argv.map((arg) => `'${arg.replaceAll("'", "'\\''")}'`).join(" ");
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in sandbox lists (spec 5.3). One auditable constant each; extend a
|
|
3
|
+
* project's set through `execution.sandbox.filesystem`, not by editing these.
|
|
4
|
+
*/
|
|
5
|
+
/** Package-manager caches, relative to $HOME. The bun cache is REQUIRED: bun stages temp files there (spec F3). */
|
|
6
|
+
export declare const BUILTIN_CACHE_WRITE_ROOTS: readonly string[];
|
|
7
|
+
/** macOS-only cache root, relative to $HOME. */
|
|
8
|
+
export declare const MACOS_CACHE_WRITE_ROOT = "Library/Caches";
|
|
9
|
+
/** srt forces TMPDIR to this inside the macOS sandbox and does not create it (spec F3). */
|
|
10
|
+
export declare const SRT_MACOS_TMPDIR = "/tmp/claude";
|
|
11
|
+
/**
|
|
12
|
+
* #2301 — the same directory as `SRT_MACOS_TMPDIR`, named through macOS's
|
|
13
|
+
* `/tmp` -> `/private/tmp` symlink. srt force-allows both spellings, and which of
|
|
14
|
+
* them survives nax's own `realOrRaw` depends on the host, so a deny names both.
|
|
15
|
+
*/
|
|
16
|
+
export declare const SRT_MACOS_TMPDIR_PRIVATE_SPELLING = "/private/tmp/claude";
|
|
17
|
+
/**
|
|
18
|
+
* #2301 — both spellings of srt's forced TMPDIR, as a deny list.
|
|
19
|
+
*
|
|
20
|
+
* srt always includes `/tmp/claude` and `/private/tmp/claude` in
|
|
21
|
+
* `SANDBOX_OWN_WRITE_PATHS` and `getDefaultWritePaths` never drops them, so the
|
|
22
|
+
* only way to take the write back is an explicit `denyWrite` — srt renders its
|
|
23
|
+
* allow rules before its deny rules in a macOS profile, where the later deny wins.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SRT_MACOS_TMPDIR_DENIES: readonly string[];
|
|
26
|
+
/** Credential stores the agent's commands may not read, relative to $HOME. nax's own credential files are listed separately (they live under globalConfigDir()). */
|
|
27
|
+
export declare const BUILTIN_CREDENTIAL_READ_DENIES: readonly string[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in sandbox lists (spec 5.3). One auditable constant each; extend a
|
|
3
|
+
* project's set through `execution.sandbox.filesystem`, not by editing these.
|
|
4
|
+
*/
|
|
5
|
+
/** Package-manager caches, relative to $HOME. The bun cache is REQUIRED: bun stages temp files there (spec F3). */
|
|
6
|
+
export const BUILTIN_CACHE_WRITE_ROOTS = [
|
|
7
|
+
".bun/install/cache",
|
|
8
|
+
".npm",
|
|
9
|
+
".cache",
|
|
10
|
+
".cargo/registry",
|
|
11
|
+
".cargo/git",
|
|
12
|
+
"go/pkg/mod",
|
|
13
|
+
".gradle/caches",
|
|
14
|
+
".m2/repository",
|
|
15
|
+
".pnpm-store",
|
|
16
|
+
];
|
|
17
|
+
/** macOS-only cache root, relative to $HOME. */
|
|
18
|
+
export const MACOS_CACHE_WRITE_ROOT = "Library/Caches";
|
|
19
|
+
/** srt forces TMPDIR to this inside the macOS sandbox and does not create it (spec F3). */
|
|
20
|
+
export const SRT_MACOS_TMPDIR = "/tmp/claude";
|
|
21
|
+
/**
|
|
22
|
+
* #2301 — the same directory as `SRT_MACOS_TMPDIR`, named through macOS's
|
|
23
|
+
* `/tmp` -> `/private/tmp` symlink. srt force-allows both spellings, and which of
|
|
24
|
+
* them survives nax's own `realOrRaw` depends on the host, so a deny names both.
|
|
25
|
+
*/
|
|
26
|
+
export const SRT_MACOS_TMPDIR_PRIVATE_SPELLING = "/private/tmp/claude";
|
|
27
|
+
/**
|
|
28
|
+
* #2301 — both spellings of srt's forced TMPDIR, as a deny list.
|
|
29
|
+
*
|
|
30
|
+
* srt always includes `/tmp/claude` and `/private/tmp/claude` in
|
|
31
|
+
* `SANDBOX_OWN_WRITE_PATHS` and `getDefaultWritePaths` never drops them, so the
|
|
32
|
+
* only way to take the write back is an explicit `denyWrite` — srt renders its
|
|
33
|
+
* allow rules before its deny rules in a macOS profile, where the later deny wins.
|
|
34
|
+
*/
|
|
35
|
+
export const SRT_MACOS_TMPDIR_DENIES = [SRT_MACOS_TMPDIR, SRT_MACOS_TMPDIR_PRIVATE_SPELLING];
|
|
36
|
+
/** Credential stores the agent's commands may not read, relative to $HOME. nax's own credential files are listed separately (they live under globalConfigDir()). */
|
|
37
|
+
export const BUILTIN_CREDENTIAL_READ_DENIES = [
|
|
38
|
+
".ssh",
|
|
39
|
+
".aws",
|
|
40
|
+
".config/gcloud",
|
|
41
|
+
".docker/config.json",
|
|
42
|
+
".netrc",
|
|
43
|
+
".npmrc",
|
|
44
|
+
".pypirc",
|
|
45
|
+
".git-credentials",
|
|
46
|
+
".config/gh",
|
|
47
|
+
];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The I/O half of the sandbox's git guards (#2198): the git-read files that
|
|
3
|
+
* redirect where git finds its config and hooks, as they exist on disk now.
|
|
4
|
+
*
|
|
5
|
+
* A sandboxed command that repoints one of these at an agent-authored
|
|
6
|
+
* directory (its own `config` with core.hooksPath / core.fsmonitor) makes
|
|
7
|
+
* nax's next UNSANDBOXED git run agent code. The redirecting files are:
|
|
8
|
+
* `<common>/commondir`, and for every linked worktree `worktrees/<id>/`
|
|
9
|
+
* `{gitdir, commondir, config.worktree}` plus the worktree's `.git` pointer.
|
|
10
|
+
*
|
|
11
|
+
* Only files that EXIST are denied here: srt on Linux stubs an absent deny
|
|
12
|
+
* with an empty file (host-side too, while the command runs), and git dies
|
|
13
|
+
* reading an empty `commondir` / `gitdir` -- inside the sandbox and in any
|
|
14
|
+
* concurrent nax git. An absent `<common>/commondir` is therefore guarded by
|
|
15
|
+
* a tripwire instead (strayCommonDirTripwire). `config.worktree` is the
|
|
16
|
+
* exception: an empty one is valid config, so it is always denied.
|
|
17
|
+
*
|
|
18
|
+
* The tripwire is what protects a main checkout, whose git dir sits inside the
|
|
19
|
+
* root write root. In a linked worktree the common dir is not a write root at
|
|
20
|
+
* all (WORKTREE_COMMON_WRITE_DIRS, #2211), so the file cannot be created there
|
|
21
|
+
* and the tripwire is only a second line.
|
|
22
|
+
*/
|
|
23
|
+
import { readdir } from "node:fs/promises";
|
|
24
|
+
import type { GitLayout } from "./policy-inputs.ts";
|
|
25
|
+
export declare const WORKTREE_CONFIG_FILE = "config.worktree";
|
|
26
|
+
/**
|
|
27
|
+
* What a linked worktree's git writes in the SHARED common dir (#2211): loose
|
|
28
|
+
* and packed objects, refs and their reflogs (`reftable/` holds both when
|
|
29
|
+
* `extensions.refStorage=reftable`, git >= 2.45), and git-lfs's object store.
|
|
30
|
+
* Only these are write roots, never the common dir itself, so the redirecting
|
|
31
|
+
* files at its top level (`commondir`, `config`, `hooks/`) and every sibling
|
|
32
|
+
* worktree's admin dir lie outside the sandbox's writes. `modules/` is left
|
|
33
|
+
* out on purpose: it holds submodule git dirs, each with its own config.
|
|
34
|
+
*
|
|
35
|
+
* Cost: a lock file at the common dir's top level cannot be created, so a
|
|
36
|
+
* sandboxed command cannot rewrite `packed-refs` (deleting a packed branch or
|
|
37
|
+
* tag) or take the `gc --auto` lock; commits, loose ref updates and fetches
|
|
38
|
+
* into loose objects are unaffected. (A reftable repo locks inside
|
|
39
|
+
* `reftable/`, so it keeps ref deletion too.)
|
|
40
|
+
*/
|
|
41
|
+
export declare const WORKTREE_COMMON_WRITE_DIRS: readonly string[];
|
|
42
|
+
export declare const _gitGuardDeps: {
|
|
43
|
+
readdir: typeof readdir;
|
|
44
|
+
exists: (p: string) => Promise<boolean>;
|
|
45
|
+
readText: (p: string) => Promise<string>;
|
|
46
|
+
remove: (p: string) => Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
/** The directory holding the shared config/hooks: the git dir itself for a main checkout. */
|
|
49
|
+
export declare function commonDirOf(git: GitLayout): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Every redirecting git file for the repo that `git` describes, for every
|
|
52
|
+
* worktree registered when the policy is built (literal paths: srt on Linux
|
|
53
|
+
* drops glob denies).
|
|
54
|
+
*/
|
|
55
|
+
export declare function listGitGuardFiles(git: GitLayout): Promise<string[]>;
|
|
56
|
+
/**
|
|
57
|
+
* `<common>/commondir` never legitimately exists (git reads it only from a
|
|
58
|
+
* linked worktree's own git dir), and an absent one cannot be denied on Linux
|
|
59
|
+
* (see the header). So when it is absent at session start, one created by a
|
|
60
|
+
* sandboxed command is removed after that command, before nax's next git.
|
|
61
|
+
* Returns undefined when there is nothing to guard (no repo, or the file
|
|
62
|
+
* already exists -- listGitGuardFiles then denies it outright).
|
|
63
|
+
*/
|
|
64
|
+
export declare function strayCommonDirTripwire(git: GitLayout, storyId?: string): Promise<(() => Promise<void>) | undefined>;
|