u-foo 2.3.31 → 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 +9 -5
- package/scripts/chat-app-smoke.js +30 -0
- package/scripts/global-chat-switch-benchmark.js +5 -5
- package/scripts/ink-demo.js +23 -0
- package/scripts/ink-smoke.js +30 -0
- package/scripts/ucode-app-smoke.js +36 -0
- 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 +56 -28
- 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 +54 -4
- package/src/{chat → app/chat}/daemonTransport.js +2 -1
- package/src/{chat → app/chat}/dashboardView.js +2 -21
- package/src/app/chat/index.js +6 -0
- package/src/{chat → app/chat}/inputSubmitHandler.js +38 -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}/projectCloseController.js +1 -1
- package/src/app/chat/shellCommand.js +42 -0
- package/src/{chat → app/chat}/transport.js +16 -3
- 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} +62 -59
- 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/taskDecomposer.js +5 -4
- package/src/code/tui.js +39 -1997
- 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/{ufoo → coordination/state}/agentRegistryDiagnostics.js +43 -0
- 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 +273 -79
- package/src/{daemon → runtime/daemon}/ipcServer.js +24 -2
- 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 +13 -8
- package/src/runtime/daemon/providerSessions.js +230 -0
- package/src/{daemon → runtime/daemon}/reporting.js +4 -4
- package/src/{daemon → runtime/daemon}/run.js +12 -5
- 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/runtime/process/nodeExecutable.js +26 -0
- package/src/{projects → runtime/projects}/registry.js +1 -1
- package/src/{projects → runtime/projects}/runtimes.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 +336 -0
- package/src/ui/format/index.js +974 -0
- package/src/ui/index.js +9 -0
- package/src/ui/ink/ChatApp.js +3674 -0
- package/src/ui/ink/DashboardBar.js +685 -0
- package/src/ui/ink/InkDemo.js +96 -0
- package/src/ui/ink/MultilineInput.js +612 -0
- package/src/ui/ink/UcodeApp.js +822 -0
- package/src/ui/ink/agentMirror.js +730 -0
- package/src/ui/ink/chatReducer.js +359 -0
- package/src/ui/runInk.js +57 -0
- 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 -573
- package/src/chat/index.js +0 -2214
- 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/{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}/daemonTransportDefaults.js +0 -0
- /package/src/{chat → app/chat}/inputMath.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}/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}/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/{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/{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
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Chat reducer — the entire bag of UI state needed to render ChatApp,
|
|
5
|
+
* isolated as a pure function so jest can drive it through transitions
|
|
6
|
+
* without mounting ink.
|
|
7
|
+
*
|
|
8
|
+
* Action types (kept simple and explicit; we don't mint constants because
|
|
9
|
+
* the reducer is the only consumer):
|
|
10
|
+
* { type: "log/append", text } append a log line
|
|
11
|
+
* { type: "log/appendMany", lines } append multiple lines at once
|
|
12
|
+
* { type: "log/clear" } reset log to banner
|
|
13
|
+
* { type: "draft/set", value }
|
|
14
|
+
* { type: "draft/clear" }
|
|
15
|
+
* { type: "focus/toggle" } Tab between input/dashboard
|
|
16
|
+
* { type: "focus/set", mode } "input" | "dashboard"
|
|
17
|
+
* { type: "view/set", view } projects|agents|mode|provider|cron
|
|
18
|
+
* { type: "view/cycle", direction } "left" | "right"
|
|
19
|
+
* { type: "agents/set", list } list of {fullId, type, id, nickname, …}
|
|
20
|
+
* { type: "agents/select", index }
|
|
21
|
+
* { type: "agents/cycle", direction }
|
|
22
|
+
* { type: "agents/clearTarget" }
|
|
23
|
+
* { type: "agents/window", windowStart }
|
|
24
|
+
* { type: "projects/set", list, activeProjectRoot }
|
|
25
|
+
* { type: "projects/select", index, projectRoot }
|
|
26
|
+
* { type: "scope/set", scope } "controller" | "project"
|
|
27
|
+
* { type: "status/set", payload } { message, type, showTimer, startedAt }
|
|
28
|
+
* { type: "status/idle" }
|
|
29
|
+
* { type: "history/push", value }
|
|
30
|
+
* { type: "history/setIndex", index }
|
|
31
|
+
* { type: "merge/append", entry } tool merge add
|
|
32
|
+
* { type: "merge/flush" } freeze active group into log
|
|
33
|
+
* { type: "merge/expand" } Ctrl+O
|
|
34
|
+
* { type: "settings/set", patch } merge launch mode / provider / autoResume
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const fmt = require("../format");
|
|
38
|
+
|
|
39
|
+
const LOG_CAP = 1000;
|
|
40
|
+
const HISTORY_CAP = 200;
|
|
41
|
+
const DASHBOARD_VIEWS = ["projects", "agents", "mode", "provider", "cron"];
|
|
42
|
+
const DEFAULT_PROVIDER_OPTIONS = [
|
|
43
|
+
{ label: "codex", value: "codex-cli" },
|
|
44
|
+
{ label: "claude", value: "claude-cli" },
|
|
45
|
+
{ label: "agy", value: "agy-cli" },
|
|
46
|
+
];
|
|
47
|
+
function projectRootOf(row = {}) {
|
|
48
|
+
return String((row && (row.root || row.project_root || row.projectRoot)) || "");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createInitialState({ banner = [], globalMode = false, globalScope = "controller", settings = {} } = {}) {
|
|
52
|
+
const initialLaunchMode = settings.launchMode || "auto";
|
|
53
|
+
const initialAgentProvider = settings.agentProvider || "codex-cli";
|
|
54
|
+
const selectedProviderIndex = Math.max(0, DEFAULT_PROVIDER_OPTIONS.findIndex((opt) => opt.value === initialAgentProvider));
|
|
55
|
+
return {
|
|
56
|
+
logLines: banner.concat([""]).map((line, idx) => ({ id: `b-${idx}`, text: line })),
|
|
57
|
+
lineSeq: banner.length + 1,
|
|
58
|
+
draft: "",
|
|
59
|
+
focusMode: "input",
|
|
60
|
+
dashboardView: globalMode ? "projects" : "agents",
|
|
61
|
+
globalMode,
|
|
62
|
+
globalScope,
|
|
63
|
+
agents: [],
|
|
64
|
+
activeAgentMeta: new Map(),
|
|
65
|
+
selectedAgentIndex: -1,
|
|
66
|
+
agentSelectionMode: false,
|
|
67
|
+
agentListWindowStart: 0,
|
|
68
|
+
projects: [],
|
|
69
|
+
selectedProjectIndex: -1,
|
|
70
|
+
selectedProjectRoot: "",
|
|
71
|
+
projectListWindowStart: 0,
|
|
72
|
+
emptyProjectsDownArmed: false,
|
|
73
|
+
activeProjectRoot: "",
|
|
74
|
+
modeOptions: ["auto", "host", "terminal", "tmux", "internal"],
|
|
75
|
+
selectedModeIndex: Math.max(0, ["auto", "host", "terminal", "tmux", "internal"].indexOf(initialLaunchMode)),
|
|
76
|
+
providerOptions: DEFAULT_PROVIDER_OPTIONS,
|
|
77
|
+
selectedProviderIndex,
|
|
78
|
+
cronTasks: [],
|
|
79
|
+
selectedCronIndex: -1,
|
|
80
|
+
loopSummary: null,
|
|
81
|
+
viewingAgentId: null,
|
|
82
|
+
// activeStream is the in-flight chunk-by-chunk publisher message (set
|
|
83
|
+
// while the daemon is streaming). Rendered live below <Static>;
|
|
84
|
+
// promoted to <Static> when the stream finishes the same way the
|
|
85
|
+
// tool-merge group is.
|
|
86
|
+
activeStream: null,
|
|
87
|
+
inputHistory: [],
|
|
88
|
+
historyIndex: 0,
|
|
89
|
+
activeMerge: null,
|
|
90
|
+
lastMerge: null,
|
|
91
|
+
mergeId: 0,
|
|
92
|
+
status: { message: "", type: "thinking", showTimer: false, startedAt: 0 },
|
|
93
|
+
settings: { launchMode: initialLaunchMode, agentProvider: initialAgentProvider, autoResume: settings.autoResume === true },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function appendLog(state, lines) {
|
|
98
|
+
const incoming = Array.isArray(lines) ? lines : [lines];
|
|
99
|
+
let seq = state.lineSeq;
|
|
100
|
+
const out = state.logLines.concat(incoming.map((text) => {
|
|
101
|
+
const id = `l-${seq}`;
|
|
102
|
+
seq += 1;
|
|
103
|
+
return { id, text: String(text == null ? "" : text) };
|
|
104
|
+
}));
|
|
105
|
+
return {
|
|
106
|
+
...state,
|
|
107
|
+
logLines: out.length > LOG_CAP ? out.slice(-LOG_CAP) : out,
|
|
108
|
+
lineSeq: seq,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function freezeMergeIntoLog(state) {
|
|
113
|
+
if (!state.activeMerge) return state;
|
|
114
|
+
const summary = fmt.buildToolMergeRowText(state.activeMerge.entries);
|
|
115
|
+
return appendLog({ ...state, activeMerge: null }, summary);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function reducer(state, action) {
|
|
119
|
+
if (!action || !action.type) return state;
|
|
120
|
+
switch (action.type) {
|
|
121
|
+
case "log/append":
|
|
122
|
+
return appendLog(freezeMergeIntoLog(state), action.text);
|
|
123
|
+
case "log/appendMany":
|
|
124
|
+
return appendLog(freezeMergeIntoLog(state), action.lines);
|
|
125
|
+
case "log/clear":
|
|
126
|
+
return {
|
|
127
|
+
...state,
|
|
128
|
+
logLines: [],
|
|
129
|
+
lineSeq: 0,
|
|
130
|
+
activeMerge: null,
|
|
131
|
+
};
|
|
132
|
+
case "draft/set":
|
|
133
|
+
return { ...state, draft: String(action.value || "") };
|
|
134
|
+
case "draft/clear":
|
|
135
|
+
return { ...state, draft: "" };
|
|
136
|
+
case "focus/toggle":
|
|
137
|
+
return {
|
|
138
|
+
...state,
|
|
139
|
+
focusMode: state.focusMode === "input" ? "dashboard" : "input",
|
|
140
|
+
emptyProjectsDownArmed: state.focusMode === "input" ? state.emptyProjectsDownArmed : false,
|
|
141
|
+
};
|
|
142
|
+
case "focus/set":
|
|
143
|
+
return {
|
|
144
|
+
...state,
|
|
145
|
+
focusMode: action.mode === "dashboard" ? "dashboard" : "input",
|
|
146
|
+
emptyProjectsDownArmed: action.mode === "dashboard" ? state.emptyProjectsDownArmed : false,
|
|
147
|
+
};
|
|
148
|
+
case "view/set": {
|
|
149
|
+
const view = action.view;
|
|
150
|
+
const inAgentsView = view === "agents";
|
|
151
|
+
return {
|
|
152
|
+
...state,
|
|
153
|
+
dashboardView: view,
|
|
154
|
+
agentSelectionMode: inAgentsView && state.focusMode === "dashboard" && state.selectedAgentIndex >= 0,
|
|
155
|
+
emptyProjectsDownArmed: view === "projects" ? state.emptyProjectsDownArmed : false,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
case "view/cycle": {
|
|
159
|
+
const i = DASHBOARD_VIEWS.indexOf(state.dashboardView);
|
|
160
|
+
const direction = action.direction === "left" ? -1 : 1;
|
|
161
|
+
const start = i < 0 ? 0 : i;
|
|
162
|
+
const next = Math.max(0, Math.min(DASHBOARD_VIEWS.length - 1, start + direction));
|
|
163
|
+
return { ...state, dashboardView: DASHBOARD_VIEWS[next] };
|
|
164
|
+
}
|
|
165
|
+
case "agents/set": {
|
|
166
|
+
const list = Array.isArray(action.list) ? action.list : [];
|
|
167
|
+
const ids = list.map((a) => a.fullId || `${a.type}:${a.id}`);
|
|
168
|
+
const meta = new Map(list.map((a) => [a.fullId || `${a.type}:${a.id}`, a]));
|
|
169
|
+
let nextIdx = state.selectedAgentIndex;
|
|
170
|
+
let nextMode = state.agentSelectionMode;
|
|
171
|
+
if (ids.length === 0) {
|
|
172
|
+
nextIdx = -1;
|
|
173
|
+
nextMode = false;
|
|
174
|
+
} else if (nextIdx >= ids.length) {
|
|
175
|
+
nextIdx = ids.length - 1;
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...state,
|
|
179
|
+
agents: ids,
|
|
180
|
+
activeAgentMeta: meta,
|
|
181
|
+
selectedAgentIndex: nextIdx,
|
|
182
|
+
agentSelectionMode: nextMode,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
case "agents/patchMeta": {
|
|
186
|
+
const agentId = String(action.agentId || "").trim();
|
|
187
|
+
if (!agentId) return state;
|
|
188
|
+
const meta = new Map(state.activeAgentMeta instanceof Map ? state.activeAgentMeta : []);
|
|
189
|
+
const current = meta.get(agentId) || {};
|
|
190
|
+
meta.set(agentId, { ...current, ...(action.patch || {}) });
|
|
191
|
+
return { ...state, activeAgentMeta: meta };
|
|
192
|
+
}
|
|
193
|
+
case "agents/select":
|
|
194
|
+
return {
|
|
195
|
+
...state,
|
|
196
|
+
selectedAgentIndex: action.index,
|
|
197
|
+
agentSelectionMode: action.index >= 0,
|
|
198
|
+
};
|
|
199
|
+
case "agents/cycle": {
|
|
200
|
+
if (state.agents.length === 0) return state;
|
|
201
|
+
const cur = state.selectedAgentIndex < 0 ? 0 : state.selectedAgentIndex;
|
|
202
|
+
const next = action.direction === "left"
|
|
203
|
+
? Math.max(0, cur - 1)
|
|
204
|
+
: Math.min(state.agents.length - 1, cur + 1);
|
|
205
|
+
return { ...state, selectedAgentIndex: next, agentSelectionMode: true };
|
|
206
|
+
}
|
|
207
|
+
case "agents/clearTarget":
|
|
208
|
+
return { ...state, selectedAgentIndex: -1, agentSelectionMode: false };
|
|
209
|
+
case "agents/window":
|
|
210
|
+
return { ...state, agentListWindowStart: action.windowStart };
|
|
211
|
+
case "projects/set": {
|
|
212
|
+
const list = Array.isArray(action.list) ? action.list : [];
|
|
213
|
+
const previousSelectedRoot = state.selectedProjectRoot
|
|
214
|
+
|| projectRootOf(state.projects[state.selectedProjectIndex]);
|
|
215
|
+
const selectedRoot = String(action.selectedProjectRoot || previousSelectedRoot || "");
|
|
216
|
+
const selectedIndex = selectedRoot
|
|
217
|
+
? list.findIndex((row) => projectRootOf(row) === selectedRoot)
|
|
218
|
+
: -1;
|
|
219
|
+
return {
|
|
220
|
+
...state,
|
|
221
|
+
projects: list,
|
|
222
|
+
selectedProjectRoot: selectedIndex >= 0 ? selectedRoot : "",
|
|
223
|
+
selectedProjectIndex: selectedIndex,
|
|
224
|
+
activeProjectRoot: action.activeProjectRoot || state.activeProjectRoot,
|
|
225
|
+
emptyProjectsDownArmed: list.length === 0 ? state.emptyProjectsDownArmed : false,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
case "projects/select":
|
|
229
|
+
return {
|
|
230
|
+
...state,
|
|
231
|
+
selectedProjectIndex: action.index,
|
|
232
|
+
selectedProjectRoot: String(action.projectRoot || projectRootOf(state.projects[action.index]) || ""),
|
|
233
|
+
emptyProjectsDownArmed: false,
|
|
234
|
+
};
|
|
235
|
+
case "projects/clearSelection":
|
|
236
|
+
return { ...state, selectedProjectIndex: -1, selectedProjectRoot: "", emptyProjectsDownArmed: false };
|
|
237
|
+
case "projects/armEmptyDown":
|
|
238
|
+
return { ...state, emptyProjectsDownArmed: true };
|
|
239
|
+
case "projects/window":
|
|
240
|
+
return { ...state, projectListWindowStart: Math.max(0, action.windowStart | 0) };
|
|
241
|
+
case "scope/set":
|
|
242
|
+
return { ...state, globalScope: action.scope === "project" ? "project" : "controller" };
|
|
243
|
+
case "status/set":
|
|
244
|
+
return { ...state, status: { ...state.status, ...action.payload } };
|
|
245
|
+
case "status/idle":
|
|
246
|
+
return { ...state, status: { message: "", type: "thinking", showTimer: false, startedAt: 0 } };
|
|
247
|
+
case "history/push": {
|
|
248
|
+
const value = String(action.value || "").trim();
|
|
249
|
+
if (!value) return state;
|
|
250
|
+
const next = state.inputHistory.concat([value]).slice(-HISTORY_CAP);
|
|
251
|
+
return { ...state, inputHistory: next, historyIndex: next.length };
|
|
252
|
+
}
|
|
253
|
+
case "history/load": {
|
|
254
|
+
const list = Array.isArray(action.list) ? action.list : [];
|
|
255
|
+
const next = list.slice(-HISTORY_CAP);
|
|
256
|
+
return { ...state, inputHistory: next, historyIndex: next.length };
|
|
257
|
+
}
|
|
258
|
+
case "history/setIndex":
|
|
259
|
+
return { ...state, historyIndex: Math.max(0, Math.min(state.inputHistory.length, action.index)) };
|
|
260
|
+
case "merge/append": {
|
|
261
|
+
const entry = fmt.normalizeToolMergeEntry(action.entry || {});
|
|
262
|
+
let next;
|
|
263
|
+
if (state.activeMerge) {
|
|
264
|
+
next = { ...state.activeMerge, entries: state.activeMerge.entries.concat([entry]) };
|
|
265
|
+
} else {
|
|
266
|
+
next = { id: state.mergeId + 1, entries: [entry], expanded: false };
|
|
267
|
+
}
|
|
268
|
+
const lastMerge = next.entries.length >= 2 ? next : state.lastMerge;
|
|
269
|
+
return {
|
|
270
|
+
...state,
|
|
271
|
+
activeMerge: next,
|
|
272
|
+
lastMerge,
|
|
273
|
+
mergeId: state.activeMerge ? state.mergeId : state.mergeId + 1,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
case "merge/flush":
|
|
277
|
+
return freezeMergeIntoLog(state);
|
|
278
|
+
case "merge/expand": {
|
|
279
|
+
const candidate = (state.activeMerge && !state.activeMerge.expanded && state.activeMerge.entries.length >= 2)
|
|
280
|
+
? state.activeMerge
|
|
281
|
+
: (state.lastMerge && !state.lastMerge.expanded && state.lastMerge.entries.length >= 2
|
|
282
|
+
? state.lastMerge
|
|
283
|
+
: null);
|
|
284
|
+
if (!candidate) return state;
|
|
285
|
+
const lines = fmt.buildMergedToolExpandedLines(candidate.entries).map((line, i, arr) =>
|
|
286
|
+
`${i === arr.length - 1 ? "└" : "│"} ${line}`
|
|
287
|
+
);
|
|
288
|
+
const after = appendLog(state, lines);
|
|
289
|
+
return {
|
|
290
|
+
...after,
|
|
291
|
+
activeMerge: state.activeMerge && state.activeMerge.id === candidate.id ? null : state.activeMerge,
|
|
292
|
+
lastMerge: state.lastMerge && state.lastMerge.id === candidate.id
|
|
293
|
+
? { ...state.lastMerge, expanded: true }
|
|
294
|
+
: state.lastMerge,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
case "settings/set":
|
|
298
|
+
return { ...state, settings: { ...state.settings, ...(action.patch || {}) } };
|
|
299
|
+
case "settings/applyMode": {
|
|
300
|
+
const mode = state.modeOptions[state.selectedModeIndex] || state.settings.launchMode;
|
|
301
|
+
return { ...state, settings: { ...state.settings, launchMode: mode } };
|
|
302
|
+
}
|
|
303
|
+
case "settings/applyProvider": {
|
|
304
|
+
const selected = state.providerOptions[state.selectedProviderIndex];
|
|
305
|
+
const agentProvider = selected && selected.value ? selected.value : state.settings.agentProvider;
|
|
306
|
+
return { ...state, settings: { ...state.settings, agentProvider } };
|
|
307
|
+
}
|
|
308
|
+
case "modeIndex/set":
|
|
309
|
+
return { ...state, selectedModeIndex: Math.max(0, action.index | 0) };
|
|
310
|
+
case "providerIndex/set":
|
|
311
|
+
return { ...state, selectedProviderIndex: Math.max(0, action.index | 0) };
|
|
312
|
+
case "cronIndex/set":
|
|
313
|
+
return { ...state, selectedCronIndex: Math.max(-1, action.index | 0) };
|
|
314
|
+
case "cron/set":
|
|
315
|
+
return { ...state, cronTasks: Array.isArray(action.list) ? action.list : [] };
|
|
316
|
+
case "loop/set":
|
|
317
|
+
return { ...state, loopSummary: action.summary && typeof action.summary === "object" ? action.summary : null };
|
|
318
|
+
case "stream/begin":
|
|
319
|
+
return {
|
|
320
|
+
...state,
|
|
321
|
+
activeStream: { publisher: action.publisher || "", text: "" },
|
|
322
|
+
};
|
|
323
|
+
case "stream/delta": {
|
|
324
|
+
if (!state.activeStream) {
|
|
325
|
+
return {
|
|
326
|
+
...state,
|
|
327
|
+
activeStream: { publisher: action.publisher || "", text: String(action.delta || "") },
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
...state,
|
|
332
|
+
activeStream: {
|
|
333
|
+
...state.activeStream,
|
|
334
|
+
text: state.activeStream.text + String(action.delta || ""),
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
case "stream/end": {
|
|
339
|
+
if (!state.activeStream) return state;
|
|
340
|
+
const lines = String(state.activeStream.text || "").split(/\r?\n/);
|
|
341
|
+
const prefix = state.activeStream.publisher
|
|
342
|
+
? `${state.activeStream.publisher}: `
|
|
343
|
+
: "";
|
|
344
|
+
const annotated = prefix && lines.length > 0
|
|
345
|
+
? [`${prefix}${lines[0]}`, ...lines.slice(1).map((l) => ` ${l}`)]
|
|
346
|
+
: lines;
|
|
347
|
+
const next = appendLog(state, annotated);
|
|
348
|
+
return { ...next, activeStream: null };
|
|
349
|
+
}
|
|
350
|
+
case "agentView/enter":
|
|
351
|
+
return { ...state, viewingAgentId: action.agentId || null };
|
|
352
|
+
case "agentView/exit":
|
|
353
|
+
return { ...state, viewingAgentId: null };
|
|
354
|
+
default:
|
|
355
|
+
return state;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
module.exports = { reducer, createInitialState, DASHBOARD_VIEWS };
|
package/src/ui/runInk.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ink runtime launcher for ufoo TUIs.
|
|
5
|
+
*
|
|
6
|
+
* ufoo is CommonJS, ink@5 is ESM-only. We bridge by dynamic-importing ink
|
|
7
|
+
* and exposing a CJS-friendly API that takes a `createRoot(React, ink)`
|
|
8
|
+
* factory instead of a pre-built React element. The factory is invoked with
|
|
9
|
+
* the loaded `React` module and ink namespace so callers don't need to
|
|
10
|
+
* import them themselves.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const { runInk } = require("./runInk");
|
|
14
|
+
* const handle = await runInk(
|
|
15
|
+
* (React, ink) => React.createElement(App, props),
|
|
16
|
+
* { stdin, stdout },
|
|
17
|
+
* );
|
|
18
|
+
* await handle.waitUntilExit();
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
async function runInk(createRoot, options = {}) {
|
|
22
|
+
const ink = await import("ink");
|
|
23
|
+
const React = require("react");
|
|
24
|
+
const element = createRoot(React, ink);
|
|
25
|
+
if (!element) throw new Error("runInk: createRoot returned no element");
|
|
26
|
+
const stdout = options.stdout || process.stdout;
|
|
27
|
+
const inst = ink.render(element, {
|
|
28
|
+
stdin: options.stdin || process.stdin,
|
|
29
|
+
stdout,
|
|
30
|
+
stderr: options.stderr || process.stderr,
|
|
31
|
+
exitOnCtrlC: options.exitOnCtrlC !== false,
|
|
32
|
+
patchConsole: options.patchConsole === true,
|
|
33
|
+
});
|
|
34
|
+
// ink keeps the final rendered frame on screen when it unmounts (it writes
|
|
35
|
+
// the frame as plain stdout output), which leaves the TUI lingering after
|
|
36
|
+
// Ctrl+C. Clear and home the cursor on every clean exit so the shell prompt
|
|
37
|
+
// returns to the top of a blank screen.
|
|
38
|
+
const wrappedExit = inst.waitUntilExit().then((value) => {
|
|
39
|
+
try {
|
|
40
|
+
if (stdout && stdout.isTTY && typeof stdout.write === "function") {
|
|
41
|
+
stdout.write("\x1b[2J\x1b[H");
|
|
42
|
+
}
|
|
43
|
+
} catch { /* ignore */ }
|
|
44
|
+
return value;
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
waitUntilExit: () => wrappedExit,
|
|
48
|
+
rerender: (next) => inst.rerender(next),
|
|
49
|
+
unmount: () => inst.unmount(),
|
|
50
|
+
cleanup: () => inst.cleanup(),
|
|
51
|
+
instance: inst,
|
|
52
|
+
React,
|
|
53
|
+
ink,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { runInk };
|
package/src/bus/messageMeta.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const INJECTION_MODES = {
|
|
4
|
-
IMMEDIATE: "immediate",
|
|
5
|
-
QUEUED: "queued",
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
function normalizeInjectionMode(value, fallback = INJECTION_MODES.IMMEDIATE) {
|
|
9
|
-
const raw = String(value || "").trim().toLowerCase();
|
|
10
|
-
if (raw === INJECTION_MODES.QUEUED) return INJECTION_MODES.QUEUED;
|
|
11
|
-
if (raw === INJECTION_MODES.IMMEDIATE) return INJECTION_MODES.IMMEDIATE;
|
|
12
|
-
return fallback;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function normalizeMessageSource(value) {
|
|
16
|
-
const raw = String(value || "").trim();
|
|
17
|
-
return raw || "";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function buildMessageData(message, options = {}) {
|
|
21
|
-
const base = options && typeof options.data === "object" && options.data
|
|
22
|
-
? { ...options.data }
|
|
23
|
-
: {};
|
|
24
|
-
const data = { ...base, message };
|
|
25
|
-
data.injection_mode = normalizeInjectionMode(
|
|
26
|
-
options.injectionMode || data.injection_mode,
|
|
27
|
-
INJECTION_MODES.IMMEDIATE,
|
|
28
|
-
);
|
|
29
|
-
const source = normalizeMessageSource(options.source || data.source);
|
|
30
|
-
if (source) {
|
|
31
|
-
data.source = source;
|
|
32
|
-
} else {
|
|
33
|
-
delete data.source;
|
|
34
|
-
}
|
|
35
|
-
return data;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getInjectionModeFromEvent(evt, fallback = INJECTION_MODES.IMMEDIATE) {
|
|
39
|
-
const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
|
|
40
|
-
return normalizeInjectionMode(
|
|
41
|
-
data.injection_mode || evt?.injection_mode,
|
|
42
|
-
fallback,
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = {
|
|
47
|
-
INJECTION_MODES,
|
|
48
|
-
normalizeInjectionMode,
|
|
49
|
-
normalizeMessageSource,
|
|
50
|
-
buildMessageData,
|
|
51
|
-
getInjectionModeFromEvent,
|
|
52
|
-
};
|