@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
@@ -1,7 +1,8 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import { performance } from "node:perf_hooks";
4
- import { ClaudeRuntimeAdapter, CodexRuntimeAdapter, GeminiRuntimeAdapter, KimiRuntimeAdapter, LegnaRuntimeAdapter, OpenCodeRuntimeAdapter, ProviderRuntimeService } from "@codingns/session-sync-core";
5
+ import { ClaudeRuntimeAdapter, CodexRuntimeAdapter, GeminiRuntimeAdapter, KimiRuntimeAdapter, LegnaRuntimeAdapter, OpenCodeRuntimeAdapter, ProviderRuntimeService, resolveCodexPermissionResolution } from "@codingns/session-sync-core";
5
6
  import { AppError, isAppError } from "../../shared/errors/app-error.js";
6
7
  import { createId } from "../../shared/utils/id.js";
7
8
  import { isPerfDebugEnabled, logPerformance } from "../../shared/utils/perf-log.js";
@@ -17,6 +18,42 @@ import { ClaudeRuntimeHelperAdapter } from "./claude-runtime-helper-client.js";
17
18
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
18
19
  const OPENCODE_ORDER_DEBUG_ENABLED = /^(1|true|yes)$/i.test(process.env.CODINGNS_OPENCODE_ORDER_DEBUG?.trim() ?? "");
19
20
  const RUNTIME_EVENT_SQLITE_BUSY_RETRY_DELAYS_MS = [100, 250, 500, 1_000, 1_500, 2_000];
21
+ function buildSessionRuntimePermissionStatus(input) {
22
+ if (input.provider !== "codex") {
23
+ return {
24
+ requestedPermissionMode: input.requestedPermissionMode,
25
+ effectivePermissionMode: "default",
26
+ effectiveSandboxMode: null,
27
+ effectiveApprovalPolicy: null,
28
+ source: "provider-non-codex",
29
+ summary: "当前 provider 不使用 Codex 沙箱权限映射。",
30
+ detail: "这份权限说明只对 Codex 会话生效,其他 provider 仍沿用各自的权限体系。"
31
+ };
32
+ }
33
+ const resolution = resolveCodexPermissionResolution(input.requestedPermissionMode);
34
+ if (resolution.effectivePermissionMode === "default") {
35
+ return {
36
+ requestedPermissionMode: input.requestedPermissionMode,
37
+ effectivePermissionMode: resolution.effectivePermissionMode,
38
+ effectiveSandboxMode: resolution.sandboxMode,
39
+ effectiveApprovalPolicy: resolution.approvalPolicy ? "never" : "cli-default",
40
+ source: "codex-cli-default",
41
+ summary: "当前会话跟随 Codex CLI 默认权限。",
42
+ detail: resolution.reasoning
43
+ };
44
+ }
45
+ return {
46
+ requestedPermissionMode: input.requestedPermissionMode,
47
+ effectivePermissionMode: resolution.effectivePermissionMode,
48
+ effectiveSandboxMode: resolution.sandboxMode,
49
+ effectiveApprovalPolicy: resolution.approvalPolicy ? "never" : "cli-default",
50
+ source: "codingns-override",
51
+ summary: resolution.effectivePermissionMode === "acceptEdits"
52
+ ? "当前会话已切到工作区可写权限。"
53
+ : "当前会话已切到完整权限。",
54
+ detail: resolution.reasoning
55
+ };
56
+ }
20
57
  const RUNTIME_START_BINDING_WAIT_TIMEOUT_MS = 10_000;
21
58
  const START_BINDING_POLL_INTERVAL_MS = 50;
22
59
  const EXTERNAL_RUNTIME_INTERRUPT_SUPPRESSION_MS = 30_000;
