@jingyi0605/codingns 1.0.0-beta.1 → 1.0.0-beta.3

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 (123) hide show
  1. package/dist/public/assets/AdaptiveButlerPage-CLHLiZSY.js +2 -0
  2. package/dist/public/assets/{App-CHsm-VrM.js → App-FYuZfAoO.js} +6 -6
  3. package/dist/public/assets/{BootstrapPage-Bp0KfySv.js → BootstrapPage-3xpnsG-A.js} +1 -1
  4. package/dist/public/assets/ConversationPage-C5_ZqhU3.js +9 -0
  5. package/dist/public/assets/{DesktopDetachPreviewPage-DlDSb_tf.js → DesktopDetachPreviewPage-B77MG82y.js} +1 -1
  6. package/dist/public/assets/{DesktopModal-Csj5AdAN.js → DesktopModal-K5bEcj38.js} +1 -1
  7. package/dist/public/assets/{DesktopWindowPage-CacXiWjV.js → DesktopWindowPage-DbG-RBAf.js} +1 -1
  8. package/dist/public/assets/{FileContextPanel-DzMUQvE5.js → FileContextPanel-C-1yOton.js} +1 -1
  9. package/dist/public/assets/{GitSidebar-C8IC18GH.js → GitSidebar-DFHAPT3S.js} +1 -1
  10. package/dist/public/assets/{MobileCreateSessionSheet-BtqR_hKc.js → MobileCreateSessionSheet-gOyAZ857.js} +1 -1
  11. package/dist/public/assets/{MobileSheet-Co-qPMBD.js → MobileSheet-D0MfNkTv.js} +1 -1
  12. package/dist/public/assets/{PluginAccessOverview-D-KXkcyj.js → PluginAccessOverview-IrYIPLg3.js} +1 -1
  13. package/dist/public/assets/{PluginContainerPage-BKwCIs4r.js → PluginContainerPage-b9LQVuqG.js} +1 -1
  14. package/dist/public/assets/{PluginDetailPage-Djv_swcO.js → PluginDetailPage-BX7gohai.js} +1 -1
  15. package/dist/public/assets/{PluginsListPage-DAECNoN6.js → PluginsListPage-R0SibHMi.js} +1 -1
  16. package/dist/public/assets/{PureConversationPage-DP1X7Hix.js → PureConversationPage-CcakFvo0.js} +1 -1
  17. package/dist/public/assets/{RelayConnectEntryPage-xL_IPH8a.js → RelayConnectEntryPage-cAVq_kNM.js} +1 -1
  18. package/dist/public/assets/{ServerSettingsModal-CyhMgk10.js → ServerSettingsModal-BJwYHdKD.js} +1 -1
  19. package/dist/public/assets/{SessionIndexPage-CVEz50tc.js → SessionIndexPage-CJKRHB_F.js} +1 -1
  20. package/dist/public/assets/SettingsPage-Csp7wEBC.js +2 -0
  21. package/dist/public/assets/{TerminalManagerPanel-wVnoRA8u.js → TerminalManagerPanel-2HG-NyfR.js} +1 -1
  22. package/dist/public/assets/{ToolFilesPage-BospXumK.js → ToolFilesPage-BAZdjNaF.js} +1 -1
  23. package/dist/public/assets/{ToolGitPage-B2zOeCAD.js → ToolGitPage-CGkPmgsz.js} +1 -1
  24. package/dist/public/assets/{ToolProcessesPage-BDShao4b.js → ToolProcessesPage-BHpS7-VH.js} +1 -1
  25. package/dist/public/assets/{ToolsHomePage-s4zH7D9M.js → ToolsHomePage-DDGqYwYU.js} +1 -1
  26. package/dist/public/assets/{WorkbenchLandingPage-D51QCU_u.js → WorkbenchLandingPage-B4aooEYJ.js} +1 -1
  27. package/dist/public/assets/WorkbenchLayout-DrAvGjGK.js +1083 -0
  28. package/dist/public/assets/{WorkbenchModal-YsyEdJ_m.js → WorkbenchModal-BK5ix8YI.js} +1 -1
  29. package/dist/public/assets/WorkbenchShellRoute-DpycFsbp.css +1 -0
  30. package/dist/public/assets/WorkbenchShellRoute-p1FvkoPN.js +1 -0
  31. package/dist/public/assets/{WorkspaceDebugDetailPage-CMjNLqFq.js → WorkspaceDebugDetailPage-CPgAe8dd.js} +1 -1
  32. package/dist/public/assets/{WorkspaceDetailPage-CKxTbPKh.js → WorkspaceDetailPage-YM85dTGg.js} +1 -1
  33. package/dist/public/assets/{WorkspaceHomePage-CEy4ShCu.js → WorkspaceHomePage-DPPvwM93.js} +1 -1
  34. package/dist/public/assets/{client-runtime-manager-BFXU9DmS.js → client-runtime-manager-Clq-8rWh.js} +1 -1
  35. package/dist/public/assets/{host-alias-Zb2xyVrf.js → host-alias-BxzpRYrt.js} +1 -1
  36. package/dist/public/assets/index-BArTKWXk.js +50 -0
  37. package/dist/public/assets/index-CqKepLbu.css +1 -0
  38. package/dist/public/assets/{login-direct-candidate-resolver-DL8DS-Si.js → login-direct-candidate-resolver-BiWwFKnk.js} +1 -1
  39. package/dist/public/assets/peer-host-config-sync-kTom1_pp.js +1 -0
  40. package/dist/public/assets/{plugin-permission-copy-DrLk22m5.js → plugin-permission-copy-1_1wViri.js} +1 -1
  41. package/dist/public/assets/{plugins-api-COF4oh24.js → plugins-api-C0JqR2-W.js} +1 -1
  42. package/dist/public/assets/{preferences-service-CEWNV1w9.js → preferences-service-DWeLWZST.js} +1 -1
  43. package/dist/public/assets/{relay-entry-Cmc8vTlE.js → relay-entry-ClEd3cjN.js} +1 -1
  44. package/dist/public/assets/{useRegisteredDebugTemplates-FdmHG2S4.js → useRegisteredDebugTemplates-wJGOYam9.js} +1 -1
  45. package/dist/public/assets/{workbench-navigation-ED0157V-.js → workbench-navigation-vIWt3Rv_.js} +1 -1
  46. package/dist/public/index.html +2 -2
  47. package/dist/server/modules/client/client-controller.d.ts +1 -0
  48. package/dist/server/modules/client/client-controller.js +4 -0
  49. package/dist/server/modules/client/client-controller.js.map +1 -1
  50. package/dist/server/modules/client/client-service.d.ts +3 -0
  51. package/dist/server/modules/client/client-service.js +3 -0
  52. package/dist/server/modules/client/client-service.js.map +1 -1
  53. package/dist/server/modules/client/npm-global-package-service.js +32 -7
  54. package/dist/server/modules/client/npm-global-package-service.js.map +1 -1
  55. package/dist/server/modules/peer-host/host-ws-proxy-service.js +73 -12
  56. package/dist/server/modules/peer-host/host-ws-proxy-service.js.map +1 -1
  57. package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +2 -0
  58. package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
  59. package/dist/server/modules/provider/provider-discovery-helper-process.js +6 -3
  60. package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
  61. package/dist/server/modules/provider/provider-discovery-runtime.js +4 -1
  62. package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
  63. package/dist/server/modules/sessions/codex-app-server-helper-process.js +84 -20
  64. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  65. package/dist/server/modules/sessions/session-controller.js +2 -1
  66. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  67. package/dist/server/modules/sessions/session-history-service.d.ts +8 -0
  68. package/dist/server/modules/sessions/session-history-service.js +123 -5
  69. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  70. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +27 -0
  71. package/dist/server/modules/sessions/session-live-runtime-service.js +457 -9
  72. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  73. package/dist/server/modules/sessions/session-permission-request-service.d.ts +17 -1
  74. package/dist/server/modules/sessions/session-permission-request-service.js +397 -9
  75. package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
  76. package/dist/server/modules/sessions/session-provider-config-service.js +1 -0
  77. package/dist/server/modules/sessions/session-provider-config-service.js.map +1 -1
  78. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js +15 -1
  79. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js.map +1 -1
  80. package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +1 -0
  81. package/dist/server/modules/tasks/task-helper-process-handlers.js +4 -1
  82. package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -1
  83. package/dist/server/modules/workbench/workbench-service.js +7 -4
  84. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  85. package/dist/server/routes/client.js +1 -0
  86. package/dist/server/routes/client.js.map +1 -1
  87. package/dist/server/shared/utils/peer-host-proxy-log.d.ts +3 -0
  88. package/dist/server/shared/utils/peer-host-proxy-log.js +48 -0
  89. package/dist/server/shared/utils/peer-host-proxy-log.js.map +1 -0
  90. package/dist/server/storage/repositories/session-send-queue-repository.d.ts +1 -0
  91. package/dist/server/storage/repositories/session-send-queue-repository.js +23 -0
  92. package/dist/server/storage/repositories/session-send-queue-repository.js.map +1 -1
  93. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +1 -0
  94. package/node_modules/@codingns/session-sync-core/dist/index.js +1 -0
  95. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  96. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +2 -0
  97. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +93 -12
  98. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  99. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +3 -0
  100. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +116 -2
  101. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  102. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +1 -0
  103. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +19 -8
  104. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
  105. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.d.ts +27 -1
  106. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js +48 -4
  107. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js.map +1 -1
  108. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +53 -15
  109. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  110. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.d.ts +1 -0
  111. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +30 -2
  112. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +1 -1
  113. package/package.json +2 -1
  114. package/scripts/claude-hook-bridge.cjs +167 -0
  115. package/dist/public/assets/AdaptiveButlerPage-BYETYaIe.js +0 -2
  116. package/dist/public/assets/ConversationPage-BCrNml1k.js +0 -9
  117. package/dist/public/assets/SettingsPage-CQNCrgaj.js +0 -2
  118. package/dist/public/assets/WorkbenchLayout-Bo2BbMY6.js +0 -1081
  119. package/dist/public/assets/WorkbenchShellRoute-D3l4aWJS.js +0 -1
  120. package/dist/public/assets/WorkbenchShellRoute-D5fnyF8z.css +0 -1
  121. package/dist/public/assets/index-BilHJjYU.js +0 -50
  122. package/dist/public/assets/index-OR7OITpQ.css +0 -1
  123. package/dist/public/assets/peer-host-config-sync-d2ZcPC5P.js +0 -1
