u-foo 2.3.32 → 2.4.0

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 (235) hide show
  1. package/README.md +157 -213
  2. package/README.zh-CN.md +151 -197
  3. package/SKILLS/ufoo/SKILL.md +8 -8
  4. package/bin/uagy.js +69 -0
  5. package/bin/uclaude.js +2 -2
  6. package/bin/ucode.js +4 -4
  7. package/bin/ucodex.js +2 -2
  8. package/bin/ufoo.js +5 -23
  9. package/modules/AGENTS.template.md +1 -1
  10. package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
  11. package/package.json +5 -5
  12. package/scripts/chat-app-smoke.js +1 -1
  13. package/scripts/global-chat-switch-benchmark.js +5 -5
  14. package/scripts/ink-demo.js +1 -1
  15. package/scripts/ink-smoke.js +1 -1
  16. package/scripts/ucode-app-smoke.js +1 -1
  17. package/src/{agent → agents/activity}/activityDetector.js +39 -2
  18. package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
  19. package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
  20. package/src/{agent → agents/activity}/activityTracker.js +1 -1
  21. package/src/agents/activity/index.js +8 -0
  22. package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
  23. package/src/agents/controller/index.js +8 -0
  24. package/src/{agent → agents/controller}/loopObservability.js +2 -2
  25. package/src/{agent → agents/controller}/loopRuntime.js +1 -1
  26. package/src/{agent → agents/controller}/ufooAgent.js +9 -9
  27. package/src/agents/index.js +10 -0
  28. package/src/agents/internal/index.js +3 -0
  29. package/src/{agent → agents/internal}/internalRunner.js +45 -22
  30. package/src/agents/launch/agyConversation.js +159 -0
  31. package/src/agents/launch/index.js +12 -0
  32. package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
  33. package/src/{agent → agents/launch}/launcher.js +64 -21
  34. package/src/{agent → agents/launch}/notifier.js +23 -12
  35. package/src/{agent → agents/launch}/ptyRunner.js +44 -12
  36. package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
  37. package/src/{agent → agents/launch}/publisherRouting.js +1 -1
  38. package/src/{agent → agents/launch}/readyDetector.js +23 -0
  39. package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
  40. package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
  41. package/src/agents/prompts/index.js +8 -0
  42. package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
  43. package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
  44. package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
  45. package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
  46. package/src/agents/providers/index.js +13 -0
  47. package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
  48. package/src/{chat → app/chat}/agentSockets.js +1 -1
  49. package/src/{chat → app/chat}/commandExecutor.js +50 -26
  50. package/src/{chat → app/chat}/commands.js +119 -5
  51. package/src/{chat → app/chat}/daemonConnection.js +1 -1
  52. package/src/{chat → app/chat}/daemonMessageRouter.js +45 -3
  53. package/src/{chat → app/chat}/dashboardView.js +2 -1
  54. package/src/app/chat/index.js +6 -0
  55. package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
  56. package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
  57. package/src/app/chat/multiWindow/index.js +268 -0
  58. package/src/app/chat/multiWindow/paneLayout.js +84 -0
  59. package/src/app/chat/multiWindow/paneManager.js +299 -0
  60. package/src/app/chat/multiWindow/renderer.js +384 -0
  61. package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
  62. package/src/{chat → app/chat}/transport.js +1 -1
  63. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  64. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  65. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  66. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  67. package/src/app/cli/index.js +9 -0
  68. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  69. package/src/{cli.js → app/cli/run.js} +59 -57
  70. package/src/app/index.js +6 -0
  71. package/src/code/agent.js +10 -9
  72. package/src/code/index.js +2 -0
  73. package/src/code/launcher/index.js +9 -0
  74. package/src/{agent → code/launcher}/ucode.js +7 -8
  75. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  76. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  77. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  78. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  79. package/src/code/nativeRunner.js +4 -4
  80. package/src/code/tui.js +3 -1454
  81. package/src/config.js +15 -2
  82. package/src/{bus → coordination/bus}/activate.js +2 -2
  83. package/src/{bus → coordination/bus}/daemon.js +15 -5
  84. package/src/coordination/bus/envelope.js +173 -0
  85. package/src/{bus → coordination/bus}/index.js +7 -3
  86. package/src/{bus → coordination/bus}/inject.js +11 -3
  87. package/src/{bus → coordination/bus}/message.js +1 -1
  88. package/src/coordination/bus/messageMeta.js +130 -0
  89. package/src/coordination/bus/promptEnvelope.js +65 -0
  90. package/src/{bus → coordination/bus}/shake.js +1 -1
  91. package/src/{bus → coordination/bus}/store.js +3 -3
  92. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  93. package/src/{bus → coordination/bus}/utils.js +2 -2
  94. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  95. package/src/coordination/index.js +10 -0
  96. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  97. package/src/{memory → coordination/memory}/index.js +3 -3
  98. package/src/{report → coordination/report}/store.js +2 -2
  99. package/src/{status → coordination/status}/index.js +3 -3
  100. package/src/online/bridge.js +2 -2
  101. package/src/{controller → orchestration/controller}/flags.js +1 -1
  102. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  103. package/src/orchestration/controller/index.js +10 -0
  104. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  105. package/src/orchestration/groups/bootstrap.js +3 -0
  106. package/src/orchestration/groups/index.js +10 -0
  107. package/src/orchestration/groups/promptProfiles.js +3 -0
  108. package/src/{group → orchestration/groups}/templates.js +1 -1
  109. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  110. package/src/orchestration/index.js +7 -0
  111. package/src/orchestration/solo/index.js +3 -0
  112. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  113. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  114. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  115. package/src/{daemon → runtime/daemon}/index.js +105 -53
  116. package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
  117. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  118. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  119. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  120. package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
  121. package/src/runtime/daemon/providerSessions.js +230 -0
  122. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  123. package/src/{daemon → runtime/daemon}/run.js +4 -4
  124. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  125. package/src/{daemon → runtime/daemon}/status.js +5 -5
  126. package/src/runtime/index.js +10 -0
  127. package/src/{projects → runtime/projects}/registry.js +1 -1
  128. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  129. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  130. package/src/tools/handlers/common.js +1 -1
  131. package/src/tools/handlers/listAgents.js +1 -1
  132. package/src/tools/handlers/memory.js +3 -3
  133. package/src/tools/handlers/readBusSummary.js +1 -1
  134. package/src/tools/handlers/readOpenDecisions.js +1 -1
  135. package/src/tools/handlers/readProjectRegistry.js +1 -1
  136. package/src/tools/handlers/readPromptHistory.js +2 -2
  137. package/src/tools/schemaFixtures.js +1 -1
  138. package/src/ui/MIGRATION.md +42 -88
  139. package/src/ui/format/index.js +5 -28
  140. package/src/ui/index.js +1 -1
  141. package/src/ui/{components → ink}/ChatApp.js +812 -88
  142. package/src/ui/ink/DashboardBar.js +685 -0
  143. package/src/ui/{components → ink}/MultilineInput.js +230 -5
  144. package/src/ui/{components → ink}/UcodeApp.js +16 -7
  145. package/src/ui/{components → ink}/agentMirror.js +24 -19
  146. package/src/ui/{components → ink}/chatReducer.js +29 -7
  147. package/src/bus/messageMeta.js +0 -52
  148. package/src/chat/agentViewController.js +0 -1072
  149. package/src/chat/chatLogController.js +0 -138
  150. package/src/chat/completionController.js +0 -533
  151. package/src/chat/dashboardKeyController.js +0 -533
  152. package/src/chat/index.js +0 -2222
  153. package/src/chat/inputHistoryController.js +0 -135
  154. package/src/chat/inputListenerController.js +0 -470
  155. package/src/chat/layout.js +0 -186
  156. package/src/chat/pasteController.js +0 -81
  157. package/src/chat/statusLineController.js +0 -223
  158. package/src/chat/streamTracker.js +0 -156
  159. package/src/code/config +0 -0
  160. package/src/daemon/providerSessions.js +0 -488
  161. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  162. package/src/ui/components/DashboardBar.js +0 -417
  163. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  164. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  165. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  166. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  167. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  168. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  169. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  170. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  171. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  176. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  177. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  178. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  179. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  180. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  181. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  182. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  183. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  184. /package/src/{chat → app/chat}/agentBar.js +0 -0
  185. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  186. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  187. /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
  188. /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
  189. /package/src/{chat → app/chat}/daemonTransport.js +0 -0
  190. /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
  191. /package/src/{chat → app/chat}/inputMath.js +0 -0
  192. /package/src/{chat → app/chat}/projectCloseController.js +0 -0
  193. /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
  194. /package/src/{chat → app/chat}/settingsController.js +0 -0
  195. /package/src/{chat → app/chat}/shellCommand.js +0 -0
  196. /package/src/{chat → app/chat}/text.js +0 -0
  197. /package/src/{chat → app/chat}/transientAgentState.js +0 -0
  198. /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
  199. /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
  200. /package/src/{bus → coordination/bus}/nickname.js +0 -0
  201. /package/src/{bus → coordination/bus}/queue.js +0 -0
  202. /package/src/{context → coordination/context}/decisions.js +0 -0
  203. /package/src/{context → coordination/context}/doctor.js +0 -0
  204. /package/src/{context → coordination/context}/index.js +0 -0
  205. /package/src/{context → coordination/context}/sync.js +0 -0
  206. /package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +0 -0
  207. /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
  208. /package/src/{ufoo → coordination/state}/paths.js +0 -0
  209. /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
  210. /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
  211. /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
  212. /package/src/{group → orchestration/groups}/diagram.js +0 -0
  213. /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
  214. /package/src/{solo → orchestration/solo}/commands.js +0 -0
  215. /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
  216. /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
  217. /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
  218. /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
  219. /package/src/{utils → runtime/process}/nodeExecutable.js +0 -0
  220. /package/src/{projects → runtime/projects}/identity.js +0 -0
  221. /package/src/{projects → runtime/projects}/index.js +0 -0
  222. /package/src/{projects → runtime/projects}/projectId.js +0 -0
  223. /package/src/{projects → runtime/projects}/runtimes.js +0 -0
  224. /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
  225. /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
  226. /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
  227. /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
  228. /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
  229. /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
  230. /package/src/{terminal → runtime/terminal}/detect.js +0 -0
  231. /package/src/{terminal → runtime/terminal}/index.js +0 -0
  232. /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
  233. /package/src/{utils → ui/format}/banner.js +0 -0
  234. /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
  235. /package/src/ui/{components → ink}/InkDemo.js +0 -0
