@jingyi0605/codingns 0.9.9 → 1.0.0-beta.2

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 (139) hide show
  1. package/bin/codingns.mjs +14 -0
  2. package/dist/public/assets/AdaptiveButlerPage-BHUAx_24.js +2 -0
  3. package/dist/public/assets/{App-Pe30N_Dy.js → App-D5hc6KuU.js} +6 -6
  4. package/dist/public/assets/App-Z0Zd7gXR.css +1 -0
  5. package/dist/public/assets/{BootstrapPage-D9ai1XhC.js → BootstrapPage-C2sMZMJa.js} +1 -1
  6. package/dist/public/assets/ConversationPage-CDpyEOUA.js +9 -0
  7. package/dist/public/assets/{DesktopDetachPreviewPage-BJVPVFf2.js → DesktopDetachPreviewPage-D9Abm5NU.js} +1 -1
  8. package/dist/public/assets/{DesktopModal-DyC_e0eF.js → DesktopModal-BCVZq_PQ.js} +1 -1
  9. package/dist/public/assets/{DesktopWindowPage-BZleww0T.js → DesktopWindowPage-Cotl5fte.js} +1 -1
  10. package/dist/public/assets/{FileContextPanel-BklQRQXj.js → FileContextPanel-DbPOq5w-.js} +1 -1
  11. package/dist/public/assets/{GitSidebar-D0tb3W87.js → GitSidebar-Cuxi2R93.js} +2 -2
  12. package/dist/public/assets/{MobileCreateSessionSheet-BS881Agk.js → MobileCreateSessionSheet-DYqarJqO.js} +1 -1
  13. package/dist/public/assets/{MobileSheet-C32hwIFd.js → MobileSheet-DPYtp5Ze.js} +1 -1
  14. package/dist/public/assets/{PluginAccessOverview-BDiz6fV8.js → PluginAccessOverview-CiVzb_NI.js} +1 -1
  15. package/dist/public/assets/{PluginContainerPage-BMT6J0YS.js → PluginContainerPage-BfZ3E41c.js} +1 -1
  16. package/dist/public/assets/{PluginDetailPage-ztNhvCWP.js → PluginDetailPage-DQQAg_ba.js} +1 -1
  17. package/dist/public/assets/{PluginsListPage-Cuk2-U6E.js → PluginsListPage-bF1NmA_r.js} +1 -1
  18. package/dist/public/assets/{PureConversationPage-ShluI2En.js → PureConversationPage-CWji-ARJ.js} +1 -1
  19. package/dist/public/assets/{RelayConnectEntryPage-CMbjlD0k.js → RelayConnectEntryPage-D4-3Fher.js} +1 -1
  20. package/dist/public/assets/{ServerSettingsModal-CLSWyHu6.js → ServerSettingsModal-BUY1Y3g6.js} +1 -1
  21. package/dist/public/assets/{SessionIndexPage-dqfauVAj.js → SessionIndexPage-zVB9jnzP.js} +1 -1
  22. package/dist/public/assets/SettingsPage-DeqR2p7d.js +2 -0
  23. package/dist/public/assets/{TerminalManagerPanel-75khWhRz.js → TerminalManagerPanel-BjnC70ga.js} +1 -1
  24. package/dist/public/assets/{ToolFilesPage-S3eWxO_D.js → ToolFilesPage-BZseQqvZ.js} +1 -1
  25. package/dist/public/assets/{ToolGitPage-8RAMpope.js → ToolGitPage-SrOO8Ftz.js} +1 -1
  26. package/dist/public/assets/{ToolProcessesPage-TZACKEoH.js → ToolProcessesPage-Dbzlqjdy.js} +1 -1
  27. package/dist/public/assets/{ToolsHomePage-Whgu6Tyf.js → ToolsHomePage-BBilUc0P.js} +1 -1
  28. package/dist/public/assets/{WorkbenchLandingPage-CC24vHza.js → WorkbenchLandingPage-FdsBC8WN.js} +1 -1
  29. package/dist/public/assets/WorkbenchLayout-yGxGKcG1.js +1081 -0
  30. package/dist/public/assets/{WorkbenchModal-2PaCY_Gj.js → WorkbenchModal-Cm-ciXO9.js} +1 -1
  31. package/dist/public/assets/WorkbenchShellRoute-CijuAd9-.js +1 -0
  32. package/dist/public/assets/WorkbenchShellRoute-DpycFsbp.css +1 -0
  33. package/dist/public/assets/{WorkspaceDebugDetailPage-Ce_oMmJ8.js → WorkspaceDebugDetailPage-CZtpOiY0.js} +1 -1
  34. package/dist/public/assets/{WorkspaceDetailPage-aX6w4Q7S.js → WorkspaceDetailPage-CbbgUz1H.js} +1 -1
  35. package/dist/public/assets/{WorkspaceHomePage-DQttgj2w.js → WorkspaceHomePage-DMNJKAYe.js} +1 -1
  36. package/dist/public/assets/{client-runtime-manager-CGVLChqs.js → client-runtime-manager-tsFkLtO_.js} +1 -1
  37. package/dist/public/assets/host-alias-df5bYDlE.js +1 -0
  38. package/dist/public/assets/index-CILPuSCp.js +50 -0
  39. package/dist/public/assets/index-CSlwKnM4.css +1 -0
  40. package/dist/public/assets/{login-direct-candidate-resolver-Bvs5uPix.js → login-direct-candidate-resolver-pkfXrMJy.js} +1 -1
  41. package/dist/public/assets/peer-host-config-sync-nS_66OLg.js +1 -0
  42. package/dist/public/assets/{plugin-permission-copy-Do028HzJ.js → plugin-permission-copy-LDy-lW7h.js} +1 -1
  43. package/dist/public/assets/{plugins-api-BOugklJ-.js → plugins-api-BGF9OybZ.js} +1 -1
  44. package/dist/public/assets/{preferences-service-Dys1mbBy.js → preferences-service-BC25oxA_.js} +1 -1
  45. package/dist/public/assets/{relay-entry-B6UDc5cD.js → relay-entry-ErQKnI7B.js} +1 -1
  46. package/dist/public/assets/{useRegisteredDebugTemplates-BafVuBvE.js → useRegisteredDebugTemplates-BTr3ATCZ.js} +1 -1
  47. package/dist/public/assets/workbench-navigation-hwM28xIb.js +1 -0
  48. package/dist/public/index.html +2 -2
  49. package/dist/server/config/env.js +5 -3
  50. package/dist/server/config/env.js.map +1 -1
  51. package/dist/server/modules/butler/butler-session-service.d.ts +1 -0
  52. package/dist/server/modules/butler/butler-session-service.js +2 -1
  53. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  54. package/dist/server/modules/client/client-controller.d.ts +1 -0
  55. package/dist/server/modules/client/client-controller.js +4 -0
  56. package/dist/server/modules/client/client-controller.js.map +1 -1
  57. package/dist/server/modules/client/client-service.d.ts +3 -0
  58. package/dist/server/modules/client/client-service.js +3 -0
  59. package/dist/server/modules/client/client-service.js.map +1 -1
  60. package/dist/server/modules/client/npm-global-package-service.js +35 -7
  61. package/dist/server/modules/client/npm-global-package-service.js.map +1 -1
  62. package/dist/server/modules/client/service-update-types.d.ts +3 -0
  63. package/dist/server/modules/peer-host/host-ws-proxy-service.js +73 -12
  64. package/dist/server/modules/peer-host/host-ws-proxy-service.js.map +1 -1
  65. package/dist/server/modules/peer-host/peer-host-controller.d.ts +5 -0
  66. package/dist/server/modules/peer-host/peer-host-controller.js +3 -0
  67. package/dist/server/modules/peer-host/peer-host-controller.js.map +1 -1
  68. package/dist/server/modules/peer-host/peer-host-service.d.ts +1 -0
  69. package/dist/server/modules/peer-host/peer-host-service.js +44 -0
  70. package/dist/server/modules/peer-host/peer-host-service.js.map +1 -1
  71. package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +2 -0
  72. package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
  73. package/dist/server/modules/provider/provider-discovery-helper-process.js +6 -3
  74. package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
  75. package/dist/server/modules/provider/provider-discovery-runtime.js +4 -1
  76. package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
  77. package/dist/server/modules/sessions/session-history-service.d.ts +9 -0
  78. package/dist/server/modules/sessions/session-history-service.js +129 -7
  79. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  80. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +13 -0
  81. package/dist/server/modules/sessions/session-live-runtime-service.js +354 -5
  82. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  83. package/dist/server/modules/sessions/session-permission-request-service.d.ts +16 -1
  84. package/dist/server/modules/sessions/session-permission-request-service.js +375 -9
  85. package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
  86. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js +10 -0
  87. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js.map +1 -1
  88. package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +1 -0
  89. package/dist/server/modules/tasks/task-helper-process-handlers.js +4 -1
  90. package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -1
  91. package/dist/server/modules/terminal/runtime/terminal-log-spooler.d.ts +5 -0
  92. package/dist/server/modules/terminal/runtime/terminal-log-spooler.js +54 -7
  93. package/dist/server/modules/terminal/runtime/terminal-log-spooler.js.map +1 -1
  94. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.js +2 -1
  95. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.js.map +1 -1
  96. package/dist/server/modules/workbench/workbench-service.js +18 -5
  97. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  98. package/dist/server/modules/workspace/affairs-lightweight-session-controller.d.ts +4 -0
  99. package/dist/server/modules/workspace/affairs-lightweight-session-controller.js +26 -0
  100. package/dist/server/modules/workspace/affairs-lightweight-session-controller.js.map +1 -1
  101. package/dist/server/modules/workspace/affairs-lightweight-session-service.d.ts +15 -1
  102. package/dist/server/modules/workspace/affairs-lightweight-session-service.js +201 -22
  103. package/dist/server/modules/workspace/affairs-lightweight-session-service.js.map +1 -1
  104. package/dist/server/routes/client.js +1 -0
  105. package/dist/server/routes/client.js.map +1 -1
  106. package/dist/server/routes/peer-hosts.js +1 -0
  107. package/dist/server/routes/peer-hosts.js.map +1 -1
  108. package/dist/server/server/create-server.js +1 -1
  109. package/dist/server/server/create-server.js.map +1 -1
  110. package/dist/server/shared/utils/peer-host-proxy-log.d.ts +3 -0
  111. package/dist/server/shared/utils/peer-host-proxy-log.js +48 -0
  112. package/dist/server/shared/utils/peer-host-proxy-log.js.map +1 -0
  113. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +2 -0
  114. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +93 -12
  115. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  116. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +3 -0
  117. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +116 -2
  118. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  119. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +1 -0
  120. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +19 -8
  121. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
  122. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.d.ts +1 -0
  123. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +30 -2
  124. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +1 -1
  125. package/package.json +3 -2
  126. package/scripts/node22-runtime.mjs +176 -0
  127. package/scripts/postinstall.mjs +6 -0
  128. package/dist/public/assets/AdaptiveButlerPage-CqBM1XiC.js +0 -2
  129. package/dist/public/assets/App-7zrCMhE-.css +0 -1
  130. package/dist/public/assets/ConversationPage-C8aU99KN.js +0 -9
  131. package/dist/public/assets/SettingsPage-PUga-wwJ.js +0 -2
  132. package/dist/public/assets/WorkbenchLayout-CoYxMzzR.js +0 -1081
  133. package/dist/public/assets/WorkbenchShellRoute-DLVRpGzb.css +0 -1
  134. package/dist/public/assets/WorkbenchShellRoute-QXztW_Ny.js +0 -1
  135. package/dist/public/assets/host-alias-CeDJeL0T.js +0 -1
  136. package/dist/public/assets/index-BehUkul4.css +0 -1
  137. package/dist/public/assets/index-Bp1FnRo_.js +0 -50
  138. package/dist/public/assets/peer-host-config-sync-oelf2T0_.js +0 -1
  139. package/dist/public/assets/workbench-navigation-Bs4OQYD3.js +0 -1