@@ -5,6 +5,8 @@ import { createId } from "../../shared/utils/id.js";
5
5
  import { nowIso } from "../../shared/utils/time.js";
6
6
  import { buildClaudeCompatibleRawStoreRef } from "./claude-compatible-provider-registry.js";
7
7
  const CLAUDE_PRE_TOOL_USE_TIMEOUT_MS = 90_000;
8
+ const CLAUDE_ASK_USER_QUESTION_TIMEOUT_MS = 600_000;
9
+ const CLAUDE_PLAN_APPROVAL_TIMEOUT_MS = 600_000;
8
10
  const OPENCODE_RECONNECT_DELAY_MS = 1_500;
9
11
  export class SessionPermissionRequestService {
10
12
  sessionHistoryService;
@@ -70,11 +72,38 @@ export class SessionPermissionRequestService {
70
72
  }
71
73
  if (request.source.kind === "claude-pre-tool-use") {
72
74
  const action = normalizeText(input.action);
73
- if (action !== "allow" && action !== "allow_session" && action !== "deny") {
75
+ if (action !== "allow" &&
76
+ action !== "allow_session" &&
77
+ action !== "deny" &&
78
+ action !== "submit") {
74
79
  throw new AppError({
75
80
  statusCode: 400,
76
81
  errorCode: "INVALID_INPUT",
77
- detail: "Claude 权限申请只支持 allow、allow_session 或 deny",
82
+ detail: "Claude 请求只支持 allow、allow_session、denysubmit",
83
+ field: "action"
84
+ });
85
+ }
86
+ if (request.kind === "user_input" && action !== "submit") {
87
+ throw new AppError({
88
+ statusCode: 400,
89
+ errorCode: "INVALID_INPUT",
90
+ detail: "Claude 问题请求只支持提交答案",
91
+ field: "action"
92
+ });
93
+ }
94
+ if (request.kind !== "user_input" && action === "submit") {
95
+ throw new AppError({
96
+ statusCode: 400,
97
+ errorCode: "INVALID_INPUT",
98
+ detail: "只有问题请求可以提交答案",
99
+ field: "action"
100
+ });
101
+ }
102
+ if (request.kind === "plan_approval" && action === "allow_session") {
103
+ throw new AppError({
104
+ statusCode: 400,
105
+ errorCode: "INVALID_INPUT",
106
+ detail: "计划审批不支持设置整场会话默认允许",
78
107
  field: "action"
79
108
  });
80
109
  }
@@ -91,7 +120,8 @@ export class SessionPermissionRequestService {
91
120
  });
92
121
  }
93
122
  request.source.resolve({
94
- action: action === "deny" ? "deny" : "allow"
123
+ action: action === "deny" ? "deny" : "allow",
124
+ answers: request.kind === "user_input" ? input.answers : undefined
95
125
  });
96
126
  return await this.markResolved(request, action === "deny" ? "declined" : "approved");
97
127
  }
