@qoder-ai/qoder-agent-sdk 1.0.51 → 1.0.53

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.
@@ -1,16 +1,10 @@
1
1
  import { Transform, type TransformCallback } from 'node:stream';
2
2
  export declare const DEFAULT_MAX_JSONL_FRAME_BYTES: number;
3
- export declare class FrameTooLargeError extends Error {
4
- readonly direction: string;
5
- readonly limit: number;
6
- readonly code = "DAEMON_FRAME_TOO_LARGE";
7
- constructor(direction: string, limit: number);
8
- }
9
3
  export declare class BoundedLineTransform extends Transform {
10
4
  private readonly maxLineBytes;
11
- private readonly direction;
5
+ private readonly source;
12
6
  private bytesSinceNewline;
13
- constructor(maxLineBytes?: number, direction?: string);
7
+ constructor(maxLineBytes?: number, source?: string);
14
8
  _transform(chunk: Buffer | string, encoding: BufferEncoding, callback: TransformCallback): void;
15
9
  }
16
- export declare function assertBoundedSerializedFrame(serializedFrame: string, maxFrameBytes?: number, direction?: string): void;
10
+ export declare function assertBoundedSerializedFrame(serializedFrame: string, maxFrameBytes?: number, source?: string, inputMessageUuid?: string): void;
@@ -0,0 +1,4 @@
1
+ import type { CloseDiagnosticEvent } from '../types/close-diagnostics.js';
2
+ import type { DiagnosticsSink } from './sdk-diagnostics.js';
3
+ /** 观测不能改变原关闭操作的完成、失败或等待语义。 */
4
+ export declare function observeCloseStage<T>(diagnostics: DiagnosticsSink | undefined, component: CloseDiagnosticEvent['component'], stage: CloseDiagnosticEvent['stage'], operation: () => T | PromiseLike<T>): Promise<T>;
@@ -1,6 +1,26 @@
1
1
  /**
2
2
  * Custom error types for the Qoder Agent SDK.
3
3
  */
4
+ import type { SDKResultMessage } from '../protocol/messages.js';
5
+ /** JSONL 帧超过传输预算;输入与输出使用不同错误码,避免把执行端输出问题归因于用户输入。 */
6
+ export declare class FrameTooLargeError extends Error {
7
+ readonly direction: 'input' | 'output';
8
+ readonly limit: number;
9
+ readonly actualBytes: number;
10
+ readonly source: string;
11
+ /** 写入前被拒绝的用户输入 UUID;输出帧或控制帧不提供此字段。 */
12
+ readonly inputMessageUuid?: string | undefined;
13
+ readonly code: 'SDK_INPUT_TOO_LARGE' | 'SDK_OUTPUT_TOO_LARGE';
14
+ constructor(direction: 'input' | 'output', limit: number, actualBytes: number, source: string,
15
+ /** 写入前被拒绝的用户输入 UUID;输出帧或控制帧不提供此字段。 */
16
+ inputMessageUuid?: string | undefined);
17
+ }
18
+ /** CLI 在初始化完成前返回失败结果时抛出,保留原始提示、错误码和结果。 */
19
+ export declare class QoderCliInitializationError extends Error {
20
+ readonly code: number | string | undefined;
21
+ readonly result: SDKResultMessage;
22
+ constructor(result: SDKResultMessage);
23
+ }
4
24
  /**
5
25
  * Thrown when an operation is aborted, typically via an AbortSignal.
6
26
  * Mirrors the built-in DOMException AbortError semantics.
@@ -1,8 +1,11 @@
1
+ import type { CloseDiagnosticEvent } from '../types/close-diagnostics.js';
1
2
  /** Minimal internal diagnostics sink used by query runtimes. */
2
3
  export interface DiagnosticsSink {
3
4
  record(message: string, error?: unknown): void;
5
+ recordClose?(event: Omit<CloseDiagnosticEvent, 'correlationId'>): void;
4
6
  }
