@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/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
- /** Per-turn driver factory (test seam). Called once per turn. */
2526
- makeDriver?: () => SessionDriver;
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
- declare function addMessage(sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[], attachments?: StoredAttachment[]): Promise<void>;
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 };