lark-remote 0.1.7 → 0.1.8

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/bridge/approval-coordinator.d.ts +15 -1
  3. package/dist/bridge/approval-coordinator.js +35 -2
  4. package/dist/bridge/index.d.ts +19 -6
  5. package/dist/bridge/index.js +25 -8
  6. package/dist/card/approval-render.js +187 -45
  7. package/dist/card/card-shared.d.ts +6 -4
  8. package/dist/card/card-shared.js +6 -4
  9. package/dist/card/run-renderer.d.ts +7 -0
  10. package/dist/card/run-renderer.js +22 -11
  11. package/dist/config/index.d.ts +24 -1
  12. package/dist/config/index.js +34 -3
  13. package/dist/index.js +28 -14
  14. package/dist/router/card-action-payload.d.ts +30 -0
  15. package/dist/router/card-action-payload.js +39 -0
  16. package/dist/router/config/claude.js +8 -3
  17. package/dist/router/config/dsh.d.ts +51 -0
  18. package/dist/router/config/dsh.js +179 -0
  19. package/dist/router/config/index.d.ts +1 -1
  20. package/dist/router/config/index.js +4 -1
  21. package/dist/router/config/types.d.ts +6 -0
  22. package/dist/router/index.d.ts +20 -1
  23. package/dist/router/index.js +113 -7
  24. package/dist/router/utils.js +12 -2
  25. package/dist/runner/agent-choices-common.js +3 -0
  26. package/dist/runner/claude/session.js +24 -5
  27. package/dist/runner/codex/app-server/protocol-types.d.ts +35 -0
  28. package/dist/runner/codex/app-server/protocol-types.js +1 -1
  29. package/dist/runner/codex/app-server/runner.d.ts +10 -0
  30. package/dist/runner/codex/app-server/runner.js +56 -1
  31. package/dist/runner/codex/app-server/translator.d.ts +7 -0
  32. package/dist/runner/codex/app-server/translator.js +45 -0
  33. package/dist/runner/common/acp/protocol-types.d.ts +33 -0
  34. package/dist/runner/common/acp/protocol-types.js +2 -0
  35. package/dist/runner/dsh/client.d.ts +95 -0
  36. package/dist/runner/dsh/client.js +418 -0
  37. package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
  38. package/dist/runner/dsh/fake-dsh-server.js +156 -0
  39. package/dist/runner/dsh/index.d.ts +3 -0
  40. package/dist/runner/dsh/index.js +4 -0
  41. package/dist/runner/dsh/runner.d.ts +57 -0
  42. package/dist/runner/dsh/runner.js +191 -0
  43. package/dist/runner/dsh/translator.d.ts +28 -0
  44. package/dist/runner/dsh/translator.js +98 -0
  45. package/dist/runner/dsh/types.d.ts +155 -0
  46. package/dist/runner/dsh/types.js +14 -0
  47. package/dist/runner/index.d.ts +1 -0
  48. package/dist/runner/index.js +1 -0
  49. package/dist/runner/kimi/acp/runner.js +69 -1
  50. package/dist/runner/kimi/acp/translator.d.ts +14 -0
  51. package/dist/runner/kimi/acp/translator.js +108 -3
  52. package/dist/runner/pi/rpc/runner.d.ts +1 -7
  53. package/dist/runner/pi/rpc/runner.js +2 -11
  54. package/dist/runner/probe.js +6 -1
  55. package/dist/runner/question-common.d.ts +26 -0
  56. package/dist/runner/question-common.js +42 -0
  57. package/dist/runner/types.d.ts +63 -10
  58. package/dist/session/claude/sessions.js +37 -15
  59. package/dist/session/dsh/index.d.ts +1 -0
  60. package/dist/session/dsh/index.js +2 -0
  61. package/dist/session/dsh/sessions.d.ts +39 -0
  62. package/dist/session/dsh/sessions.js +237 -0
  63. package/dist/session/index.d.ts +1 -0
  64. package/dist/session/index.js +1 -0
  65. package/dist/workspace/index.d.ts +4 -0
  66. package/dist/workspace/index.js +8 -1
  67. package/package.json +2 -1
@@ -41,10 +41,10 @@ export const ServerRequestMethod = {
41
41
  COMMAND_EXECUTION_APPROVAL: 'item/commandExecution/requestApproval',
42
42
  FILE_CHANGE_APPROVAL: 'item/fileChange/requestApproval',
43
43
  PERMISSIONS_APPROVAL: 'item/permissions/requestApproval',
44
+ REQUEST_USER_INPUT: 'item/tool/requestUserInput',
44
45
  };
