@nathapp/nax-agent 0.1.0 → 0.3.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 +43 -0
- package/README.md +56 -2
- package/dist/coding-tools/coding-tool-sandbox.d.ts +3 -0
- package/dist/coding-tools/coding-tool-sandbox.js +14 -3
- package/dist/coding-tools/coding-tool-support.d.ts +3 -0
- package/dist/coding-tools/coding-tool-support.js +2 -0
- package/dist/index.d.ts +21 -3
- package/dist/index.js +13 -3
- package/dist/infra/stderr-tail.d.ts +8 -0
- package/dist/infra/stderr-tail.js +48 -0
- package/dist/internal/redact.d.ts +23 -0
- package/dist/internal/redact.js +60 -0
- package/dist/internal.d.ts +7 -0
- package/dist/internal.js +8 -0
- package/dist/native/adapter-deps.d.ts +3 -3
- package/dist/native/adapter-deps.js +3 -3
- package/dist/native/client.d.ts +7 -1
- package/dist/native/client.js +1 -1
- package/dist/native/credentials/change-guard.d.ts +8 -1
- package/dist/native/credentials/change-guard.js +1 -1
- package/dist/native/credentials/fingerprint.d.ts +4 -1
- package/dist/native/credentials/fingerprint.js +6 -3
- package/dist/native/credentials/session-source.d.ts +12 -0
- package/dist/native/credentials/session-source.js +33 -0
- package/dist/native/index.d.ts +3 -1
- package/dist/native/index.js +2 -1
- package/dist/native/session/loop-events/types.d.ts +1 -1
- package/dist/native/session/loop-handlers.d.ts +3 -0
- package/dist/native/session/loop-handlers.js +1 -1
- package/dist/native/session/memory-transcript-store.d.ts +12 -0
- package/dist/native/session/memory-transcript-store.js +36 -0
- package/dist/native/session/session.d.ts +138 -96
- package/dist/native/session/session.js +107 -127
- package/dist/native/session/transcript-identity.d.ts +39 -0
- package/dist/native/session/transcript-identity.js +81 -0
- package/dist/native/session/transcript-store.d.ts +22 -21
- package/dist/native/session/transcript-store.js +59 -68
- package/dist/native/session/transcript-types.d.ts +54 -0
- package/dist/native/session/transcript-types.js +7 -0
- package/dist/native/session/truncation-handler.d.ts +4 -3
- package/dist/native/session/truncation-handler.js +7 -10
- package/dist/native/session/turn-complete-step.d.ts +4 -1
- package/dist/native/session/turn-complete-step.js +11 -4
- package/dist/native/session/turn-event-emitter.d.ts +48 -0
- package/dist/native/session/turn-event-emitter.js +134 -0
- package/dist/native/session/turn-loop-round-trip.d.ts +5 -2
- package/dist/native/session/turn-loop-round-trip.js +8 -4
- package/dist/native/session/turn-loop.js +27 -13
- package/dist/native/session/turn-result.d.ts +1 -1
- package/dist/native/session/turn-result.js +1 -1
- package/dist/native/session/turn-tool-batch.d.ts +3 -0
- package/dist/native/session/turn-tool-batch.js +14 -6
- package/dist/native/session/turn-types.d.ts +17 -1
- package/dist/native/session-adapter.d.ts +22 -4
- package/dist/native/session-adapter.js +79 -35
- package/dist/native/stream-complete.d.ts +17 -0
- package/dist/native/stream-complete.js +40 -0
- package/dist/permissions/approvals-link.js +5 -4
- package/dist/permissions/ask-chain.d.ts +1 -1
- package/dist/permissions/ask.d.ts +6 -0
- package/dist/permissions/ask.js +6 -0
- package/dist/permissions/index.d.ts +1 -1
- package/dist/permissions/index.js +1 -1
- package/dist/sandbox/policy-builder.d.ts +6 -1
- package/dist/sandbox/policy-builder.js +18 -14
- package/dist/session/agent-session-deps.d.ts +12 -0
- package/dist/session/agent-session-deps.js +28 -0
- package/dist/session/agent-session-errors.d.ts +11 -0
- package/dist/session/agent-session-errors.js +12 -0
- package/dist/session/agent-session-options.d.ts +12 -0
- package/dist/session/agent-session-options.js +96 -0
- package/dist/session/agent-session-resume.d.ts +14 -0
- package/dist/session/agent-session-resume.js +49 -0
- package/dist/session/agent-session-turn.d.ts +41 -0
- package/dist/session/agent-session-turn.js +177 -0
- package/dist/session/agent-session-types.d.ts +182 -0
- package/dist/session/agent-session-types.js +1 -0
- package/dist/session/agent-session.d.ts +8 -0
- package/dist/session/agent-session.js +184 -0
- package/dist/session/native-backend-options.d.ts +24 -0
- package/dist/session/native-backend-options.js +81 -0
- package/dist/session/native-backend.d.ts +4 -0
- package/dist/session/native-backend.js +106 -0
- package/dist/session/pending-asks.d.ts +24 -0
- package/dist/session/pending-asks.js +77 -0
- package/dist/session/session-ask-link.d.ts +33 -0
- package/dist/session/session-ask-link.js +53 -0
- package/dist/session/session-ask-port.d.ts +12 -0
- package/dist/session/session-ask-port.js +64 -0
- package/dist/session/session-backend.d.ts +79 -0
- package/dist/session/session-backend.js +1 -0
- package/dist/session/session-event-channel.d.ts +17 -0
- package/dist/session/session-event-channel.js +132 -0
- package/dist/session/session-interaction.d.ts +20 -0
- package/dist/session/session-interaction.js +161 -0
- package/dist/session/session-tool-support.d.ts +48 -0
- package/dist/session/session-tool-support.js +104 -0
- package/dist/session/session-types.d.ts +33 -2
- package/dist/session/turn-event.d.ts +66 -0
- package/dist/session/turn-event.js +1 -0
- package/dist/tools/ask-request.js +3 -1
- package/dist/tools/bash-cwd.d.ts +3 -3
- package/dist/tools/credential-read-deny.d.ts +22 -0
- package/dist/tools/credential-read-deny.js +56 -0
- package/dist/tools/glob.js +9 -2
- package/dist/tools/grep.js +55 -26
- package/dist/tools/owned-paths.d.ts +43 -0
- package/dist/tools/owned-paths.js +17 -0
- package/dist/tools/package-managers.d.ts +3 -0
- package/dist/tools/package-managers.js +7 -3
- package/dist/tools/policy-bash-raw.d.ts +3 -0
- package/dist/tools/policy-bash-raw.js +49 -62
- package/dist/tools/policy-command-branch.d.ts +3 -0
- package/dist/tools/policy-command-branch.js +2 -1
- package/dist/tools/policy-paths-branch.d.ts +14 -8
- package/dist/tools/policy-paths-branch.js +20 -19
- package/dist/tools/policy.d.ts +10 -7
- package/dist/tools/policy.js +9 -7
- package/dist/tools/protected-paths.d.ts +6 -6
- package/dist/tools/read.js +38 -20
- package/dist/tools/registry.d.ts +3 -0
- package/dist/tools/run-command-exec.js +1 -0
- package/dist/tools/runtime.d.ts +3 -0
- package/dist/tools/runtime.js +1 -0
- package/dist/tools/scratchpad.js +2 -1
- package/dist/tools/types.d.ts +2 -2
- package/package.json +2 -2
- package/dist/tools/nax-owned-writes.d.ts +0 -135
- package/dist/tools/nax-owned-writes.js +0 -259
|
@@ -16,14 +16,21 @@ import type { AuthStamp } from "#src/session/session-types";
|
|
|
16
16
|
/**
|
|
17
17
|
* Where a credential came from, as reported by `describe`. `AuthStamp` without
|
|
18
18
|
* the fingerprint — the guard computes that itself from the credential.
|
|
19
|
+
* `memory` is a store held in process (a session's own credentials), distinct
|
|
20
|
+
* from the on-disk `file` and the `exec` helper.
|
|
19
21
|
*/
|
|
20
22
|
export interface CredentialOrigin {
|
|
21
|
-
source: "file" | "exec";
|
|
23
|
+
source: "file" | "exec" | "memory";
|
|
22
24
|
account?: string;
|
|
23
25
|
}
|
|
24
26
|
export interface ChangeGuardOptions {
|
|
25
27
|
/** `warn` adopts the new identity and serves it; `refuse` throws instead. */
|
|
26
28
|
onChange: CredentialAuthConfig["onChange"];
|
|
29
|
+
/**
|
|
30
|
+
* Fingerprint key. Absent = the machine salt file (today). A session store
|
|
31
|
+
* passes an in-memory salt, so its fingerprints never touch the slot.
|
|
32
|
+
*/
|
|
33
|
+
readonly salt?: Buffer;
|
|
27
34
|
/**
|
|
28
35
|
* Where the credential read from `inner` came from. Called immediately after
|
|
29
36
|
* each successful read, because the credential itself carries no provenance.
|
|
@@ -87,7 +87,7 @@ export function createChangeGuard(inner, options) {
|
|
|
87
87
|
const described = options.describe(providerId);
|
|
88
88
|
const identity = {
|
|
89
89
|
kind: credential.kind,
|
|
90
|
-
fingerprint: await fingerprintCredential(credential),
|
|
90
|
+
fingerprint: await fingerprintCredential(credential, options.salt),
|
|
91
91
|
source: described?.source ?? "file",
|
|
92
92
|
...(described?.account !== undefined ? { account: described.account } : {}),
|
|
93
93
|
};
|
|
@@ -22,5 +22,8 @@ export declare function _resetFingerprintSalt(): void;
|
|
|
22
22
|
* is fingerprinted as the template, because nax-ai documents `key` as opaque.
|
|
23
23
|
* `oauth` fingerprints its `refresh` token, so access-token rotation is
|
|
24
24
|
* invisible while refresh-token rotation is not.
|
|
25
|
+
*
|
|
26
|
+
* `salt` overrides the machine salt file: an in-process caller that keeps its own
|
|
27
|
+
* key (a session store) passes one, and a supplied salt never touches the slot.
|
|
25
28
|
*/
|
|
26
|
-
export declare function fingerprintCredential(credential: StoredCredential): Promise<string>;
|
|
29
|
+
export declare function fingerprintCredential(credential: StoredCredential, salt?: Buffer): Promise<string>;
|
|
@@ -166,9 +166,12 @@ function identifyingSecret(credential) {
|
|
|
166
166
|
* is fingerprinted as the template, because nax-ai documents `key` as opaque.
|
|
167
167
|
* `oauth` fingerprints its `refresh` token, so access-token rotation is
|
|
168
168
|
* invisible while refresh-token rotation is not.
|
|
169
|
+
*
|
|
170
|
+
* `salt` overrides the machine salt file: an in-process caller that keeps its own
|
|
171
|
+
* key (a session store) passes one, and a supplied salt never touches the slot.
|
|
169
172
|
*/
|
|
170
|
-
export async function fingerprintCredential(credential) {
|
|
171
|
-
const
|
|
173
|
+
export async function fingerprintCredential(credential, salt) {
|
|
174
|
+
const key = salt ?? (await resolveSalt());
|
|
172
175
|
const secret = identifyingSecret(credential);
|
|
173
|
-
return createHmac("sha256",
|
|
176
|
+
return createHmac("sha256", key).update(secret).digest("hex").slice(0, FINGERPRINT_CHARS);
|
|
174
177
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type StoredCredential } from "@nathapp/nax-ai";
|
|
2
|
+
import { type GuardedCredentialStore } from "./change-guard.ts";
|
|
3
|
+
export type CredentialSource = {
|
|
4
|
+
readonly kind: "memory";
|
|
5
|
+
readonly credentials: Readonly<Record<string, StoredCredential>>;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "exec";
|
|
8
|
+
readonly command: readonly string[];
|
|
9
|
+
readonly timeoutMs?: number;
|
|
10
|
+
readonly onChange?: "warn" | "refuse";
|
|
11
|
+
};
|
|
12
|
+
export declare function createSessionCredentialStore(source: CredentialSource): GuardedCredentialStore;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A credential source owned by one session (S3 spec 5.2), instead of the
|
|
3
|
+
* process-wide store behind `configureCredentials`. It never reads the slot:
|
|
4
|
+
* the fingerprint salt is random per store, so an embedder that never called
|
|
5
|
+
* `configureCredentials` can still run a session and stamp its cost rows.
|
|
6
|
+
*/
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { createMemoryCredentialStore } from "@nathapp/nax-ai";
|
|
9
|
+
import { createChangeGuard } from "./change-guard.js";
|
|
10
|
+
import { createExecCredentialSource } from "./exec-source.js";
|
|
11
|
+
const SALT_BYTES = 32;
|
|
12
|
+
export function createSessionCredentialStore(source) {
|
|
13
|
+
const salt = randomBytes(SALT_BYTES);
|
|
14
|
+
if (source.kind === "memory") {
|
|
15
|
+
return createChangeGuard(createMemoryCredentialStore(source.credentials), {
|
|
16
|
+
onChange: "warn",
|
|
17
|
+
salt,
|
|
18
|
+
describe: () => ({ source: "memory" }),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const exec = createExecCredentialSource({
|
|
22
|
+
command: source.command,
|
|
23
|
+
...(source.timeoutMs !== undefined ? { timeoutMs: source.timeoutMs } : {}),
|
|
24
|
+
});
|
|
25
|
+
return createChangeGuard(exec, {
|
|
26
|
+
onChange: source.onChange ?? "warn",
|
|
27
|
+
salt,
|
|
28
|
+
describe: (providerId) => {
|
|
29
|
+
const account = exec.accountOf(providerId);
|
|
30
|
+
return account === undefined ? { source: "exec" } : { source: "exec", account };
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
package/dist/native/index.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export { credentialFilePath, naxCredentialStore, type StoredEntry, servedAuth }
|
|
|
19
19
|
export { NativeSessionUnsupportedError } from "./errors.ts";
|
|
20
20
|
export { type ResolveResult, type ResolveStatus, resolveNativeId, } from "./model-resolver.ts";
|
|
21
21
|
export { NATIVE_AGENT } from "./models.ts";
|
|
22
|
-
export {
|
|
22
|
+
export { createMemoryTranscriptStore, type MemoryTranscriptStore } from "./session/memory-transcript-store.ts";
|
|
23
|
+
export { createFileTranscriptStore, MAX_RETAINED_TRANSCRIPTS, pruneRetainedTranscripts, } from "./session/transcript-store.ts";
|
|
24
|
+
export type { TranscriptAcpRecord, TranscriptDoc, TranscriptStore, TurnMarker } from "./session/transcript-types.ts";
|
|
23
25
|
export { NativeSessionAdapter } from "./session-adapter.ts";
|
|
24
26
|
export { newSessionKey } from "./session-affinity.ts";
|
|
25
27
|
export { type NativeTierConfig, nativeTierProviders } from "./tier-providers.ts";
|
package/dist/native/index.js
CHANGED
|
@@ -17,7 +17,8 @@ export { credentialFilePath, naxCredentialStore, servedAuth } from "./credential
|
|
|
17
17
|
export { NativeSessionUnsupportedError } from "./errors.js";
|
|
18
18
|
export { resolveNativeId, } from "./model-resolver.js";
|
|
19
19
|
export { NATIVE_AGENT } from "./models.js";
|
|
20
|
-
export {
|
|
20
|
+
export { createMemoryTranscriptStore } from "./session/memory-transcript-store.js";
|
|
21
|
+
export { createFileTranscriptStore, MAX_RETAINED_TRANSCRIPTS, pruneRetainedTranscripts, } from "./session/transcript-store.js";
|
|
21
22
|
export { NativeSessionAdapter } from "./session-adapter.js";
|
|
22
23
|
export { newSessionKey } from "./session-affinity.js";
|
|
23
24
|
export { nativeTierProviders } from "./tier-providers.js";
|
|
@@ -159,7 +159,7 @@ export interface BeforeCompactionPatch {
|
|
|
159
159
|
/** A replacement summary, skipping the summarizer call. */
|
|
160
160
|
readonly summary?: string;
|
|
161
161
|
}
|
|
162
|
-
/** `before_turn_end` — fires before the
|
|
162
|
+
/** `before_turn_end` — fires before the turn-end transcript save. */
|
|
163
163
|
export interface BeforeTurnEndPayload {
|
|
164
164
|
readonly messages: readonly NativeTranscriptMessage[];
|
|
165
165
|
readonly roundTrips: number;
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
import { type SpinBreaker } from "#src/infra/spin-breaker/index";
|
|
34
34
|
import { type InvalidCallBudget } from "./handle-invalid-tool-call.ts";
|
|
35
35
|
import { type LoopEventRegistry, type LoopHandlerContext, type LoopHandlerSet } from "./loop-events/index.ts";
|
|
36
|
+
import type { NativeSessionState } from "./session.ts";
|
|
36
37
|
export interface BuiltinLoopHandlerDeps {
|
|
37
38
|
/**
|
|
38
39
|
* The session the handlers run for. Truncation resolves its spill root
|
|
@@ -40,6 +41,8 @@ export interface BuiltinLoopHandlerDeps {
|
|
|
40
41
|
* per-turn dep, so a repointed registry never keeps a stale session.
|
|
41
42
|
*/
|
|
42
43
|
readonly sessionName: string;
|
|
44
|
+
/** The owning adapter's per-session state, read through `state.current` at dispatch like the session name. */
|
|
45
|
+
readonly sessionState: NativeSessionState;
|
|
43
46
|
/** Per-turn invalid-call budget. The loop reads `exceeded` for the halt. */
|
|
44
47
|
readonly budget: InvalidCallBudget;
|
|
45
48
|
/** Absent disables the breaker, exactly as `TurnDeps.spinBreaker` does. */
|
|
@@ -96,7 +96,7 @@ export function registerBuiltinLoopHandlers(registry, deps) {
|
|
|
96
96
|
// whatever the handlers produced. The handler is rebuilt per dispatch so the
|
|
97
97
|
// session name is read through `state.current` — the same lifetime the other
|
|
98
98
|
// per-turn deps use — rather than frozen at install time.
|
|
99
|
-
registry.register("after_tool", (payload) => createTruncationHandler(state.current.sessionName)(payload));
|
|
99
|
+
registry.register("after_tool", (payload) => createTruncationHandler(state.current.sessionState, state.current.sessionName)(payload));
|
|
100
100
|
}
|
|
101
101
|
function repairInvalidCall(state, call, tools) {
|
|
102
102
|
const invalid = state.current.budget.observe(call, tools);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An in-process `TranscriptStore` for tests and simple embedders (S3 spec 5.5).
|
|
3
|
+
* Every document crossing the boundary is copied, so a caller mutating what it
|
|
4
|
+
* saved or loaded cannot rewrite stored history. History is lost with the
|
|
5
|
+
* process; an embedder that must resume after a restart injects a durable store.
|
|
6
|
+
*/
|
|
7
|
+
import type { TranscriptDoc, TranscriptStore } from "./transcript-types.ts";
|
|
8
|
+
export interface MemoryTranscriptStore extends TranscriptStore {
|
|
9
|
+
/** The last document `retainFailed` moved aside for `sessionId`, if any. */
|
|
10
|
+
retained(sessionId: string): TranscriptDoc | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare function createMemoryTranscriptStore(): MemoryTranscriptStore;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An in-process `TranscriptStore` for tests and simple embedders (S3 spec 5.5).
|
|
3
|
+
* Every document crossing the boundary is copied, so a caller mutating what it
|
|
4
|
+
* saved or loaded cannot rewrite stored history. History is lost with the
|
|
5
|
+
* process; an embedder that must resume after a restart injects a durable store.
|
|
6
|
+
*/
|
|
7
|
+
export function createMemoryTranscriptStore() {
|
|
8
|
+
const live = new Map();
|
|
9
|
+
const failed = new Map();
|
|
10
|
+
const copy = (doc) => doc === undefined ? undefined : structuredClone(doc);
|
|
11
|
+
return {
|
|
12
|
+
load: (sessionId) => Promise.resolve(copy(live.get(sessionId)) ?? null),
|
|
13
|
+
save: (sessionId, doc) => {
|
|
14
|
+
live.set(sessionId, structuredClone(doc));
|
|
15
|
+
return Promise.resolve();
|
|
16
|
+
},
|
|
17
|
+
retainFailed: (sessionId) => {
|
|
18
|
+
const doc = live.get(sessionId);
|
|
19
|
+
if (doc !== undefined) {
|
|
20
|
+
failed.set(sessionId, doc);
|
|
21
|
+
live.delete(sessionId);
|
|
22
|
+
}
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
},
|
|
25
|
+
delete: (sessionId) => {
|
|
26
|
+
live.delete(sessionId);
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
},
|
|
29
|
+
markTurn: (sessionId, marker) => {
|
|
30
|
+
const base = live.get(sessionId) ?? { savedAt: new Date().toISOString(), messages: [] };
|
|
31
|
+
live.set(sessionId, { ...base, turn: { ...marker } });
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
},
|
|
34
|
+
retained: (sessionId) => copy(failed.get(sessionId)),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -10,104 +10,133 @@ import { type SpinBreaker } from "#src/infra/spin-breaker/index";
|
|
|
10
10
|
import type { AgentStreamEvent } from "#src/session/agent-stream-events";
|
|
11
11
|
import type { OpenSessionOpts, SessionHandle } from "#src/session/session-types";
|
|
12
12
|
import type { ResolvedCompaction } from "./compaction.ts";
|
|
13
|
+
import type { TranscriptStore } from "./transcript-types.ts";
|
|
13
14
|
import type { TurnRetryConfig } from "./turn-retry.ts";
|
|
14
|
-
/**
|
|
15
|
-
|
|
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 declare const nativeTranscriptDirs: Map<string, string>;
|
|
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 declare const nativeSessionScratchpadRoots: Map<string, string>;
|
|
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 declare const nativeSessionTimeouts: Map<string, number>;
|
|
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 declare const nativeSessionStreamHooks: Map<string, {
|
|
15
|
+
/** The runtime hooks handed to openSession. */
|
|
16
|
+
export interface NativeSessionStreamHooks {
|
|
49
17
|
onStreamActivity?: (event: AgentStreamEvent) => void;
|
|
50
18
|
onActiveCall?: (callId: string, cancel: () => Promise<void>) => void;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* for both -- deleting the transcript of exactly the session whose history the
|
|
58
|
-
* retry needs and a human would read (nax#1838). Rather than widen the adapter
|
|
59
|
-
* interface for one transport, the turn records what it knows here and the
|
|
60
|
-
* close reads it. Same lifecycle as the maps above: written per turn, cleared
|
|
61
|
-
* on close.
|
|
62
|
-
*
|
|
63
|
-
* A later successful turn clears the mark. "Failed" describes the session's
|
|
64
|
-
* current state, not whether it ever stumbled -- a session that recovered and
|
|
65
|
-
* finished is still cleaned up.
|
|
66
|
-
*/
|
|
67
|
-
export declare const nativeSessionFailed: Set<string>;
|
|
68
|
-
/**
|
|
69
|
-
* Session name -> the identity that owns this session's transcript (nax#1877).
|
|
70
|
-
*
|
|
71
|
-
* Read by `runNativeTurn` on both the load and the save, so a transcript left
|
|
72
|
-
* behind by an abandoned invocation of the same deterministic session name is
|
|
73
|
-
* recognised as foreign rather than silently resumed. Same lifecycle as the
|
|
74
|
-
* maps above: set on open, cleared on close.
|
|
75
|
-
*/
|
|
76
|
-
export declare const nativeSessionTranscriptOwners: Map<string, string>;
|
|
77
|
-
/** Session name -> resolved compaction settings. Same lifecycle as the maps above. */
|
|
78
|
-
export declare const nativeSessionCompaction: Map<string, ResolvedCompaction>;
|
|
79
|
-
/**
|
|
80
|
-
* Session name -> resolved transport-fault retry settings (nax#1870). Same
|
|
81
|
-
* lifecycle as the maps above: set on open, cleared on close.
|
|
82
|
-
*/
|
|
83
|
-
export declare const nativeSessionTransportRetry: Map<string, TurnRetryConfig>;
|
|
84
|
-
/**
|
|
85
|
-
* Session name -> live `SpinBreaker` instance (nax#2013, lifetime extended in
|
|
86
|
-
* nax#2047). Constructed once at open so the cumulative counter survives
|
|
87
|
-
* across `runNativeTurn` calls — a fix round that opens a new turn on the
|
|
88
|
-
* same `handle.id` must inherit the prior turn's evidence rather than start
|
|
89
|
-
* fresh. Same lifecycle as `nativeSessionTransportRetry`: set on open, cleared
|
|
90
|
-
* on close.
|
|
91
|
-
*/
|
|
92
|
-
export declare const nativeSessionSpinBreaker: Map<string, SpinBreaker>;
|
|
19
|
+
}
|
|
20
|
+
/** Where one open session's history lives, and whether close may remove it. */
|
|
21
|
+
export interface SessionTranscript {
|
|
22
|
+
readonly store: TranscriptStore;
|
|
23
|
+
readonly retainOnClose: boolean;
|
|
24
|
+
}
|
|
93
25
|
/**
|
|
94
|
-
*
|
|
95
|
-
* covers, so the next estimate can anchor on a real number.
|
|
96
|
-
*
|
|
97
|
-
* In-memory rather than persisted because runNativeTurn reloads the transcript
|
|
98
|
-
* from disk on EVERY turn: without this the second turn of every session would
|
|
99
|
-
* be estimated from scratch. A process restart still loses it, which is the case
|
|
100
|
-
* the reactive backstop covers.
|
|
26
|
+
* The per-owner state behind the native session lifecycle.
|
|
101
27
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
28
|
+
* Every map below is keyed by session name and owned by one state instance, so
|
|
29
|
+
* two owners (the orchestrator and a test, or two concurrent runs) never share
|
|
30
|
+
* an entry for the same deterministic name.
|
|
104
31
|
*/
|
|
32
|
+
export interface NativeSessionState {
|
|
33
|
+
/**
|
|
34
|
+
* Session name -> transcript directory, so sendTurn and close can find it.
|
|
35
|
+
*
|
|
36
|
+
* Cleared by every close path — `closeNativeSession` (in-process) and the
|
|
37
|
+
* adapter's `closePhysicalSession` (run teardown, which only has the name) —
|
|
38
|
+
* through the shared `clearNativeSessionState`. A caller that opens a session
|
|
39
|
+
* and never closes it (e.g. an early return, or a thrown error between open and
|
|
40
|
+
* close) still leaks an entry for the lifetime of the state. Harmless in
|
|
41
|
+
* practice (it is a small in-memory map keyed by session name, not a handle
|
|
42
|
+
* to a real resource), but worth knowing when debugging a growing map.
|
|
43
|
+
*/
|
|
44
|
+
readonly transcriptDirs: Map<string, string>;
|
|
45
|
+
/**
|
|
46
|
+
* Session name -> the session's transcript store and close policy (S3-1).
|
|
47
|
+
* Set by every `openNativeSession`; read through `sessionTranscriptFor`. Same
|
|
48
|
+
* lifecycle as the maps above: set on open, cleared on close.
|
|
49
|
+
*/
|
|
50
|
+
readonly transcripts: Map<string, SessionTranscript>;
|
|
51
|
+
/**
|
|
52
|
+
* Session name -> the root whose `.nax/scratchpad/` holds this session's
|
|
53
|
+
* spill files (US-003).
|
|
54
|
+
*
|
|
55
|
+
* Recorded from the session's `workdir` at open — the same root the coding
|
|
56
|
+
* tools are confined to — so a spilled body is reachable through
|
|
57
|
+
* `ScratchpadRead` with the relative path the truncation marker names. Same
|
|
58
|
+
* lifecycle as the maps above: set on open, cleared on close.
|
|
59
|
+
*/
|
|
60
|
+
readonly scratchpadRoots: Map<string, string>;
|
|
61
|
+
/**
|
|
62
|
+
* Session name -> timeoutSeconds, so sendTurn can bound each `complete()`
|
|
63
|
+
* call with a deadline (whole-branch review finding 4). Same lifecycle as
|
|
64
|
+
* `transcriptDirs` — populated on open, cleared on close only.
|
|
65
|
+
*/
|
|
66
|
+
readonly timeouts: Map<string, number>;
|
|
67
|
+
/**
|
|
68
|
+
* Session name -> the runtime hooks handed to openSession. SessionManager
|
|
69
|
+
* passes both (manager.ts, openSession) and the native adapter previously
|
|
70
|
+
* ignored them, which is why the idle watchdog never covered native. Same
|
|
71
|
+
* lifecycle as `transcriptDirs`: set on open, cleared on close only.
|
|
72
|
+
*/
|
|
73
|
+
readonly streamHooks: Map<string, NativeSessionStreamHooks>;
|
|
74
|
+
/**
|
|
75
|
+
* Session names whose most recent turn failed.
|
|
76
|
+
*
|
|
77
|
+
* `AgentAdapter.closeSession(handle)` carries no failure signal, so the close
|
|
78
|
+
* cannot tell a finished session from a broken one and passed `failed: false`
|
|
79
|
+
* for both -- deleting the transcript of exactly the session whose history the
|
|
80
|
+
* retry needs and a human would read (nax#1838). Rather than widen the adapter
|
|
81
|
+
* interface for one transport, the turn records what it knows here and the
|
|
82
|
+
* close reads it. Same lifecycle as the maps above: written per turn, cleared
|
|
83
|
+
* on close.
|
|
84
|
+
*
|
|
85
|
+
* A later successful turn clears the mark. "Failed" describes the session's
|
|
86
|
+
* current state, not whether it ever stumbled -- a session that recovered and
|
|
87
|
+
* finished is still cleaned up.
|
|
88
|
+
*/
|
|
89
|
+
readonly failed: Set<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Session name -> the identity that owns this session's transcript (nax#1877).
|
|
92
|
+
*
|
|
93
|
+
* Read by `runNativeTurn` on both the load and the save, so a transcript left
|
|
94
|
+
* behind by an abandoned invocation of the same deterministic session name is
|
|
95
|
+
* recognised as foreign rather than silently resumed. Same lifecycle as the
|
|
96
|
+
* maps above: set on open, cleared on close.
|
|
97
|
+
*/
|
|
98
|
+
readonly transcriptOwners: Map<string, string>;
|
|
99
|
+
/** Session name -> resolved compaction settings. Same lifecycle as the maps above. */
|
|
100
|
+
readonly compaction: Map<string, ResolvedCompaction>;
|
|
101
|
+
/**
|
|
102
|
+
* Session name -> resolved transport-fault retry settings (nax#1870). Same
|
|
103
|
+
* lifecycle as the maps above: set on open, cleared on close.
|
|
104
|
+
*/
|
|
105
|
+
readonly transportRetry: Map<string, TurnRetryConfig>;
|
|
106
|
+
/**
|
|
107
|
+
* Session name -> live `SpinBreaker` instance (nax#2013, lifetime extended in
|
|
108
|
+
* nax#2047). Constructed once at open so the cumulative counter survives
|
|
109
|
+
* across `runNativeTurn` calls — a fix round that opens a new turn on the
|
|
110
|
+
* same `handle.id` must inherit the prior turn's evidence rather than start
|
|
111
|
+
* fresh. Same lifecycle as `transportRetry`: set on open, cleared on close.
|
|
112
|
+
*/
|
|
113
|
+
readonly spinBreakers: Map<string, SpinBreaker>;
|
|
114
|
+
/**
|
|
115
|
+
* Session name -> the last round trip's reported input tokens and the index it
|
|
116
|
+
* covers, so the next estimate can anchor on a real number.
|
|
117
|
+
*
|
|
118
|
+
* In-memory rather than persisted because runNativeTurn reloads the transcript
|
|
119
|
+
* from disk on EVERY turn: without this the second turn of every session would
|
|
120
|
+
* be estimated from scratch. A process restart still loses it, which is the case
|
|
121
|
+
* the reactive backstop covers.
|
|
122
|
+
*
|
|
123
|
+
* `model` is the transcript model identity the anchor was measured under
|
|
124
|
+
* (P3 spec 8.3(d)); read it through `sessionAnchorFor`, never directly.
|
|
125
|
+
*/
|
|
126
|
+
readonly lastUsage: Map<string, SessionAnchor>;
|
|
127
|
+
/**
|
|
128
|
+
* Session name -> the system prompt sent as each round trip's top-level
|
|
129
|
+
* `system` (S3-4; the facade's `instructions`). nax opens without one. Same
|
|
130
|
+
* lifecycle as the maps above: set on open, cleared on close.
|
|
131
|
+
*/
|
|
132
|
+
readonly systemPrompts: Map<string, string>;
|
|
133
|
+
}
|
|
134
|
+
export declare function createNativeSessionState(): NativeSessionState;
|
|
105
135
|
export interface SessionAnchor {
|
|
106
136
|
readonly promptTokens: number;
|
|
107
137
|
readonly anchorIndex: number;
|
|
108
138
|
readonly model?: string;
|
|
109
139
|
}
|
|
110
|
-
export declare const nativeSessionLastUsage: Map<string, SessionAnchor>;
|
|
111
140
|
/**
|
|
112
141
|
* The persisted anchor a turn on `model` may use. Prefix stability is a
|
|
113
142
|
* property of (model, prefix) (P3 spec 3.3): an anchor measured under a
|
|
@@ -115,12 +144,23 @@ export declare const nativeSessionLastUsage: Map<string, SessionAnchor>;
|
|
|
115
144
|
* so it is dropped here rather than mis-sizing the next compaction decision.
|
|
116
145
|
* An entry or a turn with no model makes no claim — the store's own rule.
|
|
117
146
|
*/
|
|
118
|
-
export declare function sessionAnchorFor(sessionName: string, model: string | undefined): SessionAnchor | undefined;
|
|
147
|
+
export declare function sessionAnchorFor(state: NativeSessionState, sessionName: string, model: string | undefined): SessionAnchor | undefined;
|
|
119
148
|
/** Records how the session's latest turn ended, for `closeNativeSession`. */
|
|
120
|
-
export declare function markNativeTurnOutcome(sessionName: string, failed: boolean): void;
|
|
121
|
-
|
|
149
|
+
export declare function markNativeTurnOutcome(state: NativeSessionState, sessionName: string, failed: boolean): void;
|
|
150
|
+
/**
|
|
151
|
+
* The session's transcript. A state seeded with only a transcript directory
|
|
152
|
+
* (tests that drive `runNativeTurn` without opening) resolves to the file store
|
|
153
|
+
* over it; `openNativeSession` always records an entry, so production does not
|
|
154
|
+
* take that path.
|
|
155
|
+
*/
|
|
156
|
+
export declare function sessionTranscriptFor(state: NativeSessionState, sessionName: string): SessionTranscript | undefined;
|
|
157
|
+
/** The request field for the session's system prompt: `{ system }`, or `{}` when it has none. */
|
|
158
|
+
export declare function systemFieldFor(state: NativeSessionState, sessionName: string): {
|
|
159
|
+
readonly system?: string;
|
|
160
|
+
};
|
|
161
|
+
export declare function openNativeSession(state: NativeSessionState, name: string, opts: OpenSessionOpts): Promise<SessionHandle>;
|
|
122
162
|
/**
|
|
123
|
-
* Clear every
|
|
163
|
+
* Clear every state entry for one session name.
|
|
124
164
|
*
|
|
125
165
|
* Extracted so both close paths share one source of truth. `closeNativeSession`
|
|
126
166
|
* has a `SessionHandle`; the adapter's `closePhysicalSession` gets only the
|
|
@@ -129,11 +169,13 @@ export declare function openNativeSession(name: string, opts: OpenSessionOpts):
|
|
|
129
169
|
* is that name — but the signatures are not interchangeable, so this helper is
|
|
130
170
|
* string-keyed rather than reconstructed into a synthetic handle.
|
|
131
171
|
*/
|
|
132
|
-
export declare function clearNativeSessionState(sessionName: string): void;
|
|
172
|
+
export declare function clearNativeSessionState(state: NativeSessionState, sessionName: string): void;
|
|
133
173
|
/**
|
|
134
174
|
* Kept on failure, deleted on success. Every Phase B op is lifetime "fresh", so
|
|
135
|
-
* the transcript survives exactly when it is worth reading.
|
|
136
|
-
*
|
|
137
|
-
* `sessions/` directory down to `MAX_RETAINED_TRANSCRIPTS` (ADR-028 section 3)
|
|
175
|
+
* the transcript survives exactly when it is worth reading. Keeping is the
|
|
176
|
+
* store's `retainFailed`; the file store's also prunes the feature's
|
|
177
|
+
* `sessions/` directory down to `MAX_RETAINED_TRANSCRIPTS` (ADR-028 section 3),
|
|
178
|
+
* since the kept-on-failure set is otherwise unbounded. A session opened with
|
|
179
|
+
* `retainOnClose` skips both: its live document stays resumable.
|
|
138
180
|
*/
|
|
139
|
-
export declare function closeNativeSession(handle: SessionHandle, failed?: boolean): Promise<void>;
|
|
181
|
+
export declare function closeNativeSession(state: NativeSessionState, handle: SessionHandle, failed?: boolean): Promise<void>;
|