@@ -51,6 +88,7 @@ export class SessionLiveRuntimeService {
51
88
  queueRetryTimers = new Map();
52
89
  pendingSendDebugTracesBySessionId = new Map();
53
90
  runtimePersistenceQueues = new Map();
91
+ sessionPermissionRuntimeStates = new Map();
54
92
  constructor(sessionHistoryService, sessionMessageAttachmentService, workspaceService, sessionChangedFileService, sessionBindingRepository, authUserRepository, sessionSendQueueRepository, sessionIndexRepository, sessionStateRepository, sessionStatusSnapshotRepository, sessionProviderConfigService, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), openCliSessionPromptService = null, workspaceSessionRuntimeContextService = null) {
55
93
  this.sessionHistoryService = sessionHistoryService;
56
94
  this.sessionMessageAttachmentService = sessionMessageAttachmentService;
@@ -90,6 +128,7 @@ export class SessionLiveRuntimeService {
90
128
  clientRequestId: input.clientRequestId
91
129
  });
92
130
  try {
131
+ this.rememberRequestedPermissionMode(sessionId, input.runtimeOptions?.permissionMode ?? null, "start_live");
93
132
  const capabilities = this.sessionHistoryService.getProviderCapabilitiesSnapshot(input.provider);
94
133
  const providerBinding = this.sessionProviderConfigService.prepareSessionBinding({
95
134
  sessionId,
@@ -449,6 +488,13 @@ export class SessionLiveRuntimeService {
449
488
  });
450
489
  return this.sessionPermissionRequestService.handleClaudePermissionRequest(resolvedPayload, provider);
451
490
  }
491
+ if (hookEventName === "Elicitation") {
492
+ logPermissionDebug("claude_hook_event.route", {
493
+ hookEventName,
494
+ route: "handleClaudeElicitation"
495
+ });
496
+ return this.sessionPermissionRequestService.handleClaudeElicitation(resolvedPayload, provider);
497
+ }
452
498
  const providerSessionId = normalizeRequiredText(resolvedPayload.session_id, "session_id");
453
499
  const workspacePath = normalizeRequiredText(resolvedPayload.cwd, "cwd");
454
500
  const workspace = this.workspaceService.findWorkspaceByPath(workspacePath);
@@ -527,6 +573,12 @@ export class SessionLiveRuntimeService {
527
573
  this.maybeDispatchQueuedMessages(session);
528
574
  const capabilities = await this.sessionHistoryService.getSessionCapabilities(sessionId, userId);
529
575
  const contextUsage = await this.sessionHistoryService.getSessionContextUsage(sessionId).catch(() => null);
576
+ const requestedPermissionMode = this.resolveRequestedPermissionMode(sessionId);
577
+ const permissionStatus = buildSessionRuntimePermissionStatus({
578
+ provider: session.provider,
579
+ requestedPermissionMode
580
+ });
581
+ this.logSessionPermissionStatus(sessionId, session.provider, permissionStatus);
530
582
  const resolution = runtimeSnapshot
531
583
  ? this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(runtimeSessionId, runtimeSnapshot))
532
584
  : externalRuntimeSnapshot
@@ -555,7 +607,8 @@ export class SessionLiveRuntimeService {
555
607
  : null,
556
608
  updatedAt: resolution.updatedAt,
557
609
  watchdogTriggeredAt: resolution.watchdogTriggeredAt,
558
- contextUsage
610
+ contextUsage,
611
+ permissionStatus
559
612
  };
560
613
  }
561
614
  if (externalRuntimeSnapshot) {
@@ -577,7 +630,8 @@ export class SessionLiveRuntimeService {
577
630
  errorDetail: resolution.runningState === "failed" ? resolution.detail ?? session.lastErrorDetail : null,
578
631
  updatedAt: resolution.updatedAt,
579
632
  watchdogTriggeredAt: resolution.watchdogTriggeredAt,
580
- contextUsage
633
+ contextUsage,
634
+ permissionStatus
581
635
  };
582
636
  }
583
637
  const persistedErrorCode = resolution.runningState === "failed" ? resolution.errorCode ?? session.lastErrorCode : null;
@@ -600,7 +654,8 @@ export class SessionLiveRuntimeService {
600
654
  errorDetail: persistedErrorDetail,
601
655
  updatedAt: resolution.updatedAt,
602
656
  watchdogTriggeredAt: resolution.watchdogTriggeredAt,
603
- contextUsage
657
+ contextUsage,
658
+ permissionStatus
604
659
  };
605
660
  }
606
661
  resolveLiveActivityObservation(sessionId) {
@@ -822,6 +877,48 @@ export class SessionLiveRuntimeService {
822
877
  }
823
878
  return snapshot;
824
879
  }