@@ -1,488 +0,0 @@
1
- const fs = require("fs");
2
- const os = require("os");
3
- const path = require("path");
4
- const EventBus = require("../bus");
5
- const { loadAgentsData, saveAgentsData } = require("../ufoo/agentsStore");
6
- const { getUfooPaths } = require("../ufoo/paths");
7
-
8
- /**
9
- * Build probe marker using nickname (e.g., "claude-47")
10
- * Simpler than the old token format, easier to search
11
- */
12
- function buildProbeMarker(nickname) {
13
- return nickname || "";
14
- }
15
-
16
- /**
17
- * Build probe command:
18
- * - claude-code: /ufoo <nickname>
19
- * - codex: $ufoo <nickname>
20
- */
21
- function buildProbeCommand(agentType, nickname) {
22
- const marker = String(nickname || "").trim();
23
- if (agentType === "claude-code") {
24
- return `/ufoo ${marker}`;
25
- }
26
- return `$ufoo ${marker}`;
27
- }
28
-
29
- function readLines(filePath) {
30
- try {
31
- const raw = fs.readFileSync(filePath, "utf8");
32
- return raw.split(/\r?\n/).filter(Boolean);
33
- } catch {
34
- return [];
35
- }
36
- }
37
-
38
- function escapeRegExp(value = "") {
39
- return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
40
- }
41
-
42
- function containsProbeCommand(text, marker) {
43
- if (!text || !marker) return false;
44
- const escapedMarker = escapeRegExp(marker);
45
- const pattern = `(?:^|[\\s"'\\\`])(?:\\/ufoo|\\$ufoo|ufoo)\\s+${escapedMarker}(?=$|[\\s"'\\\`.,:;!?\\]\\)\\}])`;
46
- const re = new RegExp(pattern);
47
- return re.test(String(text));
48
- }
49
-
50
- /**
51
- * Check if a history record contains our probe marker
52
- * Searches for probe marker command patterns:
53
- * - "/ufoo <marker>" (claude)
54
- * - "$ufoo <marker>" (codex)
55
- * - "ufoo <marker>" (legacy compatibility)
56
- */
57
- function recordContainsMarker(record, marker, rawLine) {
58
- if (!marker) return false;
59
-
60
- // Check raw line first (fastest)
61
- if (containsProbeCommand(rawLine, marker)) return true;
62
-
63
- if (!record || typeof record !== "object") return false;
64
-
65
- // Check common fields where user input might appear
66
- const fields = [
67
- record.display, // history.jsonl uses "display" for user input
68
- record.text,
69
- record.prompt,
70
- record.input,
71
- record.message,
72
- record.query,
73
- record.content,
74
- ];
75
-
76
- for (const field of fields) {
77
- if (containsProbeCommand(field, marker)) return true;
78
- }
79
- return false;
80
- }
81
-
82
- function extractSessionId(record, rawLine) {
83
- if (record && typeof record === "object") {
84
- return record.session_id || record.sessionId || record.session || "";
85
- }
86
- if (typeof rawLine === "string") {
87
- const match = rawLine.match(/"session(?:_id|Id)"\s*:\s*"([^"]+)"/);
88
- if (match && match[1]) return match[1];
89
- }
90
- return "";
91
- }
92
-
93
- /**
94
- * Find session ID in a history file by searching for the probe marker
95
- */
96
- function findSessionInFile(filePath, marker) {
97
- if (!filePath || !fs.existsSync(filePath)) return null;
98
- const lines = readLines(filePath);
99
-
100
- // Search from end (most recent first)
101
- for (let i = lines.length - 1; i >= 0; i -= 1) {
102
- const line = lines[i];
103
-
104
- // Quick check: line must contain the marker string
105
- if (!line.includes(marker)) continue;
106
-
107
- let record = null;
108
- try {
109
- record = JSON.parse(line);
110
- } catch {
111
- record = null;
112
- }
113
-
114
- if (!recordContainsMarker(record, marker, line)) continue;
115
-
116
- const sessionId = extractSessionId(record, line);
117
- if (sessionId) {
118
- return { sessionId, source: filePath };
119
- }
120
- }
121
- return null;
122
- }
123
-
124
- function getClaudeHistoryPath() {
125
- return path.join(os.homedir(), ".claude", "history.jsonl");
126
- }
127
-
128
- function getCodexHistoryPath() {
129
- return path.join(os.homedir(), ".codex", "history.jsonl");
130
- }
131
-
132
- /**
133
- * Search provider history for the probe marker and return session ID
134
- */
135
- function resolveProviderSession(agentType, marker) {
136
- if (agentType === "codex") {
137
- return findSessionInFile(getCodexHistoryPath(), marker);
138
- }
139
- if (agentType === "claude-code") {
140
- return findSessionInFile(getClaudeHistoryPath(), marker);
141
- }
142
- return null;
143
- }
144
-
145
- /**
146
- * Save probe marker to agents data (for debugging/tracking)
147
- */
148
- function persistProbeMarker(projectRoot, subscriberId, marker) {
149
- const filePath = getUfooPaths(projectRoot).agentsFile;
150
- const data = loadAgentsData(filePath);
151
- const meta = data.agents[subscriberId] || {};
152
- data.agents[subscriberId] = {
153
- ...meta,
154
- provider_session_probe: marker,
155
- provider_session_updated_at: new Date().toISOString(),
156
- };
157
- saveAgentsData(filePath, data);
158
- }
159
-
160
- function persistProviderSession(projectRoot, subscriberId, payload) {
161
- const filePath = getUfooPaths(projectRoot).agentsFile;
162
- const data = loadAgentsData(filePath);
163
- const meta = data.agents[subscriberId] || {};
164
- data.agents[subscriberId] = {
165
- ...meta,
166
- provider_session_id: payload.sessionId || "",
167
- provider_session_source: payload.source || "",
168
- provider_session_updated_at: new Date().toISOString(),
169
- };
170
- saveAgentsData(filePath, data);
171
- }
172
-
173
- /**
174
- * Retry searching for session ID with the given marker
175
- */
176
- async function resolveWithRetries(agentType, marker, attempts = 12, intervalMs = 2000) {
177
- for (let i = 0; i < attempts; i += 1) {
178
- const resolved = resolveProviderSession(agentType, marker);
179
- if (resolved && resolved.sessionId) return resolved;
180
- // eslint-disable-next-line no-await-in-loop
181
- await new Promise((r) => setTimeout(r, intervalMs));
182
- }
183
- return null;
184
- }
185
-
186
-
187
- function loadProviderSessionCache(projectRoot) {
188
- const filePath = getUfooPaths(projectRoot).agentsFile;
189
- const data = loadAgentsData(filePath);
190
- const cache = new Map();
191
- for (const [id, meta] of Object.entries(data.agents || {})) {
192
- if (meta && meta.provider_session_id) {
193
- cache.set(id, {
194
- sessionId: meta.provider_session_id,
195
- source: meta.provider_session_source || "",
196
- updated_at: meta.provider_session_updated_at || "",
197
- });
198
- }
199
- }
200
- return cache;
201
- }
202
-
203
- /**
204
- * Execute probe: inject command and search for session ID
205
- */
206
- async function executeProbe({
207
- projectRoot,
208
- subscriberId,
209
- agentType,
210
- nickname,
211
- attempts = 15,
212
- intervalMs = 2000,
213
- onResolved = null,
214
- }) {
215
- const marker = buildProbeMarker(nickname);
216
-
217
- try {
218
- const command = buildProbeCommand(agentType, nickname);
219
- const bus = new EventBus(projectRoot);
220
- bus.ensureBus();
221
- await bus.inject(subscriberId, command);
222
- } catch {
223
- // ignore injection failures
224
- }
225
-
226
- const resolved = await resolveWithRetries(agentType, marker, attempts, intervalMs);
227
- if (resolved && resolved.sessionId) {
228
- persistProviderSession(projectRoot, subscriberId, resolved);
229
- if (typeof onResolved === "function") {
230
- onResolved(subscriberId, resolved);
231
- }
232
- }
233
- }
234
-
235
- /**
236
- * Resolve Claude Code session ID directly from session file.
237
- * Claude writes ~/.claude/sessions/<pid>.json with { sessionId, pid, cwd, ... }
238
- */
239
- function resolveClaudeSessionFromFile(pid) {
240
- if (!pid) return null;
241
- const filePath = path.join(os.homedir(), ".claude", "sessions", `${pid}.json`);
242
- try {
243
- if (!fs.existsSync(filePath)) return null;
244
- const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
245
- const sessionId = data.sessionId || data.session_id || "";
246
- if (!sessionId) return null;
247
- return { sessionId, source: filePath };
248
- } catch {
249
- return null;
250
- }
251
- }
252
-
253
- /**
254
- * Resolve Codex session ID from session rollout files.
255
- * Codex writes ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<id>.jsonl
256
- * First line contains { type: "session_meta", payload: { id, cwd, ... } }
257
- */
258
- function resolveCodexSessionFromFile(cwd) {
259
- if (!cwd) return null;
260
- try {
261
- const now = new Date();
262
- // Check today and yesterday (session may have started before midnight)
263
- const dates = [now];
264
- const yesterday = new Date(now);
265
- yesterday.setDate(yesterday.getDate() - 1);
266
- dates.push(yesterday);
267
-
268
- let bestMatch = null;
269
- let bestMtime = 0;
270
-
271
- for (const d of dates) {
272
- const yyyy = String(d.getFullYear());
273
- const mm = String(d.getMonth() + 1).padStart(2, "0");
274
- const dd = String(d.getDate()).padStart(2, "0");
275
- const dir = path.join(os.homedir(), ".codex", "sessions", yyyy, mm, dd);
276
- if (!fs.existsSync(dir)) continue;
277
-
278
- const files = fs.readdirSync(dir)
279
- .filter((f) => f.startsWith("rollout-") && f.endsWith(".jsonl"));
280
-
281
- for (const file of files) {
282
- const filePath = path.join(dir, file);
283
- try {
284
- const stat = fs.statSync(filePath);
285
- if (stat.mtimeMs <= bestMtime) continue;
286
-
287
- // Read first line for session_meta
288
- const fd = fs.openSync(filePath, "r");
289
- const buf = Buffer.alloc(4096);
290
- const bytesRead = fs.readSync(fd, buf, 0, 4096, 0);
291
- fs.closeSync(fd);
292
- const firstLine = buf.toString("utf8", 0, bytesRead).split("\n")[0];
293
- if (!firstLine) continue;
294
-
295
- const record = JSON.parse(firstLine);
296
- const payload = record.payload || record;
297
- const sessionCwd = payload.cwd || "";
298
- const sessionId = payload.id || "";
299
-
300
- if (sessionId && sessionCwd === cwd && stat.mtimeMs > bestMtime) {
301
- bestMatch = { sessionId, source: filePath };
302
- bestMtime = stat.mtimeMs;
303
- }
304
- } catch {
305
- continue;
306
- }
307
- }
308
- }
309
- return bestMatch;
310
- } catch {
311
- return null;
312
- }
313
- }
314
-
315
- /**
316
- * Resolve provider session ID directly from session files (no probe needed).
317
- * @param {string} agentType - "claude-code" or "codex"
318
- * @param {object} opts - { pid, cwd }
319
- */
320
- function resolveSessionFromFile(agentType, opts = {}) {
321
- if (agentType === "claude-code") {
322
- return resolveClaudeSessionFromFile(opts.pid);
323
- }
324
- if (agentType === "codex") {
325
- return resolveCodexSessionFromFile(opts.cwd);
326
- }
327
- return null;
328
- }
329
-
330
- /**
331
- * Retry reading session file (agent may not have written it yet)
332
- */
333
- async function resolveSessionFromFileWithRetries(agentType, opts = {}, attempts = 10, intervalMs = 1000) {
334
- for (let i = 0; i < attempts; i += 1) {
335
- const resolved = resolveSessionFromFile(agentType, opts);
336
- if (resolved && resolved.sessionId) return resolved;
337
- // eslint-disable-next-line no-await-in-loop
338
- await new Promise((r) => setTimeout(r, intervalMs));
339
- }
340
- return null;
341
- }
342
-
343
- /**
344
- * Schedule provider session resolution.
345
- * Tries direct file read first (fast, non-invasive), falls back to probe if needed.
346
- *
347
- * @param {Object} options
348
- * @param {string} options.projectRoot - Project root directory
349
- * @param {string} options.subscriberId - Subscriber ID (e.g., "claude-code:abc123")
350
- * @param {string} options.agentType - Agent type ("claude-code" or "codex")
351
- * @param {string} options.nickname - Agent nickname (e.g., "claude-47")
352
- * @param {number} options.agentPid - Agent child process PID (for claude-code)
353
- * @param {string} options.agentCwd - Agent working directory (for codex)
354
- * @param {number} options.delayMs - Delay before starting resolution
355
- * @param {number} options.fileAttempts - File read retry attempts
356
- * @param {number} options.fileIntervalMs - File read retry interval
357
- * @param {number} options.probeAttempts - Probe retry attempts (fallback)
358
- * @param {number} options.probeIntervalMs - Probe retry interval (fallback)
359
- * @param {Function} options.onResolved - Callback when session ID is found
360
- */
361
- function scheduleProviderSessionResolve({
362
- projectRoot,
363
- subscriberId,
364
- agentType,
365
- nickname,
366
- agentPid = 0,
367
- agentCwd = "",
368
- delayMs = 3000,
369
- fileAttempts = 10,
370
- fileIntervalMs = 1000,
371
- probeAttempts = 15,
372
- probeIntervalMs = 2000,
373
- onResolved = null,
374
- }) {
375
- if (!subscriberId || !agentType) return null;
376
- if (agentType !== "codex" && agentType !== "claude-code") return null;
377
-
378
- const marker = nickname ? buildProbeMarker(nickname) : "";
379
- if (marker) {
380
- persistProbeMarker(projectRoot, subscriberId, marker);
381
- }
382
-
383
- let executed = false;
384
- let cancelled = false;
385
- let timer = null;
386
-
387
- const execute = async () => {
388
- if (executed || cancelled) return;
389
- executed = true;
390
- if (timer) {
391
- clearTimeout(timer);
392
- timer = null;
393
- }
394
-
395
- // 1. Try direct file read (fast, non-invasive)
396
- const fileOpts = { pid: agentPid, cwd: agentCwd || projectRoot };
397
- const fileResolved = await resolveSessionFromFileWithRetries(
398
- agentType, fileOpts, fileAttempts, fileIntervalMs,
399
- );
400
- if (cancelled) return;
401
- if (fileResolved && fileResolved.sessionId) {
402
- persistProviderSession(projectRoot, subscriberId, fileResolved);
403
- if (typeof onResolved === "function") {
404
- onResolved(subscriberId, fileResolved);
405
- }
406
- return;
407
- }
408
-
409
- // 2. Fallback to probe (inject command + search history)
410
- // Re-check cancelled: AGENT_READY may have resolved session while we were retrying
411
- if (cancelled) return;
412
- if (nickname) {
413
- await executeProbe({
414
- projectRoot,
415
- subscriberId,
416
- agentType,
417
- nickname,
418
- attempts: probeAttempts,
419
- intervalMs: probeIntervalMs,
420
- onResolved,
421
- });
422
- }
423
- };
424
-
425
- // Schedule delayed execution
426
- timer = setTimeout(execute, delayMs);
427
-
428
- // Return handle for early trigger or cancellation
429
- return {
430
- subscriberId,
431
- marker,
432
- triggerNow: execute,
433
- cancel: () => {
434
- cancelled = true;
435
- executed = true;
436
- if (timer) {
437
- clearTimeout(timer);
438
- timer = null;
439
- }
440
- },
441
- };
442
- }
443
-
444
- /**
445
- * Schedule a provider session probe (legacy wrapper)
446
- */
447
- function scheduleProviderSessionProbe({
448
- projectRoot,
449
- subscriberId,
450
- agentType,
451
- nickname,
452
- delayMs = 8000,
453
- attempts = 15,
454
- intervalMs = 2000,
455
- onResolved = null,
456
- agentPid = 0,
457
- agentCwd = "",
458
- }) {
459
- // Delegate to new resolve function which tries file read first
460
- return scheduleProviderSessionResolve({
461
- projectRoot,
462
- subscriberId,
463
- agentType,
464
- nickname,
465
- agentPid,
466
- agentCwd,
467
- delayMs: agentPid || agentCwd ? 3000 : delayMs,
468
- probeAttempts: attempts,
469
- probeIntervalMs: intervalMs,
470
- onResolved,
471
- });
472
- }
473
-
474
- module.exports = {
475
- scheduleProviderSessionProbe,
476
- scheduleProviderSessionResolve,
477
- resolveSessionFromFile,
478
- persistProviderSession,
479
- loadProviderSessionCache,
480
- __private: {
481
- buildProbeCommand,
482
- recordContainsMarker,
483
- containsProbeCommand,
484
- escapeRegExp,
485
- resolveClaudeSessionFromFile,
486
- resolveCodexSessionFromFile,
487
- },
488
- };
@@ -1,42 +0,0 @@
1
- const { createTerminalCapabilities } = require("../adapterContract");
2
-
3
- function createInternalPtyAdapter(options = {}) {
4
- const {
5
- sendRaw = () => {},
6
- sendResize = () => {},
7
- requestSnapshot = () => false,
8
- createAdapter = () => {},
9
- } = options;
10
-
11
- const capabilities = createTerminalCapabilities({
12
- supportsInternalQueueLoop: true,
13
- supportsSocketProtocol: true,
14
- supportsSubscribeFull: true,
15
- supportsSubscribeScreen: true,
16
- supportsSnapshot: true,
17
- });
18
-
19
- return createAdapter({
20
- capabilities,
21
- handlers: {
22
- send: (data) => {
23
- sendRaw(data);
24
- return true;
25
- },
26
- sendRaw: (data) => {
27
- sendRaw(data);
28
- return true;
29
- },
30
- resize: (cols, rows) => {
31
- sendResize(cols, rows);
32
- return true;
33
- },
34
- snapshot: () => Boolean(requestSnapshot("screen")),
35
- subscribe: () => Boolean(requestSnapshot("full")),
36
- },
37
- });
38
- }
39
-
40
- module.exports = {
41
- createInternalPtyAdapter,
42
- };