5
7
  export type SdkDiagnosticsOptions = {
8
+ onCloseDiagnostic?: (event: CloseDiagnosticEvent) => void;
6
9
  debug?: boolean;
7
10
  debugFile?: string;
8
11
  stderr?: (data: string) => void;
@@ -10,11 +13,15 @@ export type SdkDiagnosticsOptions = {
10
13
  export declare function isSdkDiagnosticsEnabled(options: SdkDiagnosticsOptions): boolean;
11
14
  export declare function resolveSdkDiagnosticsFilePath(options: SdkDiagnosticsOptions): string | undefined;
12
15
  export declare class SdkDiagnostics implements DiagnosticsSink {
16
+ private readonly correlationId;
17
+ private readonly onCloseDiagnostic?;
13
18
  private readonly enabled;
14
19
  private readonly debugFile?;
15
20
  private readonly stderr?;
16
21
  constructor(options?: SdkDiagnosticsOptions);
17
22
  record(message: string, error?: unknown): void;
23
+ /** 独立于 debug/stderr 的安全通道,适用于禁用原始诊断的 BYOK 宿主。 */
24
+ recordClose(event: Omit<CloseDiagnosticEvent, 'correlationId'>): void;
18
25
  get filePath(): string | undefined;
19
26
  private writeToFile;
20
27
  private writeToStderr;
@@ -2,6 +2,7 @@ import type { Readable, Writable } from "node:stream";
2
2
  import { type DaemonCommandResultMap, type DaemonCommandParamsMap, type DaemonControlRequestFrame, type DaemonQueryMethod, type DaemonWireSessionMessage, type JsonObject, type JsonValue, type OpenSessionParams, type RenameSessionResult } from "./protocol.js";
3
3
  import type { SdkAuthPayload } from "../types/auth.js";
4
4
  import { DaemonSessionTransport, type DaemonSessionTransportOptions } from "./daemon-session-transport.js";
5
+ import type { DiagnosticsSink } from "../core/sdk-diagnostics.js";
5
6
  export declare const SESSION_STORE_FILE_CHECKPOINTING_CAPABILITY = "session_store_file_checkpointing_v1";
6
7
  export declare const SESSION_STORE_FORK_BARRIER_CAPABILITY = "session_store_fork_barrier_v1";
7
8
  export interface DaemonConnectionOptions {
@@ -9,6 +10,23 @@ export interface DaemonConnectionOptions {
9
10
  output: Readable;
10
11
  requestHandler?: DaemonRequestHandler;
11
12
  maxFrameBytes?: number;
13
+ /**
14
+ * 对端为什么没了——由持有子进程的一侧回答。
15
+ *
16
+ * stdout EOF 与子进程 'exit' 是两个独立事件,先后顺序不确定,而 `fail()` 是
17
+ * 「第一个失败者获胜」(`if (this.failure) return`)。若让裸的 EOF 先落定,
18
+ * 退出码与 stderr 尾巴就永久丢失,宿主只拿到一句 "Daemon connection closed"——
19
+ * 这正是联调时最难排查的形态:daemon 明明在 stderr 上写清了原因。
20
+ * 所以 EOF 路径先向进程侧要一次退出上下文;要得到就用它带来的错误落定。
21
+ * 返回 undefined 表示进程确实还活着(真正的半开管道),此时才退回笼统文案。
22
+ */
23
+ describeUnexpectedClose?: () => Promise<Error | undefined>;
24
+ /**
25
+ * SDK 侧诊断输出。daemon 是多会话共用的单进程,一条连接上的请求必须靠
26
+ * `request_id` + `session_id` 才能归属,所以这里每个记录点都带这两个字段。
27
+ * 只打协议元数据(方法名、id、耗时、字节数),绝不打帧原文、凭据与 prompt 正文。
28
+ */
29
+ diagnostics?: DiagnosticsSink;
12
30
  }
13
31
  export interface DaemonInitializeOptions {
14
32
  /** Advertised in addition to the capability this SDK cannot work without. */
@@ -52,6 +70,13 @@ export declare class DaemonConnection {
52
70
  private readonly activeInboundRequestIds;
53
71
  private readonly sessions;
54
72
  private readonly closedSessionIds;
73
+ /**
74
+ * 每个 daemon-bound 会话 open 时携带的工作目录(sessionId → cwd)。daemon 已无 cwd
75
+ * 概念,rename/delete 的磁盘定位目录只能来自请求;宿主调用 renameSession/deleteSession
76
+ * 通常不再显式传 cwd,故这里记住会话自己的 cwd,发请求时自动补上。会话关闭后仍保留,
77
+ * 因为「关掉后再改名/删除历史会话」是正常路径(此时 CLI 侧已无在世 cwd 可回落)。
78
+ */
79
+ private readonly sessionCwds;
55
80
  private readonly pendingSessionMessages;
56
81
  private readonly pendingSessionMessageBytes;
57
82
  private pendingSessionMessageFrameCount;
@@ -66,17 +91,19 @@ export declare class DaemonConnection {
66
91
  private stopping;
67
92
  private failure;
68
93
  private readonly failureListeners;
94
+ private readonly describeUnexpectedClose;
95
+ private readonly diagnostics;
69
96
  constructor(options: DaemonConnectionOptions);
70
97
  initialize(sdkVersion: string, options?: DaemonInitializeOptions): Promise<void>;
71
98
  createSessionTransport(options: DaemonSessionTransportOptions): DaemonSessionTransport;
72
99
  supportsCapability(capability: string): boolean;
73
100
  onFailure(listener: (error: Error) => void): () => void;
74
101
  openSession(params: OpenSessionParams, transport: DaemonSessionTransport): Promise<string>;
75
- listSessions(): Promise<import("./protocol.js").DaemonSessionInfo[]>;
76
- renameSession(sessionId: string, title: string): Promise<RenameSessionResult>;
102
+ listSessions(cwd?: string): Promise<import("./protocol.js").DaemonSessionInfo[]>;
103
+ renameSession(sessionId: string, title: string, cwd?: string): Promise<RenameSessionResult>;
77
104
  sendSessionMessage(message: DaemonWireSessionMessage): Promise<void>;
78
105
  closeSession(sessionId: string): Promise<void>;
79
- deleteSession(localSessionId: string): Promise<void>;
106
+ deleteSession(localSessionId: string, cwd?: string): Promise<void>;
80
107
  /**
81
108
  * Sends one account-level query (`daemon_queries_v1`).
82
109
  *
@@ -87,6 +114,11 @@ export declare class DaemonConnection {
87
114
  */
88
115
  query<Method extends DaemonQueryMethod>(method: Method, params: DaemonCommandParamsMap[Method]): Promise<DaemonCommandResultMap[Method]>;
89
116
  shutdown(timeoutMs?: number): Promise<void>;
117
+ /**
118
+ * EOF 落定前问一次进程侧:对端是不是已经退了、为什么退。
119
+ * 提供者自己带 grace 超时,所以这里不会无限等;它抛错或答不上来就退回笼统文案。
120
+ */
121
+ private resolveUnexpectedClose;
90
122
  fail(error: unknown, notifyListeners?: boolean): void;
91
123
  private request;
92
124
  private readOutput;
@@ -2,11 +2,17 @@ import type { Transport } from '../core/transport.js';
2
2
  import type { StdoutMessage } from '../types/control.js';
3
3
  import type { DaemonWireSessionMessage } from './protocol.js';
4
4
  import type { DaemonConnection } from './daemon-connection.js';
5
+ import type { DiagnosticsSink } from '../core/sdk-diagnostics.js';
5
6
  export type DaemonSessionTransportOptions = {
6
7
  workerArgs: string[];
7
8
  cwd?: string;
8
9
  configDir?: string;
9
10
  cleanup?: () => void;
11
+ /**
12
+ * 由 DaemonConnection.createSessionTransport 注入,调用方不需要传。
13
+ * 会话级记录一律带 sessionId——daemon 是多会话共用一条连接,不带归属的日志无法用。
14
+ */
15
+ diagnostics?: DiagnosticsSink;
10
16
  };
11
17
  export declare class DaemonSessionTransport implements Transport {
12
18
  private readonly connection;
@@ -35,6 +41,8 @@ export declare class DaemonSessionTransport implements Transport {
35
41
  fail(error: unknown): void;
36
42
  private open;
37
43
  private failAndRelease;
44
+ /** 会话级记录一律带 sessionId;daemon 多会话共用一条连接,不带归属无法排查。 */
45
+ private record;
38
46
  private startRelease;
39
47
  private releaseSessionOnce;
40
48
  private releaseSession;
@@ -44,5 +52,7 @@ export declare class DaemonSessionTransport implements Transport {
44
52
  private observeCommandLifecycle;
45
53
  private maybeCloseAfterResult;
46
54
  private performClose;
55
+ /** 幂等地置 closed 并跑一次 cleanup;performClose 的成功与失败两条出口共用。 */
56
+ private markClosed;
47
57
  private cleanup;
48
58
  }
@@ -8,6 +8,12 @@ export interface InitDaemonOptions {
8
8
  pathToQoderCLIExecutable?: string;
9
9
  proxy?: string;
10
10
  env?: Record<string, string | undefined>;
11
+ /** 语义同 `StartDaemonOptions.stderr`:daemon 子进程 stderr 的实时回调。 */
12
+ stderr?: (data: string) => void;
13
+ /** 语义同 `StartDaemonOptions.debug`。 */
14
+ debug?: boolean;
15
+ /** 语义同 `StartDaemonOptions.debugFile`。 */
16
+ debugFile?: string;
11
17
  }
12
18
  export type DaemonFallbackReason = 'protocol_version_mismatch' | 'daemon_unavailable';
13
19
  export type DaemonInitResult = {
@@ -106,12 +106,24 @@ export type DaemonSessionInfo = {
106
106
  created_at: string;
107
107
  updated_at: string;
108
108
  };
109
- export type ListSessionsParams = Record<string, never>;
109
+ export type ListSessionsParams = {
110
+ /**
111
+ * 目录过滤:给定时 daemon 只列该工作目录名下的磁盘会话;缺省时全量扫盘、返回所有
112
+ * 目录的会话。daemon 不再持有 cwd 概念,因此「不传目录」的语义是「不过滤」,而不是
113
+ * 「回落到 daemon 启动目录」——后者会让非 daemon 目录下的会话离线不可发现。
114
+ */
115
+ cwd?: string;
116
+ };
110
117
  export type ListSessionsResult = {
111
118
  sessions: DaemonSessionInfo[];
112
119
  };
113
120
  export type RenameSessionParams = {
114
121
  title: string;
122
+ /**
123
+ * 会话所在工作目录。daemon 不再有 cwd,磁盘定位目录只能来自请求;SDK 侧对 daemon-bound
124
+ * 会话会自动带上该会话 open 时的 cwd,宿主也可显式指定以定位任意目录下的历史会话。
125
+ */
126
+ cwd?: string;
115
127
  };
116
128
  export type RenameSessionResult = {
117
129
  session: DaemonSessionInfo;
@@ -119,6 +131,11 @@ export type RenameSessionResult = {
119
131
  export type CloseSessionParams = Record<string, never>;
120
132
  export type DeleteSessionParams = {
121
133
  local_session_id: string;
134
+ /**
135
+ * 会话所在工作目录。同 rename:daemon 无 cwd,磁盘定位目录只能来自请求;SDK 对
136
+ * daemon-bound 会话自动带上其 open 时的 cwd。
137
+ */
138
+ cwd?: string;
122
139
  };
123
140
  export type ShutdownParams = Record<string, never>;
124
141
  /**
@@ -29,16 +29,42 @@ export interface StartDaemonOptions {
29
29
  * resolves once that work is complete.
30
30
  */
31
31
  initializeTimeoutMs?: number;
32
- cwd?: string;
33
32
  pathToQoderCLIExecutable?: string;
34
33
  proxy?: string;
35
34
  env?: Record<string, string | undefined>;
35
+ /**
36
+ * daemon 子进程 stderr 的实时回调。
37
+ *
38
+ * `query()` 的 `Options.stderr` 一直有这个通道,daemon 侧此前没有——于是 daemon
39
+ * 写在 stderr 上的一切宿主都收不到,只能等进程死后从错误对象里捡一个截断的尾巴。
40
+ * 联调时最需要的恰恰是实时看到 daemon 说了什么,所以补上。
41
+ * 与 `QoderCliProcessError.stderr` 互补:这个是流式的、完整的;那个是事后截断的。
42
+ * 回调抛错不影响 daemon,也不会中断 stderr 采集。
43
+ */
44
+ stderr?: (data: string) => void;
45
+ /**
46
+ * 打开 SDK 侧诊断日志,语义与 `query()` 的 `Options.debug` 一致;
47
+ * 环境变量 `QODER_AGENT_SDK_DEBUG` 也能打开。此前 daemon 链路完全没有诊断输出,
48
+ * 而它是唯一承载多会话的进程,出问题时最需要这条通道。
49
+ */
50
+ debug?: boolean;
51
+ /** 诊断日志落盘路径。给了它就隐含打开诊断,且不再走 stderr 回调。 */
52
+ debugFile?: string;
36
53
  }
37
54
  export interface Daemon {
38
55
  readonly state: DaemonState;
39
- listSessions(): Promise<DaemonSessionInfo[]>;
40
- renameSession(localSessionId: string, title: string): Promise<DaemonSessionInfo>;
41
- deleteSession(localSessionId: string): Promise<void>;
56
+ /**
57
+ * 列出会话。`cwd` 给定时只列该工作目录名下的会话;缺省时返回所有目录的全部会话
58
+ * (daemon 无 cwd 概念,「不传目录」即「不过滤」,由 CLI 侧全量扫盘)。
59
+ */
60
+ listSessions(cwd?: string): Promise<DaemonSessionInfo[]>;
61
+ /**
62
+ * 重命名会话。`cwd` 用于在磁盘上定位会话目录;daemon-bound 会话可省略——SDK 会自动
63
+ * 带上该会话 open 时的 cwd。仅当重命名一个并非经本 daemon 打开的历史会话时才需显式传。
64
+ */
65
+ renameSession(localSessionId: string, title: string, cwd?: string): Promise<DaemonSessionInfo>;
66
+ /** 删除会话。`cwd` 语义同 renameSession。 */
67
+ deleteSession(localSessionId: string, cwd?: string): Promise<void>;
42
68
  /**
43
69
  * Account-level queries, answered from the credentials the daemon was
44
70
  * started with — never from a Session's. They require a daemon that accepted
@@ -68,11 +94,30 @@ interface InternalDaemonRuntime {
68
94
  proxy?: string;
69
95
  cliPath?: string;
70
96
  env?: Record<string, string | undefined>;
71
- cwd?: string;
72
97
  };
73
98
  readonly auth: InternalAuthOptions;
74
99
  readonly transportProvider: QueryTransportProvider<ProcessTransportOptions>;
75
100
  }
76
101
  export declare function getDaemonRuntime(daemon: Daemon): InternalDaemonRuntime | undefined;
77
102
  export declare function startDaemon(options: StartDaemonOptions): Promise<Daemon>;
103
+ /**
104
+ * 宿主什么都没配时的默认诊断落盘位置。
105
+ *
106
+ * 联调场景里客户只是正常使用,不会去设 `QODER_AGENT_SDK_DEBUG`,也不会在代码里传
107
+ * `debug`/`debugFile`——而 SdkDiagnostics 三者全无时是彻底静默的,于是 SDK 侧那半条
108
+ * 链路(spawn、握手、请求往返、会话开关)对客户完全不可见。默认落到文件补上这一段。
109
+ *
110
+ * 三个刻意的选择:
111
+ * - **写文件而不是 stderr**:daemon 侧默认往 process.stderr 写会污染宿主输出,
112
+ * 而宿主可能正把 stderr 当协议通道或展示给用户。文件是安全的默认。
113
+ * - **放在 CLI 日志同一个 logs/ 目录下**:`getConfigDir()` 与 CLI 的日志根同源
114
+ * (同样认 QODER_CONFIG_DIR 与品牌目录),排障时两侧日志在一处,不用猜路径。
115
+ * - **文件名带 pid 与时间戳**:一个宿主进程可能反复起停 daemon,共用一个追加文件
116
+ * 会无限增长且多实例互相插话;按次分文件后单个文件的生命周期等于一个 daemon。
117
+ *
118
+ * 显式传了 `debug: true`(意图是走 stderr 回调)时不覆盖宿主的选择不返回默认路径。
119
+ */
120
+ export declare function defaultDaemonDiagnosticsFile(options: {
121
+ debug?: boolean;
122
+ }): string | undefined;
78
123
  export {};
package/dist/index.d.ts CHANGED
@@ -80,7 +80,7 @@ export { submitFeedback } from './feedback.js';
80
80
  export type { SubmitFeedbackParams, SubmitFeedbackOptions, IntegrationMode, } from './feedback.js';
81
81
  export type { HostActionOptions, HostActionParams } from './types/host-action.js';
82
82
  export type { HostAction, HostActionEvent, } from './protocol/feedback.js';
83
- export { AbortError, ControlRequestError, CONTROL_REQUEST_TIMEOUT_CODE, ModelPolicyTimeoutError, ProtocolVersionMismatchError, UnsupportedCliCapabilityError, } from './core/errors.js';
83
+ export { AbortError, ControlRequestError, CONTROL_REQUEST_TIMEOUT_CODE, FrameTooLargeError, QoderCliInitializationError, ModelPolicyTimeoutError, ProtocolVersionMismatchError, UnsupportedCliCapabilityError, } from './core/errors.js';
84
84
  export { DaemonRpcError } from './daemon/daemon-connection.js';
85
85
  export { AuthAccessTokenEnvVarError, AuthServiceAccountEnvVarError, } from './core/runtime-launch-options.js';
86
86
  export { WIRE_PROTOCOL_VERSION } from './protocol/version.js';
@@ -120,4 +120,5 @@ export type { SDKControlGetModelPolicyRequest, SDKControlGetModelPolicyResponse,
120
120
  export type { SDKControlGetByokConfigRequest, SDKControlGetByokConfigResponse, SDKControlValidateByokModelRequest, SDKControlValidateByokModelResponse, SDKControlListByokConfigsRequest, SDKControlListByokConfigsResponse, SDKControlCreateByokConfigRequest, SDKControlCreateByokConfigResponse, SDKControlUpdateByokConfigRequest, SDKControlDeleteByokConfigRequest, SDKControlCheckByokConfigRequest, SDKControlCheckByokConfigResponse, BYOKModelValidationInput, BYOKLocalizedName, BYOKProviderInfo, BYOKModelTypeInfo, BYOKModelInfo, ByokProtocol, ByokModelConfigInfo, CustomByokProviderConfigInfo, ByokConfigInfo, ByokModelConfigInput, CustomByokProviderThinkingMode, CustomByokProviderReasoningEffortLevel, CustomByokProviderThinkingCapabilitiesInput, CustomByokProviderModelCapabilitiesInput, CustomByokProviderModelInput, CustomByokProviderConfigInput, CreateByokConfigInput, UpdateByokModelConfigInput, UpdateCustomByokProviderConfigInput, UpdateByokConfigInput, DeleteByokConfigInput, CheckByokConfigInput, ByokConfigReference, ByokConfigCheckResult, CustomModel, } from './types/byok.js';
121
121
  export { isBashToolBackgroundLaunchResult } from './types/tools.js';
122
122
  export type { AgentInput, AgentOutput, AgentToolCompletionResult, AgentToolBackgroundLaunchResult, AgentToolTeammateSpawnedResult, BashInput, BashToolBackgroundLaunchResult, FileReadInput, FileReadOutput, FileEditInput, FileWriteInput, GlobInput, GrepInput, WebFetchInput, WebSearchInput, AskUserQuestionInput, NotebookEditInput, TaskCreateInput, TaskCreateOutput, TaskGetInput, TaskGetOutput, TaskUpdateInput, TaskUpdateOutput, TaskListInput, TaskListOutput, ExitPlanModeInput, TodoWriteInput, ToolInputSchemas, ToolOutputSchemas, } from './types/tools.js';
123
- export type { FeedbackResult } from './types/account.js';
123
+ export type { FeedbackResult } from './types/account.js';
124
+ export type { CloseDiagnosticEvent } from './types/close-diagnostics.js';