@@ -240,7 +270,7 @@ export class SessionPermissionRequestService {
240
270
  const timer = setTimeout(() => {
241
271
  resolvedByTimeout = true;
242
272
  resolve({ action: "ask" });
243
- }, CLAUDE_PRE_TOOL_USE_TIMEOUT_MS);
273
+ }, resolveClaudeBlockingRequestTimeoutMs(normalized.kind));
244
274
  const record = {
245
275
  ...normalized,
246
276
  source: {
@@ -273,7 +303,11 @@ export class SessionPermissionRequestService {
273
303
  accepted: true,
274
304
  ignored: false,
275
305
  sessionId: binding.sessionId,
276
- bridgeResponse: buildClaudePreToolUseBridgeResponse(decision.action, buildClaudeDecisionReason(decision.action, normalized.title, resolvedByTimeout))
306
+ bridgeResponse: normalized.kind === "user_input"
307
+ ? buildClaudeAskUserQuestionBridgeResponse(decision.action, decision.answers ?? {}, normalized.questions, payload.tool_input, buildClaudeDecisionReason(decision.action, normalized.title, resolvedByTimeout))
308
+ : normalized.kind === "plan_approval"
309
+ ? buildClaudeExitPlanModeBridgeResponse(decision.action, payload.tool_input, buildClaudeDecisionReason(decision.action, normalized.title, resolvedByTimeout))
310
+ : buildClaudePreToolUseBridgeResponse(decision.action, buildClaudeDecisionReason(decision.action, normalized.title, resolvedByTimeout))
277
311
  };
278
312
  }
279
313
  async handleClaudePermissionRequest(payload, provider = "claude-code") {
@@ -404,6 +438,96 @@ export class SessionPermissionRequestService {
404
438
  : "CodingNS 已拒绝本次权限申请")
405
439
  };
406
440
  }
