@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,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native session lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* There is no subprocess and no backend that remembers, so opening a session
|
|
5
|
+
* establishes nothing — it records where the conversation will be kept. ADR-027
|
|
6
|
+
* section 10 predicted exactly this shape: "openSession and closeSession become
|
|
7
|
+
* either no-ops or transcript-file handles".
|
|
8
|
+
*/
|
|
9
|
+
import { NaxError } from "#src/infra/index";
|
|
10
|
+
import { createSpinBreaker } from "#src/infra/spin-breaker/index";
|
|
11
|
+
import { NATIVE_AGENT } from "../models.js";
|
|
12
|
+
import { nativeSessionId } from "../session-affinity.js";
|
|
13
|
+
import { deleteTranscript, pruneRetainedTranscripts, retainTranscript } from "./transcript-store.js";
|
|
14
|
+
/**
|
|
15
|
+
* Session name -> transcript directory, so sendTurn and close can find it.
|
|
16
|
+
*
|
|
17
|
+
* Cleared by every close path — `closeNativeSession` (in-process) and the
|
|
18
|
+
* adapter's `closePhysicalSession` (run teardown, which only has the name) —
|
|
19
|
+
* through the shared `clearNativeSessionState`. A caller that opens a session
|
|
20
|
+
* and never closes it (e.g. an early return, or a thrown error between open and
|
|
21
|
+
* close) still leaks an entry for the lifetime of the process. Harmless in
|
|
22
|
+
* practice (it is a small in-memory map keyed by session name, not a handle
|
|
23
|
+
* to a real resource), but worth knowing when debugging a growing map.
|
|
24
|
+
*/
|
|
25
|
+
export const nativeTranscriptDirs = new Map();
|
|
26
|
+
/**
|
|
27
|
+
* Session name -> the root whose `.nax/scratchpad/` holds this session's
|
|
28
|
+
* spill files (US-003).
|
|
29
|
+
*
|
|
30
|
+
* Recorded from the session's `workdir` at open — the same root the coding
|
|
31
|
+
* tools are confined to — so a spilled body is reachable through
|
|
32
|
+
* `ScratchpadRead` with the relative path the truncation marker names. Same
|
|
33
|
+
* lifecycle as the maps above: set on open, cleared on close.
|
|
34
|
+
*/
|
|
35
|
+
export const nativeSessionScratchpadRoots = new Map();
|
|
36
|
+
/**
|
|
37
|
+
* Session name -> timeoutSeconds, so sendTurn can bound each `complete()`
|
|
38
|
+
* call with a deadline (whole-branch review finding 4). Same lifecycle as
|
|
39
|
+
* `nativeTranscriptDirs` — populated on open, cleared on close only.
|
|
40
|
+
*/
|
|
41
|
+
export const nativeSessionTimeouts = new Map();
|
|
42
|
+
/**
|
|
43
|
+
* Session name -> the runtime hooks handed to openSession. SessionManager
|
|
44
|
+
* passes both (manager.ts, openSession) and the native adapter previously
|
|
45
|
+
* ignored them, which is why the idle watchdog never covered native. Same
|
|
46
|
+
* lifecycle as `nativeTranscriptDirs`: set on open, cleared on close only.
|
|
47
|
+
*/
|
|
48
|
+
export const nativeSessionStreamHooks = new Map();
|
|
49
|
+
/**
|
|
50
|
+
* Session names whose most recent turn failed.
|
|
51
|
+
*
|
|
52
|
+
* `AgentAdapter.closeSession(handle)` carries no failure signal, so the close
|
|
53
|
+
* cannot tell a finished session from a broken one and passed `failed: false`
|
|
54
|
+
* for both -- deleting the transcript of exactly the session whose history the
|
|
55
|
+
* retry needs and a human would read (nax#1838). Rather than widen the adapter
|
|
56
|
+
* interface for one transport, the turn records what it knows here and the
|
|
57
|
+
* close reads it. Same lifecycle as the maps above: written per turn, cleared
|
|
58
|
+
* on close.
|
|
59
|
+
*
|
|
60
|
+
* A later successful turn clears the mark. "Failed" describes the session's
|
|
61
|
+
* current state, not whether it ever stumbled -- a session that recovered and
|
|
62
|
+
* finished is still cleaned up.
|
|
63
|
+
*/
|
|
64
|
+
export const nativeSessionFailed = new Set();
|
|
65
|
+
/**
|
|
66
|
+
* Session name -> the identity that owns this session's transcript (nax#1877).
|
|
67
|
+
*
|
|
68
|
+
* Read by `runNativeTurn` on both the load and the save, so a transcript left
|
|
69
|
+
* behind by an abandoned invocation of the same deterministic session name is
|
|
70
|
+
* recognised as foreign rather than silently resumed. Same lifecycle as the
|
|
71
|
+
* maps above: set on open, cleared on close.
|
|
72
|
+
*/
|
|
73
|
+
export const nativeSessionTranscriptOwners = new Map();
|
|
74
|
+
/** Session name -> resolved compaction settings. Same lifecycle as the maps above. */
|
|
75
|
+
export const nativeSessionCompaction = new Map();
|
|
76
|
+
/**
|
|
77
|
+
* Session name -> resolved transport-fault retry settings (nax#1870). Same
|
|
78
|
+
* lifecycle as the maps above: set on open, cleared on close.
|
|
79
|
+
*/
|
|
80
|
+
export const nativeSessionTransportRetry = new Map();
|
|
81
|
+
/**
|
|
82
|
+
* Session name -> live `SpinBreaker` instance (nax#2013, lifetime extended in
|
|
83
|
+
* nax#2047). Constructed once at open so the cumulative counter survives
|
|
84
|
+
* across `runNativeTurn` calls — a fix round that opens a new turn on the
|
|
85
|
+
* same `handle.id` must inherit the prior turn's evidence rather than start
|
|
86
|
+
* fresh. Same lifecycle as `nativeSessionTransportRetry`: set on open, cleared
|
|
87
|
+
* on close.
|
|
88
|
+
*/
|
|
89
|
+
export const nativeSessionSpinBreaker = new Map();
|
|
90
|
+
export const nativeSessionLastUsage = new Map();
|
|
91
|
+
/**
|
|
92
|
+
* The persisted anchor a turn on `model` may use. Prefix stability is a
|
|
93
|
+
* property of (model, prefix) (P3 spec 3.3): an anchor measured under a
|
|
94
|
+
* different model indexes history the transcript store refused (spec 8.3(c)),
|
|
95
|
+
* so it is dropped here rather than mis-sizing the next compaction decision.
|
|
96
|
+
* An entry or a turn with no model makes no claim — the store's own rule.
|
|
97
|
+
*/
|
|
98
|
+
export function sessionAnchorFor(sessionName, model) {
|
|
99
|
+
const entry = nativeSessionLastUsage.get(sessionName);
|
|
100
|
+
if (entry?.model === undefined || model === undefined || entry.model === model)
|
|
101
|
+
return entry;
|
|
102
|
+
nativeSessionLastUsage.delete(sessionName);
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
/** Records how the session's latest turn ended, for `closeNativeSession`. */
|
|
106
|
+
export function markNativeTurnOutcome(sessionName, failed) {
|
|
107
|
+
if (failed)
|
|
108
|
+
nativeSessionFailed.add(sessionName);
|
|
109
|
+
else
|
|
110
|
+
nativeSessionFailed.delete(sessionName);
|
|
111
|
+
}
|
|
112
|
+
export async function openNativeSession(name, opts) {
|
|
113
|
+
// Never defaulted. An adapter that picks its own path writes a transcript
|
|
114
|
+
// somewhere nobody looks, which is #1794's empty-packageDir bug one layer up.
|
|
115
|
+
if (!opts.transcriptDir) {
|
|
116
|
+
throw new NaxError(`native session "${name}" opened without a transcriptDir`, "NATIVE_TRANSCRIPT_DIR_MISSING", {
|
|
117
|
+
stage: "native-session",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
nativeTranscriptDirs.set(name, opts.transcriptDir);
|
|
121
|
+
nativeSessionTimeouts.set(name, opts.timeoutSeconds);
|
|
122
|
+
nativeSessionScratchpadRoots.set(name, opts.workdir);
|
|
123
|
+
if (opts.transcriptOwner !== undefined)
|
|
124
|
+
nativeSessionTranscriptOwners.set(name, opts.transcriptOwner);
|
|
125
|
+
else
|
|
126
|
+
nativeSessionTranscriptOwners.delete(name);
|
|
127
|
+
// `resume` is SessionManager's "this name already has a descriptor in this
|
|
128
|
+
// process" signal, and it had no consumer on this transport (nax#1877) — a
|
|
129
|
+
// native session resumed whatever transcript happened to be on disk. Honouring
|
|
130
|
+
// it here is the flag's documented contract; the owner check in loadTranscript
|
|
131
|
+
// is what covers the cases `resume` cannot see (a same-process re-entry into a
|
|
132
|
+
// stage, and a process that died without closing anything).
|
|
133
|
+
if (opts.resume !== true)
|
|
134
|
+
await deleteTranscript(opts.transcriptDir, name);
|
|
135
|
+
if (opts.compaction !== undefined)
|
|
136
|
+
nativeSessionCompaction.set(name, opts.compaction);
|
|
137
|
+
if (opts.transportRetry !== undefined)
|
|
138
|
+
nativeSessionTransportRetry.set(name, opts.transportRetry);
|
|
139
|
+
if (opts.spinBreaker !== undefined)
|
|
140
|
+
nativeSessionSpinBreaker.set(name, createSpinBreaker(opts.spinBreaker));
|
|
141
|
+
nativeSessionStreamHooks.set(name, {
|
|
142
|
+
...(opts.onStreamActivity !== undefined ? { onStreamActivity: opts.onStreamActivity } : {}),
|
|
143
|
+
...(opts.onActiveCall !== undefined ? { onActiveCall: opts.onActiveCall } : {}),
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
id: name,
|
|
147
|
+
agentName: NATIVE_AGENT,
|
|
148
|
+
// Both fields carry the same value on purpose. On ACP they differ because a
|
|
149
|
+
// physical session can be re-established under a stable logical record;
|
|
150
|
+
// native has no reconnect, so its logical and physical identity genuinely
|
|
151
|
+
// coincide. `nativeSessionId` is a pure hash of the name and deliberately
|
|
152
|
+
// not memoised, so this is exactly the id `sendTurn` later puts on the wire.
|
|
153
|
+
protocolIds: { recordId: nativeSessionId(name), sessionId: nativeSessionId(name) },
|
|
154
|
+
...(opts.modelDef !== undefined ? { modelDef: opts.modelDef } : {}),
|
|
155
|
+
...(opts.modelTier !== undefined ? { modelTier: opts.modelTier } : {}),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clear every module-level entry for one session name.
|
|
160
|
+
*
|
|
161
|
+
* Extracted so both close paths share one source of truth. `closeNativeSession`
|
|
162
|
+
* has a `SessionHandle`; the adapter's `closePhysicalSession` gets only the
|
|
163
|
+
* handle string that run teardown carries (`descriptor.handle`). The two are
|
|
164
|
+
* the same string — every map is keyed by the session name, and `SessionHandle.id`
|
|
165
|
+
* is that name — but the signatures are not interchangeable, so this helper is
|
|
166
|
+
* string-keyed rather than reconstructed into a synthetic handle.
|
|
167
|
+
*/
|
|
168
|
+
export function clearNativeSessionState(sessionName) {
|
|
169
|
+
nativeTranscriptDirs.delete(sessionName);
|
|
170
|
+
nativeSessionTimeouts.delete(sessionName);
|
|
171
|
+
nativeSessionScratchpadRoots.delete(sessionName);
|
|
172
|
+
nativeSessionTranscriptOwners.delete(sessionName);
|
|
173
|
+
nativeSessionStreamHooks.delete(sessionName);
|
|
174
|
+
nativeSessionFailed.delete(sessionName);
|
|
175
|
+
nativeSessionCompaction.delete(sessionName);
|
|
176
|
+
nativeSessionTransportRetry.delete(sessionName);
|
|
177
|
+
nativeSessionSpinBreaker.delete(sessionName);
|
|
178
|
+
nativeSessionLastUsage.delete(sessionName);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Kept on failure, deleted on success. Every Phase B op is lifetime "fresh", so
|
|
182
|
+
* the transcript survives exactly when it is worth reading. The kept-on-failure
|
|
183
|
+
* set is otherwise unbounded, so a failed close also prunes the feature's
|
|
184
|
+
* `sessions/` directory down to `MAX_RETAINED_TRANSCRIPTS` (ADR-028 section 3).
|
|
185
|
+
*/
|
|
186
|
+
export async function closeNativeSession(handle, failed) {
|
|
187
|
+
const dir = nativeTranscriptDirs.get(handle.id);
|
|
188
|
+
// An explicit argument wins; otherwise the last turn's own verdict decides.
|
|
189
|
+
// The adapter passes nothing, because its interface has no failure signal.
|
|
190
|
+
const treatAsFailed = failed ?? nativeSessionFailed.has(handle.id);
|
|
191
|
+
try {
|
|
192
|
+
if (dir !== undefined) {
|
|
193
|
+
if (treatAsFailed) {
|
|
194
|
+
// Retain for a human, out of reach of the next session of this name.
|
|
195
|
+
await retainTranscript(dir, handle.id);
|
|
196
|
+
await pruneRetainedTranscripts(dir);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
await deleteTranscript(dir, handle.id);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
// The deletes must run even when the transcript I/O throws: a failed
|
|
205
|
+
// cleanup step is no reason to strand the session's other state.
|
|
206
|
+
clearNativeSessionState(handle.id);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function exemplarFor(schema, input, violation) {
|
|
2
|
+
const exemplar = { ...input };
|
|
3
|
+
// Defensive: validateToolInput never currently emits "" (every violation
|
|
4
|
+
// names the offending property), but ToolInputViolation.property is typed
|
|
5
|
+
// as `string`, not a non-empty literal, so a synthetic or future
|
|
6
|
+
// top-level violation with no single property to correct must still be
|
|
7
|
+
// handled -- return the input verbatim rather than writing to an empty key.
|
|
8
|
+
if (violation.property === "") {
|
|
9
|
+
return exemplar;
|
|
10
|
+
}
|
|
11
|
+
if (!isPlainObject(schema))
|
|
12
|
+
return exemplar;
|
|
13
|
+
const properties = schema.properties;
|
|
14
|
+
if (!isPlainObject(properties))
|
|
15
|
+
return exemplar;
|
|
16
|
+
const propSchema = properties[violation.property];
|
|
17
|
+
if (!isPlainObject(propSchema)) {
|
|
18
|
+
exemplar[violation.property] = "<FILL IN>";
|
|
19
|
+
return exemplar;
|
|
20
|
+
}
|
|
21
|
+
const propType = propSchema.type;
|
|
22
|
+
// Enum first (nax#2200): `{type: "string", enum: [...]}` is the common
|
|
23
|
+
// spelling, and a `<FILL IN>` placeholder there violates the very enum the
|
|
24
|
+
// exemplar is meant to demonstrate. The first member is a valid value.
|
|
25
|
+
if (Array.isArray(propSchema.enum) && propSchema.enum.length > 0 && propSchema.enum.every(isString)) {
|
|
26
|
+
exemplar[violation.property] = propSchema.enum[0];
|
|
27
|
+
}
|
|
28
|
+
else if (propType === "string") {
|
|
29
|
+
exemplar[violation.property] = `<FILL IN: ${violation.property}>`;
|
|
30
|
+
}
|
|
31
|
+
else if (propType === "number" || propType === "integer") {
|
|
32
|
+
exemplar[violation.property] = 0;
|
|
33
|
+
}
|
|
34
|
+
else if (propType === "boolean") {
|
|
35
|
+
exemplar[violation.property] = false;
|
|
36
|
+
}
|
|
37
|
+
else if (propType === "null") {
|
|
38
|
+
exemplar[violation.property] = null;
|
|
39
|
+
}
|
|
40
|
+
else if (propType === "array") {
|
|
41
|
+
exemplar[violation.property] = [`<FILL IN: ${violation.property}>`];
|
|
42
|
+
}
|
|
43
|
+
else if (propType === "object") {
|
|
44
|
+
const subProperties = propSchema.properties;
|
|
45
|
+
if (isPlainObject(subProperties)) {
|
|
46
|
+
const subExemplar = {};
|
|
47
|
+
for (const key of Object.keys(subProperties)) {
|
|
48
|
+
subExemplar[key] = "<FILL IN>";
|
|
49
|
+
}
|
|
50
|
+
exemplar[violation.property] = subExemplar;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
exemplar[violation.property] = { "<FILL IN>": "<FILL IN>" };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
exemplar[violation.property] = "<FILL IN>";
|
|
58
|
+
}
|
|
59
|
+
return exemplar;
|
|
60
|
+
}
|
|
61
|
+
function isString(value) {
|
|
62
|
+
return typeof value === "string";
|
|
63
|
+
}
|
|
64
|
+
function isPlainObject(value) {
|
|
65
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ToolInputViolation {
|
|
2
|
+
readonly property: string;
|
|
3
|
+
readonly expected: string;
|
|
4
|
+
readonly actual: string;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function validateToolInput(schema: unknown, input: unknown): ToolInputViolation | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* The input with every `null`-valued optional property removed, or `undefined`
|
|
10
|
+
* when there is nothing to remove (including any schema the validator does not
|
|
11
|
+
* understand — the same fail-open contract). A property whose own schema
|
|
12
|
+
* declares `type: "null"` keeps its value: there `null` IS the supplied value.
|
|
13
|
+
* Never mutates `input`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function stripNullOptionals(schema: unknown, input: unknown): Record<string, unknown> | undefined;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-open subset validator for tool inputs (nax#2047).
|
|
3
|
+
*
|
|
4
|
+
* The model returned RunCommand calls with `values:""` 69 times and
|
|
5
|
+
* `values:"\t"` 13 times in a row -- the empty string produced no rejected
|
|
6
|
+
* keys, the tab became the key "0", and neither message ever named "values"
|
|
7
|
+
* itself as the wrong type, so the loop could not break. This validator
|
|
8
|
+
* catches that shape at the transport boundary so a malformed input never
|
|
9
|
+
* persists in the transcript.
|
|
10
|
+
*
|
|
11
|
+
* The contract is a strict subset of JSON Schema on purpose: type, enum,
|
|
12
|
+
* required, and a `properties` object. Anything we cannot decide -- unknown
|
|
13
|
+
* keywords such as `anyOf`, a missing or non-object schema, extra properties
|
|
14
|
+
* with no `additionalProperties` -- returns `undefined` (allow), so this
|
|
15
|
+
* validator never breaks a tool that was previously passing.
|
|
16
|
+
*
|
|
17
|
+
* `null` on a property the schema does not list as `required` is treated as
|
|
18
|
+
* absent (nax#2200). The tools already read it that way (`Git` treats a
|
|
19
|
+
* non-array `refs` as `[]`), and models routinely spell "not supplied" as
|
|
20
|
+
* `null`; rejecting it made the gate stricter than the tools it guards, and a
|
|
21
|
+
* verifier that sent `refs: null` three times lost its turn to the budget.
|
|
22
|
+
* `stripNullOptionals` removes those properties so the handler sees the same
|
|
23
|
+
* "absent" the validator judged.
|
|
24
|
+
*/
|
|
25
|
+
import { describeValuesType } from "#src/internal/describe-value-type";
|
|
26
|
+
export function validateToolInput(schema, input) {
|
|
27
|
+
if (!isPlainObject(schema))
|
|
28
|
+
return undefined;
|
|
29
|
+
const { properties, required, type } = schema;
|
|
30
|
+
if (!isPlainObject(properties))
|
|
31
|
+
return undefined;
|
|
32
|
+
if (type !== undefined && type !== "object")
|
|
33
|
+
return undefined;
|
|
34
|
+
if (!isPlainObject(input))
|
|
35
|
+
return undefined;
|
|
36
|
+
const requiredKeys = requiredSet(required);
|
|
37
|
+
for (const entry of requiredKeys) {
|
|
38
|
+
if (!(entry in input)) {
|
|
39
|
+
return {
|
|
40
|
+
property: entry,
|
|
41
|
+
expected: "present",
|
|
42
|
+
actual: "absent",
|
|
43
|
+
message: `\`${entry}\` is required`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const key of Object.keys(input)) {
|
|
48
|
+
const propSchema = properties[key];
|
|
49
|
+
if (!isPlainObject(propSchema))
|
|
50
|
+
continue;
|
|
51
|
+
const value = input[key];
|
|
52
|
+
// nax#2200: `null` on an optional property reads as "not supplied".
|
|
53
|
+
if (value === null && !requiredKeys.has(key))
|
|
54
|
+
continue;
|
|
55
|
+
if (typeof propSchema.type === "string" && PRIMITIVE_TYPES.has(propSchema.type)) {
|
|
56
|
+
if (!typeMatches(propSchema.type, value)) {
|
|
57
|
+
const expected = propSchema.type;
|
|
58
|
+
const actual = describeValuesType(value);
|
|
59
|
+
return {
|
|
60
|
+
property: key,
|
|
61
|
+
expected,
|
|
62
|
+
actual,
|
|
63
|
+
message: `\`${key}\` expected ${expected}, got ${actual}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const enumSchema = propSchema.enum;
|
|
68
|
+
if (Array.isArray(enumSchema) && enumSchema.every((m) => typeof m === "string")) {
|
|
69
|
+
if (!enumSchema.includes(value)) {
|
|
70
|
+
const expected = `one of: ${enumSchema.join(", ")}`;
|
|
71
|
+
const actual = describeValuesType(value);
|
|
72
|
+
return {
|
|
73
|
+
property: key,
|
|
74
|
+
expected,
|
|
75
|
+
actual,
|
|
76
|
+
message: `\`${key}\` must be ${expected} (got ${actual})`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The input with every `null`-valued optional property removed, or `undefined`
|
|
85
|
+
* when there is nothing to remove (including any schema the validator does not
|
|
86
|
+
* understand — the same fail-open contract). A property whose own schema
|
|
87
|
+
* declares `type: "null"` keeps its value: there `null` IS the supplied value.
|
|
88
|
+
* Never mutates `input`.
|
|
89
|
+
*/
|
|
90
|
+
export function stripNullOptionals(schema, input) {
|
|
91
|
+
if (!isPlainObject(schema) || !isPlainObject(input))
|
|
92
|
+
return undefined;
|
|
93
|
+
const { properties, required, type } = schema;
|
|
94
|
+
if (!isPlainObject(properties))
|
|
95
|
+
return undefined;
|
|
96
|
+
if (type !== undefined && type !== "object")
|
|
97
|
+
return undefined;
|
|
98
|
+
const requiredKeys = requiredSet(required);
|
|
99
|
+
const stripped = Object.keys(input).filter((key) => {
|
|
100
|
+
const propSchema = properties[key];
|
|
101
|
+
if (input[key] !== null || requiredKeys.has(key) || !isPlainObject(propSchema))
|
|
102
|
+
return false;
|
|
103
|
+
return propSchema.type !== "null";
|
|
104
|
+
});
|
|
105
|
+
if (stripped.length === 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
const drop = new Set(stripped);
|
|
108
|
+
return Object.fromEntries(Object.entries(input).filter(([key]) => !drop.has(key)));
|
|
109
|
+
}
|
|
110
|
+
function requiredSet(required) {
|
|
111
|
+
if (!Array.isArray(required))
|
|
112
|
+
return new Set();
|
|
113
|
+
return new Set(required.filter((entry) => typeof entry === "string"));
|
|
114
|
+
}
|
|
115
|
+
const PRIMITIVE_TYPES = new Set([
|
|
116
|
+
"string",
|
|
117
|
+
"number",
|
|
118
|
+
"integer",
|
|
119
|
+
"boolean",
|
|
120
|
+
"array",
|
|
121
|
+
"null",
|
|
122
|
+
"object",
|
|
123
|
+
]);
|
|
124
|
+
function typeMatches(type, value) {
|
|
125
|
+
switch (type) {
|
|
126
|
+
case "string":
|
|
127
|
+
return typeof value === "string";
|
|
128
|
+
case "number":
|
|
129
|
+
return typeof value === "number";
|
|
130
|
+
case "integer":
|
|
131
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
132
|
+
case "boolean":
|
|
133
|
+
return typeof value === "boolean";
|
|
134
|
+
case "array":
|
|
135
|
+
return Array.isArray(value);
|
|
136
|
+
case "null":
|
|
137
|
+
return value === null;
|
|
138
|
+
case "object":
|
|
139
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
140
|
+
default:
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function isPlainObject(value) {
|
|
145
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
146
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolDescriptor -> ToolDefinition.
|
|
3
|
+
*
|
|
4
|
+
* Nearly one-to-one. The budget fields stay behind deliberately: nax executes
|
|
5
|
+
* these tools, so `maxCallsPerSession` and `maxTokensPerCall` are enforced by
|
|
6
|
+
* PullToolBudget on this side and mean nothing to a provider.
|
|
7
|
+
*/
|
|
8
|
+
import type { ToolDefinition } from "@nathapp/nax-ai";
|
|
9
|
+
import type { ToolDescriptor } from "#src/session/tool-descriptor";
|
|
10
|
+
import type { CodingTool } from "#src/tools/index";
|
|
11
|
+
export declare function toToolDefinitions(descriptors: readonly ToolDescriptor[]): ToolDefinition[];
|
|
12
|
+
/**
|
|
13
|
+
* CodingTool -> ToolDefinition.
|
|
14
|
+
*
|
|
15
|
+
* `scope` and `run` stay behind for the same reason the pull tools' budget
|
|
16
|
+
* fields do: nax executes these, so they mean nothing to a provider.
|
|
17
|
+
*/
|
|
18
|
+
export declare function codingToolsToDefinitions(tools: readonly CodingTool[]): ToolDefinition[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolDescriptor -> ToolDefinition.
|
|
3
|
+
*
|
|
4
|
+
* Nearly one-to-one. The budget fields stay behind deliberately: nax executes
|
|
5
|
+
* these tools, so `maxCallsPerSession` and `maxTokensPerCall` are enforced by
|
|
6
|
+
* PullToolBudget on this side and mean nothing to a provider.
|
|
7
|
+
*/
|
|
8
|
+
export function toToolDefinitions(descriptors) {
|
|
9
|
+
return descriptors.map((d) => ({
|
|
10
|
+
name: d.name,
|
|
11
|
+
description: d.description,
|
|
12
|
+
inputSchema: d.inputSchema,
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* CodingTool -> ToolDefinition.
|
|
17
|
+
*
|
|
18
|
+
* `scope` and `run` stay behind for the same reason the pull tools' budget
|
|
19
|
+
* fields do: nax executes these, so they mean nothing to a provider.
|
|
20
|
+
*/
|
|
21
|
+
export function codingToolsToDefinitions(tools) {
|
|
22
|
+
return tools.map((t) => ({
|
|
23
|
+
name: t.name,
|
|
24
|
+
description: t.description,
|
|
25
|
+
inputSchema: t.inputSchema,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place a native `tool-result` message is constructed.
|
|
3
|
+
*
|
|
4
|
+
* The result used to be appended at seven separate sites in `turn-loop.ts`, so
|
|
5
|
+
* there was no single point at which a policy could be applied even if one
|
|
6
|
+
* existed, and invariants like "`toolCallId` is always set" were re-asserted by
|
|
7
|
+
* hand at each. Every site now builds its message here.
|
|
8
|
+
*
|
|
9
|
+
* That chokepoint is also what makes `after_tool` safe by construction
|
|
10
|
+
* (nax#2151): the event shapes a result BEFORE it enters the message array, so
|
|
11
|
+
* no handler can rewrite history and re-bill the prefix-cached prompt. The four
|
|
12
|
+
* synthetic sites — where no tool ran — use this builder without firing the
|
|
13
|
+
* event; the three genuine sites use both.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* A refusal the model is meant to act on. Structural (ADR-029 s5), never a
|
|
17
|
+
* string convention: `{ answer }` alone cannot distinguish "refused, and here
|
|
18
|
+
* is why" from "here is your file".
|
|
19
|
+
*/
|
|
20
|
+
export interface DenialInfo {
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
readonly breach: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ToolResultMessage {
|
|
25
|
+
readonly role: "tool-result";
|
|
26
|
+
readonly toolCallId: string;
|
|
27
|
+
readonly content: string;
|
|
28
|
+
readonly isError?: boolean;
|
|
29
|
+
readonly denied?: DenialInfo;
|
|
30
|
+
}
|
|
31
|
+
export interface BuildToolResultArgs {
|
|
32
|
+
readonly toolCallId: string;
|
|
33
|
+
readonly content: string;
|
|
34
|
+
readonly isError?: boolean;
|
|
35
|
+
readonly denied?: DenialInfo;
|
|
36
|
+
}
|
|
37
|
+
export declare function buildToolResult(args: BuildToolResultArgs): ToolResultMessage;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place a native `tool-result` message is constructed.
|
|
3
|
+
*
|
|
4
|
+
* The result used to be appended at seven separate sites in `turn-loop.ts`, so
|
|
5
|
+
* there was no single point at which a policy could be applied even if one
|
|
6
|
+
* existed, and invariants like "`toolCallId` is always set" were re-asserted by
|
|
7
|
+
* hand at each. Every site now builds its message here.
|
|
8
|
+
*
|
|
9
|
+
* That chokepoint is also what makes `after_tool` safe by construction
|
|
10
|
+
* (nax#2151): the event shapes a result BEFORE it enters the message array, so
|
|
11
|
+
* no handler can rewrite history and re-bill the prefix-cached prompt. The four
|
|
12
|
+
* synthetic sites — where no tool ran — use this builder without firing the
|
|
13
|
+
* event; the three genuine sites use both.
|
|
14
|
+
*/
|
|
15
|
+
export function buildToolResult(args) {
|
|
16
|
+
return {
|
|
17
|
+
role: "tool-result",
|
|
18
|
+
toolCallId: args.toolCallId,
|
|
19
|
+
content: args.content,
|
|
20
|
+
// Absent stays absent: a clean result must not carry an `isError: undefined`
|
|
21
|
+
// key, so the field is spread in only when it has a value.
|
|
22
|
+
...(args.isError === undefined ? {} : { isError: args.isError }),
|
|
23
|
+
...(args.denied === undefined ? {} : { denied: args.denied }),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation persistence for the native transport.
|
|
3
|
+
*
|
|
4
|
+
* nax-ai's client is stateless — every call takes the whole message array — so
|
|
5
|
+
* nax keeps the conversation. Under ACP the acpx subprocess remembered it and
|
|
6
|
+
* nax stored nothing; SessionDescriptor still has no message field, and gains
|
|
7
|
+
* none. See ADR-028 sections 2 and 3.
|
|
8
|
+
*/
|
|
9
|
+
import type { ConversationMessage } from "@nathapp/nax-ai";
|
|
10
|
+
export declare function transcriptPath(dir: string, sessionName: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Who may resume a transcript. `owner` is the op invocation (nax#1877); `model`
|
|
13
|
+
* is the native model that wrote it (nax#2150, P3 spec 8.3). A field the caller
|
|
14
|
+
* leaves undefined makes no claim, so non-op callers and tests read whatever is
|
|
15
|
+
* there.
|
|
16
|
+
*/
|
|
17
|
+
export interface TranscriptIdentity {
|
|
18
|
+
readonly owner?: string;
|
|
19
|
+
readonly model?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The model half of `TranscriptIdentity`: the native `provider/model` id with
|
|
23
|
+
* the reasoning-effort suffix stripped. A thinking signature binds to the
|
|
24
|
+
* model, not to the effort — pi-ai's own `isSameModel` compares
|
|
25
|
+
* provider/api/model for the same reason. `parseModelSpec`, not
|
|
26
|
+
* `parseNativeModel`: this must never throw.
|
|
27
|
+
*/
|
|
28
|
+
export declare function transcriptModelIdentity(rawModel: string | undefined): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Missing file means a new conversation. Anything else is a real failure.
|
|
31
|
+
*
|
|
32
|
+
* Returns `[]` — a new conversation — when the transcript on disk belongs to a
|
|
33
|
+
* different `owner` or a different recorded `model` than the caller (nax#1877,
|
|
34
|
+
* nax#2150). A reader that declares no identity is not making a claim and
|
|
35
|
+
* reads whatever is there, which keeps non-op callers and unit tests working
|
|
36
|
+
* unchanged.
|
|
37
|
+
*/
|
|
38
|
+
export declare function loadTranscript(dir: string, sessionName: string, identity?: TranscriptIdentity): Promise<ConversationMessage[]>;
|
|
39
|
+
export declare function saveTranscript(dir: string, sessionName: string, messages: readonly ConversationMessage[], identity?: TranscriptIdentity): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Move a failed session's transcript off the path the next session reads.
|
|
42
|
+
*
|
|
43
|
+
* The kept-on-failure transcript (nax#1838) used to stay at its live name, which
|
|
44
|
+
* is exactly the name `loadTranscript` opens — so the post-mortem artifact and
|
|
45
|
+
* the resumption source were one file (nax#1877). Renaming keeps every byte of
|
|
46
|
+
* debugging value and removes the resumption entirely. It also stops the NEXT
|
|
47
|
+
* attempt's `saveTranscript` from overwriting the artifact #1838 wanted kept.
|
|
48
|
+
*
|
|
49
|
+
* A missing transcript is not an error: a session can fail before its first save.
|
|
50
|
+
*/
|
|
51
|
+
export declare function retainTranscript(dir: string, sessionName: string): Promise<void>;
|
|
52
|
+
export declare function deleteTranscript(dir: string, sessionName: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Cap on transcripts retained per feature's `sessions/` directory. Transcripts
|
|
55
|
+
* are deleted on a clean close (`closeNativeSession`) but kept when a turn
|
|
56
|
+
* failed, so the kept-on-failure set otherwise grows without bound — nothing
|
|
57
|
+
* else prunes it (issue #1445 is the same class of unbounded `~/.nax` growth).
|
|
58
|
+
* Follows the precedent of `MAX_RETAINED_RUNS` in `src/metrics/tracker.ts`.
|
|
59
|
+
*/
|
|
60
|
+
export declare const MAX_RETAINED_TRANSCRIPTS = 50;
|
|
61
|
+
/**
|
|
62
|
+
* Reset the one-shot truncation-warning flag (for testing only).
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export declare function _resetTranscriptTruncationWarningForTests(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Prune a feature's `sessions/` directory to the `maxRetained` most recently
|
|
68
|
+
* modified transcript files, deleting the oldest first. Called from
|
|
69
|
+
* `closeNativeSession`'s kept-on-failure branch — the only place transcripts
|
|
70
|
+
* accumulate, since a clean close always deletes its own transcript.
|
|
71
|
+
*
|
|
72
|
+
* A missing directory is not an error (nothing to prune yet).
|
|
73
|
+
*
|
|
74
|
+
* Returns the number of transcript files deleted (0 when there is nothing to
|
|
75
|
+
* delete) so callers can report the sweep's work without re-reading the dir.
|
|
76
|
+
*/
|
|
77
|
+
export declare function pruneRetainedTranscripts(dir: string, maxRetained?: number): Promise<number>;
|