880
+ resolveRequestedPermissionMode(sessionId) {
881
+ const runtimeState = this.sessionPermissionRuntimeStates.get(sessionId);
882
+ if (runtimeState?.requestedPermissionMode?.trim()) {
883
+ return runtimeState.requestedPermissionMode.trim();
884
+ }
885
+ const queuedItems = this.sessionSendQueueRepository.listBySessionId(sessionId);
886
+ const dispatchingItem = queuedItems.find((item) => item.status === "dispatching");
887
+ if (dispatchingItem?.permissionMode?.trim()) {
888
+ return dispatchingItem.permissionMode.trim();
889
+ }
890
+ const latestPermissionItem = [...queuedItems]
891
+ .reverse()
892
+ .find((item) => item.permissionMode?.trim());
893
+ if (latestPermissionItem?.permissionMode?.trim()) {
894
+ return latestPermissionItem.permissionMode.trim();
895
+ }
896
+ return null;
897
+ }
898
+ rememberRequestedPermissionMode(sessionId, permissionMode, source) {
899
+ this.sessionPermissionRuntimeStates.set(sessionId, {
900
+ requestedPermissionMode: permissionMode?.trim() || null,
901
+ updatedAt: nowIso(),
902
+ source
903
+ });
904
+ }
905
+ logSessionPermissionStatus(sessionId, provider, permissionStatus) {
906
+ if (!permissionStatus) {
907
+ return;
908
+ }
909
+ const runtimeState = this.sessionPermissionRuntimeStates.get(sessionId);
910
+ logPermissionDebug("session_runtime.permission_status", {
911
+ sessionId,
912
+ provider,
913
+ requestedPermissionMode: permissionStatus.requestedPermissionMode,
914
+ effectivePermissionMode: permissionStatus.effectivePermissionMode,
915
+ effectiveSandboxMode: permissionStatus.effectiveSandboxMode,
916
+ effectiveApprovalPolicy: permissionStatus.effectiveApprovalPolicy,
917
+ source: permissionStatus.source,
918
+ runtimeStateSource: runtimeState?.source ?? null,
919
+ runtimeStateUpdatedAt: runtimeState?.updatedAt ?? null
920
+ });
921
+ }
825
922
  demoteDeadRuntimeSnapshot(snapshot) {
826
923
  const updatedAt = nowIso();
827
924
  for (const userId of this.authUserRepository.listIds()) {
@@ -1115,6 +1212,7 @@ export class SessionLiveRuntimeService {
1115
1212
  }
1116
1213
  }
1117
1214
  async startRuntimeRun(request, userId, mode, providerBinding) {
1215
+ this.rememberRequestedPermissionMode(request.sessionId, request.options.permissionMode ?? null, "runtime_snapshot");
1118
1216
  this.runtimeMessageSeenSessions.delete(request.sessionId);
1119
1217
  this.runtimeHistoryFallbackSentSessions.delete(request.sessionId);
1120
1218
  this.clearExternalRuntimeInterruptSuppression(request.sessionId);
@@ -1153,6 +1251,7 @@ export class SessionLiveRuntimeService {
1153
1251
  clientRequestId: input.clientRequestId
1154
1252
  });
1155
1253
  try {
1254
+ this.rememberRequestedPermissionMode(input.sessionId, input.runtimeOptions?.permissionMode ?? null, "send_live");
1156
1255
  const capabilities = await this.sessionHistoryService.getSessionCapabilities(input.sessionId, input.userId);
1157
1256
  const workspace = this.workspaceService.getWorkspaceOrThrow(session.workspaceId);
1158
1257
  const runtimeMode = shouldStartNativeSessionOnFirstMessage(session);
@@ -1193,9 +1292,12 @@ export class SessionLiveRuntimeService {
1193
1292
  const runtimeRawStoreRef = runtimeMode === "start"
1194
1293
  ? syntheticForkRawStoreRef
1195
1294
  : await this.resolveCodexRuntimeRequestRawStoreRef(session, providerBinding);
1196
- const nextUserSequence = runtimeMode === "start"
1197
- ? 1
1198
- : await this.resolveNextUserSequence(input.sessionId, session.messageCount);
1295
+ const sequenceBase = resolveRuntimeSequenceBase(session, runtimeMode);
1296
+ const nextUserSequence = sequenceBase !== null
1297
+ ? sequenceBase
1298
+ : (runtimeMode === "start"
1299
+ ? 1
1300
+ : await this.resolveNextUserSequence(input.sessionId, session.messageCount));
1199
1301
  const resolvedAttachments = persistedAttachments
1200
1302
  ?? this.persistMessageAttachments(input.sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
1201
1303
  const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(session.provider, input.content, resolvedAttachments.runtimeAttachments);
@@ -1219,7 +1321,17 @@ export class SessionLiveRuntimeService {
1219
1321
  providerLaunchRuntimeHomeDir: providerLaunchContext.runtimeHomeDir,
1220
1322
  providerBindingRuntimeHomeDir: providerBinding.runtimeHomeDir
1221
1323
  });
1222
- const providerInstructionFilePath = resolveRuntimeInstructionFilePath(session.provider, workspace.path, input.runtimeOptions?.providerInstructionFilePath
1324
+ const claudeForkBootstrap = await this.buildClaudeForkBootstrapDescriptor({
1325
+ session,
1326
+ runtimeMode,
1327
+ baseInstructionFilePath: input.runtimeOptions?.providerInstructionFilePath
1328
+ ?? workspaceRuntimeContext.instructionFilePath
1329
+ ?? providerLaunchContext.providerInstructionFilePath
1330
+ ?? null,
1331
+ runtimeHomeDir: effectiveRuntimeHomeDir
1332
+ });
1333
+ const providerInstructionFilePath = resolveRuntimeInstructionFilePath(session.provider, workspace.path, claudeForkBootstrap?.instructionFilePath
1334
+ ?? input.runtimeOptions?.providerInstructionFilePath
1223
1335
  ?? workspaceRuntimeContext.instructionFilePath
1224
1336
  ?? providerLaunchContext.providerInstructionFilePath
1225
1337
  ?? null);
@@ -1376,6 +1488,7 @@ export class SessionLiveRuntimeService {
1376
1488
  runtimeAttachments: restoredAttachments
1377
1489
  };
1378
1490
  try {
1491
+ this.rememberRequestedPermissionMode(nextQueueItem.sessionId, nextQueueItem.permissionMode, "queue_dispatch");
1379
1492
  await this.sendLiveMessageDirect({
1380
1493
  sessionId: nextQueueItem.sessionId,
1381
1494
  userId: nextQueueItem.userId,
@@ -1707,6 +1820,37 @@ export class SessionLiveRuntimeService {
1707
1820
  writeFileSync(syntheticFilePath, `${serialized}\n`, "utf8");
1708
1821
  return syntheticFilePath;
1709
1822
  }
1823
+ async buildClaudeForkBootstrapDescriptor(input) {
1824
+ if (input.runtimeMode !== "start" || !shouldBootstrapClaudeForkSession(input.session)) {
1825
+ return null;
1826
+ }
1827
+ const messages = await Promise.resolve(this.sessionHistoryService.readAllTextHistoryMessages(input.session.sessionId)).catch(() => []);
1828
+ const textMessages = Array.isArray(messages)
1829
+ ? messages.filter((message) => (message.role === "user" || message.role === "assistant")
1830
+ && message.kind === "text"
1831
+ && message.content.trim().length > 0)
1832
+ : [];
1833
+ if (textMessages.length === 0) {
1834
+ return null;
1835
+ }
1836
+ const overlay = buildClaudeForkBootstrapInstructionOverlay({
1837
+ sessionTitle: input.session.title,
1838
+ messages: textMessages
1839
+ });
1840
+ if (!overlay) {
1841
+ return null;
1842
+ }
1843
+ const baseInstructionContent = readInstructionFileSafe(input.baseInstructionFilePath);
1844
+ const targetRoot = input.runtimeHomeDir?.trim()
1845
+ || path.resolve(path.dirname(this.config.databasePath), "runtime", "claude-fork-bootstrap");
1846
+ const targetDir = path.join(targetRoot, ".codingns-fork-bootstrap");
1847
+ const instructionFilePath = path.join(targetDir, `${input.session.sessionId}.md`);
1848
+ mkdirSync(targetDir, { recursive: true });
1849
+ writeFileSync(instructionFilePath, composeForkBootstrapInstructionFile(baseInstructionContent, overlay), "utf8");
1850
+ return {
1851
+ instructionFilePath
1852
+ };
1853
+ }
1710
1854
  buildBindingSnapshot(sessionId, provider, providerSessionId, rawStoreRef) {
1711
1855
  const pendingValue = `pending://${provider}/${sessionId}`;
1712
1856
  return {
@@ -2445,8 +2589,30 @@ function normalizeClaudeHookEventName(value) {
2445
2589
  }
2446
2590
  function isSupportedClaudeHookEvent(value) {
2447
2591
  return (value === "PreToolUse" ||
2592
+ value === "PostToolUse" ||
2593
+ value === "PostToolUseFailure" ||
2448
2594
  value === "PermissionRequest" ||
2595
+ value === "PermissionDenied" ||
2596
+ value === "Elicitation" ||
2597
+ value === "ElicitationResult" ||
2598
+ value === "MessageDisplay" ||
2449
2599
  value === "Notification" ||
2600
+ value === "PostToolBatch" ||
2601
+ value === "TaskCreated" ||
2602
+ value === "TaskCompleted" ||
2603
+ value === "TeammateIdle" ||
2604
+ value === "SubagentStart" ||
2605
+ value === "SubagentStop" ||
2606
+ value === "PreCompact" ||
2607
+ value === "PostCompact" ||
2608
+ value === "InstructionsLoaded" ||
2609
+ value === "ConfigChange" ||
2610
+ value === "CwdChanged" ||
2611
+ value === "FileChanged" ||
2612
+ value === "WorktreeCreate" ||
2613
+ value === "WorktreeRemove" ||
2614
+ value === "Setup" ||
2615
+ value === "UserPromptExpansion" ||
2450
2616
  value === "UserPromptSubmit" ||
2451
2617
  value === "SessionStart" ||
2452
2618
  value === "Stop" ||
@@ -2484,6 +2650,160 @@ function mapClaudeHookToRuntimeUpdate(hookEventName, payload, timestamp) {
2484
2650
  timestamp
2485
2651
  };
2486
2652
  }
2653
+ if (hookEventName === "Setup") {
2654
+ return {
2655
+ runningState: "running",
2656
+ detail: buildClaudeHookRunningDetail("Claude 正在执行初始化", payload),
2657
+ timestamp
2658
+ };
2659
+ }
2660
+ if (hookEventName === "UserPromptExpansion") {
2661
+ return {
2662
+ runningState: "running",
2663
+ detail: buildClaudeHookRunningDetail("Claude 正在展开用户指令", payload),
2664
+ timestamp
2665
+ };
2666
+ }
2667
+ if (hookEventName === "PostToolUse") {
2668
+ return {
2669
+ runningState: "running",
2670
+ detail: buildClaudeHookRunningDetail("Claude 已完成一次工具调用", payload),
2671
+ timestamp
2672
+ };
2673
+ }
2674
+ if (hookEventName === "PostToolUseFailure") {
2675
+ return {
2676
+ runningState: "running",
2677
+ detail: buildClaudeHookRunningDetail("Claude 的一次工具调用失败", payload),
2678
+ timestamp
2679
+ };
2680
+ }
2681
+ if (hookEventName === "PermissionDenied") {
2682
+ return {
2683
+ runningState: "running",
2684
+ detail: buildClaudeHookRunningDetail("Claude 的工具权限请求被拒绝", payload),
2685
+ timestamp
2686
+ };
2687
+ }
2688
+ if (hookEventName === "Notification") {
2689
+ return {
2690
+ runningState: "running",
2691
+ detail: buildClaudeHookRunningDetail("Claude 发来一条通知", payload),
2692
+ timestamp
2693
+ };
2694
+ }
2695
+ if (hookEventName === "MessageDisplay") {
2696
+ return {
2697
+ runningState: "running",
2698
+ detail: buildClaudeHookRunningDetail("Claude 正在展示回复内容", payload),
2699
+ timestamp
2700
+ };
2701
+ }
2702
+ if (hookEventName === "ElicitationResult") {
2703
+ return {
2704
+ runningState: "running",
2705
+ detail: buildClaudeHookRunningDetail("Claude 已收到补充信息结果", payload),
2706
+ timestamp
2707
+ };
2708
+ }
2709
+ if (hookEventName === "PostToolBatch") {
2710
+ return {
2711
+ runningState: "running",
2712
+ detail: buildClaudeHookRunningDetail("Claude 已完成一批工具调用", payload),
2713
+ timestamp
2714
+ };
2715
+ }
2716
+ if (hookEventName === "TaskCreated") {
2717
+ return {
2718
+ runningState: "running",
2719
+ detail: buildClaudeHookRunningDetail("Claude 新建了一个任务", payload),
2720
+ timestamp
2721
+ };
2722
+ }
2723
+ if (hookEventName === "TaskCompleted") {
2724
+ return {
2725
+ runningState: "running",
2726
+ detail: buildClaudeHookRunningDetail("Claude 完成了一个任务", payload),
2727
+ timestamp
2728
+ };
2729
+ }
2730
+ if (hookEventName === "SubagentStart") {
2731
+ return {
2732
+ runningState: "running",
2733
+ detail: buildClaudeHookRunningDetail("Claude 子任务已启动", payload),
2734
+ timestamp
2735
+ };
2736
+ }
2737
+ if (hookEventName === "SubagentStop") {
2738
+ return {
2739
+ runningState: "running",
2740
+ detail: buildClaudeHookRunningDetail("Claude 子任务已结束", payload),
2741
+ timestamp
2742
+ };
2743
+ }
2744
+ if (hookEventName === "TeammateIdle") {
2745
+ return {
2746
+ runningState: "running",
2747
+ detail: buildClaudeHookRunningDetail("Claude 队友任务即将空闲", payload),
2748
+ timestamp
2749
+ };
2750
+ }
2751
+ if (hookEventName === "PreCompact") {
2752
+ return {
2753
+ runningState: "running",
2754
+ detail: buildClaudeHookRunningDetail("Claude 正在压缩上下文", payload),
2755
+ timestamp
2756
+ };
2757
+ }
2758
+ if (hookEventName === "PostCompact") {
2759
+ return {
2760
+ runningState: "running",
2761
+ detail: buildClaudeHookRunningDetail("Claude 已完成上下文压缩", payload),
2762
+ timestamp
2763
+ };
2764
+ }
2765
+ if (hookEventName === "InstructionsLoaded") {
2766
+ return {
2767
+ runningState: "running",
2768
+ detail: buildClaudeHookRunningDetail("Claude 已加载指令文件", payload),
2769
+ timestamp
2770
+ };
2771
+ }
2772
+ if (hookEventName === "ConfigChange") {
2773
+ return {
2774
+ runningState: "running",
2775
+ detail: buildClaudeHookRunningDetail("Claude 检测到配置变化", payload),
2776
+ timestamp
2777
+ };
2778
+ }
2779
+ if (hookEventName === "CwdChanged") {
2780
+ return {
2781
+ runningState: "running",
2782
+ detail: buildClaudeHookRunningDetail("Claude 已切换工作目录", payload),
2783
+ timestamp
2784
+ };
2785
+ }
2786
+ if (hookEventName === "FileChanged") {
2787
+ return {
2788
+ runningState: "running",
2789
+ detail: buildClaudeHookRunningDetail("Claude 检测到文件变化", payload),
2790
+ timestamp
2791
+ };
2792
+ }
2793
+ if (hookEventName === "WorktreeCreate") {
2794
+ return {
2795
+ runningState: "running",
2796
+ detail: buildClaudeHookRunningDetail("Claude 正在创建工作树", payload),
2797
+ timestamp
2798
+ };
2799
+ }
2800
+ if (hookEventName === "WorktreeRemove") {
2801
+ return {
2802
+ runningState: "running",
2803
+ detail: buildClaudeHookRunningDetail("Claude 正在移除工作树", payload),
2804
+ timestamp
2805
+ };
2806
+ }
2487
2807
  if (hookEventName === "StopFailure") {
2488
2808
  return {
2489
2809
  runningState: "failed",
@@ -2514,6 +2834,32 @@ function mapClaudeHookToRuntimeUpdate(hookEventName, payload, timestamp) {
2514
2834
  }
2515
2835
  return null;
2516
2836
  }
2837
+ function buildClaudeHookRunningDetail(base, payload) {
2838
+ const candidates = [
2839
+ normalizeOptionalText(payload.title),
2840
+ normalizeOptionalText(payload.message),
2841
+ normalizeOptionalText(payload.reason),
2842
+ normalizeOptionalText(payload.prompt),
2843
+ normalizeOptionalText(payload.path),
2844
+ normalizeOptionalText(payload.file_path),
2845
+ normalizeOptionalText(payload.worktree_path),
2846
+ normalizeOptionalText(payload.config_path),
2847
+ normalizeOptionalText(payload.rule_file),
2848
+ normalizeOptionalText(payload.cwd),
2849
+ normalizeOptionalText(payload.command_name),
2850
+ normalizeOptionalText(payload.notification_type),
2851
+ normalizeOptionalText(payload.mcp_server_name),
2852
+ normalizeOptionalText(payload.matcher),
2853
+ normalizeOptionalText(payload.trigger),
2854
+ normalizeOptionalText(payload.action)
2855
+ ].filter((value) => Boolean(value));
2856
+ const suffix = payload.tool_name?.trim()
2857
+ ? `(${payload.tool_name.trim()})`
2858
+ : candidates[0]
2859
+ ? `:${candidates[0]}`
2860
+ : "";
2861
+ return `${base}${suffix}`;
2862
+ }
2517
2863
  function resolveRuntimeSessionTitle(provider, existingTitle, content, parentTitle, forkMethod, forkSourceType) {
2518
2864
  const normalizedExistingTitle = existingTitle.trim();
2519
2865
  const normalizedParentTitle = parentTitle?.trim() ?? "";
@@ -2525,6 +2871,76 @@ function resolveRuntimeSessionTitle(provider, existingTitle, content, parentTitl
2525
2871
  }
2526
2872
  return buildSessionTitleFromContent(content, "继续对话");
2527
2873
  }
2874
+ function shouldBootstrapClaudeForkSession(session) {
2875
+ if (session.provider !== "claude-code") {
2876
+ return false;
2877
+ }
2878
+ if (session.forkMethod !== "native_message_fork"
2879
+ && session.forkMethod !== "native_session_fork") {
2880
+ return false;
2881
+ }
2882
+ const inheritedCount = typeof session.inheritedPrefixMessageCount === "number"
2883
+ ? Math.max(0, session.inheritedPrefixMessageCount)
2884
+ : null;
2885
+ if (inheritedCount === null) {
2886
+ return false;
2887
+ }
2888
+ return session.messageCount <= inheritedCount;
2889
+ }
2890
+ function resolveRuntimeSequenceBase(session, runtimeMode) {
2891
+ if (runtimeMode !== "start"
2892
+ || session.provider !== "claude-code"
2893
+ || (session.forkMethod !== "native_message_fork"
2894
+ && session.forkMethod !== "native_session_fork")
2895
+ || typeof session.inheritedPrefixMessageCount !== "number") {
2896
+ return null;
2897
+ }
2898
+ return Math.max(0, session.inheritedPrefixMessageCount);
2899
+ }
2900
+ function buildClaudeForkBootstrapInstructionOverlay(input) {
2901
+ const title = input.sessionTitle.trim() || "未命名会话";
2902
+ const lines = [
2903
+ `这是一个从既有 Claude Code 会话 fork 出来的子会话,标题:${title}。`,
2904
+ "下面这些历史内容已经发生过,只是给你恢复上下文。",
2905
+ "不要把它们当成新的用户问题逐条重答,也不要重复总结。",
2906
+ "真正需要处理的新用户输入,会在你看到这段说明之后单独发给你。",
2907
+ ""
2908
+ ];
2909
+ let appended = 0;
2910
+ for (const message of input.messages) {
2911
+ if (message.kind !== "text") {
2912
+ continue;
2913
+ }
2914
+ const normalizedContent = message.content.trim();
2915
+ if (!normalizedContent) {
2916
+ continue;
2917
+ }
2918
+ lines.push(message.role === "user" ? "[历史用户]" : "[历史助手]");
2919
+ lines.push(normalizedContent);
2920
+ lines.push("");
2921
+ appended += 1;
2922
+ }
2923
+ if (appended === 0) {
2924
+ return null;
2925
+ }
2926
+ return lines.join("\n").trim();
2927
+ }
2928
+ function readInstructionFileSafe(filePath) {
2929
+ const normalizedPath = filePath?.trim() || null;
2930
+ if (!normalizedPath || !existsSync(normalizedPath)) {
2931
+ return null;
2932
+ }
2933
+ try {
2934
+ return readFileSync(normalizedPath, "utf8");
2935
+ }
2936
+ catch {
2937
+ return null;
2938
+ }
2939
+ }
2940
+ function composeForkBootstrapInstructionFile(baseInstructionContent, overlay) {
2941
+ const sections = [baseInstructionContent?.trim() ?? "", overlay.trim()].filter((value) => value.length > 0);
2942
+ return `${sections.join("\n\n")}\n`;
2943
+ }
2528
2944
  function isSyntheticRuntimeSessionTitle(provider, title) {
2529
2945
  if (provider !== "codex") {
2530
2946
  return false;
@@ -2534,6 +2950,10 @@ function isSyntheticRuntimeSessionTitle(provider, title) {
2534
2950
  }
2535
2951
  function shouldStartNativeSessionOnFirstMessage(session) {
2536
2952
  if (session.provider !== "codex" && session.provider !== "opencode") {
2953
+ if (session.provider === "claude-code"
2954
+ && shouldBootstrapClaudeForkSession(session)) {
2955
+ return "start";
2956
+ }
2537
2957
  return "continue";
2538
2958
  }
2539
2959
  if (session.provider === "codex" && session.providerSessionId.startsWith("rollout-")) {
@@ -2798,8 +3218,30 @@ function buildClaudeHookBridgeConfig(config, provider) {
2798
3218
  command,
2799
3219
  supportedEvents: [
2800
3220
  "PreToolUse",
3221
+ "PostToolUse",
3222
+ "PostToolUseFailure",
3223
+ "PostToolBatch",
2801
3224
  "PermissionRequest",
3225
+ "PermissionDenied",
3226
+ "Elicitation",
3227
+ "ElicitationResult",
3228
+ "MessageDisplay",
2802
3229
  "Notification",
3230
+ "TaskCreated",
3231
+ "TaskCompleted",
3232
+ "TeammateIdle",
3233
+ "SubagentStart",
3234
+ "SubagentStop",
3235
+ "PreCompact",
3236
+ "PostCompact",
3237
+ "InstructionsLoaded",
3238
+ "ConfigChange",
3239
+ "CwdChanged",
3240
+ "FileChanged",
3241
+ "WorktreeCreate",
3242
+ "WorktreeRemove",
3243
+ "Setup",
3244
+ "UserPromptExpansion",
2803
3245
  "UserPromptSubmit",
2804
3246
  "SessionStart",
2805
3247
  "Stop",
@@ -2809,7 +3251,13 @@ function buildClaudeHookBridgeConfig(config, provider) {
2809
3251
  };
2810
3252
  }
2811
3253
  function resolveClaudeHookBridgeScriptPath() {
3254
+ const currentFilePath = fileURLToPath(import.meta.url);
3255
+ const currentDir = path.dirname(currentFilePath);
3256
+ const packageRoot = path.resolve(currentDir, "..", "..", "..", "..");
3257
+ const workspaceRoot = path.resolve(packageRoot, "..", "..", "..");
2812
3258
  const candidates = [
3259
+ path.resolve(packageRoot, "scripts", "claude-hook-bridge.cjs"),
3260
+ path.resolve(workspaceRoot, "scripts", "claude-hook-bridge.cjs"),
2813
3261
  path.resolve(process.cwd(), "scripts", "claude-hook-bridge.cjs"),
2814
3262
  path.resolve(process.cwd(), "..", "scripts", "claude-hook-bridge.cjs"),
2815
3263
  path.resolve(process.cwd(), "..", "..", "scripts", "claude-hook-bridge.cjs")