441
+ async handleClaudeElicitation(payload, provider = "claude-code") {
442
+ logPermissionDebug("claude_permission.elicitation.begin", {
443
+ provider,
444
+ providerSessionId: payload.session_id ?? null,
445
+ cwd: payload.cwd ?? null,
446
+ transcriptPath: payload.transcript_path ?? null,
447
+ title: payload.title ?? null
448
+ });
449
+ const providerSessionId = requireNonEmptyText(payload.session_id, "session_id");
450
+ const workspacePath = requireNonEmptyText(payload.cwd, "cwd");
451
+ const workspace = this.workspaceService.findWorkspaceByPath(workspacePath);
452
+ if (!workspace) {
453
+ return {
454
+ accepted: true,
455
+ ignored: true,
456
+ sessionId: null,
457
+ bridgeResponse: buildClaudePreToolUseBridgeResponse("ask", "未匹配到工作区,回退 Claude 原生征询")
458
+ };
459
+ }
460
+ const transcriptPath = normalizeText(payload.transcript_path) || null;
461
+ const binding = await this.resolveClaudeBinding(provider, providerSessionId, workspace.id, workspace.path, transcriptPath, workspace.ownerUserId ?? null).catch(() => null)
462
+ ?? this.resolveClaudeWorkspaceSessionFallback({
463
+ provider,
464
+ providerSessionId,
465
+ workspaceId: workspace.id,
466
+ workspacePath: workspace.path,
467
+ transcriptPath,
468
+ userId: workspace.ownerUserId ?? null
469
+ })
470
+ ?? (this.resolveActiveClaudeSession
471
+ ? await this.resolveActiveClaudeSession({
472
+ provider,
473
+ providerSessionId,
474
+ workspaceId: workspace.id,
475
+ workspacePath: workspace.path,
476
+ transcriptPath
477
+ }).catch(() => null)
478
+ : null);
479
+ if (!binding) {
480
+ return {
481
+ accepted: true,
482
+ ignored: true,
483
+ sessionId: null,
484
+ bridgeResponse: buildClaudePreToolUseBridgeResponse("ask", "未匹配到会话绑定,回退 Claude 原生征询")
485
+ };
486
+ }
487
+ const now = nowIso();
488
+ const normalized = normalizeClaudeElicitationRequest({
489
+ provider,
490
+ sessionId: binding.sessionId,
491
+ providerSessionId,
492
+ payload,
493
+ createdAt: now
494
+ });
495
+ let resolvedByTimeout = false;
496
+ const decision = await new Promise((resolve) => {
497
+ const timer = setTimeout(() => {
498
+ resolvedByTimeout = true;
499
+ resolve({ action: "deny" });
500
+ }, CLAUDE_ASK_USER_QUESTION_TIMEOUT_MS);
501
+ const record = {
502
+ ...normalized,
503
+ source: {
504
+ kind: "claude-pre-tool-use",
505
+ resolve,
506
+ timer
507
+ }
508
+ };
509
+ this.upsertRequest(record);
510
+ void this.emitEnvelope({
511
+ type: "session.permission_request",
512
+ sessionId: binding.sessionId,
513
+ request: this.toRequestView(record)
514
+ });
515
+ });
516
+ const existing = this.requestsById.get(normalized.id);
517
+ if (existing) {
518
+ await this.markResolved(existing, resolvedByTimeout ? "expired" : "approved");
519
+ }
520
+ return {
521
+ accepted: true,
522
+ ignored: false,
523
+ sessionId: binding.sessionId,
524
+ bridgeResponse: buildClaudeAskUserQuestionBridgeResponse(decision.action, decision.answers ?? {}, normalized.questions, payload, decision.action === "allow"
525
+ ? "用户已提供补充信息"
526
+ : resolvedByTimeout
527
+ ? "用户补充信息超时,回退 Claude 原生处理"
528
+ : "用户拒绝补充信息")
529
+ };
530
+ }
407
531
  ingestCodexServerRequest(sessionId, providerSessionId, request) {
408
532
  const normalized = normalizeCodexServerRequest(sessionId, providerSessionId, request);
409
533
  if (!normalized) {
@@ -1017,7 +1141,7 @@ export function normalizeClaudePreToolUseRequest(input) {
1017
1141
  cwd: normalizeText(toRecord(toolInput)?.cwd) || null,
1018
1142
  paths: normalized.paths,
1019
1143
  permissionProfile: null,
1020
- questions: [],
1144
+ questions: normalized.questions,
1021
1145
  actions: buildClaudeActions({
1022
1146
  kind: normalized.kind,
1023
1147
  command: normalized.command,
@@ -1035,6 +1159,46 @@ export function normalizeClaudePreToolUseRequest(input) {
1035
1159
  }
1036
1160
  };
1037
1161
  }
1162
+ export function normalizeClaudeElicitationRequest(input) {
1163
+ const rawPayload = stringifyPayload(input.payload);
1164
+ const questions = readClaudeElicitationQuestions(input.payload);
1165
+ const requestKey = normalizeText(input.payload.title) ||
1166
+ normalizeText(input.payload.prompt) ||
1167
+ normalizeText(input.payload.question) ||
1168
+ normalizeText(input.payload.message) ||
1169
+ `Elicitation:${hashLike(rawPayload)}`;
1170
+ return {
1171
+ id: `permission-${createId()}`,
1172
+ sessionId: input.sessionId,
1173
+ provider: input.provider,
1174
+ providerSessionId: input.providerSessionId,
1175
+ requestKey,
1176
+ kind: "user_input",
1177
+ status: "pending",
1178
+ title: normalizeText(input.payload.title) || "Claude 需要你补充信息",
1179
+ summary: questions[0]?.question ?? "Claude 需要你补充信息后才能继续",
1180
+ detail: rawPayload,
1181
+ reason: normalizeText(input.payload.reason) || null,
1182
+ toolName: "Elicitation",
1183
+ command: null,
1184
+ cwd: normalizeText(input.payload.cwd) || null,
1185
+ paths: [],
1186
+ permissionProfile: null,
1187
+ questions,
1188
+ actions: [
1189
+ createAction("submit", "提交答案", "primary", "把补充信息交给 Claude 继续处理")
1190
+ ],
1191
+ rawPayload,
1192
+ createdAt: input.createdAt,
1193
+ updatedAt: input.createdAt,
1194
+ resolvedAt: null,
1195
+ source: {
1196
+ kind: "claude-pre-tool-use",
1197
+ resolve: () => undefined,
1198
+ timer: null
1199
+ }
1200
+ };
1201
+ }
1038
1202
  export function normalizeOpenCodePermissionRequest(input) {
1039
1203
  const requestKey = normalizeText(input.permission.id) || createId();
1040
1204
  const title = normalizeText(input.permission.title) || "OpenCode 请求权限";
@@ -1215,6 +1379,7 @@ export function normalizeCodexServerRequest(sessionId, providerSessionId, reques
1215
1379
  question: normalizeText(question.question) || "请输入答案",
1216
1380
  allowOther: Boolean(question.isOther),
1217
1381
  secret: Boolean(question.isSecret),
1382
+ multiSelect: Boolean(question.multiSelect),
1218
1383
  options: Array.isArray(question.options)
1219
1384
  ? question.options
1220
1385
  .map((option) => toRecord(option))
@@ -1268,6 +1433,37 @@ function buildClaudePreToolUseBridgeResponse(action, reason) {
1268
1433
  }
1269
1434
  };
1270
1435
  }
1436
+ function buildClaudeAskUserQuestionBridgeResponse(action, answers, questions, originalInput, reason) {
1437
+ const response = buildClaudePreToolUseBridgeResponse(action, reason);
1438
+ const originalInputRecord = toRecord(originalInput);
1439
+ if (action === "allow") {
1440
+ return {
1441
+ hookSpecificOutput: {
1442
+ ...response.hookSpecificOutput,
1443
+ updatedInput: {
1444
+ ...(originalInputRecord ?? {}),
1445
+ answers: buildClaudeAskUserQuestionAnswers(answers, questions)
1446
+ }
1447
+ }
1448
+ };
1449
+ }
1450
+ return response;
1451
+ }
1452
+ function buildClaudeExitPlanModeBridgeResponse(action, originalInput, reason) {
1453
+ const originalInputRecord = toRecord(originalInput);
1454
+ const response = buildClaudePreToolUseBridgeResponse(action, reason);
1455
+ if (action !== "allow") {
1456
+ return response;
1457
+ }
1458
+ return {
1459
+ hookSpecificOutput: {
1460
+ ...response.hookSpecificOutput,
1461
+ updatedInput: {
1462
+ ...(originalInputRecord ?? {})
1463
+ }
1464
+ }
1465
+ };
1466
+ }
1271
1467
  function buildClaudePermissionRequestBridgeResponse(action, message) {
1272
1468
  return {
1273
1469
  hookSpecificOutput: {
@@ -1422,6 +1618,7 @@ function buildClaudeKind(toolName, toolInput) {
1422
1618
  const command = normalizeText(inputRecord?.command) ||
1423
1619
  normalizeText(inputRecord?.cmd) ||
1424
1620
  null;
1621
+ const allowedPrompts = readClaudeAllowedPrompts(inputRecord);
1425
1622
  if (normalizedToolName === "bash" || normalizedToolName === "shell") {
1426
1623
  return {
1427
1624
  kind: "command",
@@ -1429,7 +1626,37 @@ function buildClaudeKind(toolName, toolInput) {
1429
1626
  summary: command ?? "Bash 工具需要确认",
1430
1627
  detail: stringifyPayload(toolInput),
1431
1628
  command,
1432
- paths: []
1629
+ paths: [],
1630
+ questions: []
1631
+ };
1632
+ }
1633
+ if (normalizedToolName === "askuserquestion") {
1634
+ const questions = readClaudeAskUserQuestionQuestions(inputRecord);
1635
+ return {
1636
+ kind: "user_input",
1637
+ title: "Claude 需要你回答问题",
1638
+ summary: questions[0]?.question ?? "Claude 需要你补充选择",
1639
+ detail: stringifyPayload(toolInput),
1640
+ command: null,
1641
+ paths: [],
1642
+ questions
1643
+ };
1644
+ }
1645
+ if (normalizedToolName === "exitplanmode") {
1646
+ const summary = normalizeText(inputRecord?.plan) ||
1647
+ normalizeText(inputRecord?.summary) ||
1648
+ normalizeText(inputRecord?.title) ||
1649
+ (allowedPrompts[0]
1650
+ ? `Claude 准备按计划继续执行:${allowedPrompts[0].prompt}`
1651
+ : "Claude 准备退出计划模式并继续执行");
1652
+ return {
1653
+ kind: "plan_approval",
1654
+ title: "Claude 请求确认执行计划",
1655
+ summary,
1656
+ detail: stringifyPayload(toolInput),
1657
+ command: null,
1658
+ paths: [],
1659
+ questions: []
1433
1660
  };
1434
1661
  }
1435
1662
  if (normalizedToolName === "edit" ||
@@ -1442,7 +1669,8 @@ function buildClaudeKind(toolName, toolInput) {
1442
1669
  summary: paths[0] ?? `${toolName} 工具需要确认`,
1443
1670
  detail: stringifyPayload(toolInput),
1444
1671
  command: null,
1445
- paths
1672
+ paths,
1673
+ questions: []
1446
1674
  };
1447
1675
  }
1448
1676
  return {
@@ -1451,9 +1679,143 @@ function buildClaudeKind(toolName, toolInput) {
1451
1679
  summary: toolName,
1452
1680
  detail: stringifyPayload(toolInput),
1453
1681
  command,
1454
- paths
1682
+ paths,
1683
+ questions: []
1684
+ };
1685
+ }
1686
+ function readClaudeAskUserQuestionQuestions(inputRecord) {
1687
+ if (!inputRecord) {
1688
+ return [];
1689
+ }
1690
+ const rawQuestions = Array.isArray(inputRecord.questions)
1691
+ ? inputRecord.questions
1692
+ : [
1693
+ {
1694
+ ...inputRecord,
1695
+ id: normalizeText(inputRecord.id) || "question"
1696
+ }
1697
+ ];
1698
+ return rawQuestions
1699
+ .map((question, index) => normalizeClaudeAskUserQuestion(question, index))
1700
+ .filter((question) => question !== null);
1701
+ }
1702
+ function normalizeClaudeAskUserQuestion(value, index) {
1703
+ const record = toRecord(value);
1704
+ if (!record) {
1705
+ return null;
1706
+ }
1707
+ const questionText = normalizeText(record.question) ||
1708
+ normalizeText(record.prompt) ||
1709
+ normalizeText(record.message) ||
1710
+ "请选择一个选项";
1711
+ const options = readClaudeAskUserQuestionOptions(record.options ?? record.choices ?? record.answers);
1712
+ return {
1713
+ id: normalizeText(record.id) ||
1714
+ normalizeText(record.name) ||
1715
+ `question-${index + 1}`,
1716
+ header: normalizeText(record.header) ||
1717
+ normalizeText(record.title) ||
1718
+ `问题 ${index + 1}`,
1719
+ question: questionText,
1720
+ allowOther: readBoolean(record.allowOther) ??
1721
+ readBoolean(record.allow_other) ??
1722
+ readBoolean(record.isOther) ??
1723
+ readBoolean(record.allowFreeform) ??
1724
+ readBoolean(record.allow_freeform) ??
1725
+ true,
1726
+ secret: readBoolean(record.secret) ??
1727
+ readBoolean(record.isSecret) ??
1728
+ false,
1729
+ multiSelect: readBoolean(record.multiSelect) ??
1730
+ readBoolean(record.multi_select) ??
1731
+ false,
1732
+ options
1455
1733
  };
1456
1734
  }
1735
+ function readClaudeAskUserQuestionOptions(value) {
1736
+ if (!Array.isArray(value)) {
1737
+ return [];
1738
+ }
1739
+ return value
1740
+ .map((item) => {
1741
+ if (typeof item === "string") {
1742
+ const label = item.trim();
1743
+ return label ? { label, description: null } : null;
1744
+ }
1745
+ const record = toRecord(item);
1746
+ const label = normalizeText(record?.label) ||
1747
+ normalizeText(record?.value) ||
1748
+ normalizeText(record?.text) ||
1749
+ normalizeText(record?.title);
1750
+ if (!label) {
1751
+ return null;
1752
+ }
1753
+ return {
1754
+ label,
1755
+ description: normalizeText(record?.description) || normalizeText(record?.detail) || null
1756
+ };
1757
+ })
1758
+ .filter((option) => option !== null);
1759
+ }
1760
+ function readClaudeElicitationQuestions(payload) {
1761
+ const options = readClaudeAskUserQuestionOptions(payload.options);
1762
+ const questionText = normalizeText(payload.question) ||
1763
+ normalizeText(payload.prompt) ||
1764
+ normalizeText(payload.message) ||
1765
+ "请补充 Claude 继续执行所需的信息";
1766
+ return [
1767
+ {
1768
+ id: "elicitation",
1769
+ header: normalizeText(payload.title) || "补充信息",
1770
+ question: questionText,
1771
+ allowOther: true,
1772
+ secret: false,
1773
+ multiSelect: false,
1774
+ options
1775
+ }
1776
+ ];
1777
+ }
1778
+ export function buildClaudeAskUserQuestionAnswers(answers, questions) {
1779
+ return Object.fromEntries(questions
1780
+ .map((question) => {
1781
+ const values = Array.isArray(answers[question.id])
1782
+ ? answers[question.id].map((value) => normalizeText(value)).filter(Boolean)
1783
+ : [];
1784
+ if (values.length === 0) {
1785
+ return null;
1786
+ }
1787
+ return [
1788
+ question.question,
1789
+ question.multiSelect ? values.join(", ") : values[0] ?? ""
1790
+ ];
1791
+ })
1792
+ .filter((entry) => entry !== null));
1793
+ }
1794
+ export function resolveClaudeBlockingRequestTimeoutMs(kind) {
1795
+ if (kind === "user_input") {
1796
+ return CLAUDE_ASK_USER_QUESTION_TIMEOUT_MS;
1797
+ }
1798
+ if (kind === "plan_approval") {
1799
+ return CLAUDE_PLAN_APPROVAL_TIMEOUT_MS;
1800
+ }
1801
+ return CLAUDE_PRE_TOOL_USE_TIMEOUT_MS;
1802
+ }
1803
+ function readClaudeAllowedPrompts(inputRecord) {
1804
+ if (!inputRecord || !Array.isArray(inputRecord.allowedPrompts)) {
1805
+ return [];
1806
+ }
1807
+ return inputRecord.allowedPrompts
1808
+ .map((value) => {
1809
+ const record = toRecord(value);
1810
+ const tool = normalizeText(record?.tool);
1811
+ const prompt = normalizeText(record?.prompt);
1812
+ if (!tool || !prompt) {
1813
+ return null;
1814
+ }
1815
+ return { tool, prompt };
1816
+ })
1817
+ .filter((item) => item !== null);
1818
+ }
1457
1819
  function buildOpenCodeKind(title, metadata, pattern, fallbackToolName) {
1458
1820
  const command = normalizeText(metadata?.command);
1459
1821
  const toolName = normalizeText(metadata?.tool) || fallbackToolName;
@@ -1581,6 +1943,17 @@ function buildClaudeAllowedScopeKey(request) {
1581
1943
  return null;
1582
1944
  }
1583
1945
  function buildClaudeActions(request) {
1946
+ if (request.kind === "user_input") {
1947
+ return [
1948
+ createAction("submit", "提交选择", "primary", "把选择结果交给 Claude 继续处理")
1949
+ ];
1950
+ }
1951
+ if (request.kind === "plan_approval") {
1952
+ return [
1953
+ createAction("allow", "批准计划", "primary", "允许 Claude 按当前计划继续执行"),
1954
+ createAction("deny", "退回计划", "danger", "拒绝这次计划,要求 Claude 停在计划阶段")
1955
+ ];
1956
+ }
1584
1957
  const actions = [
1585
1958
  createAction("allow", "允许", "primary", "只允许这一次")
1586
1959
  ];
@@ -1908,6 +2281,21 @@ function normalizeText(value) {
1908
2281
  }
1909
2282
  return null;
1910
2283
  }
2284
+ function readBoolean(value) {
2285
+ if (typeof value === "boolean") {
2286
+ return value;
2287
+ }
2288
+ if (typeof value === "string") {
2289
+ const normalized = value.trim().toLowerCase();
2290
+ if (normalized === "true" || normalized === "1" || normalized === "yes") {
2291
+ return true;
2292
+ }
2293
+ if (normalized === "false" || normalized === "0" || normalized === "no") {
2294
+ return false;
2295
+ }
2296
+ }
2297
+ return null;
2298
+ }
1911
2299
  function toRecord(value) {
1912
2300
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1913
2301
  return null;