@alfe.ai/claude-code-host 0.1.15 → 0.1.16

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.d.ts CHANGED
@@ -862,11 +862,21 @@ interface AgentClaudeCredential {
862
862
  revision: string;
863
863
  updatedAt: string;
864
864
  }
865
- /** Claude auth health a claude-code host reports for its agent. */
865
+ /**
866
+ * Claude auth health a claude-code host reports for its agent. `missing` is
867
+ * legacy: hosts since the AI-proxy fallback (2026-10-15) never send it.
868
+ */
866
869
  type ClaudeCredentialHealth = "ok" | "missing" | "invalid";
870
+ /**
871
+ * Which auth the host's turns run on: the stored credential, the machine's
872
+ * own `claude` login (self-hosted only), or the Alfe AI proxy (Alfe credits).
873
+ */
874
+ type ClaudeCredentialSource = "credential" | "local_login" | "ai_proxy";
867
875
  /** Body of PUT /agent/claude-credential/status. */
868
876
  interface ClaudeCredentialStatusReport {
869
877
  status: ClaudeCredentialHealth;
878
+ /** The auth source the status describes. Omitted only by older hosts. */
879
+ source?: ClaudeCredentialSource;
870
880
  /** Short (≤200 chars), fixed host-side explanation. Never `claude` output or a secret. */
871
881
  detail?: string;
872
882
  /**
@@ -894,8 +904,8 @@ declare class WorkspaceApi extends ApiBase {
894
904
  }>;
895
905
  /**
896
906
  * 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
907
+ * Claude credential, or `null` when none is stored (the host then falls back
908
+ * to the local `claude` login when self-hosted, else the Alfe AI proxy). Fetch on every runtime start: the
899
909
  * daemon restarts the host whenever the credential changes.
900
910
  */
901
911
  getClaudeCredential(): Promise<AgentClaudeCredential | null>;
@@ -2271,22 +2281,35 @@ declare class ChatServiceClient {
2271
2281
  /**
2272
2282
  * Child-process environment construction for spawned `claude` CLI processes.
2273
2283
  *
2274
- * THREE non-negotiable invariants:
2284
+ * Every turn runs on exactly ONE auth source (`ChildAuth`), resolved by the
2285
+ * host (see DEVELOPING.md "Auth source resolution"):
2286
+ *
2287
+ * - `credential` — the agent's stored Claude credential, set as exactly one
2288
+ * variable: `CLAUDE_CODE_OAUTH_TOKEN` (subscription token from
2289
+ * `claude setup-token`) or `ANTHROPIC_API_KEY` (Anthropic API key).
2290
+ * - `local_login` — self-hosted only: no auth variable, so `claude` uses the
2291
+ * machine's own login (and whatever provider the operator configured).
2292
+ * - `ai_proxy` — the Alfe AI proxy via the daemon's loopback proxy:
2293
+ * `ANTHROPIC_BASE_URL` = the local proxy, `ANTHROPIC_AUTH_TOKEN` = a
2294
+ * placeholder the local proxy replaces with the daemon's agent key, and
2295
+ * every model alias pinned to an Alfe registry id (the proxy routes exact
2296
+ * registry ids only; the CLI's built-in aliases resolve to dated ids that
2297
+ * are a 409 there).
2298
+ *
2299
+ * Invariants:
2275
2300
  *
2276
2301
  * 1. **Scrub inherited Claude auth.** `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL`
2277
2302
  * / `ANTHROPIC_AUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN` are removed from BOTH
2278
2303
  * the parent env and any caller `extra`. A self-hosted Mac very often has
2279
2304
  * 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.
2305
+ * accounts or billing plane. Credential and proxy modes also drop provider
2306
+ * overrides (Bedrock/Vertex/Foundry); proxy mode additionally drops model
2307
+ * and header overrides so nothing reaches the proxy that it cannot price.
2282
2308
  *
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.
2309
+ * 2. **Inject the source last.** Nothing in `base` or `extra` can override the
2310
+ * injected credential or proxy settings. A credential lives only in the
2311
+ * host's memory and this env, never on disk, never logged. Proxy settings
2312
+ * never appear in a credential or local-login env.
2290
2313
  *
2291
2314
  * 3. **Set `ALFE_CLAUDE_BRIDGE=1`.** The Phase-1 CLI hooks
2292
2315
  * (`alfe claude hook ...` in `packages/cli/src/commands/claude.ts`) check
@@ -2299,14 +2322,55 @@ declare class ChatServiceClient {
2299
2322
  declare const SCRUBBED_ANTHROPIC_ENV_VARS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"];
2300
2323
  /**
2301
2324
  * 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.
2325
+ * the Anthropic API. Dropped whenever a credential or the AI proxy is in use,
2326
+ * so an inherited `CLAUDE_CODE_USE_BEDROCK=1` (say) can't send the turn to
2327
+ * Bedrock and ignore the chosen source. On a local login they are left alone:
2328
+ * the machine's own `claude` setup is the source, whatever provider it uses.
2306
2329
  */
2307
2330
 
2308
2331
  /** Cross-phase contract marker (see header + packages/cli/src/commands/claude.ts). */
2309
2332
  declare const BRIDGE_ENV_VAR = "ALFE_CLAUDE_BRIDGE";
2333
+ /**
2334
+ * Placeholder `ANTHROPIC_AUTH_TOKEN` (sent as `Authorization: Bearer`) for
2335
+ * proxy mode. `@alfe.ai/ai-proxy-local` keeps an incoming `alfe_…` key and
2336
+ * replaces anything else with the daemon's agent key (as `x-api-key`, dropping
2337
+ * `authorization`), so the agent key itself never enters the `claude` env (a
2338
+ * managed agent has Bash). Never `alfe_…`.
2339
+ */
2340
+ declare const PROXY_API_KEY_PLACEHOLDER = "alfe-local-proxy";
2341
+ /**
2342
+ * Default local AI proxy (`DEFAULT_AI_PROXY_PORT` in `@alfe.ai/ai-proxy-local`).
2343
+ * The daemon passes the URL it actually bound as `ALFE_LOCAL_AI_PROXY_URL`;
2344
+ * this is the fallback. Bare host, no `/v1`: `claude` appends `/v1/messages`.
2345
+ */
2346
+ declare const DEFAULT_LOCAL_AI_PROXY_URL = "http://127.0.0.1:18193";
2347
+ /**
2348
+ * The model `claude -p --model` runs in proxy mode when the agent has no
2349
+ * `defaultModel` (or the daemon's lookup timed out). `claude`'s own default is
2350
+ * a dated id the proxy cannot route. Haiku 4.5 is the one Claude model seeded
2351
+ * on every stage (the memory integration depends on it), so it is the only
2352
+ * id guaranteed to route; the dashboard always sets a model, so this is rare.
2353
+ */
2354
+ declare const PROXY_DEFAULT_MODEL = "claude-haiku-4-5";
2355
+ /**
2356
+ * Fixed model env in proxy mode, Alfe registry ids (`AnthropicModel` in
2357
+ * `@alfe/types`; the child-env test asserts it). `ANTHROPIC_SMALL_FAST_MODEL`
2358
+ * drives background calls (titles, summaries); `ANTHROPIC_DEFAULT_HAIKU_MODEL`
2359
+ * resolves the `haiku` alias. Haiku 4.5 because it is seeded on every stage.
2360
+ * claude 2.1.284/2.1.287 read these and {@link PROXY_AGENT_MODEL_ENV_VARS}.
2361
+ */
2362
+ declare const PROXY_MODEL_ENV: {
2363
+ readonly ANTHROPIC_SMALL_FAST_MODEL: "claude-haiku-4-5";
2364
+ readonly ANTHROPIC_DEFAULT_HAIKU_MODEL: "claude-haiku-4-5";
2365
+ };
2366
+ /**
2367
+ * The `sonnet` / `opus` aliases (subagents) resolve to the agent's OWN model
2368
+ * in proxy mode: the agents service validated it against the registry, while a
2369
+ * fixed 5.x id may not be enabled on the workspace.
2370
+ */
2371
+ declare const PROXY_AGENT_MODEL_ENV_VARS: readonly ["ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_OPUS_MODEL"];
2372
+ /** Set only in proxy mode (besides the auth pair). */
2373
+
2310
2374
  /** The agent's Claude credential, as fetched from `GET /agent/claude-credential`. */
2311
2375
  interface ChildCredential {
2312
2376
  kind: 'oauth_token' | 'api_key';
@@ -2314,16 +2378,29 @@ interface ChildCredential {
2314
2378
  }
2315
2379
  /** The single env var each credential kind is exported as. */
2316
2380
  declare const CREDENTIAL_ENV_VAR: Record<ChildCredential['kind'], string>;
2381
+ /** The auth source of one `claude` child. */
2382
+ type ChildAuth = {
2383
+ mode: 'credential';
2384
+ credential: ChildCredential;
2385
+ } | {
2386
+ mode: 'local_login';
2387
+ }
2388
+ /** `model` = the turn's registry model id (`--model`); also the sonnet/opus aliases. */ | {
2389
+ mode: 'ai_proxy';
2390
+ baseUrl: string;
2391
+ model: string;
2392
+ };
2317
2393
  /**
2318
2394
  * Build the env handed to a spawned `claude` child. Pure — takes the base env
2319
2395
  * explicitly so it unit-tests without touching `process.env`.
2320
2396
  *
2321
2397
  * 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.
2398
+ * overrides for a credential or the proxy, plus model overrides for the
2399
+ * proxy) → set the bridge marker → set the source's variables. Neither `base`
2400
+ * nor `extra` can smuggle an auth variable in or override the source.
2401
+ * `auth` omitted = local login.
2325
2402
  */
2326
- declare function buildChildEnv(base: NodeJS.ProcessEnv, extra?: Record<string, string>, credential?: ChildCredential | null): NodeJS.ProcessEnv;
2403
+ declare function buildChildEnv(base: NodeJS.ProcessEnv, extra?: Record<string, string>, auth?: ChildAuth): NodeJS.ProcessEnv;
2327
2404
  //#endregion
2328
2405
  //#region src/stream-mapper.d.ts
2329
2406
  /**
@@ -2421,6 +2498,11 @@ interface EndEmission {
2421
2498
  * message). Present only on error turns.
2422
2499
  */
2423
2500
  authFailed?: true;
2501
+ /**
2502
+ * The refusal was a 403 (forbidden: an org or provider policy), not a dead
2503
+ * login or key. Present only with `authFailed`.
2504
+ */
2505
+ authForbidden?: true;
2424
2506
  }
2425
2507
  type MapperEmission = ActivityEmission | TextEmission | EndEmission;
2426
2508
  interface PendingTool {
@@ -2535,6 +2617,8 @@ interface DriverTurnResult {
2535
2617
  aborted: boolean;
2536
2618
  /** Present (true) when the turn failed because Claude refused the credential. */
2537
2619
  authFailed?: true;
2620
+ /** With `authFailed`: the refusal was a 403 (policy), not a dead login/key. */
2621
+ authForbidden?: true;
2538
2622
  }
2539
2623
  interface RunTurnParams {
2540
2624
  /** The user prompt for this turn. */
@@ -2547,7 +2631,10 @@ interface RunTurnParams {
2547
2631
  mcpConfigPath?: string;
2548
2632
  /** Persona + reply-style text appended to the system prompt. Omitted → none. */
2549
2633
  appendSystemPrompt?: string;
2550
- /** Bare Claude model id for `--model` (subscription models only). Omitted → CLI default. */
2634
+ /**
2635
+ * Bare Claude model id for `--model`. Omitted → CLI default (never in proxy
2636
+ * mode: the host always passes a registry id there).
2637
+ */
2551
2638
  model?: string;
2552
2639
  /** Tool allow-list (defaults to DEFAULT_ALLOWED_TOOLS). */
2553
2640
  allowedTools?: readonly string[];
@@ -2580,7 +2667,7 @@ declare class SessionDriver {
2580
2667
  private abortCurrent;
2581
2668
  private readonly bin;
2582
2669
  private readonly baseEnv;
2583
- private readonly credential;
2670
+ private readonly auth;
2584
2671
  private readonly managed;
2585
2672
  private readonly log;
2586
2673
  /** Injected spawn — tests replace this with a fake child. */
@@ -2589,10 +2676,10 @@ declare class SessionDriver {
2589
2676
  claudeBin?: string;
2590
2677
  baseEnv?: NodeJS.ProcessEnv;
2591
2678
  /**
2592
- * The agent's Claude credential, injected into the child env after the
2593
- * scrub. Null/absent → the machine's own `claude` login.
2679
+ * The turn's auth source, applied to the child env after the scrub
2680
+ * (`buildChildEnv`). Absent → the machine's own `claude` login.
2594
2681
  */
2595
- credential?: ChildCredential | null;
2682
+ auth?: ChildAuth;
2596
2683
  /** Managed VM: Bash allowed + deny rules (see MANAGED_ALLOWED_TOOLS). */
2597
2684
  managed?: boolean;
2598
2685
  logger?: Logger;
@@ -2638,22 +2725,46 @@ declare function loadCredentialWithRetry(fetchCredential: () => Promise<HostCred
2638
2725
  credential: HostCredential | null;
2639
2726
  } | null>;
2640
2727
  declare function isAuthFailureText(text: string): boolean;
2728
+ /** Why the Alfe AI proxy refused a request it otherwise authenticated. */
2729
+ type ProxyDenial = 'credits' | 'usage_limit' | 'model_unavailable';
2730
+ /**
2731
+ * Classify an Alfe AI proxy refusal from the text `claude` reports. The proxy
2732
+ * answers credit and usage-limit denials on `/v1/messages` with a 429
2733
+ * `rate_limit_error` (never 402, see services/ai DEVELOPING.md "Credit denials
2734
+ * on LLM routes are 429"); claude 2.1.287 surfaces it as
2735
+ * `API Error: Request rejected (429) · <message>` (fixture
2736
+ * `turn-proxy-credits.stream.jsonl`). Credit texts also say "usage limit
2737
+ * reached", so they are checked first. An Anthropic subscription limit
2738
+ * ("Claude AI usage limit reached") is not an Alfe denial.
2739
+ */
2740
+ declare function classifyProxyDenial(text: string): ProxyDenial | null;
2641
2741
  /** HTTP statuses on a `result` event that mean "credential refused". */
2642
2742
 
2643
2743
  type ProbeOutcome = {
2644
2744
  result: 'ok';
2645
- } | {
2745
+ }
2746
+ /** `forbidden`: a 403 (an org or provider policy), not a dead login or key. */ | {
2646
2747
  result: 'invalid';
2748
+ forbidden?: true;
2749
+ }
2750
+ /** The Alfe AI proxy authenticated the agent but refused the request. */ | {
2751
+ result: 'denied';
2752
+ denial: ProxyDenial;
2647
2753
  } | {
2648
2754
  result: 'unknown';
2649
2755
  reason: string;
2650
2756
  };
2651
2757
  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[];
2758
+ /**
2759
+ * argv for the probe. No tools, no session file, no MCP, no settings files.
2760
+ * `model` is `haiku` (the alias) on a credential or local login, and the
2761
+ * registry Haiku id through the AI proxy.
2762
+ */
2763
+ declare function buildProbeArgs(model?: string): string[];
2654
2764
  declare function parseProbeOutput(stdout: string, stderr: string, exitCode: number | null): ProbeOutcome;
2655
2765
  interface AuthProbeOptions {
2656
- credential: ChildCredential | null;
2766
+ /** The auth source to check (a credential, the local login, or the AI proxy). */
2767
+ auth: ChildAuth;
2657
2768
  baseEnv: NodeJS.ProcessEnv;
2658
2769
  /** Any existing directory; the probe reads and writes nothing there. */
2659
2770
  cwd: string;
@@ -2664,12 +2775,17 @@ interface AuthProbeOptions {
2664
2775
  signal?: AbortSignal;
2665
2776
  }
2666
2777
  /**
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.
2778
+ * One minimal real turn on the given auth source: Anthropic with the agent's
2779
+ * credential or the machine's own login, or the Alfe AI proxy (a Haiku ping
2780
+ * billed from Alfe credits). Never throws.
2669
2781
  */
2670
2782
  declare function runAuthProbe(opts: AuthProbeOptions): Promise<ProbeOutcome>;
2783
+ /** Which auth the host's turns run on (projected as `claudeCredentialSource`). */
2784
+ type CredentialSource = 'credential' | 'local_login' | 'ai_proxy';
2671
2785
  interface CredentialStatusReport {
2786
+ /** `missing` is legacy; this host never sends it (it falls back to the AI proxy). */
2672
2787
  status: 'ok' | 'missing' | 'invalid';
2788
+ source?: CredentialSource;
2673
2789
  /** Fixed, host-authored text only. Never `claude` output. */
2674
2790
  detail?: string;
2675
2791
  revision?: string;
@@ -2715,21 +2831,31 @@ interface ClaudeCodeHostConfig {
2715
2831
  /**
2716
2832
  * Bare Claude model id for `claude -p --model` (e.g. `claude-opus-4-8` or the
2717
2833
  * `opus`/`sonnet`/`haiku` aliases). Sourced from the agent's `defaultModel`
2718
- * via `ALFE_DEFAULT_MODEL`. Omitted → the operator's `claude` CLI default.
2834
+ * via `ALFE_DEFAULT_MODEL`. Omitted → the operator's `claude` CLI default on
2835
+ * a credential or local login, `PROXY_DEFAULT_MODEL` on the AI proxy.
2719
2836
  * NEVER provider-qualified (`anthropic/…`) — that's an OpenClaw-only shape.
2720
2837
  */
2721
2838
  model?: string;
2722
2839
  /**
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.
2840
+ * Managed VM (`ALFE_MANAGED=1|true`, set by the daemon). Enables Bash. With
2841
+ * no stored credential a managed agent always runs on the Alfe AI proxy (no
2842
+ * local login to fall back to). Self-hosted when false/absent.
2726
2843
  */
2727
2844
  managed?: boolean;
2845
+ /**
2846
+ * The daemon's loopback AI proxy (`ALFE_LOCAL_AI_PROXY_URL`). Defaults to
2847
+ * `DEFAULT_LOCAL_AI_PROXY_URL`.
2848
+ */
2849
+ localAiProxyUrl?: string;
2728
2850
  logger?: Logger;
2729
2851
  }
2730
2852
  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.";
2853
+ declare const OUT_OF_CREDITS_MESSAGE = "This agent is out of Alfe credits. Add credits in Alfe Billing, or add a Claude credential in agent settings, then resend your message.";
2854
+ declare const USAGE_LIMIT_MESSAGE = "This agent hit an Alfe usage limit. Raise the limit in Alfe or wait for it to reset, then resend your message.";
2855
+ declare const LOCAL_LOGIN_SWITCHED_MESSAGE = "Your machine's Claude login stopped working, so this agent switched to Alfe credits. Please resend your message.";
2856
+ declare const LOCAL_LOGIN_REFUSED_MESSAGE = "Claude refused this machine's claude login (403). Check the account or its organisation policy, or add a Claude credential in agent settings.";
2857
+ declare const MODEL_UNAVAILABLE_MESSAGE = "This agent's model isn't available on this workspace. Choose another Claude model in agent settings, then resend your message.";
2858
+ declare const PROXY_AUTH_FAILED_MESSAGE = "Alfe AI rejected this agent's key. Reconnect the agent (run `alfe setup` on its machine), or add a Claude credential in agent settings.";
2733
2859
  declare const CREDENTIAL_UNAVAILABLE_MESSAGE = "This agent's Claude credential couldn't be loaded yet. Try again in a moment.";
2734
2860
  /** How long a turn waits for the start-up credential fetch before failing. */
2735
2861
 
@@ -2768,8 +2894,16 @@ declare class ClaudeCodeHost {
2768
2894
  /** Turns waiting for the credential, before queue admission (chat.abort). */
2769
2895
  private readonly waitingTurns;
2770
2896
  private readonly loaderRestartMs;
2897
+ /** How often a fallen-back self-hosted agent re-checks its local login. */
2898
+ private readonly localLoginRecheckMs;
2899
+ private localLoginRecheck;
2771
2900
  /** In memory only. Never logged, never persisted. */
2772
2901
  private credential;
2902
+ /**
2903
+ * The auth every new turn runs on, resolved once the credential fetch
2904
+ * answers (see `resolveAuthSource`). Null before that.
2905
+ */
2906
+ private authSource;
2773
2907
  private readonly credentialAbort;
2774
2908
  private readonly reporter;
2775
2909
  private readonly probe;
@@ -2777,17 +2911,22 @@ declare class ClaudeCodeHost {
2777
2911
  private readonly credentialRetry;
2778
2912
  constructor(cfg: ClaudeCodeHostConfig, deps?: {
2779
2913
  /**
2780
- * Per-turn driver factory (test seam). Called once per turn with the
2781
- * agent's credential (null → the machine's own `claude` login).
2914
+ * Per-turn driver factory (test seam). Called once per turn (and once
2915
+ * more for a local-login turn re-run on the AI proxy) with its auth.
2782
2916
  */
2783
- makeDriver?: (credential: ChildCredential | null) => SessionDriver;
2917
+ makeDriver?: (auth: ChildAuth) => SessionDriver;
2784
2918
  chat?: ChatServiceClient;
2785
2919
  api?: AgentApiClient;
2786
2920
  /** Start-up auth check (test seam; default: a minimal `claude -p`). */
2787
- probe?: (credential: ChildCredential | null, signal: AbortSignal) => Promise<ProbeOutcome>;
2921
+ probe?: (auth: ChildAuth, signal: AbortSignal) => Promise<ProbeOutcome>;
2788
2922
  credentialWaitMs?: number;
2789
2923
  /** Delay before restarting a credential loader that threw (default 5 s). */
2790
2924
  loaderRestartMs?: number;
2925
+ /**
2926
+ * After falling back from a refused local login, re-probe it this often
2927
+ * and switch back when it works (default: the status-report interval).
2928
+ */
2929
+ localLoginRecheckMs?: number;
2791
2930
  credentialRetry?: {
2792
2931
  baseDelayMs?: number;
2793
2932
  maxDelayMs?: number;
@@ -2801,11 +2940,33 @@ declare class ClaudeCodeHost {
2801
2940
  */
2802
2941
  private runCredentialLoader;
2803
2942
  private loadCredential;
2804
- private childCredential;
2805
- /** Start-up health check: one minimal real call, reported to the cloud. */
2943
+ /**
2944
+ * A method, not a property read: `stopping` flips across awaits, which
2945
+ * control-flow narrowing cannot see.
2946
+ */
2947
+ private isStopping;
2948
+ /** Same reason as `isStopping`: `authSource` can change across an await. */
2949
+ private currentAuthSource;
2950
+ /** The auth a new turn runs on. Local login until the fetch has answered. */
2951
+ private childAuth;
2952
+ /**
2953
+ * A self-hosted agent with no credential whose local login was refused (401
2954
+ * or an explicit auth message, never a 403): run new turns through the Alfe
2955
+ * AI proxy, and re-check the login periodically so a re-login moves the agent
2956
+ * back off Alfe credits. Never for a stored credential (the user chose it; a
2957
+ * refusal there needs their fix) and never on managed.
2958
+ */
2959
+ private fallBackToProxy;
2960
+ private scheduleLocalLoginRecheck;
2961
+ /** While fallen back: one local-login probe; back to the login when it works. */
2962
+ private recheckLocalLogin;
2963
+ /**
2964
+ * Start-up check: one minimal real call on the resolved source, reported to
2965
+ * the cloud. A refused local login falls back to the proxy and checks that.
2966
+ */
2806
2967
  private checkCredential;
2807
- /** Report the credential (or the local login) as working or refused. */
2808
- private reportAuth;
2968
+ /** Report the current auth source as working or refused. */
2969
+ private reportStatus;
2809
2970
  /**
2810
2971
  * Wait for the first credential answer BEFORE queue admission, so a waiting
2811
2972
  * turn holds no slot. Bounded by the start-up deadline: once it has passed,
@@ -2829,6 +2990,13 @@ declare class ClaudeCodeHost {
2829
2990
  private handleAgent;
2830
2991
  private deliverAgentOutcome;
2831
2992
  private runAgentTurn;
2993
+ /**
2994
+ * Settle a failed turn (C3) after storing its message as the assistant's
2995
+ * reply, so the history shows what the user saw. Stored first: a client that
2996
+ * reloads on the failure must find it. Best-effort: a store failure never
2997
+ * changes the outcome.
2998
+ */
2999
+ private failTurn;
2832
3000
  /**
2833
3001
  * Send every message the memory service has not accepted yet. Indices are
2834
3002
  * the messages' positions in the session file, so they advance across turns
@@ -2854,6 +3022,12 @@ declare class ClaudeCodeHost {
2854
3022
  * Run a turn; on a `--resume` failure (stale/missing claude session id) start
2855
3023
  * fresh and prepend the last-N stored messages so the model keeps context.
2856
3024
  */
3025
+ /**
3026
+ * Run one turn on `auth` with its own driver, registered as the session's
3027
+ * running turn so `chat.abort` reaches it. Proxy turns always carry a
3028
+ * registry model.
3029
+ */
3030
+ private runTurnOn;
2857
3031
  private runWithResumeFallback;
2858
3032
  private forwardEmission;
2859
3033
  private handleChatAbort;
@@ -2901,6 +3075,12 @@ interface ChatMessage {
2901
3075
  role: 'user' | 'assistant';
2902
3076
  content: string;
2903
3077
  timestamp: number;
3078
+ /**
3079
+ * `'error'`: the failure message a turn settled with, stored so the history
3080
+ * shows what the user saw. History only: memory ingest and the resume
3081
+ * preamble skip it (it is not something the agent said).
3082
+ */
3083
+ kind?: 'error';
2904
3084
  senderId?: string;
2905
3085
  senderName?: string;
2906
3086
  components?: StoredMessageComponent[];
@@ -2936,7 +3116,10 @@ type ChatActivityRecord = {
2936
3116
  interface SessionData {
2937
3117
  sessionId: string;
2938
3118
  agentId: string;
3119
+ /** Always `CHAT_SESSION_CHANNEL` for sessions this host writes. */
2939
3120
  channel: string;
3121
+ /** The chat surface that opened the conversation (`mobile`, `dashboard`, ...). */
3122
+ clientType?: string;
2940
3123
  tenantId?: string;
2941
3124
  userId?: string;
2942
3125
  createdAt: string;
@@ -2979,21 +3162,24 @@ interface SessionSummary {
2979
3162
  preview?: string;
2980
3163
  messageCount: number;
2981
3164
  }
3165
+ /**
3166
+ * The channel every chat session is stored on. Every chat client lists with
3167
+ * `sessions.list { channel: 'alfe' }` and openclaw-chat stores `'alfe'`, so any
3168
+ * other value hides the conversation from the rail.
3169
+ */
3170
+
2982
3171
  declare function getSession(sessionId: string): Promise<SessionData | null>;
2983
3172
  declare function saveSession(session: SessionData): Promise<void>;
2984
- declare function createSession(sessionId: string, agentId: string, channel: string, tenantId?: string, userId?: string): Promise<SessionData>;
3173
+ declare function createSession(sessionId: string, agentId: string, channel: string, tenantId?: string, userId?: string, clientType?: string): Promise<SessionData>;
2985
3174
  /**
2986
3175
  * Append a message. Returns its index (position in `messages`), the stable
2987
3176
  * memory-ingest index, or -1 when the session does not exist.
2988
3177
  */
2989
3178
  declare function addMessage(sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[], attachments?: StoredAttachment[]): Promise<number>;
2990
3179
  /**
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.
3180
+ * Store the failure message a turn settled with as the assistant's reply, so
3181
+ * the history shows what the user saw. Returns its index, or -1 when the
3182
+ * session does not exist.
2997
3183
  */
2998
3184
 
2999
3185
  /**
@@ -3015,4 +3201,4 @@ declare function listSessions(filters?: {
3015
3201
  */
3016
3202
  declare function resolveHostConfig(): ClaudeCodeHostConfig;
3017
3203
  //#endregion
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 };
3204
+ export { AUTH_PROBE_TIMEOUT_MS, type ActivityEmission, BRIDGE_ENV_VAR, CLAUDE_PERMISSION_MODES, CREDENTIAL_ENV_VAR, CREDENTIAL_INVALID_MESSAGE, CREDENTIAL_UNAVAILABLE_MESSAGE, type ChatActivityRecord, type ChatMessage, type ChildAuth, type ChildCredential, ClaudeCodeHost, type ClaudeCodeHostConfig, type ClaudePermissionMode, type CredentialSource, type CredentialStatusReport, CredentialStatusReporter, DEFAULT_ALLOWED_TOOLS, DEFAULT_LOCAL_AI_PROXY_URL, DEFAULT_PERMISSION_MODE, type DriverTurnResult, type EndEmission, type HostCredential, LOCAL_LOGIN_REFUSED_MESSAGE, LOCAL_LOGIN_SWITCHED_MESSAGE, MANAGED_ALLOWED_TOOLS, MANAGED_DISALLOWED_TOOLS, MODEL_UNAVAILABLE_MESSAGE, type MapperEmission, type MapperState, OUT_OF_CREDITS_MESSAGE, PROXY_AGENT_MODEL_ENV_VARS, PROXY_API_KEY_PLACEHOLDER, PROXY_AUTH_FAILED_MESSAGE, PROXY_DEFAULT_MODEL, PROXY_MODEL_ENV, type ProbeOutcome, type ProxyDenial, 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, USAGE_LIMIT_MESSAGE, WARM_STREAMING_SUPPORTED, addMessage, appendActivity, buildChildEnv, buildProbeArgs, classifyProxyDenial, collectTurnActivityRecords, createMapperState, createSession, getSession, isAuthFailureText, listSessions, loadCredentialWithRetry, mapStreamEvent, mapStreamEvents, parseProbeOutput, resolveHostConfig, runAuthProbe, saveSession, stripAgentPrefix };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as buildProbeArgs, C as collectTurnActivityRecords, D as AUTH_PROBE_TIMEOUT_MS, E as mapStreamEvents, F as BRIDGE_ENV_VAR, I as CREDENTIAL_ENV_VAR, L as SCRUBBED_ANTHROPIC_ENV_VARS, M as loadCredentialWithRetry, N as parseProbeOutput, O as CredentialStatusReporter, P as runAuthProbe, R as buildChildEnv, S as WARM_STREAMING_SUPPORTED, T as mapStreamEvent, _ as MANAGED_ALLOWED_TOOLS, a as ClaudeCodeHost, b as TERMINATION_GRACE_MS, c as addMessage, d as getSession, f as listSessions, g as DEFAULT_PERMISSION_MODE, h as DEFAULT_ALLOWED_TOOLS, i as CREDENTIAL_UNAVAILABLE_MESSAGE, j as isAuthFailureText, k as STATUS_REPORT_INTERVAL_MS, l as appendActivity, m as CLAUDE_PERMISSION_MODES, n as CREDENTIAL_INVALID_MESSAGE, o as LOCAL_LOGIN_FAILED_MESSAGE, p as saveSession, r as CREDENTIAL_MISSING_MESSAGE, s as stripAgentPrefix, t as resolveHostConfig, u as createSession, v as MANAGED_DISALLOWED_TOOLS, w as createMapperState, x as TURN_TIMEOUT_MS, y as SessionDriver } from "./host-config.js";
2
- export { AUTH_PROBE_TIMEOUT_MS, BRIDGE_ENV_VAR, CLAUDE_PERMISSION_MODES, CREDENTIAL_ENV_VAR, CREDENTIAL_INVALID_MESSAGE, CREDENTIAL_MISSING_MESSAGE, CREDENTIAL_UNAVAILABLE_MESSAGE, ClaudeCodeHost, CredentialStatusReporter, DEFAULT_ALLOWED_TOOLS, DEFAULT_PERMISSION_MODE, LOCAL_LOGIN_FAILED_MESSAGE, MANAGED_ALLOWED_TOOLS, MANAGED_DISALLOWED_TOOLS, SCRUBBED_ANTHROPIC_ENV_VARS, STATUS_REPORT_INTERVAL_MS, SessionDriver, TERMINATION_GRACE_MS, TURN_TIMEOUT_MS, WARM_STREAMING_SUPPORTED, addMessage, appendActivity, buildChildEnv, buildProbeArgs, collectTurnActivityRecords, createMapperState, createSession, getSession, isAuthFailureText, listSessions, loadCredentialWithRetry, mapStreamEvent, mapStreamEvents, parseProbeOutput, resolveHostConfig, runAuthProbe, saveSession, stripAgentPrefix };
1
+ import { A as mapStreamEvents, B as BRIDGE_ENV_VAR, C as SessionDriver, D as collectTurnActivityRecords, E as WARM_STREAMING_SUPPORTED, F as classifyProxyDenial, G as PROXY_DEFAULT_MODEL, H as DEFAULT_LOCAL_AI_PROXY_URL, I as isAuthFailureText, J as buildChildEnv, K as PROXY_MODEL_ENV, L as loadCredentialWithRetry, M as CredentialStatusReporter, N as STATUS_REPORT_INTERVAL_MS, O as createMapperState, P as buildProbeArgs, R as parseProbeOutput, S as MANAGED_DISALLOWED_TOOLS, T as TURN_TIMEOUT_MS, U as PROXY_AGENT_MODEL_ENV_VARS, V as CREDENTIAL_ENV_VAR, W as PROXY_API_KEY_PLACEHOLDER, _ as saveSession, a as LOCAL_LOGIN_REFUSED_MESSAGE, b as DEFAULT_PERMISSION_MODE, c as OUT_OF_CREDITS_MESSAGE, d as stripAgentPrefix, f as addMessage, g as listSessions, h as getSession, i as ClaudeCodeHost, j as AUTH_PROBE_TIMEOUT_MS, k as mapStreamEvent, l as PROXY_AUTH_FAILED_MESSAGE, m as createSession, n as CREDENTIAL_INVALID_MESSAGE, o as LOCAL_LOGIN_SWITCHED_MESSAGE, p as appendActivity, q as SCRUBBED_ANTHROPIC_ENV_VARS, r as CREDENTIAL_UNAVAILABLE_MESSAGE, s as MODEL_UNAVAILABLE_MESSAGE, t as resolveHostConfig, u as USAGE_LIMIT_MESSAGE, v as CLAUDE_PERMISSION_MODES, w as TERMINATION_GRACE_MS, x as MANAGED_ALLOWED_TOOLS, y as DEFAULT_ALLOWED_TOOLS, z as runAuthProbe } from "./host-config.js";
2
+ export { AUTH_PROBE_TIMEOUT_MS, BRIDGE_ENV_VAR, CLAUDE_PERMISSION_MODES, CREDENTIAL_ENV_VAR, CREDENTIAL_INVALID_MESSAGE, CREDENTIAL_UNAVAILABLE_MESSAGE, ClaudeCodeHost, CredentialStatusReporter, DEFAULT_ALLOWED_TOOLS, DEFAULT_LOCAL_AI_PROXY_URL, DEFAULT_PERMISSION_MODE, LOCAL_LOGIN_REFUSED_MESSAGE, LOCAL_LOGIN_SWITCHED_MESSAGE, MANAGED_ALLOWED_TOOLS, MANAGED_DISALLOWED_TOOLS, MODEL_UNAVAILABLE_MESSAGE, OUT_OF_CREDITS_MESSAGE, PROXY_AGENT_MODEL_ENV_VARS, PROXY_API_KEY_PLACEHOLDER, PROXY_AUTH_FAILED_MESSAGE, PROXY_DEFAULT_MODEL, PROXY_MODEL_ENV, SCRUBBED_ANTHROPIC_ENV_VARS, STATUS_REPORT_INTERVAL_MS, SessionDriver, TERMINATION_GRACE_MS, TURN_TIMEOUT_MS, USAGE_LIMIT_MESSAGE, WARM_STREAMING_SUPPORTED, addMessage, appendActivity, buildChildEnv, buildProbeArgs, classifyProxyDenial, collectTurnActivityRecords, createMapperState, createSession, getSession, isAuthFailureText, listSessions, loadCredentialWithRetry, mapStreamEvent, mapStreamEvents, parseProbeOutput, resolveHostConfig, runAuthProbe, saveSession, stripAgentPrefix };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alfe.ai/claude-code-host",
3
- "version": "0.1.15",
4
- "description": "Chat bridge that makes a self-hosted Claude Code agent reachable from Alfe chat on the user's Claude subscription",
3
+ "version": "0.1.16",
4
+ "description": "Chat bridge that makes a Claude Code agent reachable from Alfe chat, on its Claude credential, the local claude login, or Alfe AI credits",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "dependencies": {
22
- "@alfe.ai/agent-api-client": "^0.21.0",
22
+ "@alfe.ai/agent-api-client": "^0.21.1",
23
23
  "@alfe.ai/chat": "^0.0.19",
24
24
  "@alfe.ai/config": "^0.4.2"
25
25
  },