@alfe.ai/claude-code-host 0.1.14 → 0.1.15
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/dist/cli.js +1 -1
- package/dist/host-config.cjs +1153 -71
- package/dist/host-config.js +1065 -73
- package/dist/index.cjs +15 -0
- package/dist/index.d.cts +346 -43
- package/dist/index.d.ts +346 -43
- package/dist/index.js +2 -2
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_host_config = require("./host-config.cjs");
|
|
3
|
+
exports.AUTH_PROBE_TIMEOUT_MS = require_host_config.AUTH_PROBE_TIMEOUT_MS;
|
|
3
4
|
exports.BRIDGE_ENV_VAR = require_host_config.BRIDGE_ENV_VAR;
|
|
4
5
|
exports.CLAUDE_PERMISSION_MODES = require_host_config.CLAUDE_PERMISSION_MODES;
|
|
6
|
+
exports.CREDENTIAL_ENV_VAR = require_host_config.CREDENTIAL_ENV_VAR;
|
|
7
|
+
exports.CREDENTIAL_INVALID_MESSAGE = require_host_config.CREDENTIAL_INVALID_MESSAGE;
|
|
8
|
+
exports.CREDENTIAL_MISSING_MESSAGE = require_host_config.CREDENTIAL_MISSING_MESSAGE;
|
|
9
|
+
exports.CREDENTIAL_UNAVAILABLE_MESSAGE = require_host_config.CREDENTIAL_UNAVAILABLE_MESSAGE;
|
|
5
10
|
exports.ClaudeCodeHost = require_host_config.ClaudeCodeHost;
|
|
11
|
+
exports.CredentialStatusReporter = require_host_config.CredentialStatusReporter;
|
|
6
12
|
exports.DEFAULT_ALLOWED_TOOLS = require_host_config.DEFAULT_ALLOWED_TOOLS;
|
|
7
13
|
exports.DEFAULT_PERMISSION_MODE = require_host_config.DEFAULT_PERMISSION_MODE;
|
|
14
|
+
exports.LOCAL_LOGIN_FAILED_MESSAGE = require_host_config.LOCAL_LOGIN_FAILED_MESSAGE;
|
|
15
|
+
exports.MANAGED_ALLOWED_TOOLS = require_host_config.MANAGED_ALLOWED_TOOLS;
|
|
16
|
+
exports.MANAGED_DISALLOWED_TOOLS = require_host_config.MANAGED_DISALLOWED_TOOLS;
|
|
8
17
|
exports.SCRUBBED_ANTHROPIC_ENV_VARS = require_host_config.SCRUBBED_ANTHROPIC_ENV_VARS;
|
|
18
|
+
exports.STATUS_REPORT_INTERVAL_MS = require_host_config.STATUS_REPORT_INTERVAL_MS;
|
|
9
19
|
exports.SessionDriver = require_host_config.SessionDriver;
|
|
10
20
|
exports.TERMINATION_GRACE_MS = require_host_config.TERMINATION_GRACE_MS;
|
|
11
21
|
exports.TURN_TIMEOUT_MS = require_host_config.TURN_TIMEOUT_MS;
|
|
@@ -13,13 +23,18 @@ exports.WARM_STREAMING_SUPPORTED = require_host_config.WARM_STREAMING_SUPPORTED;
|
|
|
13
23
|
exports.addMessage = require_host_config.addMessage;
|
|
14
24
|
exports.appendActivity = require_host_config.appendActivity;
|
|
15
25
|
exports.buildChildEnv = require_host_config.buildChildEnv;
|
|
26
|
+
exports.buildProbeArgs = require_host_config.buildProbeArgs;
|
|
16
27
|
exports.collectTurnActivityRecords = require_host_config.collectTurnActivityRecords;
|
|
17
28
|
exports.createMapperState = require_host_config.createMapperState;
|
|
18
29
|
exports.createSession = require_host_config.createSession;
|
|
19
30
|
exports.getSession = require_host_config.getSession;
|
|
31
|
+
exports.isAuthFailureText = require_host_config.isAuthFailureText;
|
|
20
32
|
exports.listSessions = require_host_config.listSessions;
|
|
33
|
+
exports.loadCredentialWithRetry = require_host_config.loadCredentialWithRetry;
|
|
21
34
|
exports.mapStreamEvent = require_host_config.mapStreamEvent;
|
|
22
35
|
exports.mapStreamEvents = require_host_config.mapStreamEvents;
|
|
36
|
+
exports.parseProbeOutput = require_host_config.parseProbeOutput;
|
|
23
37
|
exports.resolveHostConfig = require_host_config.resolveHostConfig;
|
|
38
|
+
exports.runAuthProbe = require_host_config.runAuthProbe;
|
|
24
39
|
exports.saveSession = require_host_config.saveSession;
|
|
25
40
|
exports.stripAgentPrefix = require_host_config.stripAgentPrefix;
|
package/dist/index.d.cts
CHANGED
|
@@ -849,6 +849,33 @@ interface AgentWorkspaceInfo {
|
|
|
849
849
|
teamIds?: string[];
|
|
850
850
|
projectIds?: string[];
|
|
851
851
|
}
|
|
852
|
+
/**
|
|
853
|
+
* The agent's own Claude credential (claude-code runtime), from
|
|
854
|
+
* GET /agent/claude-credential. `oauth_token` is a Claude subscription token
|
|
855
|
+
* (`claude setup-token`) for `CLAUDE_CODE_OAUTH_TOKEN`; `api_key` is an
|
|
856
|
+
* Anthropic API key for `ANTHROPIC_API_KEY`. `revision` changes on every
|
|
857
|
+
* dashboard write. Hold the value in memory only — never log or persist it.
|
|
858
|
+
*/
|
|
859
|
+
interface AgentClaudeCredential {
|
|
860
|
+
kind: "oauth_token" | "api_key";
|
|
861
|
+
value: string;
|
|
862
|
+
revision: string;
|
|
863
|
+
updatedAt: string;
|
|
864
|
+
}
|
|
865
|
+
/** Claude auth health a claude-code host reports for its agent. */
|
|
866
|
+
type ClaudeCredentialHealth = "ok" | "missing" | "invalid";
|
|
867
|
+
/** Body of PUT /agent/claude-credential/status. */
|
|
868
|
+
interface ClaudeCredentialStatusReport {
|
|
869
|
+
status: ClaudeCredentialHealth;
|
|
870
|
+
/** Short (≤200 chars), fixed host-side explanation. Never `claude` output or a secret. */
|
|
871
|
+
detail?: string;
|
|
872
|
+
/**
|
|
873
|
+
* The credential revision the host checked (from `getClaudeCredential()`);
|
|
874
|
+
* omit when it found none. The server drops reports for a superseded
|
|
875
|
+
* revision and answers `{ accepted: false }`.
|
|
876
|
+
*/
|
|
877
|
+
revision?: string;
|
|
878
|
+
}
|
|
852
879
|
declare class WorkspaceApi extends ApiBase {
|
|
853
880
|
/**
|
|
854
881
|
* GET /agent/workspace — workspace config for the authenticated agent
|
|
@@ -865,6 +892,22 @@ declare class WorkspaceApi extends ApiBase {
|
|
|
865
892
|
}): Promise<{
|
|
866
893
|
files: Record<string, string>;
|
|
867
894
|
}>;
|
|
895
|
+
/**
|
|
896
|
+
* GET /agent/claude-credential — the authenticated claude-code agent's own
|
|
897
|
+
* Claude credential, or `null` when none is stored (a self-hosted host then
|
|
898
|
+
* falls back to the local `claude` login). Fetch on every runtime start: the
|
|
899
|
+
* daemon restarts the host whenever the credential changes.
|
|
900
|
+
*/
|
|
901
|
+
getClaudeCredential(): Promise<AgentClaudeCredential | null>;
|
|
902
|
+
/**
|
|
903
|
+
* PUT /agent/claude-credential/status — report whether this claude-code agent
|
|
904
|
+
* can authenticate to Claude. `accepted: false` means the report described a
|
|
905
|
+
* credential that has since been replaced; nothing was written. Not retried
|
|
906
|
+
* automatically (a mutation): the host re-reports on its next signal.
|
|
907
|
+
*/
|
|
908
|
+
reportClaudeCredentialStatus(report: ClaudeCredentialStatusReport): Promise<{
|
|
909
|
+
accepted: boolean;
|
|
910
|
+
}>;
|
|
868
911
|
}
|
|
869
912
|
//# sourceMappingURL=workspace.d.ts.map
|
|
870
913
|
//#endregion
|
|
@@ -2224,6 +2267,64 @@ declare class ChatServiceClient {
|
|
|
2224
2267
|
* Resolution priority: explicit overrides → config file → env vars.
|
|
2225
2268
|
*/
|
|
2226
2269
|
//#endregion
|
|
2270
|
+
//#region src/child-env.d.ts
|
|
2271
|
+
/**
|
|
2272
|
+
* Child-process environment construction for spawned `claude` CLI processes.
|
|
2273
|
+
*
|
|
2274
|
+
* THREE non-negotiable invariants:
|
|
2275
|
+
*
|
|
2276
|
+
* 1. **Scrub inherited Claude auth.** `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL`
|
|
2277
|
+
* / `ANTHROPIC_AUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN` are removed from BOTH
|
|
2278
|
+
* the parent env and any caller `extra`. A self-hosted Mac very often has
|
|
2279
|
+
* these set for other tooling; if one reaches `claude` it silently switches
|
|
2280
|
+
* accounts or billing plane (or routes through a proxy). The break is
|
|
2281
|
+
* silent: the turn "works" but bills the wrong account.
|
|
2282
|
+
*
|
|
2283
|
+
* 2. **Inject the agent's own credential, last.** When the agents service holds
|
|
2284
|
+
* a Claude credential for this agent, it is set AFTER the scrub as exactly
|
|
2285
|
+
* one variable: `CLAUDE_CODE_OAUTH_TOKEN` (subscription token from
|
|
2286
|
+
* `claude setup-token`) or `ANTHROPIC_API_KEY` (Anthropic API key). Nothing
|
|
2287
|
+
* else can override it. With no credential, no auth variable is set and
|
|
2288
|
+
* `claude` uses the machine's own login (self-hosted fallback). The value
|
|
2289
|
+
* lives only in the host's memory and this env — never on disk, never logged.
|
|
2290
|
+
*
|
|
2291
|
+
* 3. **Set `ALFE_CLAUDE_BRIDGE=1`.** The Phase-1 CLI hooks
|
|
2292
|
+
* (`alfe claude hook ...` in `packages/cli/src/commands/claude.ts`) check
|
|
2293
|
+
* `process.env.ALFE_CLAUDE_BRIDGE` FIRST and no-op when set, so the host —
|
|
2294
|
+
* not the interactive-session hooks — owns memory recall/capture for bridged
|
|
2295
|
+
* turns. Omitting this double-captures every message. The env var NAME is a
|
|
2296
|
+
* cross-phase contract; do not rename without updating the CLI.
|
|
2297
|
+
*/
|
|
2298
|
+
/** Claude auth env vars that MUST NOT be inherited by a `claude` child. */
|
|
2299
|
+
declare const SCRUBBED_ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"];
|
|
2300
|
+
/**
|
|
2301
|
+
* Provider-selection and endpoint overrides that would route `claude` away from
|
|
2302
|
+
* the Anthropic API. Dropped whenever an agent credential is injected, so an
|
|
2303
|
+
* inherited `CLAUDE_CODE_USE_BEDROCK=1` (say) can't send the turn to Bedrock
|
|
2304
|
+
* and ignore the stored credential. Without a credential they are left alone:
|
|
2305
|
+
* the machine's own `claude` setup is the fallback, whatever provider it uses.
|
|
2306
|
+
*/
|
|
2307
|
+
|
|
2308
|
+
/** Cross-phase contract marker (see header + packages/cli/src/commands/claude.ts). */
|
|
2309
|
+
declare const BRIDGE_ENV_VAR = "ALFE_CLAUDE_BRIDGE";
|
|
2310
|
+
/** The agent's Claude credential, as fetched from `GET /agent/claude-credential`. */
|
|
2311
|
+
interface ChildCredential {
|
|
2312
|
+
kind: 'oauth_token' | 'api_key';
|
|
2313
|
+
value: string;
|
|
2314
|
+
}
|
|
2315
|
+
/** The single env var each credential kind is exported as. */
|
|
2316
|
+
declare const CREDENTIAL_ENV_VAR: Record<ChildCredential['kind'], string>;
|
|
2317
|
+
/**
|
|
2318
|
+
* Build the env handed to a spawned `claude` child. Pure — takes the base env
|
|
2319
|
+
* explicitly so it unit-tests without touching `process.env`.
|
|
2320
|
+
*
|
|
2321
|
+
* Order: merge `base` + `extra` → drop every scrubbed var (plus provider
|
|
2322
|
+
* overrides when a credential is injected) → set the bridge
|
|
2323
|
+
* marker → set the agent credential (if any). Neither `base` nor `extra` can
|
|
2324
|
+
* smuggle an auth variable in or override the injected credential.
|
|
2325
|
+
*/
|
|
2326
|
+
declare function buildChildEnv(base: NodeJS.ProcessEnv, extra?: Record<string, string>, credential?: ChildCredential | null): NodeJS.ProcessEnv;
|
|
2327
|
+
//#endregion
|
|
2227
2328
|
//#region src/stream-mapper.d.ts
|
|
2228
2329
|
/**
|
|
2229
2330
|
* Claude Code `--output-format stream-json` → Alfe chat-activity mapper.
|
|
@@ -2314,6 +2415,12 @@ interface EndEmission {
|
|
|
2314
2415
|
durationMs?: number;
|
|
2315
2416
|
/** Error reason (present when `isError`). Goes under `payload.message` (C3). */
|
|
2316
2417
|
errorMessage?: string;
|
|
2418
|
+
/**
|
|
2419
|
+
* True when Claude refused the credential (assistant `error:
|
|
2420
|
+
* "authentication_failed"`, a 401/403 `api_error_status`, or a known auth
|
|
2421
|
+
* message). Present only on error turns.
|
|
2422
|
+
*/
|
|
2423
|
+
authFailed?: true;
|
|
2317
2424
|
}
|
|
2318
2425
|
type MapperEmission = ActivityEmission | TextEmission | EndEmission;
|
|
2319
2426
|
interface PendingTool {
|
|
@@ -2333,6 +2440,8 @@ interface MapperState {
|
|
|
2333
2440
|
pendingTools: Map<string, PendingTool>;
|
|
2334
2441
|
/** Whether the terminal `result` event has been seen. */
|
|
2335
2442
|
ended: boolean;
|
|
2443
|
+
/** Set by an assistant `error: "authentication_failed"` frame. */
|
|
2444
|
+
authFailed: boolean;
|
|
2336
2445
|
}
|
|
2337
2446
|
declare function createMapperState(): MapperState;
|
|
2338
2447
|
/**
|
|
@@ -2382,6 +2491,26 @@ declare const WARM_STREAMING_SUPPORTED = false;
|
|
|
2382
2491
|
* shell. Callers that want a wider set pass `allowedTools` explicitly.
|
|
2383
2492
|
*/
|
|
2384
2493
|
declare const DEFAULT_ALLOWED_TOOLS: readonly ["Read", "Grep", "Glob", "LS", "Edit", "Write", "WebFetch", "WebSearch", "Task", "TodoWrite"];
|
|
2494
|
+
/**
|
|
2495
|
+
* Managed VMs (`ALFE_MANAGED=1|true`) add `Bash` to the allow-list. The VM is
|
|
2496
|
+
* the agent's own machine (no user data beside the agent's workspace), and
|
|
2497
|
+
* shell access is what makes a managed coding agent useful. Still
|
|
2498
|
+
* `--permission-mode dontAsk` (never bypass), and the managed VM runs as root:
|
|
2499
|
+
* claude refuses only `--dangerously-skip-permissions` under root, not
|
|
2500
|
+
* `dontAsk`. `Edit`/`Write` are already in the default set; `MultiEdit` is not
|
|
2501
|
+
* a tool in the pinned claude (2.1.x), so it is not listed.
|
|
2502
|
+
*/
|
|
2503
|
+
declare const MANAGED_ALLOWED_TOOLS: readonly ["Read", "Grep", "Glob", "LS", "Edit", "Write", "WebFetch", "WebSearch", "Task", "TodoWrite", "Bash"];
|
|
2504
|
+
/**
|
|
2505
|
+
* Deny rules layered on the managed allow-list (`--disallowedTools`, which
|
|
2506
|
+
* wins over `--allowedTools`). They keep the agent from re-running Google
|
|
2507
|
+
* Workspace CLI auth and from reading its token directory with the Read tool.
|
|
2508
|
+
* They are NOT a security boundary: with Bash allowed, `cat`, `cp`, `env` and
|
|
2509
|
+
* friends can still read anything the root user can. Verified on claude
|
|
2510
|
+
* 2.1.284: a `Bash(<prefix>:*)` rule denies the matching command under
|
|
2511
|
+
* `dontAsk` while other Bash commands run.
|
|
2512
|
+
*/
|
|
2513
|
+
declare const MANAGED_DISALLOWED_TOOLS: readonly ["Bash(gws auth)", "Bash(gws auth:*)", "Read(~/.config/gws-*/**)"];
|
|
2385
2514
|
declare const CLAUDE_PERMISSION_MODES: readonly ["acceptEdits", "auto", "manual", "dontAsk", "plan"];
|
|
2386
2515
|
type ClaudePermissionMode = (typeof CLAUDE_PERMISSION_MODES)[number];
|
|
2387
2516
|
/**
|
|
@@ -2404,6 +2533,8 @@ interface DriverTurnResult {
|
|
|
2404
2533
|
errorMessage?: string;
|
|
2405
2534
|
/** True if the turn was aborted via `abort()` (SIGINT). */
|
|
2406
2535
|
aborted: boolean;
|
|
2536
|
+
/** Present (true) when the turn failed because Claude refused the credential. */
|
|
2537
|
+
authFailed?: true;
|
|
2407
2538
|
}
|
|
2408
2539
|
interface RunTurnParams {
|
|
2409
2540
|
/** The user prompt for this turn. */
|
|
@@ -2449,12 +2580,21 @@ declare class SessionDriver {
|
|
|
2449
2580
|
private abortCurrent;
|
|
2450
2581
|
private readonly bin;
|
|
2451
2582
|
private readonly baseEnv;
|
|
2583
|
+
private readonly credential;
|
|
2584
|
+
private readonly managed;
|
|
2452
2585
|
private readonly log;
|
|
2453
2586
|
/** Injected spawn — tests replace this with a fake child. */
|
|
2454
2587
|
private readonly spawnImpl;
|
|
2455
2588
|
constructor(opts?: {
|
|
2456
2589
|
claudeBin?: string;
|
|
2457
2590
|
baseEnv?: NodeJS.ProcessEnv;
|
|
2591
|
+
/**
|
|
2592
|
+
* The agent's Claude credential, injected into the child env after the
|
|
2593
|
+
* scrub. Null/absent → the machine's own `claude` login.
|
|
2594
|
+
*/
|
|
2595
|
+
credential?: ChildCredential | null;
|
|
2596
|
+
/** Managed VM: Bash allowed + deny rules (see MANAGED_ALLOWED_TOOLS). */
|
|
2597
|
+
managed?: boolean;
|
|
2458
2598
|
logger?: Logger;
|
|
2459
2599
|
/** Test seam: inject a fake `spawn`. */
|
|
2460
2600
|
__spawn?: typeof spawn;
|
|
@@ -2475,6 +2615,89 @@ declare class SessionDriver {
|
|
|
2475
2615
|
private runPerTurn;
|
|
2476
2616
|
}
|
|
2477
2617
|
//#endregion
|
|
2618
|
+
//#region src/credential.d.ts
|
|
2619
|
+
interface HostCredential extends ChildCredential {
|
|
2620
|
+
/** Non-secret revision; travels with status reports as the fence. */
|
|
2621
|
+
revision: string;
|
|
2622
|
+
}
|
|
2623
|
+
interface LoadCredentialOptions {
|
|
2624
|
+
/** Aborted on host stop; the loop then resolves `null`. */
|
|
2625
|
+
signal: AbortSignal;
|
|
2626
|
+
log: Logger;
|
|
2627
|
+
baseDelayMs?: number;
|
|
2628
|
+
maxDelayMs?: number;
|
|
2629
|
+
/** Jitter source in [0,1]; the delay is `cap * (0.5 + random/2)`. */
|
|
2630
|
+
random?: () => number;
|
|
2631
|
+
sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Fetch until it answers. `{ credential: null }` = nothing stored (a real
|
|
2635
|
+
* answer); `null` = aborted before any answer.
|
|
2636
|
+
*/
|
|
2637
|
+
declare function loadCredentialWithRetry(fetchCredential: () => Promise<HostCredential | null>, opts: LoadCredentialOptions): Promise<{
|
|
2638
|
+
credential: HostCredential | null;
|
|
2639
|
+
} | null>;
|
|
2640
|
+
declare function isAuthFailureText(text: string): boolean;
|
|
2641
|
+
/** HTTP statuses on a `result` event that mean "credential refused". */
|
|
2642
|
+
|
|
2643
|
+
type ProbeOutcome = {
|
|
2644
|
+
result: 'ok';
|
|
2645
|
+
} | {
|
|
2646
|
+
result: 'invalid';
|
|
2647
|
+
} | {
|
|
2648
|
+
result: 'unknown';
|
|
2649
|
+
reason: string;
|
|
2650
|
+
};
|
|
2651
|
+
declare const AUTH_PROBE_TIMEOUT_MS = 60000;
|
|
2652
|
+
/** argv for the probe. No tools, no session file, no MCP, no settings files. */
|
|
2653
|
+
declare function buildProbeArgs(): string[];
|
|
2654
|
+
declare function parseProbeOutput(stdout: string, stderr: string, exitCode: number | null): ProbeOutcome;
|
|
2655
|
+
interface AuthProbeOptions {
|
|
2656
|
+
credential: ChildCredential | null;
|
|
2657
|
+
baseEnv: NodeJS.ProcessEnv;
|
|
2658
|
+
/** Any existing directory; the probe reads and writes nothing there. */
|
|
2659
|
+
cwd: string;
|
|
2660
|
+
claudeBin?: string;
|
|
2661
|
+
timeoutMs?: number;
|
|
2662
|
+
spawnImpl?: typeof spawn;
|
|
2663
|
+
/** Host stop: the child is SIGKILLed and the probe settles `unknown`. */
|
|
2664
|
+
signal?: AbortSignal;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* One minimal real turn against Anthropic with the agent's credential (or,
|
|
2668
|
+
* when none is stored, the machine's own login). Never throws.
|
|
2669
|
+
*/
|
|
2670
|
+
declare function runAuthProbe(opts: AuthProbeOptions): Promise<ProbeOutcome>;
|
|
2671
|
+
interface CredentialStatusReport {
|
|
2672
|
+
status: 'ok' | 'missing' | 'invalid';
|
|
2673
|
+
/** Fixed, host-authored text only. Never `claude` output. */
|
|
2674
|
+
detail?: string;
|
|
2675
|
+
revision?: string;
|
|
2676
|
+
}
|
|
2677
|
+
/** An unchanged status is re-sent at most this often (a liveness signal). */
|
|
2678
|
+
declare const STATUS_REPORT_INTERVAL_MS: number;
|
|
2679
|
+
/**
|
|
2680
|
+
* Sends health reports on change, or when the same report is older than
|
|
2681
|
+
* {@link STATUS_REPORT_INTERVAL_MS}. Sends are serialized; a failed send is
|
|
2682
|
+
* not remembered, so the next signal retries. Never throws.
|
|
2683
|
+
*/
|
|
2684
|
+
declare class CredentialStatusReporter {
|
|
2685
|
+
private readonly send;
|
|
2686
|
+
private lastKey;
|
|
2687
|
+
private lastSentAt;
|
|
2688
|
+
private chain;
|
|
2689
|
+
private readonly now;
|
|
2690
|
+
private readonly log;
|
|
2691
|
+
constructor(send: (report: CredentialStatusReport) => Promise<{
|
|
2692
|
+
accepted: boolean;
|
|
2693
|
+
}>, opts: {
|
|
2694
|
+
log: Logger;
|
|
2695
|
+
now?: () => number;
|
|
2696
|
+
});
|
|
2697
|
+
report(report: CredentialStatusReport): Promise<void>;
|
|
2698
|
+
private deliver;
|
|
2699
|
+
}
|
|
2700
|
+
//#endregion
|
|
2478
2701
|
//#region src/host.d.ts
|
|
2479
2702
|
interface ClaudeCodeHostConfig {
|
|
2480
2703
|
/** Chat service WS URL (e.g. wss://chat.dev.alfe.ai/ws). */
|
|
@@ -2496,8 +2719,20 @@ interface ClaudeCodeHostConfig {
|
|
|
2496
2719
|
* NEVER provider-qualified (`anthropic/…`) — that's an OpenClaw-only shape.
|
|
2497
2720
|
*/
|
|
2498
2721
|
model?: string;
|
|
2722
|
+
/**
|
|
2723
|
+
* Managed VM (`ALFE_MANAGED=1|true`, set by the daemon). Enables Bash, and a
|
|
2724
|
+
* missing Claude credential is a hard "missing" state (no local login to
|
|
2725
|
+
* fall back to). Self-hosted when false/absent.
|
|
2726
|
+
*/
|
|
2727
|
+
managed?: boolean;
|
|
2499
2728
|
logger?: Logger;
|
|
2500
2729
|
}
|
|
2730
|
+
declare const CREDENTIAL_INVALID_MESSAGE = "This agent's Claude credential is invalid or expired. Update it in agent settings.";
|
|
2731
|
+
declare const LOCAL_LOGIN_FAILED_MESSAGE = "Claude Code is not signed in on this machine. Run `claude` and log in, or add a Claude credential in agent settings.";
|
|
2732
|
+
declare const CREDENTIAL_MISSING_MESSAGE = "This agent has no Claude credential. Add a Claude subscription token or an Anthropic API key in agent settings.";
|
|
2733
|
+
declare const CREDENTIAL_UNAVAILABLE_MESSAGE = "This agent's Claude credential couldn't be loaded yet. Try again in a moment.";
|
|
2734
|
+
/** How long a turn waits for the start-up credential fetch before failing. */
|
|
2735
|
+
|
|
2501
2736
|
declare class ClaudeCodeHost {
|
|
2502
2737
|
private readonly chat;
|
|
2503
2738
|
/**
|
|
@@ -2521,14 +2756,69 @@ declare class ClaudeCodeHost {
|
|
|
2521
2756
|
private claudeSessionSave;
|
|
2522
2757
|
private stopping;
|
|
2523
2758
|
private stopPromise;
|
|
2759
|
+
/**
|
|
2760
|
+
* Resolves once the first fetch has answered. Null until `start()` (the
|
|
2761
|
+
* direct handleRequest test seam runs on the local login, no wait).
|
|
2762
|
+
*/
|
|
2763
|
+
private credentialReady;
|
|
2764
|
+
private resolveCredentialReady;
|
|
2765
|
+
/** Turns wait for the first fetch only until this time (start + wait). */
|
|
2766
|
+
private credentialDeadline;
|
|
2767
|
+
private credentialLoaded;
|
|
2768
|
+
/** Turns waiting for the credential, before queue admission (chat.abort). */
|
|
2769
|
+
private readonly waitingTurns;
|
|
2770
|
+
private readonly loaderRestartMs;
|
|
2771
|
+
/** In memory only. Never logged, never persisted. */
|
|
2772
|
+
private credential;
|
|
2773
|
+
private readonly credentialAbort;
|
|
2774
|
+
private readonly reporter;
|
|
2775
|
+
private readonly probe;
|
|
2776
|
+
private readonly credentialWaitMs;
|
|
2777
|
+
private readonly credentialRetry;
|
|
2524
2778
|
constructor(cfg: ClaudeCodeHostConfig, deps?: {
|
|
2525
|
-
/**
|
|
2526
|
-
|
|
2779
|
+
/**
|
|
2780
|
+
* Per-turn driver factory (test seam). Called once per turn with the
|
|
2781
|
+
* agent's credential (null → the machine's own `claude` login).
|
|
2782
|
+
*/
|
|
2783
|
+
makeDriver?: (credential: ChildCredential | null) => SessionDriver;
|
|
2527
2784
|
chat?: ChatServiceClient;
|
|
2528
2785
|
api?: AgentApiClient;
|
|
2786
|
+
/** Start-up auth check (test seam; default: a minimal `claude -p`). */
|
|
2787
|
+
probe?: (credential: ChildCredential | null, signal: AbortSignal) => Promise<ProbeOutcome>;
|
|
2788
|
+
credentialWaitMs?: number;
|
|
2789
|
+
/** Delay before restarting a credential loader that threw (default 5 s). */
|
|
2790
|
+
loaderRestartMs?: number;
|
|
2791
|
+
credentialRetry?: {
|
|
2792
|
+
baseDelayMs?: number;
|
|
2793
|
+
maxDelayMs?: number;
|
|
2794
|
+
};
|
|
2529
2795
|
});
|
|
2530
2796
|
/** Start the chat WS. Non-blocking — the client reconnects internally. */
|
|
2531
2797
|
start(): Promise<void>;
|
|
2798
|
+
/**
|
|
2799
|
+
* Run the fetch + check, restarting it after an unexpected throw so a bug
|
|
2800
|
+
* can never leave the host without a credential until the next restart.
|
|
2801
|
+
*/
|
|
2802
|
+
private runCredentialLoader;
|
|
2803
|
+
private loadCredential;
|
|
2804
|
+
private childCredential;
|
|
2805
|
+
/** Start-up health check: one minimal real call, reported to the cloud. */
|
|
2806
|
+
private checkCredential;
|
|
2807
|
+
/** Report the credential (or the local login) as working or refused. */
|
|
2808
|
+
private reportAuth;
|
|
2809
|
+
/**
|
|
2810
|
+
* Wait for the first credential answer BEFORE queue admission, so a waiting
|
|
2811
|
+
* turn holds no slot. Bounded by the start-up deadline: once it has passed,
|
|
2812
|
+
* later turns stop waiting until the load completes. Cancelled by
|
|
2813
|
+
* `chat.abort` or host stop through `cancellation`.
|
|
2814
|
+
*/
|
|
2815
|
+
private awaitCredential;
|
|
2816
|
+
/**
|
|
2817
|
+
* Instant credential gate inside the turn (no waiting: `handleAgent` already
|
|
2818
|
+
* waited). Returns a user-facing failure message, or null when the turn may
|
|
2819
|
+
* run. A host driven without `start()` runs on the local login.
|
|
2820
|
+
*/
|
|
2821
|
+
private credentialGateMessage;
|
|
2532
2822
|
/** Stop admission, abort owned children, and give active turns time to settle. */
|
|
2533
2823
|
stop(): Promise<void>;
|
|
2534
2824
|
/**
|
|
@@ -2539,6 +2829,27 @@ declare class ClaudeCodeHost {
|
|
|
2539
2829
|
private handleAgent;
|
|
2540
2830
|
private deliverAgentOutcome;
|
|
2541
2831
|
private runAgentTurn;
|
|
2832
|
+
/**
|
|
2833
|
+
* Send every message the memory service has not accepted yet. Indices are
|
|
2834
|
+
* the messages' positions in the session file, so they advance across turns
|
|
2835
|
+
* and survive host restarts; the per-conversation `ingestEpoch` tells the
|
|
2836
|
+
* service when that index space restarted. Ingest under the BARE session
|
|
2837
|
+
* id: the same key the session-store filename and the memory backfill use,
|
|
2838
|
+
* so there is one memory lineage.
|
|
2839
|
+
*
|
|
2840
|
+
* Cursor rules:
|
|
2841
|
+
* - success: advance through the batch. If the server trimmed the batch
|
|
2842
|
+
* (`messageCount` < sent) it kept the NEWEST messages and its watermark
|
|
2843
|
+
* moved past the dropped head, so a resend would be filtered anyway;
|
|
2844
|
+
* advance and warn. The byte bounds keep this path unreachable normally.
|
|
2845
|
+
* - non-retryable 4xx (400/413/422): skip only the oldest message, so one
|
|
2846
|
+
* poison message cannot wedge the conversation.
|
|
2847
|
+
* - anything else (429, 5xx, network): leave the cursor; the next turn
|
|
2848
|
+
* resends.
|
|
2849
|
+
*/
|
|
2850
|
+
private ingestPendingMemory;
|
|
2851
|
+
/** Regenerate CLAUDE.md from the workspace persona files. Never fatal. */
|
|
2852
|
+
private syncPersona;
|
|
2542
2853
|
/**
|
|
2543
2854
|
* Run a turn; on a `--resume` failure (stale/missing claude session id) start
|
|
2544
2855
|
* fresh and prepend the last-N stored messages so the model keeps context.
|
|
@@ -2559,45 +2870,6 @@ declare class ClaudeCodeHost {
|
|
|
2559
2870
|
/** Strip the `agent:{agentId}:` prefix from a full server session key. */
|
|
2560
2871
|
declare function stripAgentPrefix(sessionKey: string): string;
|
|
2561
2872
|
//#endregion
|
|
2562
|
-
//#region src/child-env.d.ts
|
|
2563
|
-
/**
|
|
2564
|
-
* Child-process environment construction for spawned `claude` CLI processes.
|
|
2565
|
-
*
|
|
2566
|
-
* TWO non-negotiable invariants (Phase 2 plan, audit B2 + the hard
|
|
2567
|
-
* constraints):
|
|
2568
|
-
*
|
|
2569
|
-
* 1. **Scrub `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` /
|
|
2570
|
-
* `ANTHROPIC_AUTH_TOKEN`.** A self-hosted Mac very often has these set (for
|
|
2571
|
-
* other tooling). If ANY of them reaches `claude`, the CLI silently switches
|
|
2572
|
-
* from the user's Claude subscription (OAuth) to API-key/proxy auth — which
|
|
2573
|
-
* is exactly what this whole runtime exists to avoid (no Agent SDK, no
|
|
2574
|
-
* proxy, subscription only). The break is SILENT: the turn "works" but bills
|
|
2575
|
-
* the wrong plane. So we strip them unconditionally from the child env.
|
|
2576
|
-
*
|
|
2577
|
-
* 2. **Set `ALFE_CLAUDE_BRIDGE=1`.** The Phase-1 CLI hooks
|
|
2578
|
-
* (`alfe claude hook ...` in `packages/cli/src/commands/claude.ts`) check
|
|
2579
|
-
* `process.env.ALFE_CLAUDE_BRIDGE` FIRST and no-op when set, so the host —
|
|
2580
|
-
* not the interactive-session hooks — owns memory recall/capture for bridged
|
|
2581
|
-
* turns. Omitting this double-captures every message. The env var NAME is a
|
|
2582
|
-
* cross-phase contract; do not rename without updating the CLI.
|
|
2583
|
-
*/
|
|
2584
|
-
/** Anthropic env vars that force API-key / proxy auth and MUST be scrubbed. */
|
|
2585
|
-
declare const SCRUBBED_ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"];
|
|
2586
|
-
/** Cross-phase contract marker (see header + packages/cli/src/commands/claude.ts). */
|
|
2587
|
-
declare const BRIDGE_ENV_VAR = "ALFE_CLAUDE_BRIDGE";
|
|
2588
|
-
/**
|
|
2589
|
-
* Build the env handed to a spawned `claude` child. Pure — takes the base env
|
|
2590
|
-
* explicitly so it unit-tests without touching `process.env`.
|
|
2591
|
-
*
|
|
2592
|
-
* - Every scrubbed Anthropic var is DELETED (not blanked) so `claude`'s
|
|
2593
|
-
* env-detection treats it as absent.
|
|
2594
|
-
* - `ALFE_CLAUDE_BRIDGE=1` is set.
|
|
2595
|
-
* - Any caller-supplied `extra` entries are layered last (e.g. a per-turn
|
|
2596
|
-
* marker) but can NEVER re-introduce a scrubbed var — the scrub runs after
|
|
2597
|
-
* the merge as the final gate.
|
|
2598
|
-
*/
|
|
2599
|
-
declare function buildChildEnv(base: NodeJS.ProcessEnv, extra?: Record<string, string>): NodeJS.ProcessEnv;
|
|
2600
|
-
//#endregion
|
|
2601
2873
|
//#region src/session/session-store.d.ts
|
|
2602
2874
|
type StoredMessageComponent = {
|
|
2603
2875
|
type: 'link_button';
|
|
@@ -2677,7 +2949,25 @@ interface SessionData {
|
|
|
2677
2949
|
* pre-persistence files.
|
|
2678
2950
|
*/
|
|
2679
2951
|
activity?: ChatActivityRecord[];
|
|
2952
|
+
/**
|
|
2953
|
+
* Alfe memory ingest cursor. Message indices are POSITIONS in `messages`
|
|
2954
|
+
* (append-only, never trimmed), so they are stable across host restarts and
|
|
2955
|
+
* match the index space the memory backfill uses for these files.
|
|
2956
|
+
* `ingestEpoch` is stamped once per conversation lineage (when this file is
|
|
2957
|
+
* created) and tells the memory service the index space restarted, e.g.
|
|
2958
|
+
* after the 30-day cleanup deleted the file and the conversation resumed.
|
|
2959
|
+
* Absent on files written before the cursor existed.
|
|
2960
|
+
*/
|
|
2961
|
+
memory?: SessionMemoryCursor;
|
|
2962
|
+
}
|
|
2963
|
+
interface SessionMemoryCursor {
|
|
2964
|
+
/** Per-conversation-lineage epoch sent as `ingestEpoch`. */
|
|
2965
|
+
ingestEpoch: number;
|
|
2966
|
+
/** Highest message index the memory service has accepted (-1 = none). */
|
|
2967
|
+
lastIngestedIndex: number;
|
|
2680
2968
|
}
|
|
2969
|
+
/** One message as sent to `memoryIngest`. */
|
|
2970
|
+
|
|
2681
2971
|
interface SessionSummary {
|
|
2682
2972
|
sessionId: string;
|
|
2683
2973
|
agentId: string;
|
|
@@ -2692,7 +2982,20 @@ interface SessionSummary {
|
|
|
2692
2982
|
declare function getSession(sessionId: string): Promise<SessionData | null>;
|
|
2693
2983
|
declare function saveSession(session: SessionData): Promise<void>;
|
|
2694
2984
|
declare function createSession(sessionId: string, agentId: string, channel: string, tenantId?: string, userId?: string): Promise<SessionData>;
|
|
2695
|
-
|
|
2985
|
+
/**
|
|
2986
|
+
* Append a message. Returns its index (position in `messages`), the stable
|
|
2987
|
+
* memory-ingest index, or -1 when the session does not exist.
|
|
2988
|
+
*/
|
|
2989
|
+
declare function addMessage(sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[], attachments?: StoredAttachment[]): Promise<number>;
|
|
2990
|
+
/**
|
|
2991
|
+
* Memory ingest bounds, in serialised UTF-8 bytes. The ingest endpoint
|
|
2992
|
+
* forwards the whole body as ONE SQS message and trims the OLDEST messages
|
|
2993
|
+
* past 250,000 bytes (`services/memory/api-agents/ingest/fit-batch.ts`); a
|
|
2994
|
+
* trimmed head is lost for good because the consumer's watermark jumps past
|
|
2995
|
+
* it. Staying well under that limit means the server never trims. Bytes, not
|
|
2996
|
+
* characters: one CJK character is three UTF-8 bytes.
|
|
2997
|
+
*/
|
|
2998
|
+
|
|
2696
2999
|
/**
|
|
2697
3000
|
* Append a turn's terminalized activity records. Called ONCE per turn (from the
|
|
2698
3001
|
* dispatch `finally`) — never per-delta.
|
|
@@ -2712,4 +3015,4 @@ declare function listSessions(filters?: {
|
|
|
2712
3015
|
*/
|
|
2713
3016
|
declare function resolveHostConfig(): ClaudeCodeHostConfig;
|
|
2714
3017
|
//#endregion
|
|
2715
|
-
export { type ActivityEmission, BRIDGE_ENV_VAR, CLAUDE_PERMISSION_MODES, type ChatActivityRecord, type ChatMessage, ClaudeCodeHost, type ClaudeCodeHostConfig, type ClaudePermissionMode, DEFAULT_ALLOWED_TOOLS, DEFAULT_PERMISSION_MODE, type DriverTurnResult, type EndEmission, type MapperEmission, type MapperState, type RunTurnParams, SCRUBBED_ANTHROPIC_ENV_VARS, type SessionData, SessionDriver, type SessionSummary, TERMINATION_GRACE_MS, TURN_TIMEOUT_MS, type TextEmission, type ThinkingActivity, type ToolActivity, WARM_STREAMING_SUPPORTED, addMessage, appendActivity, buildChildEnv, collectTurnActivityRecords, createMapperState, createSession, getSession, listSessions, mapStreamEvent, mapStreamEvents, resolveHostConfig, saveSession, stripAgentPrefix };
|
|
3018
|
+
export { AUTH_PROBE_TIMEOUT_MS, type ActivityEmission, BRIDGE_ENV_VAR, CLAUDE_PERMISSION_MODES, CREDENTIAL_ENV_VAR, CREDENTIAL_INVALID_MESSAGE, CREDENTIAL_MISSING_MESSAGE, CREDENTIAL_UNAVAILABLE_MESSAGE, type ChatActivityRecord, type ChatMessage, type ChildCredential, ClaudeCodeHost, type ClaudeCodeHostConfig, type ClaudePermissionMode, type CredentialStatusReport, CredentialStatusReporter, DEFAULT_ALLOWED_TOOLS, DEFAULT_PERMISSION_MODE, type DriverTurnResult, type EndEmission, type HostCredential, LOCAL_LOGIN_FAILED_MESSAGE, MANAGED_ALLOWED_TOOLS, MANAGED_DISALLOWED_TOOLS, type MapperEmission, type MapperState, type ProbeOutcome, type RunTurnParams, SCRUBBED_ANTHROPIC_ENV_VARS, STATUS_REPORT_INTERVAL_MS, type SessionData, SessionDriver, type SessionSummary, TERMINATION_GRACE_MS, TURN_TIMEOUT_MS, type TextEmission, type ThinkingActivity, type ToolActivity, WARM_STREAMING_SUPPORTED, addMessage, appendActivity, buildChildEnv, buildProbeArgs, collectTurnActivityRecords, createMapperState, createSession, getSession, isAuthFailureText, listSessions, loadCredentialWithRetry, mapStreamEvent, mapStreamEvents, parseProbeOutput, resolveHostConfig, runAuthProbe, saveSession, stripAgentPrefix };
|