qlogicagent 2.11.5 → 2.11.7

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.
@@ -7,7 +7,7 @@
7
7
  import { type AgentRpcError, type AgentRpcRequest } from "../../protocol/wire/index.js";
8
8
  import { AgentProcessManager } from "../../runtime/infra/agent-process.js";
9
9
  import type { AgentConfigStore } from "../../runtime/infra/agent-config-store.js";
10
- import type { AcpDetector } from "../../runtime/infra/acp-detector.js";
10
+ import { type AcpDetector } from "../../runtime/infra/acp-detector.js";
11
11
  export interface AgentsHandlerHost {
12
12
  acpDetector: AcpDetector;
13
13
  soloProcessManager: AgentProcessManager | null;
@@ -17,9 +17,24 @@ export interface AgentsHandlerHost {
17
17
  handleMcpToolCall?(memberId: string, tool: string, args: Record<string, unknown>): Promise<unknown>;
18
18
  log?(message: string): void;
19
19
  sendResponse(id: string | number, result?: unknown, error?: AgentRpcError): void;
20
+ sendNotification?(method: string, params: Record<string, unknown>): void;
20
21
  }
22
+ /** Resolve a pending external-agent approval with the user's decision (delivered by the gateway). */
23
+ export declare function handleAgentsApprovalResponse(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
21
24
  export declare function handleAgentsScan(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
22
25
  export declare function handleAgentsList(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
26
+ /**
27
+ * Curated Discover-Agent catalog (12 agents) merged with live install status, for the
28
+ * discover grid. Status comes from the detector; install carries the official command
29
+ * summary + source for the confirm dialog (no secrets).
30
+ */
31
+ export declare function handleAgentsCatalog(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
32
+ /**
33
+ * One-click install a catalog agent by id. Runs ONLY the official command from the
34
+ * catalog manifest (never a free-form command), streaming progress via the
35
+ * agent.install.progress notification, then refreshes detection. Returns the outcome.
36
+ */
37
+ export declare function handleAgentsInstall(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
23
38
  export declare function handleAgentsConfig(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
24
39
  export declare function handleAgentsSetConfig(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
25
40
  export declare function handleAgentsGetConfig(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
@@ -31,4 +46,6 @@ export declare function handleAgentsProcesses(this: AgentsHandlerHost, msg: Agen
31
46
  export declare function handleAgentsKill(this: AgentsHandlerHost, msg: AgentRpcRequest): void;
32
47
  export declare function handleAgentsGetLog(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
33
48
  export declare function handleAgentsTestConnection(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
49
+ /** Pre-warm an external agent's process (on agent-select) so the first message skips the cold start. */
50
+ export declare function handleAgentsWarm(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
34
51
  export declare function handleAgentsPrompt(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Memory CRUD + search handlers extracted from StdioServer.
2
+ * Memory CRUD + search handlers extracted from StdioServer.
3
3
  * Handles: memory.list, memory.read, memory.write, memory.search, memory.delete
4
4
  */
5
5
  import { type AgentRpcError, type AgentRpcRequest } from "../../protocol/wire/index.js";
@@ -30,15 +30,15 @@ export interface MemoryHandlerHost {
30
30
  sendResponse(id: string | number, result?: unknown, error?: AgentRpcError): void;
31
31
  }
32
32
  export declare function handleMemoryList(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
33
- /** memory.list-files Returns actual topic files from memdir. */
33
+ /** memory.list-files: returns actual topic files from memdir. */
34
34
  export declare function handleMemoryListFiles(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
35
35
  /**
36
- * memory.atlas Returns full L2 vector-memory records (wire-safe, no embedding
36
+ * memory.atlas: returns full L2 vector-memory records (wire-safe, no embedding
37
37
  * blob) plus category roll-ups, for the 3D memory atlas visualization.
38
38
  */
39
39
  export declare function handleMemoryAtlas(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
40
40
  /**
41
- * memory.activity Daily activity counts + highlights over the recent window,
41
+ * memory.activity: daily activity counts + highlights over the recent window,
42
42
  * powering the atlas timeline ticks and the "memory digest" panel.
43
43
  */
44
44
  export declare function handleMemoryActivity(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
@@ -51,11 +51,11 @@ export declare function handleMemorySearch(this: MemoryHandlerHost, msg: AgentRp
51
51
  export declare function handleMemoryDelete(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
52
52
  export declare function handleMemoryUpdate(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
53
53
  /**
54
- * memory.attachment.adopt persist an uploaded素材 into the long-term-memory
54
+ * memory.attachment.adopt: persist an uploaded attachment into the long-term-memory
55
55
  * attachment store from a transient source URL (the gateway's temp MediaStore).
56
56
  * The attachment is an orphan until linked to a memory at consolidate time.
57
57
  */
58
58
  export declare function handleMemoryAttachmentAdopt(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
59
- /** memory.attachment.locate resolve an attachment id to its on-disk path (for serving). */
59
+ /** memory.attachment.locate: resolve an attachment id to its on-disk path (for serving). */
60
60
  export declare function handleMemoryAttachmentLocate(this: MemoryHandlerHost, msg: AgentRpcRequest): Promise<void>;
61
61
  export {};
@@ -32,5 +32,6 @@ export interface RegisterMemoryToolDeps {
32
32
  memoryProvider: MemoryToolProvider | null;
33
33
  memoryUserId: string;
34
34
  toolCatalog: ToolCatalog;
35
+ sendNotification?: (method: string, params: Record<string, unknown>) => void;
35
36
  }
36
37
  export declare function registerMemoryTool(deps: RegisterMemoryToolDeps): void;
@@ -24,6 +24,7 @@ export interface RuntimeHookBootstrapDeps {
24
24
  transport: LLMTransport;
25
25
  apiKey: string;
26
26
  };
27
+ sendNotification?(method: string, params: Record<string, unknown>): void;
27
28
  }
28
29
  export interface RuntimeHookBootstrapResult {
29
30
  hooks: HookRegistry;
@@ -5,7 +5,6 @@
5
5
  export declare function findGitRoot(cwd: string): Promise<string | null>;
6
6
  /** Create an isolated git worktree. Returns the worktree path. */
7
7
  export declare function createWorktree(gitRoot: string, name: string, baseBranch?: string): Promise<string>;
8
- /** Get a git diff stat from a worktree. */
9
8
  export declare function getWorktreeDiff(worktreePath: string): Promise<string>;
10
9
  /** Remove a git worktree. */
11
10
  export declare function removeWorktree(gitRoot: string, path: string): Promise<void>;
@@ -70,6 +70,11 @@ export declare class SoloEvaluator {
70
70
  * The user chooses which agent in the session performs the evaluation comparison.
71
71
  */
72
72
  triggerEvaluation(soloId: string, evaluatorAgentId: string, evaluatorIndex?: number): Promise<SoloEvaluation>;
73
+ /** Map a spawned member back to its solo run + UI column id (for live delta forwarding). */
74
+ getMemberContext(memberId: string): {
75
+ soloId: string;
76
+ displayId: string;
77
+ } | null;
73
78
  /** Get the current status of a solo session. */
74
79
  getStatus(soloId: string): SoloStatus | null;
75
80
  /** Cancel a running solo session. Kill all agents and clean up worktrees. */
@@ -594,8 +594,6 @@ export interface MemorySearchParams {
594
594
  query: string;
595
595
  /** Max results (default 10). */
596
596
  limit?: number;
597
- /** Filter by userId (defaults to current session user). */
598
- userId?: string;
599
597
  }
600
598
  export interface MemorySearchResult {
601
599
  results: Array<{
@@ -30,6 +30,33 @@ export interface AcpBackendConfig {
30
30
  apiKeyEnvVar?: string;
31
31
  /** Environment variable name for the base URL. */
32
32
  baseUrlEnvVar?: string;
33
+ /** Official-logo key for AgentBrandIcon. */
34
+ brandId?: string;
35
+ /** Real-world usage scenario for grouping in the discover UI (not a capability tag). */
36
+ category?: "coding" | "writing" | "design" | "creative" | "research" | "assistant";
37
+ /** How the agent is driven in-app: "acp" via the ACP adapter, "custom" via a dedicated non-ACP adapter. */
38
+ integration?: "acp" | "custom";
39
+ /** Where the injected key comes from: gateway-issued (llmrouter), the user's direct third-party key, or none (OAuth). */
40
+ keySource?: "llmrouter" | "direct" | "none";
41
+ /** Auth model: "key" = login-free key injection; "oauth" = no key bypass, user account required. */
42
+ authMode?: "key" | "oauth";
43
+ /** One-click install spec (official commands per platform). */
44
+ install?: AgentInstallSpec;
45
+ /** Short UI note (e.g. "需 GitHub 账号", "非 ACP,自定义适配器驱动"). */
46
+ note?: string;
47
+ }
48
+ /** Official install commands for the one-click installer (no self-hosted CDN — runs upstream). */
49
+ export interface AgentInstallSpec {
50
+ npm?: string;
51
+ brew?: string;
52
+ curl?: string;
53
+ pip?: string;
54
+ /** Executable name after install (probe target). */
55
+ cliCommand: string;
56
+ /** Probe command (default `<cliCommand> --version`). */
57
+ verifyCmd?: string;
58
+ /** Official docs / source URL (shown in the install confirmation). */
59
+ docsUrl: string;
33
60
  }
34
61
  /**
35
62
  * Extended descriptor for external agents,
@@ -56,7 +56,6 @@ export interface MemoryCandidateWire {
56
56
  reason?: string;
57
57
  }
58
58
  export interface MemoryTextMutationParams {
59
- userId?: string;
60
59
  text?: string;
61
60
  category?: string;
62
61
  importance?: number;
@@ -583,7 +582,6 @@ export interface GatewayRpcMethodMap {
583
582
  };
584
583
  "memory.atlas": {
585
584
  params: {
586
- userId?: string;
587
585
  pageSize?: number;
588
586
  windowStartAt?: number;
589
587
  windowEndAt?: number;
@@ -650,7 +648,6 @@ export interface GatewayRpcMethodMap {
650
648
  };
651
649
  "memory.activity": {
652
650
  params: {
653
- userId?: string;
654
651
  days?: number;
655
652
  };
656
653
  result: {
@@ -669,7 +666,6 @@ export interface GatewayRpcMethodMap {
669
666
  };
670
667
  "memory.observe": {
671
668
  params: {
672
- userId?: string;
673
669
  text: string;
674
670
  category?: string;
675
671
  importance?: number;
@@ -708,7 +704,6 @@ export interface GatewayRpcMethodMap {
708
704
  };
709
705
  "memory.update": {
710
706
  params: {
711
- userId?: string;
712
707
  id: string;
713
708
  text?: string;
714
709
  category?: string;
@@ -724,7 +719,6 @@ export interface GatewayRpcMethodMap {
724
719
  };
725
720
  "memory.attachment.adopt": {
726
721
  params: {
727
- userId?: string;
728
722
  tempUrl: string;
729
723
  filename?: string;
730
724
  mimeType?: string;
@@ -387,6 +387,99 @@ export interface AgentsErrorNotification {
387
387
  error: string;
388
388
  retriesLeft: number;
389
389
  }
390
+ /** One-click install progress (state transitions + streamed log lines + final result). */
391
+ export interface AgentInstallProgressNotification {
392
+ agentId: string;
393
+ event: {
394
+ type: "state";
395
+ state: "installing" | "verifying" | "installed" | "failed";
396
+ } | {
397
+ type: "log";
398
+ stream: "stdout" | "stderr";
399
+ chunk: string;
400
+ } | {
401
+ type: "done";
402
+ ok: boolean;
403
+ error?: string;
404
+ };
405
+ }
406
+ /**
407
+ * Live streaming chunk from an EXTERNAL agent's prompt turn (agents.prompt). Echoes the gateway's
408
+ * turn identity (sessionId/turnId/requestId passed in agents.prompt params) so the gateway can
409
+ * re-emit it on the same turn.delta path the main agent uses — no separate frontend handling.
410
+ */
411
+ export interface AgentsPromptDeltaNotification {
412
+ agentId: string;
413
+ sessionId?: string;
414
+ turnId?: string;
415
+ requestId?: string;
416
+ text: string;
417
+ /** When true this chunk is the agent's reasoning/thinking (→ turn.reasoning_delta), not reply text. */
418
+ reasoning?: boolean;
419
+ }
420
+ /**
421
+ * Live tool-call lifecycle from an EXTERNAL agent's prompt turn — same echo-the-turn-identity
422
+ * mechanism as AgentsPromptDeltaNotification, so the gateway re-emits it on the main
423
+ * turn.tool_call / turn.tool_result path and the UI shows external tools running, no extra handling.
424
+ */
425
+ export interface AgentsPromptToolNotification {
426
+ agentId: string;
427
+ sessionId?: string;
428
+ turnId?: string;
429
+ requestId?: string;
430
+ event: {
431
+ kind: "call";
432
+ callId: string;
433
+ name: string;
434
+ arguments?: string;
435
+ inputSummary?: string;
436
+ } | {
437
+ kind: "result";
438
+ callId: string;
439
+ name: string;
440
+ ok: boolean;
441
+ outputPreview?: string;
442
+ error?: string;
443
+ };
444
+ }
445
+ /**
446
+ * Plan/task update from an EXTERNAL agent's prompt turn (ACP plan steps). The gateway maps the
447
+ * steps onto the main turn.task_updated path so the UI shows the agent's plan as a task list.
448
+ */
449
+ export interface AgentsPromptPlanNotification {
450
+ agentId: string;
451
+ sessionId?: string;
452
+ turnId?: string;
453
+ requestId?: string;
454
+ steps: Array<{
455
+ content?: string;
456
+ title?: string;
457
+ status?: string;
458
+ priority?: string;
459
+ }>;
460
+ }
461
+ /**
462
+ * Interactive permission request from an EXTERNAL agent's prompt turn. Echoes the gateway's turn
463
+ * identity so the gateway re-emits it on the main turn.approval_request path → the UI shows an
464
+ * approval card; the user's decision returns via the agents.approvalResponse RPC.
465
+ */
466
+ export interface AgentsPromptApprovalNotification {
467
+ agentId: string;
468
+ sessionId?: string;
469
+ turnId?: string;
470
+ requestId?: string;
471
+ approvalId: string;
472
+ callId?: string;
473
+ toolName: string;
474
+ arguments?: string;
475
+ message?: string;
476
+ category?: string;
477
+ options?: Array<{
478
+ optionId: string;
479
+ name?: string;
480
+ kind?: string;
481
+ }>;
482
+ }
390
483
  /** Solo agent progress. */
391
484
  export interface SoloProgressNotification {
392
485
  soloId: string;
@@ -761,6 +854,11 @@ export interface NotificationMethodMap {
761
854
  "pong": PongNotification;
762
855
  "agents.status": AgentsStatusNotification;
763
856
  "agents.error": AgentsErrorNotification;
857
+ "agent.install.progress": AgentInstallProgressNotification;
858
+ "agents.prompt.delta": AgentsPromptDeltaNotification;
859
+ "agents.prompt.tool": AgentsPromptToolNotification;
860
+ "agents.prompt.approval": AgentsPromptApprovalNotification;
861
+ "agents.prompt.plan": AgentsPromptPlanNotification;
764
862
  "solo.progress": SoloProgressNotification;
765
863
  "solo.evaluation": SoloEvaluationNotification;
766
864
  "solo.agentDelta": SoloAgentDeltaNotification;
@@ -8,6 +8,11 @@
8
8
  */
9
9
  import type { AcpBackendConfig, AgentDescriptor, AgentConfigStoreData } from "../../protocol/wire/acp-agent-management.js";
10
10
  export declare const ACP_BACKENDS: Record<string, AcpBackendConfig>;
11
+ export declare const AGENT_CATALOG_IDS: readonly ["claude", "codex", "gemini", "copilot", "cursor", "qwen", "kimi", "glm", "opencode", "kiro", "qoder", "openclaw", "hermes"];
12
+ /** Merged catalog entry (base ACP config + grid/install metadata) for one id. */
13
+ export declare function getCatalogEntry(id: string): AcpBackendConfig | undefined;
14
+ /** Ordered curated catalog: base ACP config merged with grid/install metadata. */
15
+ export declare function getAgentCatalog(): AcpBackendConfig[];
11
16
  export declare function resolveWindowsCopilotLoader(): string | null;
12
17
  export declare function normalizeWindowsAcpCommand(cliPath: string, args: string[]): {
13
18
  cliPath: string;
@@ -33,15 +38,44 @@ export declare function normalizeWindowsAcpCommand(cliPath: string, args: string
33
38
  * does not read). Returns undefined when there is nothing to inject, preserving the
34
39
  * prior `env?: undefined` behavior for un-configured backends.
35
40
  */
36
- export declare function buildBackendEnv(backend: AcpBackendConfig | undefined, customConfig: import("../../protocol/wire/acp-agent-management.js").AgentConfig | undefined): Record<string, string> | undefined;
41
+ export declare function buildBackendEnv(backend: AcpBackendConfig | undefined, customConfig: import("../../protocol/wire/acp-agent-management.js").AgentConfig | undefined, autoEnv?: Record<string, string>): Record<string, string> | undefined;
42
+ /** Unified key sources for login-free catalog injection (wired by the runtime). */
43
+ export interface CatalogKeySources {
44
+ /** Gateway-issued inference key (sk-qllm) for keySource:"llmrouter". */
45
+ llmrouterKey?: string;
46
+ /** Base URL an llmrouter-routed agent should call (our gateway endpoint). */
47
+ llmrouterBaseUrl?: string;
48
+ /** Resolve a provider's direct third-party key for keySource:"direct". */
49
+ getDirectKey?: (providerId: string) => string | null | undefined;
50
+ /** The host's active text-generation model (deepseek / volcengine / …) to "pass through" to
51
+ * OpenAI-compatible spawned agents (codex/qwen/opencode) that resolve no key of their own — so
52
+ * they run on the configured key+baseUrl+model with zero per-agent credentials. */
53
+ passthroughModel?: {
54
+ apiKey: string;
55
+ baseUrl?: string;
56
+ model?: string;
57
+ };
58
+ }
59
+ /**
60
+ * Resolve the login-free key env for a catalog agent from the unified key sources.
61
+ * "set key once in our system → all agents" — the user never logs into each CLI.
62
+ * - authMode "oauth" / keySource "none" → {} (no bypass; account required)
63
+ * - keySource "llmrouter" → gateway sk-qllm + gateway baseUrl (agent calls our gateway)
64
+ * - keySource "direct" → the user's provider key (agent calls the provider directly)
65
+ * Only emits env vars the backend actually declares (apiKeyEnvVar / baseUrlEnvVar).
66
+ */
67
+ export declare function resolveCatalogKeyEnv(entry: AcpBackendConfig | undefined, sources: CatalogKeySources): Record<string, string>;
37
68
  export declare class AcpDetector {
38
69
  private cache;
39
70
  private configStore;
71
+ private keySources;
40
72
  /**
41
73
  * Provide the config store data so detector can populate `hasConfig` field
42
74
  * and include user-registered custom agents.
43
75
  */
44
76
  setConfigStore(data: AgentConfigStoreData): void;
77
+ /** Wire the unified key sources for login-free catalog injection. */
78
+ setKeySources(sources: CatalogKeySources): void;
45
79
  /**
46
80
  * Scan for installed ACP agent CLIs.
47
81
  * @param force - Clear cache and re-scan.
@@ -54,6 +88,18 @@ export declare class AcpDetector {
54
88
  private detectBackend;
55
89
  private detectCustomAgent;
56
90
  private hasAgentConfig;
91
+ /**
92
+ * Descriptor for qlogicagent participating in Solo/Product as a self-hosted ACP agent.
93
+ *
94
+ * qlogicagent's own CLI speaks ACP by default (cli/main.ts: ACP server unless --no-acp), so a
95
+ * participant is just another `node <cli.js>` process. The child reads the SAME owner-profile
96
+ * settings.json (model-registry), so it runs with the configured provider (deepseek / volcengine /
97
+ * llmrouter) — that is how the configured model "passes through" to every qlogicagent instance,
98
+ * with no per-agent key. We forward the owner-profile + llmrouter env so the child resolves its
99
+ * model identically to this (host) process. This lets Solo run N independent qlogicagent sessions
100
+ * in parallel (each its own worktree) — the core "多个 qlogicagent = 多会话并行" use case.
101
+ */
102
+ private buildSelfDescriptor;
57
103
  /**
58
104
  * Build an ExternalAgentDescriptor suitable for AgentProcessManager.spawn().
59
105
  * Returns null if the agent is not available.
@@ -1,6 +1,14 @@
1
1
  import type { AcpHostRequestHandler } from "./acp-protocol-adapter.js";
2
2
  interface HostHandlerOpts {
3
3
  cwd: string;
4
+ /**
5
+ * Optional interactive permission resolver. When provided, session/request_permission is routed
6
+ * here (forwarded to the user) instead of auto-allowing. Returning a chosen optionId responds to
7
+ * the external agent. If omitted, permissions auto-allow (host-trust baseline for sub-agents).
8
+ */
9
+ requestPermission?: (params: Record<string, unknown>) => Promise<{
10
+ optionId: string;
11
+ }>;
4
12
  }
5
13
  /**
6
14
  * Host-side reverse-RPC handler for an EXTERNAL ACP agent (codex/copilot/…).
@@ -12,7 +20,7 @@ interface HostHandlerOpts {
12
20
  * disposeAll().
13
21
  *
14
22
  * Terminal response shapes align with the official ACP schema
15
- * (@zed-industries/agent-client-protocol):
23
+ * (@agentclientprotocol/sdk):
16
24
  * - createTerminal -> { terminalId }
17
25
  * - terminalOutput -> { output, truncated, exitStatus: {exitCode, signal} | null }
18
26
  * - waitForTerminalExit -> { exitCode, signal }
@@ -1,25 +1,25 @@
1
1
  /**
2
- * ACP Protocol Adapter — translates between qlogicagent's internal JSON-RPC
3
- * protocol and the ACP (Agent-Client Protocol) wire format.
2
+ * ACP Protocol Adapter — a thin wrapper over the official @agentclientprotocol/sdk
3
+ * `ClientSideConnection`. qlogicagent is the ACP *client* (host); spawned external
4
+ * agents (codex-acp, claude-agent-acp, openclaw, …) are the ACP *agents*.
4
5
  *
5
- * Aligned with @agentclientprotocol/sdk v0.18.2 (PROTOCOL_VERSION = 1).
6
- * Reference: AionUI ClientSideConnection, @agentclientprotocol/sdk.
6
+ * The SDK owns the wire protocol entirely: JSON-RPC 2.0 / NDJSON framing, request↔response
7
+ * correlation, and the reverse-RPC dispatch. This module owns only the three concerns the SDK
8
+ * leaves to the host:
9
+ * 1. bridging a Node ChildProcess's stdio to the SDK's web-stream transport (ndJsonStream)
10
+ * 2. mapping our loose `AcpHostRequestHandler` onto the typed SDK `Client` interface
11
+ * 3. translating SDK `session/update` notifications into qlogicagent's internal `turn.*` methods
7
12
  *
8
- * ACP = JSON-RPC 2.0 over NDJSON stdio:
9
- * HOST AGENT (requests):
10
- * initialize, session/new, session/prompt, session/close, session/resume
11
- * AGENT → HOST (notifications):
12
- * session/update — carries { sessionId, update: { sessionUpdate: "...", ...payload } }
13
- * AGENT → HOST (requests, host must respond):
14
- * fs/read_text_file, fs/write_text_file, session/request_permission,
15
- * session/elicitation, terminal/create, terminal/output, terminal/release,
16
- * terminal/wait_for_exit, terminal/kill
13
+ * Extension capabilities (clientCapabilities.qlogicagent) are forwarded verbatim — the SDK does
14
+ * not strip outgoing params (validation runs only on the receiving side), so openclaw interop is
15
+ * preserved.
17
16
  */
18
17
  import type { ChildProcess } from "node:child_process";
19
18
  import type { AcpInitializeResult, AcpSessionResult, AcpPromptResponse, McpServerConfig } from "../../protocol/wire/acp-agent-management.js";
20
19
  /**
21
20
  * Handler for agent→host requests (fs, terminal, permission, elicitation).
22
- * Host implementations should implement the methods they support.
21
+ * Host implementations should implement the methods they support. This is intentionally loose
22
+ * (Record-typed) — {@link AcpProtocolAdapter} bridges it onto the SDK's strongly-typed Client.
23
23
  */
24
24
  export interface AcpHostRequestHandler {
25
25
  /** Read a text file from the host filesystem. */
@@ -33,8 +33,10 @@ export interface AcpHostRequestHandler {
33
33
  path: string;
34
34
  content: string;
35
35
  }): Promise<Record<string, unknown> | undefined>;
36
- /** Request permission for a tool call (return selected option). */
37
- requestPermission?(params: Record<string, unknown>): Promise<Record<string, unknown>>;
36
+ /** Request permission for a tool call. Returns the chosen option id (host responsibility). */
37
+ requestPermission?(params: Record<string, unknown>): Promise<{
38
+ optionId: string;
39
+ }>;
38
40
  /** Elicitation request (ask user for input). */
39
41
  elicitation?(params: Record<string, unknown>): Promise<Record<string, unknown>>;
40
42
  /** Create a terminal session. */
@@ -55,73 +57,90 @@ export interface TranslatedNotification {
55
57
  method: string;
56
58
  params: Record<string, unknown>;
57
59
  }
60
+ /** An auth method an agent advertises when it needs the user to log in (subset of ACP shape). */
61
+ export interface AcpAuthMethod {
62
+ id?: string;
63
+ name?: string;
64
+ description?: string;
65
+ }
66
+ /**
67
+ * Thrown when an agent rejects session setup because the user must authenticate first — e.g. kimi
68
+ * returns -32000 "Authentication required" with a `kimi login` authMethod. Carries the agent's
69
+ * advertised authMethods and `retryable = false`, so the spawn layer surfaces it immediately (a
70
+ * missing login never heals by retrying) with an actionable message instead of a generic failure.
71
+ */
72
+ export declare class AcpAuthRequiredError extends Error {
73
+ readonly retryable: false;
74
+ readonly authMethods: AcpAuthMethod[];
75
+ constructor(message: string, authMethods: AcpAuthMethod[]);
76
+ }
77
+ /**
78
+ * Recognise the ACP "authentication required" failure (by code -32000, an authMethods payload, or
79
+ * message text) and convert it to an {@link AcpAuthRequiredError} whose message tells the user how
80
+ * to log in (from the agent's own authMethod description). Returns null for any other error.
81
+ */
82
+ export declare function asAuthRequiredError(err: unknown, fallbackAuthMethods?: AcpAuthMethod[]): AcpAuthRequiredError | null;
58
83
  export declare class AcpProtocolAdapter {
59
- private pendingRpcs;
60
- private rl;
84
+ private conn;
61
85
  private child;
62
86
  private onNotification;
63
87
  private hostHandler;
64
88
  /**
65
89
  * Attach to a child process's stdio for ACP communication.
66
- * Must be called before any RPC methods.
90
+ * Constructs the official ClientSideConnection over the child's stdin/stdout. The connection's
91
+ * read loop starts immediately, so incoming notifications + reverse-RPC are live after this call.
67
92
  *
68
93
  * @param child The spawned ACP agent process
69
- * @param onNotification Callback for agent notifications (session/update, etc.)
94
+ * @param onNotification Callback for agent notifications (raw method + params; caller translates)
70
95
  * @param hostHandler Optional handler for agent→host requests (fs, terminal, permission)
71
96
  */
72
97
  attach(child: ChildProcess, onNotification?: (method: string, params: unknown) => void, hostHandler?: AcpHostRequestHandler): void;
73
98
  /**
74
- * Handle an incoming JSON-RPC request from the agent (reverse-RPC).
75
- * Agent calls host for fs operations, terminal, permissions, etc.
99
+ * Build the SDK Client implementation (agent→host reverse-RPC) from our loose host handler.
100
+ * Re-reads this.hostHandler / this.onNotification on each call so detach() takes effect.
76
101
  */
77
- private handleAgentRequest;
78
- /** Detach from the child process. */
102
+ private buildClient;
103
+ /** Detach from the child process. The SDK read loop ends when the child's stdout closes. */
79
104
  detach(): void;
80
- /** Send a raw JSON-RPC request and await response. */
81
- sendRpc(child: ChildProcess, method: string, params?: unknown, timeoutMs?: number): Promise<unknown>;
105
+ private requireConn;
106
+ /** Race a connection call against a timeout; optionally run a side effect (e.g. cancel) on timeout. */
107
+ private withTimeout;
82
108
  /**
83
- * Perform ACP initialize handshake.
84
- * Sends standard clientInfo + clientCapabilities (aligned with SDK).
109
+ * Perform the ACP initialize handshake. Sends standard clientInfo + clientCapabilities plus the
110
+ * qlogicagent extension capability (forwarded verbatim by the SDK).
85
111
  */
86
- initialize(child: ChildProcess): Promise<AcpInitializeResult>;
112
+ initialize(_child?: ChildProcess): Promise<AcpInitializeResult>;
87
113
  /**
88
- * Create an ACP session.
89
- * Official SDK params: { cwd, mcpServers, additionalDirectories? }
90
- * Optionally injects system prompt as extension field.
114
+ * Create an ACP session (session/new). A non-standard systemPrompt is forwarded under `_meta`
115
+ * (the ACP extension mechanism) so it survives validation on the agent side.
91
116
  */
92
- createSession(child: ChildProcess, options: {
117
+ createSession(_child: ChildProcess, options: {
93
118
  cwd: string;
94
119
  mcpServers?: McpServerConfig[];
95
120
  additionalDirectories?: string[];
96
121
  systemPrompt?: string;
97
122
  }): Promise<AcpSessionResult>;
98
123
  /**
99
- * Send a prompt to a running ACP session.
100
- * Official SDK params: { sessionId, prompt: ContentBlock[], messageId? }
101
- * Official SDK response: { stopReason, usage?, userMessageId? }
102
- *
103
- * Note: Actual content is delivered via session/update notifications DURING
104
- * the prompt execution. The response only signals completion.
124
+ * Send a prompt to a running ACP session (session/prompt). Content streams back via session/update
125
+ * notifications during execution; the response carries only { stopReason, usage? }. On timeout the
126
+ * turn is cancelled (session/cancel) so the agent stops cleanly.
105
127
  */
106
- sendPrompt(child: ChildProcess, sessionId: string, content: string, timeoutMs?: number): Promise<AcpPromptResponse>;
128
+ sendPrompt(_child: ChildProcess, sessionId: string, content: string, timeoutMs?: number): Promise<AcpPromptResponse>;
107
129
  /**
108
- * Resume an existing ACP session (loadSession capability).
109
- * Official SDK method: session/load (same schema as session/new + sessionId).
130
+ * Resume an existing ACP session (session/load — replays history via notifications).
131
+ * Only valid when the agent advertised the loadSession capability.
110
132
  */
111
- resumeSession(child: ChildProcess, sessionId: string, options?: {
133
+ resumeSession(_child: ChildProcess, sessionId: string, options?: {
112
134
  cwd?: string;
113
135
  mcpServers?: McpServerConfig[];
114
136
  }): Promise<AcpSessionResult>;
115
- /**
116
- * Close an ACP session.
117
- */
118
- closeSession(child: ChildProcess, sessionId: string): Promise<void>;
119
137
  /**
120
138
  * Translate an ACP notification into qlogicagent's internal format.
121
139
  * Returns null if the notification is not translatable (handled separately).
122
140
  *
123
- * Standard ACP: agent sends "session/update" notification with
124
- * params: { sessionId, update: { sessionUpdate: "<type>", ...payload } }
141
+ * Standard ACP: agent sends "session/update" with params { sessionId, update: { sessionUpdate, … } }.
142
+ * Used by both the external-agent path (this adapter) and the internal/builtin agent path, so it
143
+ * stays a pure static function decoupled from the connection.
125
144
  */
126
145
  static translateNotification(method: string, params: unknown): TranslatedNotification | null;
127
146
  }