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.
- package/README.md +157 -213
- package/README.zh-CN.md +151 -197
- package/SKILLS/ufoo/SKILL.md +8 -8
- package/bin/uagy.js +69 -0
- package/bin/uclaude.js +2 -2
- package/bin/ucode.js +4 -4
- package/bin/ucodex.js +2 -2
- package/bin/ufoo.js +5 -23
- package/modules/AGENTS.template.md +1 -1
- package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
- package/package.json +5 -5
- package/scripts/chat-app-smoke.js +1 -1
- package/scripts/global-chat-switch-benchmark.js +5 -5
- package/scripts/ink-demo.js +1 -1
- package/scripts/ink-smoke.js +1 -1
- package/scripts/ucode-app-smoke.js +1 -1
- package/src/{agent → agents/activity}/activityDetector.js +39 -2
- package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
- package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
- package/src/{agent → agents/activity}/activityTracker.js +1 -1
- package/src/agents/activity/index.js +8 -0
- package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
- package/src/agents/controller/index.js +8 -0
- package/src/{agent → agents/controller}/loopObservability.js +2 -2
- package/src/{agent → agents/controller}/loopRuntime.js +1 -1
- package/src/{agent → agents/controller}/ufooAgent.js +9 -9
- package/src/agents/index.js +10 -0
- package/src/agents/internal/index.js +3 -0
- package/src/{agent → agents/internal}/internalRunner.js +45 -22
- package/src/agents/launch/agyConversation.js +159 -0
- package/src/agents/launch/index.js +12 -0
- package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
- package/src/{agent → agents/launch}/launcher.js +64 -21
- package/src/{agent → agents/launch}/notifier.js +23 -12
- package/src/{agent → agents/launch}/ptyRunner.js +44 -12
- package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
- package/src/{agent → agents/launch}/publisherRouting.js +1 -1
- package/src/{agent → agents/launch}/readyDetector.js +23 -0
- package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
- package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
- package/src/agents/prompts/index.js +8 -0
- package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
- package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
- package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
- package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
- package/src/agents/providers/index.js +13 -0
- package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
- package/src/{chat → app/chat}/agentSockets.js +1 -1
- package/src/{chat → app/chat}/commandExecutor.js +50 -26
- package/src/{chat → app/chat}/commands.js +119 -5
- package/src/{chat → app/chat}/daemonConnection.js +1 -1
- package/src/{chat → app/chat}/daemonMessageRouter.js +45 -3
- package/src/{chat → app/chat}/dashboardView.js +2 -1
- package/src/app/chat/index.js +6 -0
- package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
- package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
- package/src/app/chat/multiWindow/index.js +268 -0
- package/src/app/chat/multiWindow/paneLayout.js +84 -0
- package/src/app/chat/multiWindow/paneManager.js +299 -0
- package/src/app/chat/multiWindow/renderer.js +384 -0
- package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
- package/src/{chat → app/chat}/transport.js +1 -1
- package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
- package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
- package/src/{init/index.js → app/cli/features/init.js} +14 -32
- package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
- package/src/app/cli/index.js +9 -0
- package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
- package/src/{cli.js → app/cli/run.js} +59 -57
- package/src/app/index.js +6 -0
- package/src/code/agent.js +10 -9
- package/src/code/index.js +2 -0
- package/src/code/launcher/index.js +9 -0
- package/src/{agent → code/launcher}/ucode.js +7 -8
- package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
- package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
- package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
- package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
- package/src/code/nativeRunner.js +4 -4
- package/src/code/tui.js +3 -1454
- package/src/config.js +15 -2
- package/src/{bus → coordination/bus}/activate.js +2 -2
- package/src/{bus → coordination/bus}/daemon.js +15 -5
- package/src/coordination/bus/envelope.js +173 -0
- package/src/{bus → coordination/bus}/index.js +7 -3
- package/src/{bus → coordination/bus}/inject.js +11 -3
- package/src/{bus → coordination/bus}/message.js +1 -1
- package/src/coordination/bus/messageMeta.js +130 -0
- package/src/coordination/bus/promptEnvelope.js +65 -0
- package/src/{bus → coordination/bus}/shake.js +1 -1
- package/src/{bus → coordination/bus}/store.js +3 -3
- package/src/{bus → coordination/bus}/subscriber.js +2 -2
- package/src/{bus → coordination/bus}/utils.js +2 -2
- package/src/{history → coordination/history}/inputTimeline.js +5 -5
- package/src/coordination/index.js +10 -0
- package/src/{memory → coordination/memory}/historySearch.js +1 -1
- package/src/{memory → coordination/memory}/index.js +3 -3
- package/src/{report → coordination/report}/store.js +2 -2
- package/src/{status → coordination/status}/index.js +3 -3
- package/src/online/bridge.js +2 -2
- package/src/{controller → orchestration/controller}/flags.js +1 -1
- package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
- package/src/orchestration/controller/index.js +10 -0
- package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
- package/src/orchestration/groups/bootstrap.js +3 -0
- package/src/orchestration/groups/index.js +10 -0
- package/src/orchestration/groups/promptProfiles.js +3 -0
- package/src/{group → orchestration/groups}/templates.js +1 -1
- package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
- package/src/orchestration/index.js +7 -0
- package/src/orchestration/solo/index.js +3 -0
- package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
- package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
- package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
- package/src/{daemon → runtime/daemon}/index.js +105 -53
- package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
- package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
- package/src/{daemon → runtime/daemon}/ops.js +48 -61
- package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
- package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
- package/src/runtime/daemon/providerSessions.js +230 -0
- package/src/{daemon → runtime/daemon}/reporting.js +4 -4
- package/src/{daemon → runtime/daemon}/run.js +4 -4
- package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
- package/src/{daemon → runtime/daemon}/status.js +5 -5
- package/src/runtime/index.js +10 -0
- package/src/{projects → runtime/projects}/registry.js +1 -1
- package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
- package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
- package/src/tools/handlers/common.js +1 -1
- package/src/tools/handlers/listAgents.js +1 -1
- package/src/tools/handlers/memory.js +3 -3
- package/src/tools/handlers/readBusSummary.js +1 -1
- package/src/tools/handlers/readOpenDecisions.js +1 -1
- package/src/tools/handlers/readProjectRegistry.js +1 -1
- package/src/tools/handlers/readPromptHistory.js +2 -2
- package/src/tools/schemaFixtures.js +1 -1
- package/src/ui/MIGRATION.md +42 -88
- package/src/ui/format/index.js +5 -28
- package/src/ui/index.js +1 -1
- package/src/ui/{components → ink}/ChatApp.js +812 -88
- package/src/ui/ink/DashboardBar.js +685 -0
- package/src/ui/{components → ink}/MultilineInput.js +230 -5
- package/src/ui/{components → ink}/UcodeApp.js +16 -7
- package/src/ui/{components → ink}/agentMirror.js +24 -19
- package/src/ui/{components → ink}/chatReducer.js +29 -7
- package/src/bus/messageMeta.js +0 -52
- package/src/chat/agentViewController.js +0 -1072
- package/src/chat/chatLogController.js +0 -138
- package/src/chat/completionController.js +0 -533
- package/src/chat/dashboardKeyController.js +0 -533
- package/src/chat/index.js +0 -2222
- package/src/chat/inputHistoryController.js +0 -135
- package/src/chat/inputListenerController.js +0 -470
- package/src/chat/layout.js +0 -186
- package/src/chat/pasteController.js +0 -81
- package/src/chat/statusLineController.js +0 -223
- package/src/chat/streamTracker.js +0 -156
- package/src/code/config +0 -0
- package/src/daemon/providerSessions.js +0 -488
- package/src/terminal/adapters/internalPtyAdapter.js +0 -42
- package/src/ui/components/DashboardBar.js +0 -417
- /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
- /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
- /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
- /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
- /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
- /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
- /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
- /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
- /package/src/{agent → agents/providers}/credentials/index.js +0 -0
- /package/src/{chat → app/chat}/agentBar.js +0 -0
- /package/src/{chat → app/chat}/agentDirectory.js +0 -0
- /package/src/{chat → app/chat}/cronScheduler.js +0 -0
- /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
- /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
- /package/src/{chat → app/chat}/daemonTransport.js +0 -0
- /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
- /package/src/{chat → app/chat}/inputMath.js +0 -0
- /package/src/{chat → app/chat}/projectCloseController.js +0 -0
- /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
- /package/src/{chat → app/chat}/settingsController.js +0 -0
- /package/src/{chat → app/chat}/shellCommand.js +0 -0
- /package/src/{chat → app/chat}/text.js +0 -0
- /package/src/{chat → app/chat}/transientAgentState.js +0 -0
- /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
- /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
- /package/src/{bus → coordination/bus}/nickname.js +0 -0
- /package/src/{bus → coordination/bus}/queue.js +0 -0
- /package/src/{context → coordination/context}/decisions.js +0 -0
- /package/src/{context → coordination/context}/doctor.js +0 -0
- /package/src/{context → coordination/context}/index.js +0 -0
- /package/src/{context → coordination/context}/sync.js +0 -0
- /package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +0 -0
- /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
- /package/src/{ufoo → coordination/state}/paths.js +0 -0
- /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
- /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
- /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
- /package/src/{group → orchestration/groups}/diagram.js +0 -0
- /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
- /package/src/{solo → orchestration/solo}/commands.js +0 -0
- /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
- /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
- /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
- /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
- /package/src/{utils → runtime/process}/nodeExecutable.js +0 -0
- /package/src/{projects → runtime/projects}/identity.js +0 -0
- /package/src/{projects → runtime/projects}/index.js +0 -0
- /package/src/{projects → runtime/projects}/projectId.js +0 -0
- /package/src/{projects → runtime/projects}/runtimes.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/detect.js +0 -0
- /package/src/{terminal → runtime/terminal}/index.js +0 -0
- /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
- /package/src/{utils → ui/format}/banner.js +0 -0
- /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
- /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
|
-
};
|