45
46
  /** Unsupported / ignored server request methods. */
46
47
  export const UNSUPPORTED_SERVER_REQUEST_METHODS = new Set([
47
- 'item/tool/requestUserInput',
48
48
  'mcpServer/elicitation/request',
49
49
  'item/tool/call',
50
50
  'account/chatgptAuthTokens/refresh',
@@ -43,6 +43,16 @@ export interface CodexAppServerRunnerOptions {
43
43
  * object used by `thread/settings/update` (`sandboxPolicy`).
44
44
  */
45
45
  export declare function sandboxModeToSandboxPolicy(mode: SandboxMode): SandboxPolicy;
46
+ /**
47
+ * Codex 官方 feature override(thread/start·resume 的 `config` 字段):
48
+ * 让 `request_user_input` 在 Default 协作模式下也可用,无需改 codex
49
+ * config.toml。协议键与 openai/codex 的 feature registry 一致
50
+ * (key `default_mode_request_user_input`),app-server 官方测试
51
+ * (turn_start.rs)验证同一路径。
52
+ */
53
+ export declare const THREAD_CONFIG_DEFAULT_MODE_REQUEST_USER_INPUT: {
54
+ readonly 'features.default_mode_request_user_input': true;
55
+ };
46
56
  export declare class CodexAppServerRunner extends ConnectionBasedRunner<JsonRpcClient<InitializeResult>, TranslatorEvent> {
47
57
  private connectionManager;
48
58
  private currentTranslator;
@@ -13,6 +13,7 @@
13
13
  import { ConnectionManager, } from '../../common/jsonrpc/connection-manager.js';
14
14
  import { CodexAppServerTranslator } from './translator.js';
15
15
  import { RpcErrorCode } from '../../common/acp/protocol-types.js';
16
+ import { mapAnswersByIndex } from '../../question-common.js';
16
17
  import { getLogger } from '../../../logger/index.js';
17
18
  import { ConnectionBasedRunner } from '../../common/connection-based-runner.js';
18
19
  /**
@@ -34,6 +35,39 @@ function buildApprovalResponse(action, view) {
34
35
  }
35
36
  return { decision: action };
36
37
  }
38
+ /**
39
+ * 构造 request_user_input 响应。answer → 按问题 id 回编;decline/cancel →
40
+ * 空 answers。answers 值与问题 options 无强校验(Codex 接受任意字符串,
41
+ * 包括自由文本题的自定义答案)。
42
+ */
43
+ function buildRequestUserInputResponse(action, questions, response) {
44
+ if (action !== 'answer') {
45
+ return { answers: {} };
46
+ }
47
+ const answers = response.answers;
48
+ if (!answers) {
49
+ return { answers: {} };
50
+ }
51
+ const notes = response.notes;
52
+ const byIndex = mapAnswersByIndex(questions ?? [], answers);
53
+ const result = {};
54
+ (questions ?? []).forEach((q, i) => {
55
+ const value = byIndex[i];
56
+ if (!q.id || value === undefined)
57
+ return;
58
+ const values = Array.isArray(value) ? value : [value];
59
+ const cleaned = values.filter((v) => typeof v === 'string' && v.length > 0);
60
+ // Codex user_note:补充说明编码为 "user_note: <text>" 条目(2026-08-18
61
+ // live 验证 deepseek-v4-flash 明确理解并遵守该格式)。
62
+ const note = notes?.[q.question]?.trim();
63
+ if (note)
64
+ cleaned.push(`user_note: ${note}`);
65
+ if (cleaned.length === 0)
66
+ return;
67
+ result[q.id] = { answers: cleaned };
68
+ });
69
+ return { answers: result };
70
+ }
37
71
  /**
38
72
  * Map the client-side SandboxMode enum to the response-side SandboxPolicy
39
73
  * object used by `thread/settings/update` (`sandboxPolicy`).
@@ -54,6 +88,16 @@ export function sandboxModeToSandboxPolicy(mode) {
54
88
  return { type: 'dangerFullAccess' };
55
89
  }
56
90
  }
91
+ /**
92
+ * Codex 官方 feature override(thread/start·resume 的 `config` 字段):
93
+ * 让 `request_user_input` 在 Default 协作模式下也可用,无需改 codex
94
+ * config.toml。协议键与 openai/codex 的 feature registry 一致
95
+ * (key `default_mode_request_user_input`),app-server 官方测试
96
+ * (turn_start.rs)验证同一路径。
97
+ */
98
+ export const THREAD_CONFIG_DEFAULT_MODE_REQUEST_USER_INPUT = {
99
+ 'features.default_mode_request_user_input': true,
100
+ };
57
101
  export class CodexAppServerRunner extends ConnectionBasedRunner {
58
102
  connectionManager;
59
103
  currentTranslator = null;
@@ -177,7 +221,15 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
177
221
  if (!client || !pending)
178
222
  return;
179
223
  const action = response?.action ?? 'decline';
180
- if (pending.kind === 'permissions') {
224
+ if (pending.kind === 'question') {
225
+ // AskUserQuestion 回编:文本 key 答案按问题顺序展开后映射回问题 id
226
+ // ({answers:{id:{answers:[值]}}});跳过/拒绝/取消 → 空 answers
227
+ // (Codex 视为 unanswered,turn 继续,对齐 cc-connect deny 语义)。
228
+ const questions = pending.view.questions ?? [];
229
+ const answerPayload = buildRequestUserInputResponse(action, questions, response);
230
+ client.respond(requestId, answerPayload);
231
+ }
232
+ else if (pending.kind === 'permissions') {
181
233
  // 权限审批的响应没有 decision 字段(真实协议只回 granted profile +
182
234
  // scope):decline/cancel 必须返回空授权(拒绝全部),否则会把用户已
183
235
  // 勾选的条目当作授予返回(2026-08-12 review 发现:勾选后点「拒绝」实
@@ -258,6 +310,9 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
258
310
  ...(this.modelProvider ? { modelProvider: this.modelProvider } : {}),
259
311
  ...(this.approvalPolicyConfig ? { approvalPolicy: this.approvalPolicyConfig } : {}),
260
312
  ...(this.sandboxConfig ? { sandbox: this.sandboxConfig } : {}),
313
+ // Default 协作模式下默认启用 request_user_input(Ask User Question),
314
+ // 协议级 override,不依赖 codex config.toml。
315
+ config: THREAD_CONFIG_DEFAULT_MODE_REQUEST_USER_INPUT,
261
316
  };
262
317
  return params;
263
318
  }
@@ -43,6 +43,13 @@ export declare class CodexAppServerTranslator {
43
43
  * Handle a server request (incoming JSON-RPC request from server, e.g. approval).
44
44
  */
45
45
  handleServerRequest(id: number | string, method: string, params: unknown): TranslatorEvent[];
46
+ /**
47
+ * Codex AskUserQuestion:item/tool/requestUserInput → question 审批事件。
48
+ * 问题 id 保留在 UserQuestion.id(回编时文本 → id 映射),options 为
49
+ * null/空 → 自由文本题(空数组驱动卡片渲染输入框);autoResolutionMs
50
+ * 透传为 per-request 超时(coordinator 优先使用)。
51
+ */
52
+ private handleRequestUserInput;
46
53
  /**
47
54
  * Synthesize a ResultEvent from the current turn state.
48
55
  * Called when the turn completes.
@@ -7,6 +7,7 @@
7
7
  * completes. Method names and parameter shapes follow the real Codex
8
8
  * app-server v2 protocol (see protocol-types.ts).
9
9
  */
10
+ import { makeQuestionApprovalEvent } from '../../question-common.js';
10
11
  import { NotificationMethod, ServerRequestMethod, UNSUPPORTED_SERVER_REQUEST_METHODS, } from './protocol-types.js';
11
12
  // =============================================================================
12
13
  // Translator
@@ -86,10 +87,33 @@ export class CodexAppServerTranslator {
86
87
  return this.handleFileApproval(id, params);
87
88
  case ServerRequestMethod.PERMISSIONS_APPROVAL:
88
89
  return this.handlePermissionsApproval(id, params);
90
+ case ServerRequestMethod.REQUEST_USER_INPUT:
91
+ return this.handleRequestUserInput(id, params);
89
92
  default:
90
93
  return [];
91
94
  }
92
95
  }
96
+ /**
97
+ * Codex AskUserQuestion:item/tool/requestUserInput → question 审批事件。
98
+ * 问题 id 保留在 UserQuestion.id(回编时文本 → id 映射),options 为
99
+ * null/空 → 自由文本题(空数组驱动卡片渲染输入框);autoResolutionMs
100
+ * 透传为 per-request 超时(coordinator 优先使用)。
101
+ */
102
+ handleRequestUserInput(requestId, params) {
103
+ const questions = params.questions
104
+ .map((q) => requestUserInputQuestionToUserQuestion(q))
105
+ .filter((q) => q !== null);
106
+ if (questions.length === 0) {
107
+ // 无有效问题:不渲染空提问卡,返回 [] 由 runner 以 method-not-found
108
+ // 拒绝(服务端视作请求失败,不会悬挂)。
109
+ return [];
110
+ }
111
+ return [
112
+ makeQuestionApprovalEvent(requestId, questions, params.threadId, {
113
+ timeoutMs: params.autoResolutionMs ?? undefined,
114
+ }),
115
+ ];
116
+ }
93
117
  /**
94
118
  * Synthesize a ResultEvent from the current turn state.
95
119
  * Called when the turn completes.
@@ -713,4 +737,25 @@ function normalizeCommandDecisions(list) {
713
737
  ...(Object.keys(payloads).length > 0 ? { payloads } : {}),
714
738
  };
715
739
  }
740
+ /** Codex wire question → 统一 UserQuestion(options null/空 → 自由文本题)。 */
741
+ function requestUserInputQuestionToUserQuestion(q) {
742
+ const question = q.question?.trim();
743
+ if (!question)
744
+ return null;
745
+ const options = (q.options ?? []).map((opt) => ({
746
+ label: opt.label,
747
+ ...(opt.description ? { description: opt.description } : {}),
748
+ }));
749
+ return {
750
+ id: q.id,
751
+ header: q.header || undefined,
752
+ question,
753
+ isOther: q.isOther,
754
+ isSecret: q.isSecret,
755
+ options,
756
+ // Codex user_note:选项题渲染「补充说明(可选)」输入(live 验证模型理解
757
+ // "user_note:" 条目);自由文本题(options 空)不需要。
758
+ ...(options.length > 0 ? { allowNote: true } : {}),
759
+ };
760
+ }
716
761
  //# sourceMappingURL=translator.js.map
@@ -207,6 +207,8 @@ export interface SessionUpdateNotification {
207
207
  }
208
208
  export declare const ServerRequestMethod: {
209
209
  readonly REQUEST_PERMISSION: "session/request_permission";
210
+ /** Kimi AskUserQuestion elicitation form(客户端广告 elicitation.form 时启用)。 */
211
+ readonly ELICITATION_CREATE: "elicitation/create";
210
212
  };
211
213
  export interface PermissionOption {
212
214
  /** Opaque option identifier — echo back in response outcome.optionId. */
@@ -236,3 +238,34 @@ export interface RequestPermissionResponse {
236
238
  optionId?: string;
237
239
  };
238
240
  }
241
+ export interface ElicitationEnumOption {
242
+ const: string;
243
+ title?: string;
244
+ description?: string;
245
+ }
246
+ export interface ElicitationPropertySchema {
247
+ type: 'string' | 'array';
248
+ title?: string;
249
+ description?: string;
250
+ minItems?: number;
251
+ oneOf?: ElicitationEnumOption[];
252
+ items?: {
253
+ anyOf?: ElicitationEnumOption[];
254
+ };
255
+ }
256
+ export interface ElicitationCreateParams {
257
+ sessionId: string;
258
+ toolCallId?: string;
259
+ mode: 'form';
260
+ message?: string;
261
+ requestedSchema: {
262
+ type: 'object';
263
+ required?: string[];
264
+ properties: Record<string, ElicitationPropertySchema>;
265
+ };
266
+ }
267
+ export interface ElicitationCreateResponse {
268
+ action: 'accept' | 'decline' | 'cancel';
269
+ /** accept 时的答案,key 为 q0..qn(按问题顺序)。 */
270
+ content?: Record<string, string | string[]>;
271
+ }
@@ -71,5 +71,7 @@ export const SessionEventType = {
71
71
  // =============================================================================
72
72
  export const ServerRequestMethod = {
73
73
  REQUEST_PERMISSION: 'session/request_permission',
74
+ /** Kimi AskUserQuestion elicitation form(客户端广告 elicitation.form 时启用)。 */
75
+ ELICITATION_CREATE: 'elicitation/create',
74
76
  };
75
77
  //# sourceMappingURL=protocol-types.js.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * DshClient — HTTP client for the DSH Web Host proxy.
3
+ *
4
+ * Two transports:
5
+ * 1. unary POST /api/<method> (client-request → server-response).
6
+ * 2. SSE GET /api/events.mux (all-session aggregated stream). Frames are
7
+ * ServerRequest envelopes; `method` equals the MuxFrame's `type`.
8
+ *
9
+ * The client is stateless apart from the base URL: each `run()` opens its own
10
+ * mux subscription and closes it on terminal. `sessionEvents()` wraps the raw
11
+ * mux with per-session filtering plus reconnect + history-replay dedup
12
+ * (DSH v1 has no `since` resume — reconnection = reopen stream + refetch
13
+ * history, dedup by event seq).
14
+ */
15
+ import type { DshModelCatalogValue, DshModelSelection, DshPresetListValue, DshServerRequest, DshSessionModelsValue, DshStreamItem, DshTokenUsage } from './types.js';
16
+ interface WebSocketMessageEvent {
17
+ data: unknown;
18
+ }
19
+ interface WebSocketLike {
20
+ onmessage: ((event: WebSocketMessageEvent) => void) | null;
21
+ onerror: ((event: unknown) => void) | null;
22
+ onclose: ((event: unknown) => void) | null;
23
+ close(): void;
24
+ }
25
+ export type WebSocketFactory = (url: string) => WebSocketLike;
26
+ export declare class DshError extends Error {
27
+ readonly code: string;
28
+ readonly details: Record<string, unknown>;
29
+ constructor(code: string, message: string, details?: Record<string, unknown>);
30
+ }
31
+ export declare class DshClient {
32
+ readonly baseUrl: string;
33
+ private readonly fetchImpl;
34
+ private readonly webSocketImpl;
35
+ constructor(baseUrl: string, fetchImpl?: typeof fetch, webSocketImpl?: WebSocketFactory);
36
+ /** Perform a unary POST and return the ok value; throws DshError on failure. */
37
+ unary(method: string, payload: unknown): Promise<unknown>;
38
+ /** Create a session. Pass `agentPreset` to pin the session's preset (fixed at
39
+ * creation). */
40
+ createSession(payload: {
41
+ cwd: string;
42
+ agentPreset?: string;
43
+ }): Promise<{
44
+ sessionId: string;
45
+ agentPreset?: string;
46
+ }>;
47
+ /** Align the session's model/reasoningEffort (also persists as global default
48
+ * server-side). Failures are surfaced as DshError for the caller to downgrade. */
49
+ selectModel(payload: DshModelSelection & {
50
+ sessionId: string;
51
+ }): Promise<DshModelSelection>;
52
+ /** Read the session's current model selection + full model catalog. */
53
+ sessionModels(sessionId: string): Promise<DshSessionModelsValue>;
54
+ /** Read the model catalog (session-independent, llm.models). */
55
+ listModels(): Promise<DshModelCatalogValue>;
56
+ /** Read the preset catalog (agentPreset.list). */
57
+ listPresets(): Promise<DshPresetListValue>;
58
+ /**
59
+ * Return the highest event `seq` currently persisted in session.history.
60
+ *
61
+ * The caller snapshots this before `session.prompt`. `session.history` is a
62
+ * full-session replay rather than a delta, so this high-water mark is what
63
+ * lets a subsequent replay skip every already-completed turn.
64
+ */
65
+ latestEventSeq(sessionId: string): Promise<number>;
66
+ /**
67
+ * Raw SSE mux stream: yields ServerRequest envelopes until the stream ends,
68
+ * errors, or `signal` aborts. Does NOT reconnect (the caller owns retry).
69
+ */
70
+ mux(signal: AbortSignal): AsyncGenerator<DshServerRequest>;
71
+ /**
72
+ * WebSocket mux stream. The real DSH `/api/events.mux` endpoint is a
73
+ * WebSocket upgrade, not HTTP SSE: each text message is already a
74
+ * `DshServerRequest` JSON envelope. This yields those envelopes and ends on
75
+ * close/abort, letting `sessionEvents()` reconnect through the same
76
+ * history-replay loop.
77
+ */
78
+ muxWebSocket(signal: AbortSignal): AsyncGenerator<DshServerRequest>;
79
+ /**
80
+ * Replay session.history events that are newer than `state.lastSeq`, updating
81
+ * `state.lastSeq` as it goes. Yields each non-duplicate SessionEvent. Used for
82
+ * both the initial baseline (first subscription) and reconnect gap-fill.
83
+ */
84
+ private replayHistory;
85
+ /**
86
+ * Per-session subscription: yields session events (and approval notices) for
87
+ * `sessionId`, auto-reconnecting on stream drop/error and replaying
88
+ * `session.history` to fill the gap (dedup by seq). Terminates on turn/end
89
+ * or `signal` abort.
90
+ */
91
+ sessionEvents(sessionId: string, cwd: string, signal: AbortSignal, initialSeq?: number): AsyncGenerator<DshStreamItem>;
92
+ /** Extract TokenUsage from an assistant/message data payload (absent → undefined). */
93
+ static usageOf(data: Record<string, unknown>): DshTokenUsage | undefined;
94
+ }
95
+ export {};