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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agy (Antigravity CLI) conversation-id capture and reuse.
|
|
5
|
+
*
|
|
6
|
+
* Agy ends every TUI session by printing a single line to stdout:
|
|
7
|
+
*
|
|
8
|
+
* Resume: agy --conversation=<UUID> (or -c)
|
|
9
|
+
*
|
|
10
|
+
* We grep this line out of the PTY output ring buffer and persist the UUID
|
|
11
|
+
* onto the agent meta as `provider_session_id`, so the next launch of agy
|
|
12
|
+
* on the same tty/tmux pane can pass `--conversation=<UUID>` to pick up
|
|
13
|
+
* exactly that conversation.
|
|
14
|
+
*
|
|
15
|
+
* The launcher writes the id; bin/uagy.js reads it back before spawning.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require("fs");
|
|
19
|
+
const path = require("path");
|
|
20
|
+
|
|
21
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
22
|
+
const { isAgentPidAlive } = require("../../coordination/bus/utils");
|
|
23
|
+
|
|
24
|
+
// Capture group is a UUID v4-ish; agy uses standard 8-4-4-4-12 hex form.
|
|
25
|
+
// Allow trailing whitespace and anything after the close paren on the line.
|
|
26
|
+
const RESUME_LINE_RE =
|
|
27
|
+
/Resume:\s+agy\s+--conversation=([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})/;
|
|
28
|
+
|
|
29
|
+
function extractResumeConversationId(text = "") {
|
|
30
|
+
const str = String(text || "");
|
|
31
|
+
if (!str) return "";
|
|
32
|
+
const match = str.match(RESUME_LINE_RE);
|
|
33
|
+
return match ? match[1] : "";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readAgentsRegistry(projectRoot) {
|
|
37
|
+
try {
|
|
38
|
+
const file = getUfooPaths(projectRoot).agentsFile;
|
|
39
|
+
if (!fs.existsSync(file)) return { file, data: null };
|
|
40
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
41
|
+
return { file, data };
|
|
42
|
+
} catch {
|
|
43
|
+
return { file: "", data: null };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Look up the most recent agy conversation id for the given tty/tmux pane.
|
|
49
|
+
*
|
|
50
|
+
* Mirrors src/agents/launch/launcher.js#findPreviousSession but only inspects
|
|
51
|
+
* `meta.provider_session_id`. Returns "" when none found.
|
|
52
|
+
*
|
|
53
|
+
* Selection rules (in this order):
|
|
54
|
+
* 1. Only `agy:` subscribers
|
|
55
|
+
* 2. tmux_pane match (when supplied) or tty match (fallback)
|
|
56
|
+
* 3. Skip entries whose pid is still alive — that session is still
|
|
57
|
+
* running, stealing its id would resume into the live process
|
|
58
|
+
* 4. Among remaining candidates, pick the one with the most recent
|
|
59
|
+
* `provider_session_updated_at` (insertion-order fallback when no
|
|
60
|
+
* timestamp is present)
|
|
61
|
+
*/
|
|
62
|
+
function readPreviousConversationId(projectRoot, { tty = "", tmuxPane = "" } = {}) {
|
|
63
|
+
if (!projectRoot) return "";
|
|
64
|
+
if (!tty && !tmuxPane) return "";
|
|
65
|
+
const { data } = readAgentsRegistry(projectRoot);
|
|
66
|
+
if (!data || !data.agents) return "";
|
|
67
|
+
|
|
68
|
+
const candidates = [];
|
|
69
|
+
for (const [subscriberId, meta] of Object.entries(data.agents)) {
|
|
70
|
+
if (!subscriberId.startsWith("agy:")) continue;
|
|
71
|
+
if (!meta || typeof meta !== "object") continue;
|
|
72
|
+
if (tmuxPane) {
|
|
73
|
+
if (meta.tmux_pane !== tmuxPane) continue;
|
|
74
|
+
} else if (tty) {
|
|
75
|
+
if (meta.tty !== tty) continue;
|
|
76
|
+
} else {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const id = String(meta.provider_session_id || "").trim();
|
|
80
|
+
if (!id) continue;
|
|
81
|
+
|
|
82
|
+
// Skip sessions whose owning process is still alive — their
|
|
83
|
+
// conversation id belongs to a running TUI, not a closed one.
|
|
84
|
+
const pid = Number.parseInt(meta.pid, 10);
|
|
85
|
+
if (Number.isFinite(pid) && pid > 0 && isAgentPidAlive(pid)) continue;
|
|
86
|
+
|
|
87
|
+
const updatedAt = Date.parse(String(meta.provider_session_updated_at || "")) || 0;
|
|
88
|
+
candidates.push({ id, updatedAt });
|
|
89
|
+
}
|
|
90
|
+
if (candidates.length === 0) return "";
|
|
91
|
+
candidates.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
92
|
+
return candidates[0].id;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function persistConversationId(projectRoot, subscriberId, conversationId) {
|
|
96
|
+
const id = String(conversationId || "").trim();
|
|
97
|
+
if (!projectRoot || !subscriberId || !id) return false;
|
|
98
|
+
try {
|
|
99
|
+
const file = getUfooPaths(projectRoot).agentsFile;
|
|
100
|
+
if (!fs.existsSync(file)) return false;
|
|
101
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
102
|
+
if (!data || !data.agents || !data.agents[subscriberId]) return false;
|
|
103
|
+
if (data.agents[subscriberId].provider_session_id === id) return false;
|
|
104
|
+
data.agents[subscriberId].provider_session_id = id;
|
|
105
|
+
data.agents[subscriberId].provider_session_updated_at = new Date().toISOString();
|
|
106
|
+
const tmp = `${file}.tmp`;
|
|
107
|
+
fs.writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
108
|
+
fs.renameSync(tmp, file);
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Build the agy command-line args for a fresh launch:
|
|
117
|
+
* - prepend --conversation=<uuid> if previousConversationId is set
|
|
118
|
+
* (unless the user already passed --continue / -c / --conversation)
|
|
119
|
+
* - prepend --dangerously-skip-permissions in internal/auto-approve mode
|
|
120
|
+
* - return the args caller hands to AgentLauncher.launch()
|
|
121
|
+
*
|
|
122
|
+
* Bootstrap merging (i.e. `-i <text>`) is handled by defaultBootstrap.js;
|
|
123
|
+
* this helper only deals with the resume + permission flags.
|
|
124
|
+
*/
|
|
125
|
+
function buildAgyLaunchArgs({
|
|
126
|
+
userArgs = [],
|
|
127
|
+
previousConversationId = "",
|
|
128
|
+
skipPermissions = false,
|
|
129
|
+
} = {}) {
|
|
130
|
+
const args = Array.isArray(userArgs) ? userArgs.slice() : [];
|
|
131
|
+
const flat = args.map((item) => String(item || "").trim()).filter(Boolean);
|
|
132
|
+
|
|
133
|
+
const userHasResume = flat.some((item) =>
|
|
134
|
+
item === "-c"
|
|
135
|
+
|| item === "--continue"
|
|
136
|
+
|| item === "--conversation"
|
|
137
|
+
|| item.startsWith("--conversation=")
|
|
138
|
+
);
|
|
139
|
+
const userHasSkipPerms = flat.includes("--dangerously-skip-permissions");
|
|
140
|
+
|
|
141
|
+
const out = [];
|
|
142
|
+
if (skipPermissions && !userHasSkipPerms) {
|
|
143
|
+
out.push("--dangerously-skip-permissions");
|
|
144
|
+
}
|
|
145
|
+
const resumeId = String(previousConversationId || "").trim();
|
|
146
|
+
if (resumeId && !userHasResume) {
|
|
147
|
+
out.push(`--conversation=${resumeId}`);
|
|
148
|
+
}
|
|
149
|
+
out.push(...args);
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
RESUME_LINE_RE,
|
|
155
|
+
extractResumeConversationId,
|
|
156
|
+
readPreviousConversationId,
|
|
157
|
+
persistConversationId,
|
|
158
|
+
buildAgyLaunchArgs,
|
|
159
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
AgentLauncher: require("./launcher"),
|
|
5
|
+
AgentNotifier: require("./notifier"),
|
|
6
|
+
PtyWrapper: require("./ptyWrapper"),
|
|
7
|
+
ReadyDetector: require("./readyDetector"),
|
|
8
|
+
...require("./agyConversation"),
|
|
9
|
+
...require("./launchEnvironment"),
|
|
10
|
+
...require("./ptyRunner"),
|
|
11
|
+
...require("./publisherRouting"),
|
|
12
|
+
};
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* Host is detected by env var alone here; callers that want to override host
|
|
19
19
|
* with native-terminal evidence (e.g. daemon ops auto-resolution) layer that
|
|
20
20
|
* on top of the detector's output.
|
|
21
|
-
* Internal
|
|
22
|
-
* spawning worker processes (see
|
|
21
|
+
* Internal modes are always set explicitly by the daemon when
|
|
22
|
+
* spawning worker processes (see daemon ops), so they fall through
|
|
23
23
|
* the explicit short-circuit and never need to be auto-detected here.
|
|
24
24
|
*/
|
|
25
25
|
|
|
@@ -28,7 +28,6 @@ const CANONICAL_LAUNCH_MODES = Object.freeze([
|
|
|
28
28
|
"tmux",
|
|
29
29
|
"host",
|
|
30
30
|
"internal",
|
|
31
|
-
"internal-pty",
|
|
32
31
|
]);
|
|
33
32
|
|
|
34
33
|
const CANONICAL_LAUNCH_MODE_SET = new Set(CANONICAL_LAUNCH_MODES);
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
const { IPC_REQUEST_TYPES } = require("
|
|
2
|
-
const { PTY_SOCKET_MESSAGE_TYPES } = require("
|
|
1
|
+
const { IPC_REQUEST_TYPES } = require("../../runtime/contracts/eventContract");
|
|
2
|
+
const { PTY_SOCKET_MESSAGE_TYPES } = require("../../runtime/contracts/ptySocketContract");
|
|
3
3
|
const { spawn, spawnSync } = require("child_process");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const net = require("net");
|
|
6
6
|
const path = require("path");
|
|
7
|
-
const EventBus = require("
|
|
8
|
-
const { isAgentPidAlive } = require("
|
|
9
|
-
const { showBanner } = require("
|
|
7
|
+
const EventBus = require("../../coordination/bus");
|
|
8
|
+
const { isAgentPidAlive } = require("../../coordination/bus/utils");
|
|
9
|
+
const { showBanner } = require("../../ui/format/banner");
|
|
10
10
|
const AgentNotifier = require("./notifier");
|
|
11
|
-
const { ActivityDetector } = require("
|
|
12
|
-
const { createActivityStatePublisher } = require("
|
|
11
|
+
const { ActivityDetector } = require("../activity/activityDetector");
|
|
12
|
+
const { createActivityStatePublisher } = require("../activity/activityStatePublisher");
|
|
13
13
|
const { detectLaunchEnvironment } = require("./launchEnvironment");
|
|
14
|
-
const { getUfooPaths } = require("
|
|
15
|
-
const { createTerminalAdapterRouter } = require("
|
|
16
|
-
const { probeHostCapabilities } = require("
|
|
14
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
15
|
+
const { createTerminalAdapterRouter } = require("../../runtime/terminal/adapterRouter");
|
|
16
|
+
const { probeHostCapabilities } = require("../../runtime/terminal/adapters/hostAdapter");
|
|
17
17
|
const PtyWrapper = require("./ptyWrapper");
|
|
18
18
|
const ReadyDetector = require("./readyDetector");
|
|
19
|
+
const {
|
|
20
|
+
extractResumeConversationId,
|
|
21
|
+
persistConversationId,
|
|
22
|
+
} = require("./agyConversation");
|
|
19
23
|
|
|
20
24
|
function connectSocket(sockPath) {
|
|
21
25
|
return new Promise((resolve, reject) => {
|
|
@@ -204,9 +208,12 @@ function shouldShowLaunchBanner(agentType = "") {
|
|
|
204
208
|
function computeInjectedSubmitDelayMs(agentType, text) {
|
|
205
209
|
const normalizedAgent = String(agentType || "").trim().toLowerCase();
|
|
206
210
|
const input = typeof text === "string" ? text : "";
|
|
207
|
-
|
|
211
|
+
// Agy uses an ink-style TUI like claude-code, so it needs a similar grace
|
|
212
|
+
// window before the input handler picks up injected text.
|
|
213
|
+
const isInkStyle = normalizedAgent === "claude-code" || normalizedAgent === "agy";
|
|
214
|
+
let delayMs = isInkStyle ? 350 : 200;
|
|
208
215
|
if (input.includes("\n")) {
|
|
209
|
-
delayMs +=
|
|
216
|
+
delayMs += isInkStyle ? 250 : 120;
|
|
210
217
|
}
|
|
211
218
|
if (input.length > 512) {
|
|
212
219
|
delayMs += Math.min(1200, Math.ceil(input.length / 512) * 90);
|
|
@@ -237,7 +244,11 @@ async function injectPtyCommand(wrapper, agentType, commandText, source = "injec
|
|
|
237
244
|
const normalizedAgentType = String(agentType || "").trim().toLowerCase();
|
|
238
245
|
const submitDelayMs = computeInjectedSubmitDelayMs(agentType, text);
|
|
239
246
|
wrapper.write(text);
|
|
240
|
-
|
|
247
|
+
// claude-code and agy both run ink-style TUIs that accept a bare CR to
|
|
248
|
+
// submit. codex needs the Esc-prefix trick to flush its multi-byte input
|
|
249
|
+
// handler before the CR.
|
|
250
|
+
const isInkStyle = normalizedAgentType === "claude-code" || normalizedAgentType === "agy";
|
|
251
|
+
if (isInkStyle) {
|
|
241
252
|
await sleep(submitDelayMs);
|
|
242
253
|
wrapper.write("\r");
|
|
243
254
|
} else {
|
|
@@ -465,7 +476,7 @@ class AgentLauncher {
|
|
|
465
476
|
hostName,
|
|
466
477
|
hostSessionId,
|
|
467
478
|
hostCapabilities,
|
|
468
|
-
|
|
479
|
+
skipSessionResolve: process.env.UFOO_SKIP_SESSION_RESOLVE === "1",
|
|
469
480
|
// 传递旧 session 信息用于复用(仅 terminal/tmux 模式)
|
|
470
481
|
reuseSession: previousSession ? {
|
|
471
482
|
sessionId: previousSession.sessionId,
|
|
@@ -694,10 +705,20 @@ class AgentLauncher {
|
|
|
694
705
|
detail: snap.detail,
|
|
695
706
|
});
|
|
696
707
|
});
|
|
708
|
+
// Tail buffer for agy: when the TUI exits, agy prints a single line
|
|
709
|
+
// `Resume: agy --conversation=<UUID> (or -c)` to stdout. We keep
|
|
710
|
+
// the trailing ~4KB of post-frame output around and grep it on exit
|
|
711
|
+
// to capture the conversation id, then persist it as the agent's
|
|
712
|
+
// provider_session_id so the next launch can pass `--conversation`.
|
|
713
|
+
const agyTailBuffer = { text: "" };
|
|
714
|
+
const AGY_TAIL_MAX = 4096;
|
|
697
715
|
wrapper.enableMonitoring((data) => {
|
|
698
716
|
readyDetector.processOutput(data);
|
|
699
717
|
const text = typeof data === "string" ? data : Buffer.from(data).toString("utf8");
|
|
700
718
|
launcherActivityDetector.processOutput(text);
|
|
719
|
+
if (this.agentType === "agy") {
|
|
720
|
+
agyTailBuffer.text = (agyTailBuffer.text + text).slice(-AGY_TAIL_MAX);
|
|
721
|
+
}
|
|
701
722
|
});
|
|
702
723
|
readyDetector.onReady(async () => {
|
|
703
724
|
launcherActivityDetector.markReady();
|
|
@@ -708,17 +729,19 @@ class AgentLauncher {
|
|
|
708
729
|
// Claude Code's Ink TUI renders ❯ prompt before the input handler
|
|
709
730
|
// is fully mounted. Wait a short period for the TUI to be ready to
|
|
710
731
|
// accept injected text, otherwise only the trailing CR is processed
|
|
711
|
-
// and the
|
|
712
|
-
|
|
732
|
+
// and the injected slash command is lost. Agy uses the same ink-style TUI
|
|
733
|
+
// so it gets the same grace window.
|
|
734
|
+
if (this.agentType === "claude-code" || this.agentType === "agy") {
|
|
713
735
|
await new Promise((r) => setTimeout(r, 800));
|
|
714
736
|
}
|
|
715
737
|
|
|
716
|
-
//
|
|
717
|
-
// /rename
|
|
718
|
-
//
|
|
719
|
-
//
|
|
738
|
+
// Inject /rename to set the session label (before AGENT_READY/bootstrap).
|
|
739
|
+
// /rename is a slash command, completes instantly, doesn't hit the LLM.
|
|
740
|
+
// Only when an explicit nickname was supplied (UFOO_NICKNAME from group
|
|
741
|
+
// launch or user). Both claude-code and agy support /rename.
|
|
720
742
|
const explicitNickname = this._originalNickname || "";
|
|
721
|
-
|
|
743
|
+
const supportsRenameSlash = this.agentType === "claude-code" || this.agentType === "agy";
|
|
744
|
+
if (supportsRenameSlash && explicitNickname && wrapper.pty) {
|
|
722
745
|
try {
|
|
723
746
|
const safeNick = AgentLauncher._sanitizeNickname(explicitNickname);
|
|
724
747
|
if (safeNick) {
|
|
@@ -750,6 +773,21 @@ class AgentLauncher {
|
|
|
750
773
|
clearTimeout(forceReadyTimer);
|
|
751
774
|
launcherActivityDetector.destroy();
|
|
752
775
|
|
|
776
|
+
// Agy: harvest conversation id from the trailing `Resume: agy
|
|
777
|
+
// --conversation=<UUID>` line that agy prints right before exit,
|
|
778
|
+
// and persist it onto the agent meta so the next launch can
|
|
779
|
+
// pass `--conversation=<UUID>`.
|
|
780
|
+
if (this.agentType === "agy") {
|
|
781
|
+
const conversationId = extractResumeConversationId(agyTailBuffer.text);
|
|
782
|
+
if (conversationId) {
|
|
783
|
+
try {
|
|
784
|
+
persistConversationId(this.cwd, subscriberId, conversationId);
|
|
785
|
+
} catch {
|
|
786
|
+
// ignore — best-effort capture only
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
753
791
|
// 清理 bus 状态
|
|
754
792
|
try {
|
|
755
793
|
const bus = new EventBus(this.cwd);
|
|
@@ -956,5 +994,10 @@ AgentLauncher._sanitizeNickname = (nick) => nick.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
|
956
994
|
AgentLauncher._findPreviousSession = findPreviousSession;
|
|
957
995
|
AgentLauncher._notifyDaemonAgentReady = notifyDaemonAgentReady;
|
|
958
996
|
AgentLauncher._injectPtyCommand = injectPtyCommand;
|
|
997
|
+
// Exposed for bin/uagy.js so it can read the same tty value the launcher
|
|
998
|
+
// will later record on the agent meta — keeps autoResume lookups in sync
|
|
999
|
+
// with the launcher's own tty/tmux detection.
|
|
1000
|
+
AgentLauncher._detectTtyOnce = detectTtyOnce;
|
|
1001
|
+
AgentLauncher._getEnvTtyOverride = getEnvTtyOverride;
|
|
959
1002
|
|
|
960
1003
|
module.exports = AgentLauncher;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const EventBus = require("
|
|
4
|
-
const { readJSON, writeJSON } = require("
|
|
5
|
-
const Injector = require("
|
|
6
|
-
const { getUfooPaths } = require("
|
|
7
|
-
const { appendAgentRegistryDiagnostic } = require("
|
|
8
|
-
const { shakeTerminalByTty } = require("
|
|
9
|
-
const { isITerm2 } = require("
|
|
10
|
-
const iterm2 = require("
|
|
11
|
-
const { createActivityStatePublisher } = require("
|
|
12
|
-
const { INJECTION_MODES, getInjectionModeFromEvent } = require("
|
|
3
|
+
const EventBus = require("../../coordination/bus");
|
|
4
|
+
const { readJSON, writeJSON } = require("../../coordination/bus/utils");
|
|
5
|
+
const Injector = require("../../coordination/bus/inject");
|
|
6
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
7
|
+
const { appendAgentRegistryDiagnostic } = require("../../coordination/state/agentRegistryDiagnostics");
|
|
8
|
+
const { shakeTerminalByTty } = require("../../coordination/bus/shake");
|
|
9
|
+
const { isITerm2 } = require("../../runtime/terminal/detect");
|
|
10
|
+
const iterm2 = require("../../runtime/terminal/iterm2");
|
|
11
|
+
const { createActivityStatePublisher } = require("../activity/activityStatePublisher");
|
|
12
|
+
const { INJECTION_MODES, getInjectionModeFromEvent } = require("../../coordination/bus/messageMeta");
|
|
13
|
+
const { buildPromptInjectionText } = require("../../coordination/bus/promptEnvelope");
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Agent 消息通知监听器
|
|
@@ -153,6 +154,16 @@ class AgentNotifier {
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
157
|
+
getAgentsMap() {
|
|
158
|
+
try {
|
|
159
|
+
if (!this.agentsFile || !fs.existsSync(this.agentsFile)) return {};
|
|
160
|
+
const data = readJSON(this.agentsFile, null);
|
|
161
|
+
return data && data.agents && typeof data.agents === "object" ? data.agents : {};
|
|
162
|
+
} catch {
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
isBusyState(state = "") {
|
|
157
168
|
const value = String(state || "").trim().toLowerCase();
|
|
158
169
|
return value === "working"
|
|
@@ -280,9 +291,9 @@ class AgentNotifier {
|
|
|
280
291
|
requeue.push(evt);
|
|
281
292
|
continue;
|
|
282
293
|
}
|
|
283
|
-
const message =
|
|
294
|
+
const message = buildPromptInjectionText(evt, this.subscriber, this.getAgentsMap());
|
|
284
295
|
try {
|
|
285
|
-
// Inject the
|
|
296
|
+
// Inject the prompt-facing text into the terminal/tmux agent
|
|
286
297
|
// (Bus is the source of truth; inject is the delivery adapter.)
|
|
287
298
|
// eslint-disable-next-line no-await-in-loop
|
|
288
299
|
await this.injector.inject(this.subscriber, message);
|
|
@@ -2,11 +2,11 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const net = require("net");
|
|
4
4
|
const { spawnSync } = require("child_process");
|
|
5
|
-
const EventBus = require("
|
|
6
|
-
const { PTY_SOCKET_MESSAGE_TYPES, PTY_SOCKET_SUBSCRIBE_MODES } = require("
|
|
7
|
-
const { getUfooPaths } = require("
|
|
8
|
-
const { ActivityDetector } = require("
|
|
9
|
-
const { createActivityStatePublisher } = require("
|
|
5
|
+
const EventBus = require("../../coordination/bus");
|
|
6
|
+
const { PTY_SOCKET_MESSAGE_TYPES, PTY_SOCKET_SUBSCRIBE_MODES } = require("../../runtime/contracts/ptySocketContract");
|
|
7
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
8
|
+
const { ActivityDetector } = require("../activity/activityDetector");
|
|
9
|
+
const { createActivityStatePublisher } = require("../activity/activityStatePublisher");
|
|
10
10
|
const {
|
|
11
11
|
parseStreamEnvelope,
|
|
12
12
|
shouldAutoReplyFromPtyToPublisher,
|
|
@@ -15,7 +15,8 @@ const {
|
|
|
15
15
|
const {
|
|
16
16
|
isValueForCodexOption,
|
|
17
17
|
resolveDefaultManualBootstrap,
|
|
18
|
-
} = require("
|
|
18
|
+
} = require("../prompts/defaultBootstrap");
|
|
19
|
+
const { buildPromptInjectionText } = require("../../coordination/bus/promptEnvelope");
|
|
19
20
|
|
|
20
21
|
function sleep(ms) {
|
|
21
22
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -97,6 +98,32 @@ function parseInputMessage(message) {
|
|
|
97
98
|
return { raw: false, text: message };
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
function readAgentsMap(agentsFilePath) {
|
|
102
|
+
try {
|
|
103
|
+
if (!agentsFilePath || !fs.existsSync(agentsFilePath)) return {};
|
|
104
|
+
const data = JSON.parse(fs.readFileSync(agentsFilePath, "utf8"));
|
|
105
|
+
return data && data.agents && typeof data.agents === "object" ? data.agents : {};
|
|
106
|
+
} catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildPtyInputFromEvent(evt = {}, subscriber = "", agents = {}) {
|
|
112
|
+
if (!evt || !evt.data || typeof evt.data.message !== "string") return null;
|
|
113
|
+
const input = parseInputMessage(evt.data.message);
|
|
114
|
+
if (!input) return null;
|
|
115
|
+
if (input.raw) return input;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
raw: false,
|
|
119
|
+
text: buildPromptInjectionText(
|
|
120
|
+
{ ...evt, data: { ...evt.data, message: input.text } },
|
|
121
|
+
subscriber,
|
|
122
|
+
agents
|
|
123
|
+
),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
100
127
|
function getOuterTerminalSize() {
|
|
101
128
|
const cols = Number.isInteger(process.stdout && process.stdout.columns) && process.stdout.columns > 0
|
|
102
129
|
? process.stdout.columns
|
|
@@ -197,10 +224,14 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
|
|
|
197
224
|
let Terminal = null;
|
|
198
225
|
let SerializeAddon = null;
|
|
199
226
|
try {
|
|
200
|
-
const xterm = await import("xterm
|
|
201
|
-
const serialize = await import("xterm
|
|
202
|
-
Terminal = xterm.Terminal
|
|
203
|
-
|
|
227
|
+
const xterm = await import("@xterm/headless");
|
|
228
|
+
const serialize = await import("@xterm/addon-serialize");
|
|
229
|
+
Terminal = xterm.Terminal
|
|
230
|
+
|| (xterm.default && xterm.default.Terminal)
|
|
231
|
+
|| (xterm["module.exports"] && xterm["module.exports"].Terminal);
|
|
232
|
+
SerializeAddon = serialize.SerializeAddon
|
|
233
|
+
|| (serialize.default && serialize.default.SerializeAddon)
|
|
234
|
+
|| (serialize["module.exports"] && serialize["module.exports"].SerializeAddon);
|
|
204
235
|
} catch {
|
|
205
236
|
Terminal = null;
|
|
206
237
|
SerializeAddon = null;
|
|
@@ -1015,6 +1046,7 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
|
|
|
1015
1046
|
|
|
1016
1047
|
const lines = drainQueue(queueFile);
|
|
1017
1048
|
if (lines.length > 0) {
|
|
1049
|
+
const agents = readAgentsMap(agentsFilePath);
|
|
1018
1050
|
const events = [];
|
|
1019
1051
|
for (const line of lines) {
|
|
1020
1052
|
try {
|
|
@@ -1024,8 +1056,7 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
|
|
|
1024
1056
|
}
|
|
1025
1057
|
}
|
|
1026
1058
|
for (const evt of events) {
|
|
1027
|
-
|
|
1028
|
-
const input = parseInputMessage(evt.data.message);
|
|
1059
|
+
const input = buildPtyInputFromEvent(evt, subscriber, agents);
|
|
1029
1060
|
if (!input) continue;
|
|
1030
1061
|
const { raw, text } = input;
|
|
1031
1062
|
if (messageQueue.length >= maxQueue) {
|
|
@@ -1045,6 +1076,7 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
|
|
|
1045
1076
|
|
|
1046
1077
|
module.exports = {
|
|
1047
1078
|
appendStartupBootstrapArg,
|
|
1079
|
+
buildPtyInputFromEvent,
|
|
1048
1080
|
parseInputMessage,
|
|
1049
1081
|
resolvePtyBootstrapArgs,
|
|
1050
1082
|
resolveCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const pty = require("node-pty");
|
|
2
2
|
const fs = require("fs");
|
|
3
|
-
const { isITerm2 } = require("
|
|
3
|
+
const { isITerm2 } = require("../../runtime/terminal/detect");
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PTY Wrapper - 包装原始agent命令,提供IO控制和监控
|
|
@@ -12,7 +12,7 @@ const { isITerm2 } = require("../terminal/detect");
|
|
|
12
12
|
* - 完善的资源清理(防止泄漏)
|
|
13
13
|
*
|
|
14
14
|
* 参考:
|
|
15
|
-
* -
|
|
15
|
+
* - launcher.js - PTY实现参考
|
|
16
16
|
* - codex-44 review反馈 (2026-02-05)
|
|
17
17
|
*/
|
|
18
18
|
class PtyWrapper {
|
|
@@ -87,6 +87,27 @@ class ReadyDetector {
|
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Detect agy (Antigravity CLI) ready markers.
|
|
92
|
+
*
|
|
93
|
+
* agy renders an ink-style TUI with:
|
|
94
|
+
* - A status footer "? for shortcuts" that appears once the prompt box
|
|
95
|
+
* is mounted (most reliable signal — only present post-boot).
|
|
96
|
+
* - A bordered input row with a lone blue ">" prefix. We can't grep
|
|
97
|
+
* for just ">" without misfiring (codex/ucode use the same), so we
|
|
98
|
+
* anchor on the statusline phrase instead.
|
|
99
|
+
*
|
|
100
|
+
* If the account is blocked ("Eligibility check failed"), the TUI still
|
|
101
|
+
* mounts and "? for shortcuts" still appears — we let ready fire so the
|
|
102
|
+
* user sees the error. activityDetector handles the BLOCKED state.
|
|
103
|
+
*/
|
|
104
|
+
_detectAgyReady(text) {
|
|
105
|
+
if (text.includes("? for shortcuts")) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
90
111
|
/**
|
|
91
112
|
* 检测ufoo-code/ucode的ready标记
|
|
92
113
|
*/
|
|
@@ -135,6 +156,8 @@ class ReadyDetector {
|
|
|
135
156
|
isReady = this._detectClaudeCodeReady(this.buffer);
|
|
136
157
|
} else if (this.agentType === "codex") {
|
|
137
158
|
isReady = this._detectCodexReady(this.buffer);
|
|
159
|
+
} else if (this.agentType === "agy") {
|
|
160
|
+
isReady = this._detectAgyReady(this.buffer);
|
|
138
161
|
} else if (this.agentType === "ufoo" || this.agentType === "ucode" || this.agentType === "ufoo-code") {
|
|
139
162
|
isReady = this._detectUfooCodeReady(this.buffer);
|
|
140
163
|
}
|