@@ -5,7 +5,7 @@ import { AppError } from "../../shared/errors/app-error.js";
5
5
  import { createId } from "../../shared/utils/id.js";
6
6
  const LIGHTWEIGHT_PROVIDER_IDS = new Set(["codex", "claude-code"]);
7
7
  const LIGHTWEIGHT_STORAGE_VERSION = 1;
8
- const DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-20250514";
8
+ const DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6";
9
9
  const DEFAULT_OPENAI_MODEL = "gpt-5.4";
10
10
  const ANTHROPIC_API_VERSION = "2023-06-01";
11
11
  const LIGHTWEIGHT_SESSION_TMP_FILE_SUFFIX = ".tmp";
@@ -21,11 +21,15 @@ const LIGHTWEIGHT_SYSTEM_PROMPT = [
21
21
  ].join("\n");
22
22
  export class AffairsLightweightSessionService {
23
23
  hostDataRootDir;
24
+ sessionProviderConfigService;
25
+ workspaceService;
24
26
  sessionLocks = new Map();
25
27
  sessionDocumentCache = new Map();
26
28
  teableMirrorSyncNotifier = null;
27
- constructor(hostDataRootDir) {
29
+ constructor(hostDataRootDir, sessionProviderConfigService = null, workspaceService = null) {
28
30
  this.hostDataRootDir = hostDataRootDir;
31
+ this.sessionProviderConfigService = sessionProviderConfigService;
32
+ this.workspaceService = workspaceService;
29
33
  }
30
34
  configureTeableMirrorSyncNotifier(notifier) {
31
35
  this.teableMirrorSyncNotifier = notifier;
@@ -119,14 +123,23 @@ export class AffairsLightweightSessionService {
119
123
  const acceptedAt = new Date().toISOString();
120
124
  const sessionFilePath = this.resolveSessionFilePath(input.workspaceId, sessionId);
121
125
  const providerSessionId = `affairs-lightweight:${input.provider}:${sessionId}`;
126
+ const providerBinding = this.prepareLightweightProviderBinding({
127
+ sessionId,
128
+ workspaceId: input.workspaceId,
129
+ userId: input.userId,
130
+ provider: input.provider,
131
+ providerConfigMode: input.providerConfigMode ?? null,
132
+ providerPresetId: input.providerPresetId ?? null
133
+ });
122
134
  const session = {
123
135
  sessionId,
124
136
  workspaceId: input.workspaceId,
137
+ sourceWorkspaceId: input.sourceWorkspaceId?.trim() || input.workspaceId,
125
138
  provider: input.provider,
126
139
  providerSessionId,
127
140
  rawStoreRef: sessionFilePath,
128
- providerConfigMode: "global-default",
129
- providerPresetId: null,
141
+ providerConfigMode: providerBinding.providerConfigMode,
142
+ providerPresetId: providerBinding.providerPresetId,
130
143
  parentSessionId: null,
131
144
  isSubagent: false,
132
145
  subagentLabel: null,
@@ -160,12 +173,15 @@ export class AffairsLightweightSessionService {
160
173
  this.notifyTeableSessionChanged(input.userId, `session_started:${input.workspaceId}:${sessionId}`);
161
174
  return this.runTurn(document, {
162
175
  workspaceId: input.workspaceId,
176
+ sourceWorkspaceId: input.sourceWorkspaceId ?? null,
163
177
  userId: input.userId,
164
178
  sessionId,
165
179
  content: input.content,
166
180
  clientRequestId,
167
181
  model: input.model ?? null,
168
182
  reasoningLevel: input.reasoningLevel ?? null,
183
+ providerConfigMode: input.providerConfigMode ?? null,
184
+ providerPresetId: input.providerPresetId ?? null,
169
185
  attachments: input.attachments ?? []
170
186
  });
171
187
  }
@@ -182,14 +198,23 @@ export class AffairsLightweightSessionService {
182
198
  const acceptedAt = new Date().toISOString();
183
199
  const sessionFilePath = this.resolveSessionFilePath(input.workspaceId, sessionId);
184
200
  const providerSessionId = `affairs-lightweight:${input.provider}:${sessionId}`;
201
+ const providerBinding = this.prepareLightweightProviderBinding({
202
+ sessionId,
203
+ workspaceId: input.workspaceId,
204
+ userId: input.userId,
205
+ provider: input.provider,
206
+ providerConfigMode: input.providerConfigMode ?? null,
207
+ providerPresetId: input.providerPresetId ?? null
208
+ });
185
209
  const session = {
186
210
  sessionId,
187
211
  workspaceId: input.workspaceId,
212
+ sourceWorkspaceId: input.sourceWorkspaceId?.trim() || input.workspaceId,
188
213
  provider: input.provider,
189
214
  providerSessionId,
190
215
  rawStoreRef: sessionFilePath,
191
- providerConfigMode: "global-default",
192
- providerPresetId: null,
216
+ providerConfigMode: providerBinding.providerConfigMode,
217
+ providerPresetId: providerBinding.providerPresetId,
193
218
  parentSessionId: null,
194
219
  isSubagent: false,
195
220
  subagentLabel: null,
@@ -222,12 +247,15 @@ export class AffairsLightweightSessionService {
222
247
  await this.writeSessionDocument(document);
223
248
  await this.runTurn(document, {
224
249
  workspaceId: input.workspaceId,
250
+ sourceWorkspaceId: input.sourceWorkspaceId ?? null,
225
251
  userId: input.userId,
226
252
  sessionId,
227
253
  content: input.content,
228
254
  clientRequestId,
229
255
  model: input.model ?? null,
230
256
  reasoningLevel: input.reasoningLevel ?? null,
257
+ providerConfigMode: input.providerConfigMode ?? null,
258
+ providerPresetId: input.providerPresetId ?? null,
231
259
  attachments: input.attachments ?? []
232
260
  }, onEvent);
233
261
  }
@@ -259,6 +287,14 @@ export class AffairsLightweightSessionService {
259
287
  const now = new Date().toISOString();
260
288
  const document = await this.requireSessionDocument(input.workspaceId, input.sessionId, input.userId);
261
289
  const provider = document.session.provider;
290
+ const providerBinding = this.prepareLightweightProviderBinding({
291
+ sessionId: document.session.sessionId,
292
+ workspaceId: input.workspaceId,
293
+ userId: input.userId,
294
+ provider,
295
+ providerConfigMode: input.providerConfigMode ?? document.session.providerConfigMode ?? null,
296
+ providerPresetId: input.providerPresetId ?? document.session.providerPresetId ?? null
297
+ });
262
298
  const userMessage = createUserMessage({
263
299
  provider,
264
300
  providerSessionId: document.session.providerSessionId,
@@ -279,6 +315,8 @@ export class AffairsLightweightSessionService {
279
315
  lastEventAt: now,
280
316
  runningState: "running",
281
317
  activityState: "running",
318
+ providerConfigMode: providerBinding.providerConfigMode,
319
+ providerPresetId: providerBinding.providerPresetId,
282
320
  completedAt: null,
283
321
  syncStatus: "syncing",
284
322
  lastSyncAt: now,
@@ -409,7 +447,7 @@ export class AffairsLightweightSessionService {
409
447
  return this.generateAnthropicResponseSync(document, input);
410
448
  }
411
449
  async generateOpenAiResponseSync(document, input) {
412
- const runtime = await this.readOpenAiRuntimeConfig(input.model?.trim() || null);
450
+ const runtime = await this.readOpenAiRuntimeConfig(document.session, input.model?.trim() || null, input.userId);
413
451
  const responsePayload = createOpenAiResponsesPayload({
414
452
  model: runtime.model,
415
453
  messages: document.messages,
@@ -467,7 +505,7 @@ export class AffairsLightweightSessionService {
467
505
  return fallbackText;
468
506
  }
469
507
  async generateAnthropicResponseSync(document, input) {
470
- const runtime = await this.readAnthropicRuntimeConfig(input.model?.trim() || null);
508
+ const runtime = await this.readAnthropicRuntimeConfig(document.session, input.model?.trim() || null, input.userId);
471
509
  let messages = document.messages.map((message) => createAnthropicMessagePayload(message, input.clientRequestId, normalizeLightweightRuntimeAttachments(input.attachments ?? [], input.clientRequestId)));
472
510
  let finalBody = null;
473
511
  for (let index = 0; index < 4; index += 1) {
@@ -526,7 +564,7 @@ export class AffairsLightweightSessionService {
526
564
  return outputText;
527
565
  }
528
566
  async generateOpenAiResponseStream(document, input, onDelta, onTool) {
529
- const runtime = await this.readOpenAiRuntimeConfig(input.model?.trim() || null);
567
+ const runtime = await this.readOpenAiRuntimeConfig(document.session, input.model?.trim() || null, input.userId);
530
568
  const headers = {
531
569
  "content-type": "application/json",
532
570
  authorization: `Bearer ${runtime.apiKey}`
@@ -582,7 +620,7 @@ export class AffairsLightweightSessionService {
582
620
  return await streamOpenAiSseText(fallbackResponse, onDelta, onTool);
583
621
  }
584
622
  async generateAnthropicResponseStream(document, input, onDelta, onTool) {
585
- const runtime = await this.readAnthropicRuntimeConfig(input.model?.trim() || null);
623
+ const runtime = await this.readAnthropicRuntimeConfig(document.session, input.model?.trim() || null, input.userId);
586
624
  const response = await postJsonWithFallbacks({
587
625
  baseUrl: runtime.baseUrl,
588
626
  pathCandidates: ["v1/messages", "messages"],
@@ -701,17 +739,26 @@ export class AffairsLightweightSessionService {
701
739
  const cached = this.sessionDocumentCache.get(normalizedSessionId);
702
740
  return cached ? cloneSessionDocument(cached) : null;
703
741
  }
704
- async readOpenAiRuntimeConfig(modelOverride) {
742
+ async readOpenAiRuntimeConfig(session, modelOverride, userId) {
705
743
  const injectedConfig = await this.readLightweightRuntimeConfigFile();
706
- const authPath = path.join(os.homedir(), ".codex", "auth.json");
707
- const configPath = path.join(os.homedir(), ".codex", "config.toml");
744
+ const providerBinding = this.resolveLightweightProviderBinding({
745
+ ...session,
746
+ userId
747
+ });
748
+ const providerLaunchContext = providerBinding
749
+ ? this.sessionProviderConfigService?.resolveLaunchContext(providerBinding) ?? null
750
+ : null;
751
+ const runtimeEnv = providerLaunchContext?.runtimeEnv ?? {};
752
+ const runtimeHomeDir = providerLaunchContext?.runtimeHomeDir?.trim() || null;
753
+ const authPath = path.join(runtimeHomeDir ?? path.join(os.homedir(), ".codex"), "auth.json");
754
+ const configPath = path.join(runtimeHomeDir ?? path.join(os.homedir(), ".codex"), "config.toml");
708
755
  const auth = await readJsonFile(authPath);
709
756
  const toml = await safeReadTextFile(configPath);
710
757
  const parsed = parseCodexTomlConfig(toml);
711
- const key = pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_OPENAI_API_KEY, process.env.OPENAI_API_KEY, injectedConfig?.openai?.apiKey, String(auth?.OPENAI_API_KEY ?? ""));
712
- const baseUrl = normalizeBaseUrl(pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_OPENAI_BASE_URL, injectedConfig?.openai?.baseUrl, process.env.OPENAI_BASE_URL, parsed.baseUrl, "https://api.openai.com/v1") ?? "https://api.openai.com/v1");
758
+ const key = pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_OPENAI_API_KEY, runtimeEnv.OPENAI_API_KEY, process.env.OPENAI_API_KEY, injectedConfig?.openai?.apiKey, String(auth?.OPENAI_API_KEY ?? ""));
759
+ const baseUrl = normalizeBaseUrl(pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_OPENAI_BASE_URL, runtimeEnv.OPENAI_BASE_URL, injectedConfig?.openai?.baseUrl, process.env.OPENAI_BASE_URL, parsed.baseUrl, "https://api.openai.com/v1") ?? "https://api.openai.com/v1");
713
760
  const model = modelOverride
714
- || pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_OPENAI_MODEL, injectedConfig?.openai?.model, process.env.OPENAI_MODEL, parsed.model, DEFAULT_OPENAI_MODEL)
761
+ || pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_OPENAI_MODEL, runtimeEnv.OPENAI_MODEL, injectedConfig?.openai?.model, process.env.OPENAI_MODEL, parsed.model, DEFAULT_OPENAI_MODEL)
715
762
  || DEFAULT_OPENAI_MODEL;
716
763
  if (!key) {
717
764
  throw new AppError({
@@ -726,17 +773,29 @@ export class AffairsLightweightSessionService {
726
773
  model
727
774
  };
728
775
  }
729
- async readAnthropicRuntimeConfig(modelOverride) {
776
+ async readAnthropicRuntimeConfig(session, modelOverride, userId) {
730
777
  const injectedConfig = await this.readLightweightRuntimeConfigFile();
731
- const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
732
- const configPath = path.join(os.homedir(), ".claude", "config.json");
778
+ const providerBinding = this.resolveLightweightProviderBinding({
779
+ ...session,
780
+ userId
781
+ });
782
+ const providerLaunchContext = providerBinding
783
+ ? this.sessionProviderConfigService?.resolveLaunchContext(providerBinding) ?? null
784
+ : null;
785
+ const runtimeEnv = providerLaunchContext?.runtimeEnv ?? {};
786
+ const runtimeHomeDir = providerLaunchContext?.runtimeHomeDir?.trim() || null;
787
+ const settingsPath = path.join(runtimeHomeDir ?? path.join(os.homedir(), ".claude"), "settings.json");
788
+ const configPath = path.join(runtimeHomeDir ?? path.join(os.homedir(), ".claude"), "config.json");
733
789
  const settings = await readJsonFile(settingsPath);
734
790
  const config = await readJsonFile(configPath);
735
791
  const env = typeof settings?.env === "object" && settings.env ? settings.env : {};
736
- const key = pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_ANTHROPIC_API_KEY, process.env.ANTHROPIC_API_KEY, injectedConfig?.anthropic?.apiKey, String(env.ANTHROPIC_AUTH_TOKEN ?? config?.primaryApiKey ?? ""));
737
- const baseUrl = normalizeBaseUrl(pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_ANTHROPIC_BASE_URL, injectedConfig?.anthropic?.baseUrl, String(env.ANTHROPIC_BASE_URL ?? ""), process.env.ANTHROPIC_BASE_URL, "https://api.anthropic.com") ?? "https://api.anthropic.com");
792
+ const inferredWorkspaceModel = modelOverride
793
+ ? null
794
+ : await this.readLatestClaudeWorkspaceRuntimeModel(session.sourceWorkspaceId?.trim() || session.workspaceId);
795
+ const key = pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_ANTHROPIC_API_KEY, runtimeEnv.ANTHROPIC_AUTH_TOKEN, runtimeEnv.ANTHROPIC_API_KEY, process.env.ANTHROPIC_API_KEY, injectedConfig?.anthropic?.apiKey, String(env.ANTHROPIC_AUTH_TOKEN ?? config?.primaryApiKey ?? ""));
796
+ const baseUrl = normalizeBaseUrl(pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_ANTHROPIC_BASE_URL, runtimeEnv.ANTHROPIC_BASE_URL, injectedConfig?.anthropic?.baseUrl, String(env.ANTHROPIC_BASE_URL ?? ""), process.env.ANTHROPIC_BASE_URL, "https://api.anthropic.com") ?? "https://api.anthropic.com");
738
797
  const model = modelOverride
739
- || pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_ANTHROPIC_MODEL, injectedConfig?.anthropic?.model, String(env.ANTHROPIC_MODEL ?? ""), DEFAULT_CLAUDE_MODEL)
798
+ || pickFirstText(process.env.CODINGNS_LIGHTWEIGHT_ANTHROPIC_MODEL, runtimeEnv.ANTHROPIC_MODEL, injectedConfig?.anthropic?.model, String(env.ANTHROPIC_MODEL ?? ""), inferredWorkspaceModel, DEFAULT_CLAUDE_MODEL)
740
799
  || DEFAULT_CLAUDE_MODEL;
741
800
  if (!key) {
742
801
  throw new AppError({
@@ -751,6 +810,70 @@ export class AffairsLightweightSessionService {
751
810
  model
752
811
  };
753
812
  }
813
+ prepareLightweightProviderBinding(input) {
814
+ if (!this.sessionProviderConfigService) {
815
+ return {
816
+ provider: input.provider,
817
+ providerConfigMode: input.providerConfigMode ?? "global-default",
818
+ providerPresetId: input.providerPresetId?.trim() || null,
819
+ runtimeHomeDir: null
820
+ };
821
+ }
822
+ const binding = this.sessionProviderConfigService.prepareSessionBinding({
823
+ sessionId: input.sessionId,
824
+ userId: input.userId,
825
+ workspaceId: input.workspaceId,
826
+ provider: input.provider,
827
+ providerConfigMode: input.providerConfigMode ?? null,
828
+ providerPresetId: input.providerPresetId ?? null
829
+ });
830
+ return {
831
+ provider: input.provider,
832
+ providerConfigMode: binding.providerConfigMode,
833
+ providerPresetId: binding.providerPresetId,
834
+ runtimeHomeDir: binding.runtimeHomeDir
835
+ };
836
+ }
837
+ resolveLightweightProviderBinding(session) {
838
+ if (!this.sessionProviderConfigService) {
839
+ return null;
840
+ }
841
+ return this.prepareLightweightProviderBinding({
842
+ sessionId: session.sessionId,
843
+ workspaceId: session.sourceWorkspaceId?.trim() || session.workspaceId,
844
+ userId: session.userId,
845
+ provider: session.provider,
846
+ providerConfigMode: session.providerConfigMode ?? "global-default",
847
+ providerPresetId: session.providerPresetId ?? null
848
+ });
849
+ }
850
+ async readLatestClaudeWorkspaceRuntimeModel(workspaceId) {
851
+ const normalizedWorkspaceId = workspaceId.trim();
852
+ if (!normalizedWorkspaceId || !this.workspaceService) {
853
+ return null;
854
+ }
855
+ const workspace = this.workspaceService.getWorkspaceOrThrow(normalizedWorkspaceId);
856
+ const targetWorkspacePath = normalizeWorkspacePathForMatch(workspace.path);
857
+ const runtimeRootDir = path.join(this.hostDataRootDir, "workspace-session-runtime", normalizedWorkspaceId);
858
+ const runtimeEntries = await safeReadDir(runtimeRootDir);
859
+ let matchedModel = null;
860
+ for (const runtimeEntry of runtimeEntries) {
861
+ if (!runtimeEntry.isFile()) {
862
+ const projectsRootDir = path.join(runtimeRootDir, runtimeEntry.name, "projects");
863
+ const projectFiles = await collectJsonlFiles(projectsRootDir);
864
+ for (const filePath of projectFiles) {
865
+ const candidate = await readLatestClaudeTranscriptModelCandidate(filePath, targetWorkspacePath);
866
+ if (!candidate) {
867
+ continue;
868
+ }
869
+ if (!matchedModel || candidate.timestamp > matchedModel.timestamp) {
870
+ matchedModel = candidate;
871
+ }
872
+ }
873
+ }
874
+ }
875
+ return matchedModel?.model ?? null;
876
+ }
754
877
  resolveWorkspaceDir(workspaceId) {
755
878
  return path.join(this.hostDataRootDir, "affairs-lightweight-sessions", workspaceId);
756
879
  }
@@ -1503,6 +1626,62 @@ async function safeReadDir(dirPath) {
1503
1626
  throw error;
1504
1627
  }
1505
1628
  }
1629
+ async function collectJsonlFiles(rootDir) {
1630
+ const entries = await safeReadDir(rootDir);
1631
+ const results = [];
1632
+ for (const entry of entries) {
1633
+ const fullPath = path.join(rootDir, entry.name);
1634
+ if (entry.isFile()) {
1635
+ if (entry.name.endsWith(".jsonl")) {
1636
+ results.push(fullPath);
1637
+ }
1638
+ continue;
1639
+ }
1640
+ results.push(...await collectJsonlFiles(fullPath));
1641
+ }
1642
+ return results;
1643
+ }
1644
+ async function readLatestClaudeTranscriptModelCandidate(filePath, targetWorkspacePath) {
1645
+ const content = await safeReadTextFile(filePath);
1646
+ if (!content) {
1647
+ return null;
1648
+ }
1649
+ const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0);
1650
+ let latest = null;
1651
+ for (const line of lines) {
1652
+ let parsed = null;
1653
+ try {
1654
+ parsed = JSON.parse(line);
1655
+ }
1656
+ catch {
1657
+ continue;
1658
+ }
1659
+ const cwd = normalizeWorkspacePathForMatch(String(parsed?.cwd ?? ""));
1660
+ if (!cwd || cwd !== targetWorkspacePath) {
1661
+ continue;
1662
+ }
1663
+ const model = pickFirstText(parsed?.message?.model, parsed?.model);
1664
+ const timestamp = normalizeOptionalTimestamp(String(parsed?.timestamp ?? "")) ?? "";
1665
+ if (!model || !timestamp) {
1666
+ continue;
1667
+ }
1668
+ if (!latest || timestamp > latest.timestamp) {
1669
+ latest = { model, timestamp };
1670
+ }
1671
+ }
1672
+ return latest;
1673
+ }
1674
+ function normalizeWorkspacePathForMatch(value) {
1675
+ const trimmed = value.trim();
1676
+ if (!trimmed) {
1677
+ return "";
1678
+ }
1679
+ const normalized = trimmed.replaceAll("\\", "/");
1680
+ if (/^[a-z]:(?:\/|$)/i.test(normalized) || normalized.startsWith("//")) {
1681
+ return normalized.replace(/\/+$/, "").toLowerCase();
1682
+ }
1683
+ return normalized.length > 1 ? normalized.replace(/\/+$/, "") : normalized;
1684
+ }
1506
1685
  async function postJsonWithFallbacks(input) {
1507
1686
  let lastResponse = null;
1508
1687
  for (const candidate of input.pathCandidates) {