nolo-cli 0.8.0-alpha.2 → 0.8.0-alpha.21
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 +1 -1
- package/_executeModel-42U3VOVZ.js +205 -0
- package/action-TLI3V62L.js +28 -0
- package/addContentAction-57RSIIMR.js +26 -0
- package/addDialogAgentAction-G3VWEA6E.js +68 -0
- package/agentDeleteCommand-5DD2IM3J.js +327 -0
- package/agentEmailCommands-6FACJBU4.js +628 -0
- package/agentListCommands-I6KZYCIG.js +223 -0
- package/agentMachineCommands-VKLIGMZA.js +425 -0
- package/agentPullCommand-O2MTUAFR.js +122 -0
- package/agentRecordCommands-7TZICYDT.js +96 -0
- package/agentRunCommand-L4OM23KV.js +114 -0
- package/agentRunControl-K4VKNEHF.js +57 -0
- package/agentRuntimeLocal-ZYEIPTY4.js +104 -0
- package/agentSlice-BX7GXSNO.js +42 -0
- package/authCommand-EB5JSOX5.js +581 -0
- package/authCommands-XPEHTKSM.js +35 -0
- package/chunk-25LCXTML.js +175 -0
- package/chunk-2ECQG4YX.js +22296 -0
- package/chunk-2FTTDCD4.js +675 -0
- package/chunk-2JCXITPK.js +1078 -0
- package/chunk-3ASDTTEI.js +1774 -0
- package/chunk-3EUW3WEO.js +8 -0
- package/chunk-3LY4F7SV.js +9293 -0
- package/chunk-4CQLVGRV.js +50 -0
- package/chunk-4IJYA27C.js +342 -0
- package/chunk-4O3CARJ4.js +231 -0
- package/chunk-4QEWT7KD.js +933 -0
- package/chunk-4SN6J65J.js +38 -0
- package/chunk-4VNS5WPM.js +42 -0
- package/chunk-4XENYTF5.js +222 -0
- package/chunk-4ZDM73SD.js +34 -0
- package/chunk-5CS33IBL.js +301 -0
- package/chunk-5EFXE3O7.js +138 -0
- package/chunk-5KLGBAAR.js +386 -0
- package/chunk-5MIDPSSK.js +206 -0
- package/chunk-5MMAK75Q.js +15 -0
- package/chunk-63W5N6P3.js +150 -0
- package/chunk-6HYVJV7B.js +102 -0
- package/chunk-6KHNO3SK.js +1948 -0
- package/chunk-6WD3OOJS.js +20 -0
- package/chunk-7DAJ4AI4.js +191 -0
- package/chunk-7GNGZRLZ.js +1939 -0
- package/chunk-7KCUSYT2.js +354 -0
- package/chunk-7U44LYTR.js +238 -0
- package/chunk-AFON4QUU.js +112 -0
- package/chunk-AGFH73SK.js +239 -0
- package/chunk-AL2KDGR2.js +27339 -0
- package/chunk-AS4PVWA4.js +221 -0
- package/chunk-ATNTHIZI.js +13 -0
- package/chunk-AVMKZHBC.js +85 -0
- package/chunk-AZPCASRE.js +36 -0
- package/chunk-BBYVOSU2.js +792 -0
- package/chunk-C3RUNRHT.js +51 -0
- package/chunk-C4FTUXVF.js +146 -0
- package/chunk-CF2WZF4N.js +134 -0
- package/chunk-CF7PE763.js +10 -0
- package/chunk-CKOJQDKR.js +198 -0
- package/chunk-CMKUBIGW.js +1550 -0
- package/chunk-CWMSB67P.js +34 -0
- package/chunk-DCOA4QSN.js +105 -0
- package/chunk-DFFIZ25G.js +30 -0
- package/chunk-DSZUWUYB.js +478 -0
- package/chunk-DWLEOOGR.js +8 -0
- package/chunk-DZ3XJVET.js +231 -0
- package/chunk-EHLHSX7R.js +1433 -0
- package/chunk-ENOG22KJ.js +939 -0
- package/chunk-FSO5B7GH.js +171 -0
- package/chunk-FVYY6MCS.js +1447 -0
- package/chunk-G3SUCT4W.js +8 -0
- package/chunk-GAKEBQD6.js +30 -0
- package/chunk-GBRG75BQ.js +86 -0
- package/chunk-GU3PEVGZ.js +4723 -0
- package/chunk-H33743XH.js +25 -0
- package/chunk-HNMXYYJR.js +12 -0
- package/chunk-HNW7IJSK.js +71 -0
- package/chunk-I42VC5KR.js +15 -0
- package/chunk-I6XMPTMR.js +113 -0
- package/chunk-IDZTNRP2.js +85 -0
- package/chunk-ISNCLGUZ.js +146 -0
- package/chunk-IWX4UW2E.js +548 -0
- package/chunk-JBTJAPUF.js +35 -0
- package/chunk-JEXVIMYZ.js +55 -0
- package/chunk-JRKKFRAV.js +537 -0
- package/chunk-JZPBVBIY.js +94 -0
- package/chunk-KKY4B2Q2.js +21 -0
- package/chunk-KQHGZWSV.js +63 -0
- package/chunk-KUG52IAP.js +8 -0
- package/chunk-KX6YBIHG.js +8 -0
- package/chunk-LCBAMY2M.js +60 -0
- package/chunk-LM6IYQ3F.js +16 -0
- package/chunk-LTRXGFMS.js +83 -0
- package/chunk-M76ECQRD.js +13658 -0
- package/chunk-MRC4Q34M.js +188 -0
- package/chunk-MTU75CZ2.js +91 -0
- package/chunk-N54XAP77.js +19 -0
- package/chunk-O2ZNTTET.js +3272 -0
- package/chunk-O4BU24WZ.js +3212 -0
- package/chunk-OLZBQWZU.js +6 -0
- package/chunk-OOSFSFMA.js +271 -0
- package/chunk-OPZYGHUC.js +89 -0
- package/chunk-OWOHNGCN.js +50 -0
- package/chunk-P3PKED2I.js +962 -0
- package/chunk-QSJ3HFEF.js +8 -0
- package/chunk-RCSJFQPB.js +9036 -0
- package/chunk-RL776AGR.js +190 -0
- package/chunk-ROWQEJRP.js +139 -0
- package/chunk-S7Z2HMCN.js +25 -0
- package/chunk-SDJDLO5W.js +628 -0
- package/chunk-SOTZ6RDN.js +69 -0
- package/chunk-TILFE6LV.js +381 -0
- package/chunk-U4TRT5V4.js +246 -0
- package/chunk-U6VC4MU4.js +8 -0
- package/chunk-UAD2WT54.js +64 -0
- package/chunk-UB6SN4NJ.js +421 -0
- package/chunk-URUUZLPA.js +807 -0
- package/chunk-UTLZINAP.js +3066 -0
- package/chunk-VC6OZXO5.js +298 -0
- package/chunk-VF4K2QE6.js +21 -0
- package/chunk-VHBWHNVT.js +59 -0
- package/chunk-VIDPVFP3.js +20 -0
- package/chunk-VP4WJWKW.js +8882 -0
- package/chunk-VUW5FBDB.js +573 -0
- package/chunk-VVX233PH.js +482 -0
- package/chunk-VX3SXJ3N.js +2052 -0
- package/chunk-W4NVOSFF.js +37 -0
- package/chunk-WWC7I6TA.js +8 -0
- package/chunk-WZSRQNQG.js +2587 -0
- package/chunk-XBUBEFNX.js +34 -0
- package/chunk-XENYHRUE.js +104 -0
- package/chunk-Y5GCAMGP.js +9 -0
- package/chunk-YPRZSPF3.js +148 -0
- package/chunk-Z4NR32XR.js +50 -0
- package/chunk-ZNFJWEN4.js +56 -0
- package/cliExecutor-K3F7DMRO.js +36 -0
- package/clipboardy-BVE3LVEH.js +12001 -0
- package/createAgentAutomationAction-RJF3N2QY.js +866 -0
- package/createDialogAction-DRSPS6CE.js +55 -0
- package/createPageAction-6B2WMNO3.js +172 -0
- package/db-HCJ5PRB5.js +271 -0
- package/dbSlice-Q3TM2FJV.js +60 -0
- package/deleteFile-OW53A6BX.js +41 -0
- package/dialogCommands-4IX4XIZE.js +63 -0
- package/dialogSlice-3M7KX2TX.js +148 -0
- package/docCreateCommands-SIL3NPGT.js +220 -0
- package/docDeleteCommands-7IOMLUS4.js +181 -0
- package/docReadCommands-FNFXUA5L.js +134 -0
- package/docStore-SV7E34D4.js +98 -0
- package/docUpdateCommands-X2HNDVUC.js +318 -0
- package/fetchMessages-KTNM7QAR.js +17 -0
- package/getPricing-3E6UNPKL.js +30 -0
- package/handleSendMessageAction-JYETO5RZ.js +185 -0
- package/index.js +188 -112958
- package/lib-FYYBQIXS.js +19839 -0
- package/localDialogRead-2VRI24F4.js +26 -0
- package/localFirstLog-DHNOY2NA.js +19 -0
- package/localRuntimeAuthority-TEGCOWX4.js +471 -0
- package/localRuntimeDb-LGDF6FQP.js +12 -0
- package/machineCommands-RIO3RP5Q.js +781 -0
- package/machineInfo-M6KB6HZN.js +10 -0
- package/machineWsRunDispatch-W7UYLGB5.js +112 -0
- package/memoryCommands-O4MIPIWQ.js +476 -0
- package/models-4OPOOLFS.js +9 -0
- package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
- package/package.json +4 -24
- package/read-GW7ZEI23.js +17 -0
- package/readDocTool-HUFU6OCT.js +16 -0
- package/readlineWorkspace-27JR7WPW.js +5682 -0
- package/removeDialogAgentAction-CXKR4NJI.js +73 -0
- package/runAgentClientLoop-HZFAPJHS.js +293 -0
- package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
- package/setDialogExtraReferencesAction-6RKUSXHX.js +60 -0
- package/setPrimaryDialogAgentAction-T3UTBEFG.js +80 -0
- package/settingSlice-IVKXJXMG.js +199 -0
- package/skillDiscovery-UGSQGX2U.js +16 -0
- package/skillSummaryMarker-42HKSXYY.js +9 -0
- package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
- package/slateUtils-2M7MUTWX.js +19 -0
- package/spaceCommands-E7VOF4LO.js +450 -0
- package/spaceSlice-TSVROA5U.js +149 -0
- package/streamAgentChatTurn-3C5RQZJF.js +4510 -0
- package/tableCommands-O7O2LBEG.js +1286 -0
- package/toolApiClient-QI3YBNWB.js +26 -0
- package/tools-EQDEKR6N.js +87 -0
- package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
- package/understanding-3CUQQ6SV.js +271 -0
- package/updateCommands-VOSPKXFF.js +31 -0
- package/updateDialogTitleAction-3XV7VWSZ.js +188 -0
|
@@ -0,0 +1,4723 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveMachineId
|
|
3
|
+
} from "./chunk-6HYVJV7B.js";
|
|
4
|
+
import {
|
|
5
|
+
createRunActivityTracker,
|
|
6
|
+
finalizeRunRecord,
|
|
7
|
+
spawnLocalBackgroundRun
|
|
8
|
+
} from "./chunk-2FTTDCD4.js";
|
|
9
|
+
import {
|
|
10
|
+
buildLocalRunEnv,
|
|
11
|
+
isFullstackCodingAgentRef,
|
|
12
|
+
parseAgentRunArgs,
|
|
13
|
+
readFlagValue,
|
|
14
|
+
resolveRawAgentInput,
|
|
15
|
+
resolveServerUrl,
|
|
16
|
+
writeUsage
|
|
17
|
+
} from "./chunk-DZ3XJVET.js";
|
|
18
|
+
import {
|
|
19
|
+
createCliCallAgentToolExecutor,
|
|
20
|
+
createCliLocalRuntimeAdapter,
|
|
21
|
+
isBuiltinNoloAgentRef
|
|
22
|
+
} from "./chunk-RCSJFQPB.js";
|
|
23
|
+
import {
|
|
24
|
+
getReadableCliDb
|
|
25
|
+
} from "./chunk-W4NVOSFF.js";
|
|
26
|
+
import {
|
|
27
|
+
readDbRecord,
|
|
28
|
+
resolveAgentRecordFromHybridStore
|
|
29
|
+
} from "./chunk-SDJDLO5W.js";
|
|
30
|
+
import {
|
|
31
|
+
normalizeAgentHandle
|
|
32
|
+
} from "./chunk-N54XAP77.js";
|
|
33
|
+
import {
|
|
34
|
+
NOLO_PROJECT_MANAGER_AGENT_KEY,
|
|
35
|
+
isLocalCliAgentKey
|
|
36
|
+
} from "./chunk-VHBWHNVT.js";
|
|
37
|
+
import {
|
|
38
|
+
applyModelLayerOverride,
|
|
39
|
+
buildModelLayerOverride
|
|
40
|
+
} from "./chunk-LTRXGFMS.js";
|
|
41
|
+
import {
|
|
42
|
+
CliProviderQuotaError
|
|
43
|
+
} from "./chunk-CMKUBIGW.js";
|
|
44
|
+
import {
|
|
45
|
+
isGatewayHttpStatus
|
|
46
|
+
} from "./chunk-GAKEBQD6.js";
|
|
47
|
+
import {
|
|
48
|
+
buildMemorySubjectsForAgent,
|
|
49
|
+
resolveAgentMemoryPolicy
|
|
50
|
+
} from "./chunk-ENOG22KJ.js";
|
|
51
|
+
import {
|
|
52
|
+
parseExternalSkillMarkdown,
|
|
53
|
+
parseSkillDocProtocol
|
|
54
|
+
} from "./chunk-VC6OZXO5.js";
|
|
55
|
+
import {
|
|
56
|
+
touchMemoryItemsInDb
|
|
57
|
+
} from "./chunk-HNW7IJSK.js";
|
|
58
|
+
import {
|
|
59
|
+
chooseMemoryOwners,
|
|
60
|
+
loadMemoryCandidatesFromDb
|
|
61
|
+
} from "./chunk-XENYHRUE.js";
|
|
62
|
+
import {
|
|
63
|
+
getModelContextWindow,
|
|
64
|
+
parseUiAskChoiceContent
|
|
65
|
+
} from "./chunk-2ECQG4YX.js";
|
|
66
|
+
import {
|
|
67
|
+
readPipeText,
|
|
68
|
+
spawnProcess
|
|
69
|
+
} from "./chunk-IDZTNRP2.js";
|
|
70
|
+
import {
|
|
71
|
+
resolveAuthToken
|
|
72
|
+
} from "./chunk-ISNCLGUZ.js";
|
|
73
|
+
import {
|
|
74
|
+
buildSkillDiscoveryContextBlock
|
|
75
|
+
} from "./chunk-AFON4QUU.js";
|
|
76
|
+
import {
|
|
77
|
+
buildMemoryOverlayLayer,
|
|
78
|
+
buildMemoryUseGuidanceLayer
|
|
79
|
+
} from "./chunk-C4FTUXVF.js";
|
|
80
|
+
import {
|
|
81
|
+
buildPlatformChatCompletionRequest,
|
|
82
|
+
parsePlatformChatCompletionData
|
|
83
|
+
} from "./chunk-5KLGBAAR.js";
|
|
84
|
+
import {
|
|
85
|
+
resolveAgentRuntimeConfigFromRecord
|
|
86
|
+
} from "./chunk-TILFE6LV.js";
|
|
87
|
+
import {
|
|
88
|
+
LOCAL_AGENT_CONFIG_MISSING_CODE,
|
|
89
|
+
LOCAL_TURN_ABORTED_CODE,
|
|
90
|
+
readActionGate,
|
|
91
|
+
readCommandActionGatePayload
|
|
92
|
+
} from "./chunk-4QEWT7KD.js";
|
|
93
|
+
import {
|
|
94
|
+
resolvePlatformChatCompletionsEndpoint
|
|
95
|
+
} from "./chunk-JRKKFRAV.js";
|
|
96
|
+
import {
|
|
97
|
+
clipCompactText
|
|
98
|
+
} from "./chunk-5MMAK75Q.js";
|
|
99
|
+
import {
|
|
100
|
+
findModelConfig
|
|
101
|
+
} from "./chunk-BBYVOSU2.js";
|
|
102
|
+
import {
|
|
103
|
+
compactWhitespace
|
|
104
|
+
} from "./chunk-DWLEOOGR.js";
|
|
105
|
+
import {
|
|
106
|
+
toErrorMessage
|
|
107
|
+
} from "./chunk-HNMXYYJR.js";
|
|
108
|
+
import {
|
|
109
|
+
getDefaultCliLocalRuntimeDb
|
|
110
|
+
} from "./chunk-H33743XH.js";
|
|
111
|
+
import {
|
|
112
|
+
parsePositiveFiniteNumberOrFallback
|
|
113
|
+
} from "./chunk-Y5GCAMGP.js";
|
|
114
|
+
import {
|
|
115
|
+
asTrimmedLowercaseString
|
|
116
|
+
} from "./chunk-QSJ3HFEF.js";
|
|
117
|
+
import {
|
|
118
|
+
PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
|
|
119
|
+
PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
|
|
120
|
+
PUBLIC_GLM_52_AGENT_KEY,
|
|
121
|
+
PUBLIC_KIMI_K26_IMAGE_AGENT_KEY
|
|
122
|
+
} from "./chunk-DSZUWUYB.js";
|
|
123
|
+
import {
|
|
124
|
+
isLoopbackUrl
|
|
125
|
+
} from "./chunk-4SN6J65J.js";
|
|
126
|
+
import {
|
|
127
|
+
asTrimmedString
|
|
128
|
+
} from "./chunk-3EUW3WEO.js";
|
|
129
|
+
import {
|
|
130
|
+
asOptionalTrimmedString
|
|
131
|
+
} from "./chunk-G3SUCT4W.js";
|
|
132
|
+
|
|
133
|
+
// packages/cli/client/tokenUsage.ts
|
|
134
|
+
var PROVIDER_LOOKUP_ORDER = [
|
|
135
|
+
"openrouter",
|
|
136
|
+
"fireworks",
|
|
137
|
+
"openai",
|
|
138
|
+
"mimo",
|
|
139
|
+
"gmi",
|
|
140
|
+
"google",
|
|
141
|
+
"deepseek",
|
|
142
|
+
"mistral",
|
|
143
|
+
"vultr",
|
|
144
|
+
"deepinfra",
|
|
145
|
+
"cloudflare"
|
|
146
|
+
];
|
|
147
|
+
function parseUsageRecord(usage) {
|
|
148
|
+
if (!usage || typeof usage !== "object") return void 0;
|
|
149
|
+
const input = Number(usage.input_tokens ?? usage.prompt_tokens ?? 0);
|
|
150
|
+
const output = Number(usage.output_tokens ?? usage.completion_tokens ?? 0);
|
|
151
|
+
if (!Number.isFinite(input) || !Number.isFinite(output)) return void 0;
|
|
152
|
+
if (!input && !output) return void 0;
|
|
153
|
+
return { input, output };
|
|
154
|
+
}
|
|
155
|
+
function resolveContextWindow(model) {
|
|
156
|
+
const raw = model?.trim();
|
|
157
|
+
if (!raw) return void 0;
|
|
158
|
+
for (const provider of PROVIDER_LOOKUP_ORDER) {
|
|
159
|
+
const config = findModelConfig(provider, raw);
|
|
160
|
+
if (config?.contextWindow) return config.contextWindow;
|
|
161
|
+
}
|
|
162
|
+
return getModelContextWindow(raw);
|
|
163
|
+
}
|
|
164
|
+
function buildTurnTokenUsage(usage, model) {
|
|
165
|
+
const parsed = parseUsageRecord(usage);
|
|
166
|
+
if (!parsed) return void 0;
|
|
167
|
+
const contextWindow = resolveContextWindow(model);
|
|
168
|
+
const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
|
|
169
|
+
return {
|
|
170
|
+
...parsed,
|
|
171
|
+
...contextWindow ? { contextWindow } : {},
|
|
172
|
+
...remaining != null ? { remaining } : {}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function formatTokenCount(value) {
|
|
176
|
+
if (!Number.isFinite(value) || value < 0) return "\u2014";
|
|
177
|
+
if (value < 1e3) return String(Math.round(value));
|
|
178
|
+
if (value < 1e6) {
|
|
179
|
+
const compact2 = value / 1e3;
|
|
180
|
+
if (Number.isInteger(compact2)) return `${compact2}k`;
|
|
181
|
+
return `${compact2.toFixed(1).replace(/\.0$/, "")}k`;
|
|
182
|
+
}
|
|
183
|
+
const compact = value / 1e6;
|
|
184
|
+
return compact >= 100 ? `${Math.round(compact)}M` : `${compact.toFixed(1).replace(/\.0$/, "")}M`;
|
|
185
|
+
}
|
|
186
|
+
function renderTokenStatus(tokens) {
|
|
187
|
+
if (!tokens) return "in \u2014 out \u2014 left \u2014";
|
|
188
|
+
const left = tokens.remaining != null ? formatTokenCount(tokens.remaining) : tokens.contextWindow ? "\u2014" : "\u2014";
|
|
189
|
+
return `in ${formatTokenCount(tokens.input)} out ${formatTokenCount(tokens.output)} left ${left}`;
|
|
190
|
+
}
|
|
191
|
+
function shouldShowUsage(env) {
|
|
192
|
+
return env.NOLO_DEBUG === "1" || env.NOLO_SHOW_USAGE === "1";
|
|
193
|
+
}
|
|
194
|
+
function formatUsage(usage, dialogId) {
|
|
195
|
+
const parts = [];
|
|
196
|
+
if (typeof dialogId === "string" && dialogId)
|
|
197
|
+
parts.push(`dialog=${dialogId}`);
|
|
198
|
+
const input = usage?.input_tokens ?? usage?.prompt_tokens ?? 0;
|
|
199
|
+
const output = usage?.output_tokens ?? usage?.completion_tokens ?? 0;
|
|
200
|
+
if (input || output) parts.push(`tokens=${input}+${output}`);
|
|
201
|
+
return parts.length ? ` (${parts.join(" ")})` : "";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// packages/cli/client/terminalStyles.ts
|
|
205
|
+
var ANSI_FG = {
|
|
206
|
+
dim: "\x1B[2m",
|
|
207
|
+
bold: "\x1B[1m",
|
|
208
|
+
cyan: "\x1B[36m",
|
|
209
|
+
blue: "\x1B[34m",
|
|
210
|
+
green: "\x1B[32m",
|
|
211
|
+
red: "\x1B[31m",
|
|
212
|
+
yellow: "\x1B[33m",
|
|
213
|
+
magenta: "\x1B[35m",
|
|
214
|
+
white: "\x1B[37m",
|
|
215
|
+
black: "\x1B[30m"
|
|
216
|
+
};
|
|
217
|
+
var ANSI_BG = {
|
|
218
|
+
bgCyan: "\x1B[46m",
|
|
219
|
+
bgGray: "\x1B[100m",
|
|
220
|
+
bgMagenta: "\x1B[45m",
|
|
221
|
+
bgYellow: "\x1B[43m",
|
|
222
|
+
bgBlue: "\x1B[44m",
|
|
223
|
+
bgGreen: "\x1B[42m",
|
|
224
|
+
bgRed: "\x1B[41m",
|
|
225
|
+
bgWhite: "\x1B[47m"
|
|
226
|
+
};
|
|
227
|
+
var RESET = "\x1B[0m";
|
|
228
|
+
function resolveCliColorEnabled(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
|
|
229
|
+
const setting = asTrimmedLowercaseString(env.NOLO_CLI_COLOR);
|
|
230
|
+
if (setting === "0" || setting === "false" || setting === "off") return false;
|
|
231
|
+
if (env.NO_COLOR) return false;
|
|
232
|
+
if (setting === "1" || setting === "true" || setting === "on") return true;
|
|
233
|
+
return isTTY;
|
|
234
|
+
}
|
|
235
|
+
function styleCliText(text, style, enabled = resolveCliColorEnabled()) {
|
|
236
|
+
if (!enabled || !text) return text;
|
|
237
|
+
const code = ANSI_FG[style] ?? ANSI_BG[style];
|
|
238
|
+
if (!code) return text;
|
|
239
|
+
return `${code}${text}${RESET}`;
|
|
240
|
+
}
|
|
241
|
+
function dimCliText(text, enabled = resolveCliColorEnabled()) {
|
|
242
|
+
return styleCliText(text, "dim", enabled);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// packages/cli/tui/theme.ts
|
|
246
|
+
var THEME_PALETTES = {
|
|
247
|
+
trail: {
|
|
248
|
+
light: {
|
|
249
|
+
accent: { hex: "2E7DB5", ansiFallback: "\x1B[34m" },
|
|
250
|
+
// ocean blue
|
|
251
|
+
chrome: { hex: "768594", ansiFallback: "\x1B[90m" },
|
|
252
|
+
// slate — two steps below app placeholder A3B0BD (was 8A97A5); rules/tool markers on white need the extra notch
|
|
253
|
+
success: { hex: "3F8F5C", ansiFallback: "\x1B[32m" },
|
|
254
|
+
// moss green
|
|
255
|
+
warning: { hex: "9A6A1F", ansiFallback: "\x1B[33m" },
|
|
256
|
+
// deeper amber — B57F2E still read as yellow/washed on white (H2 headers like ## Plan floated); 9A6A1F raises contrast
|
|
257
|
+
info: { hex: "4A9FD4", ansiFallback: "\x1B[36m" },
|
|
258
|
+
// sky blue
|
|
259
|
+
danger: { hex: "C45C4A", ansiFallback: "\x1B[31m" },
|
|
260
|
+
// reddish
|
|
261
|
+
muted: { hex: "5E6A78", ansiFallback: "\x1B[90m" }
|
|
262
|
+
// denser slate — 687584 read too blue in dense prose (inline code/tool labels); 5E6A78 pulls blue down while staying darker than the old value
|
|
263
|
+
},
|
|
264
|
+
dark: {
|
|
265
|
+
accent: { hex: "89B4FA", ansiFallback: "\x1B[34m" },
|
|
266
|
+
// blue
|
|
267
|
+
chrome: { hex: "6C7086", ansiFallback: "\x1B[90m" },
|
|
268
|
+
success: { hex: "A6E3A1", ansiFallback: "\x1B[32m" },
|
|
269
|
+
warning: { hex: "F9E2AF", ansiFallback: "\x1B[33m" },
|
|
270
|
+
info: { hex: "94E2D5", ansiFallback: "\x1B[36m" },
|
|
271
|
+
danger: { hex: "F38BA8", ansiFallback: "\x1B[31m" },
|
|
272
|
+
muted: { hex: "9AA3B8", ansiFallback: "\x1B[90m" }
|
|
273
|
+
// dimmed from A6ADC8 so secondary text sits a step below body text
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
catppuccin: {
|
|
277
|
+
light: {
|
|
278
|
+
accent: { hex: "0969DA", ansiFallback: "\x1B[34m" },
|
|
279
|
+
chrome: { hex: "57606A", ansiFallback: "\x1B[90m" },
|
|
280
|
+
success: { hex: "116329", ansiFallback: "\x1B[32m" },
|
|
281
|
+
warning: { hex: "9A6700", ansiFallback: "\x1B[33m" },
|
|
282
|
+
info: { hex: "0969DA", ansiFallback: "\x1B[36m" },
|
|
283
|
+
danger: { hex: "CF222E", ansiFallback: "\x1B[31m" },
|
|
284
|
+
muted: { hex: "6E7781", ansiFallback: "\x1B[90m" }
|
|
285
|
+
},
|
|
286
|
+
dark: {
|
|
287
|
+
accent: { hex: "58A6FF", ansiFallback: "\x1B[34m" },
|
|
288
|
+
// GitHub sky blue
|
|
289
|
+
chrome: { hex: "6E7681", ansiFallback: "\x1B[90m" },
|
|
290
|
+
success: { hex: "3FB950", ansiFallback: "\x1B[32m" },
|
|
291
|
+
warning: { hex: "D29922", ansiFallback: "\x1B[33m" },
|
|
292
|
+
info: { hex: "58A6FF", ansiFallback: "\x1B[36m" },
|
|
293
|
+
danger: { hex: "FF7B72", ansiFallback: "\x1B[31m" },
|
|
294
|
+
muted: { hex: "B1BAC4", ansiFallback: "\x1B[90m" }
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
wave: {
|
|
298
|
+
light: {
|
|
299
|
+
accent: { hex: "4D699B", ansiFallback: "\x1B[34m" },
|
|
300
|
+
// ink blue
|
|
301
|
+
chrome: { hex: "9E9B8E", ansiFallback: "\x1B[90m" },
|
|
302
|
+
success: { hex: "6F894E", ansiFallback: "\x1B[32m" },
|
|
303
|
+
warning: { hex: "836F4A", ansiFallback: "\x1B[33m" },
|
|
304
|
+
info: { hex: "4D699B", ansiFallback: "\x1B[36m" },
|
|
305
|
+
danger: { hex: "C84053", ansiFallback: "\x1B[31m" },
|
|
306
|
+
muted: { hex: "716E61", ansiFallback: "\x1B[90m" }
|
|
307
|
+
},
|
|
308
|
+
dark: {
|
|
309
|
+
accent: { hex: "7E9CD8", ansiFallback: "\x1B[34m" },
|
|
310
|
+
// crystalBlue
|
|
311
|
+
chrome: { hex: "727169", ansiFallback: "\x1B[90m" },
|
|
312
|
+
success: { hex: "98BB6C", ansiFallback: "\x1B[32m" },
|
|
313
|
+
warning: { hex: "E6C384", ansiFallback: "\x1B[33m" },
|
|
314
|
+
info: { hex: "7FB4CA", ansiFallback: "\x1B[36m" },
|
|
315
|
+
danger: { hex: "E82424", ansiFallback: "\x1B[31m" },
|
|
316
|
+
muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
iris: {
|
|
320
|
+
light: {
|
|
321
|
+
accent: { hex: "5E6AD2", ansiFallback: "\x1B[34m" },
|
|
322
|
+
chrome: { hex: "9E9AB5", ansiFallback: "\x1B[90m" },
|
|
323
|
+
success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
|
|
324
|
+
warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
|
|
325
|
+
info: { hex: "5E6AD2", ansiFallback: "\x1B[36m" },
|
|
326
|
+
danger: { hex: "E5484D", ansiFallback: "\x1B[31m" },
|
|
327
|
+
muted: { hex: "6E6A8A", ansiFallback: "\x1B[90m" }
|
|
328
|
+
},
|
|
329
|
+
dark: {
|
|
330
|
+
accent: { hex: "8B9CF4", ansiFallback: "\x1B[34m" },
|
|
331
|
+
chrome: { hex: "5A5772", ansiFallback: "\x1B[90m" },
|
|
332
|
+
success: { hex: "26BD6C", ansiFallback: "\x1B[32m" },
|
|
333
|
+
warning: { hex: "F5A623", ansiFallback: "\x1B[33m" },
|
|
334
|
+
info: { hex: "8B9CF4", ansiFallback: "\x1B[36m" },
|
|
335
|
+
danger: { hex: "EC5B6E", ansiFallback: "\x1B[31m" },
|
|
336
|
+
muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
rose: {
|
|
340
|
+
light: {
|
|
341
|
+
accent: { hex: "D14D72", ansiFallback: "\x1B[35m" },
|
|
342
|
+
chrome: { hex: "9893A5", ansiFallback: "\x1B[90m" },
|
|
343
|
+
success: { hex: "56949F", ansiFallback: "\x1B[32m" },
|
|
344
|
+
warning: { hex: "EA9D34", ansiFallback: "\x1B[33m" },
|
|
345
|
+
info: { hex: "286983", ansiFallback: "\x1B[36m" },
|
|
346
|
+
danger: { hex: "B4637A", ansiFallback: "\x1B[31m" },
|
|
347
|
+
muted: { hex: "797593", ansiFallback: "\x1B[90m" }
|
|
348
|
+
},
|
|
349
|
+
dark: {
|
|
350
|
+
accent: { hex: "EB6F92", ansiFallback: "\x1B[35m" },
|
|
351
|
+
chrome: { hex: "6E6A86", ansiFallback: "\x1B[90m" },
|
|
352
|
+
success: { hex: "9CCFD8", ansiFallback: "\x1B[32m" },
|
|
353
|
+
warning: { hex: "F6C177", ansiFallback: "\x1B[33m" },
|
|
354
|
+
info: { hex: "C4A7E7", ansiFallback: "\x1B[36m" },
|
|
355
|
+
danger: { hex: "EB6F92", ansiFallback: "\x1B[31m" },
|
|
356
|
+
muted: { hex: "908CAA", ansiFallback: "\x1B[90m" }
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
mono: {
|
|
360
|
+
light: {
|
|
361
|
+
accent: { hex: "FF9500", ansiFallback: "\x1B[33m" },
|
|
362
|
+
chrome: { hex: "9DA6AF", ansiFallback: "\x1B[90m" },
|
|
363
|
+
success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
|
|
364
|
+
warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
|
|
365
|
+
info: { hex: "2563EB", ansiFallback: "\x1B[34m" },
|
|
366
|
+
danger: { hex: "DC2626", ansiFallback: "\x1B[31m" },
|
|
367
|
+
muted: { hex: "7B8793", ansiFallback: "\x1B[90m" }
|
|
368
|
+
},
|
|
369
|
+
dark: {
|
|
370
|
+
accent: { hex: "FFBF66", ansiFallback: "\x1B[33m" },
|
|
371
|
+
chrome: { hex: "7B8793", ansiFallback: "\x1B[90m" },
|
|
372
|
+
success: { hex: "4DE800", ansiFallback: "\x1B[32m" },
|
|
373
|
+
warning: { hex: "FCD34D", ansiFallback: "\x1B[33m" },
|
|
374
|
+
info: { hex: "60A5FA", ansiFallback: "\x1B[34m" },
|
|
375
|
+
danger: { hex: "F87171", ansiFallback: "\x1B[31m" },
|
|
376
|
+
muted: { hex: "C1C7CD", ansiFallback: "\x1B[90m" }
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
var activeThemeName = "catppuccin";
|
|
381
|
+
function getActiveThemeName() {
|
|
382
|
+
return activeThemeName;
|
|
383
|
+
}
|
|
384
|
+
function setActiveThemeName(name) {
|
|
385
|
+
if (THEME_PALETTES[name]) {
|
|
386
|
+
activeThemeName = name;
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
var activeBrightness = null;
|
|
392
|
+
function getActiveBrightness() {
|
|
393
|
+
return activeBrightness;
|
|
394
|
+
}
|
|
395
|
+
function setActiveBrightness(brightness) {
|
|
396
|
+
activeBrightness = brightness;
|
|
397
|
+
}
|
|
398
|
+
var activeDensity = "spacious";
|
|
399
|
+
function getActiveDensity() {
|
|
400
|
+
return activeDensity;
|
|
401
|
+
}
|
|
402
|
+
function setActiveDensity(density) {
|
|
403
|
+
activeDensity = density;
|
|
404
|
+
}
|
|
405
|
+
function supportsTruecolor(env = process.env) {
|
|
406
|
+
return /truecolor|24bit/i.test(env.COLORTERM ?? "");
|
|
407
|
+
}
|
|
408
|
+
function resolveTuiBrightness(env = process.env) {
|
|
409
|
+
const explicit = (env.NOLO_TUI_THEME ?? "").trim().toLowerCase();
|
|
410
|
+
if (explicit === "light") return "light";
|
|
411
|
+
if (explicit === "dark") return "dark";
|
|
412
|
+
if (activeBrightness) return activeBrightness;
|
|
413
|
+
const colorfgbg = env.COLORFGBG ?? "";
|
|
414
|
+
if (colorfgbg) {
|
|
415
|
+
const parts = colorfgbg.split(";");
|
|
416
|
+
const bg = parts.length > 1 ? Number.parseInt(parts[1] ?? "", 10) : NaN;
|
|
417
|
+
if (!Number.isNaN(bg) && bg >= 0 && bg <= 6) return "dark";
|
|
418
|
+
if (!Number.isNaN(bg) && bg >= 7 && bg <= 15) return "light";
|
|
419
|
+
}
|
|
420
|
+
return "dark";
|
|
421
|
+
}
|
|
422
|
+
function hexToSgr(hex) {
|
|
423
|
+
const r = Number.parseInt(hex.slice(0, 2), 16);
|
|
424
|
+
const g = Number.parseInt(hex.slice(2, 4), 16);
|
|
425
|
+
const b = Number.parseInt(hex.slice(4, 6), 16);
|
|
426
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
427
|
+
}
|
|
428
|
+
var SURFACE_HEX = {
|
|
429
|
+
trail: { light: "E4E8EC", dark: "313244" },
|
|
430
|
+
catppuccin: { light: "E8ECF0", dark: "161B22" },
|
|
431
|
+
wave: { light: "E5E1D6", dark: "2A2A37" },
|
|
432
|
+
iris: { light: "E9E8F2", dark: "2A2740" },
|
|
433
|
+
rose: { light: "F2EAE4", dark: "26233A" },
|
|
434
|
+
mono: { light: "E8EAED", dark: "2A2A2A" }
|
|
435
|
+
};
|
|
436
|
+
var TERMINAL_BASE = {
|
|
437
|
+
dark: "1E1E2E",
|
|
438
|
+
light: "FFFFFF"
|
|
439
|
+
};
|
|
440
|
+
function readHexChannels(hex) {
|
|
441
|
+
if (!/^[0-9A-F]{6}$/.test(hex)) return null;
|
|
442
|
+
return [
|
|
443
|
+
Number.parseInt(hex.slice(0, 2), 16),
|
|
444
|
+
Number.parseInt(hex.slice(2, 4), 16),
|
|
445
|
+
Number.parseInt(hex.slice(4, 6), 16)
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
function hexToBgSgr(hex) {
|
|
449
|
+
const channels = readHexChannels(hex.replace(/^#/, "").toUpperCase());
|
|
450
|
+
if (!channels) return "";
|
|
451
|
+
const [r, g, b] = channels;
|
|
452
|
+
return `\x1B[48;2;${r};${g};${b}m`;
|
|
453
|
+
}
|
|
454
|
+
function blendHex(fg, base, weight) {
|
|
455
|
+
const w = Math.min(1, Math.max(0, weight));
|
|
456
|
+
const normFg = fg.replace(/^#/, "").toUpperCase();
|
|
457
|
+
const normBase = base.replace(/^#/, "").toUpperCase();
|
|
458
|
+
if (w === 0) return normBase;
|
|
459
|
+
if (w === 1) return normFg;
|
|
460
|
+
if (!readHexChannels(normFg) || !readHexChannels(normBase)) return normBase;
|
|
461
|
+
const fgR = Number.parseInt(normFg.slice(0, 2), 16);
|
|
462
|
+
const fgG = Number.parseInt(normFg.slice(2, 4), 16);
|
|
463
|
+
const fgB = Number.parseInt(normFg.slice(4, 6), 16);
|
|
464
|
+
const baseR = Number.parseInt(normBase.slice(0, 2), 16);
|
|
465
|
+
const baseG = Number.parseInt(normBase.slice(2, 4), 16);
|
|
466
|
+
const baseB = Number.parseInt(normBase.slice(4, 6), 16);
|
|
467
|
+
const r = Math.round(baseR + (fgR - baseR) * w).toString(16).padStart(2, "0").toUpperCase();
|
|
468
|
+
const g = Math.round(baseG + (fgG - baseG) * w).toString(16).padStart(2, "0").toUpperCase();
|
|
469
|
+
const b = Math.round(baseB + (fgB - baseB) * w).toString(16).padStart(2, "0").toUpperCase();
|
|
470
|
+
return `${r}${g}${b}`;
|
|
471
|
+
}
|
|
472
|
+
function diffLineSequences(env = process.env, brightness = resolveTuiBrightness(env)) {
|
|
473
|
+
if (!supportsTruecolor(env)) return null;
|
|
474
|
+
const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
|
|
475
|
+
const successHex = palette[brightness].success.hex;
|
|
476
|
+
const dangerHex = palette[brightness].danger.hex;
|
|
477
|
+
const weight = brightness === "dark" ? 0.14 : 0.1;
|
|
478
|
+
const baseHex = TERMINAL_BASE[brightness];
|
|
479
|
+
const addedBgHex = blendHex(successHex, baseHex, weight);
|
|
480
|
+
const removedBgHex = blendHex(dangerHex, baseHex, weight);
|
|
481
|
+
return {
|
|
482
|
+
added: {
|
|
483
|
+
fg: hexToSgr(successHex),
|
|
484
|
+
bg: hexToBgSgr(addedBgHex)
|
|
485
|
+
},
|
|
486
|
+
removed: {
|
|
487
|
+
fg: hexToSgr(dangerHex),
|
|
488
|
+
bg: hexToBgSgr(removedBgHex)
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function surfaceBackgroundSequence(env = process.env, brightness = resolveTuiBrightness(env)) {
|
|
493
|
+
if (!supportsTruecolor(env)) return "";
|
|
494
|
+
return hexToBgSgr((SURFACE_HEX[activeThemeName] ?? SURFACE_HEX.trail)[brightness]);
|
|
495
|
+
}
|
|
496
|
+
function themeColorSequence(token, env = process.env, brightness = resolveTuiBrightness(env)) {
|
|
497
|
+
const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
|
|
498
|
+
const entry = palette[brightness][token];
|
|
499
|
+
return supportsTruecolor(env) ? hexToSgr(entry.hex) : entry.ansiFallback;
|
|
500
|
+
}
|
|
501
|
+
function themeText(text, token, colorEnabled = resolveCliColorEnabled(), env = process.env) {
|
|
502
|
+
if (!colorEnabled) return text;
|
|
503
|
+
return `${themeColorSequence(token, env)}${text}\x1B[39m`;
|
|
504
|
+
}
|
|
505
|
+
function renderDiffCodeBlock(code, env = process.env) {
|
|
506
|
+
const border = themeText("\u2502", "chrome", true, env);
|
|
507
|
+
const addedColor = themeColorSequence("success", env);
|
|
508
|
+
const deletedColor = themeColorSequence("danger", env);
|
|
509
|
+
const hunkColor = themeColorSequence("info", env);
|
|
510
|
+
const contextColor = themeColorSequence("chrome", env);
|
|
511
|
+
const reset = "\x1B[0m";
|
|
512
|
+
const lines = code.split("\n").map((line) => {
|
|
513
|
+
let color = contextColor;
|
|
514
|
+
if (line.startsWith("@@")) {
|
|
515
|
+
color = hunkColor;
|
|
516
|
+
} else if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
517
|
+
color = addedColor;
|
|
518
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
519
|
+
color = deletedColor;
|
|
520
|
+
}
|
|
521
|
+
return ` ${border} ${color}${line}${reset}`;
|
|
522
|
+
});
|
|
523
|
+
return ` ${themeText("\u250C\u2500\u2500\u2500", "chrome", true, env)}
|
|
524
|
+
${lines.join("\n")}
|
|
525
|
+
${themeText("\u2514\u2500\u2500\u2500", "chrome", true, env)}`;
|
|
526
|
+
}
|
|
527
|
+
function highlightMarkdown(text, colorEnabled = resolveCliColorEnabled(), env = process.env) {
|
|
528
|
+
if (!colorEnabled) return text;
|
|
529
|
+
let result = text;
|
|
530
|
+
result = result.replace(/```([a-zA-Z-]*)\n([\s\S]*?)\n```/g, (match, lang, code) => {
|
|
531
|
+
if (lang.toLowerCase() === "diff") {
|
|
532
|
+
return renderDiffCodeBlock(code, env);
|
|
533
|
+
}
|
|
534
|
+
const infoColor = themeColorSequence("info", env);
|
|
535
|
+
const reset2 = "\x1B[39m\x1B[22m";
|
|
536
|
+
const border = themeText("\u2502", "chrome", true, env);
|
|
537
|
+
const formattedCode = code.split("\n").map((line) => ` ${border} ${infoColor}${line}${reset2}`).join("\n");
|
|
538
|
+
return ` ${themeText("\u250C\u2500\u2500\u2500", "chrome", true, env)}
|
|
539
|
+
${formattedCode}
|
|
540
|
+
${themeText("\u2514\u2500\u2500\u2500", "chrome", true, env)}`;
|
|
541
|
+
});
|
|
542
|
+
result = result.replace(/^(#{1,3})\s+(.+)$/gm, (match, hashes, title) => {
|
|
543
|
+
const level = hashes.length;
|
|
544
|
+
const reset2 = "\x1B[0m";
|
|
545
|
+
if (level === 1) {
|
|
546
|
+
return `\x1B[1m\x1B[4m${themeColorSequence("accent", env)}${title}${reset2}`;
|
|
547
|
+
}
|
|
548
|
+
if (level === 2) {
|
|
549
|
+
return `\x1B[1m${themeColorSequence("warning", env)}${title}${reset2}`;
|
|
550
|
+
}
|
|
551
|
+
return `\x1B[1m${themeColorSequence("info", env)}${title}${reset2}`;
|
|
552
|
+
});
|
|
553
|
+
result = result.replace(
|
|
554
|
+
/^(进入 nolo-plan[^\n]*)$/gm,
|
|
555
|
+
(match, content) => `${themeColorSequence("chrome", env)}\x1B[2m${content}\x1B[0m`
|
|
556
|
+
);
|
|
557
|
+
result = result.replace(/^>\s?(.*)$/gm, (match, content) => {
|
|
558
|
+
const border = themeColorSequence("chrome", env);
|
|
559
|
+
return `${border}\u2502\x1B[39m \x1B[2m${content}\x1B[22m`;
|
|
560
|
+
});
|
|
561
|
+
result = result.replace(/\*\*([\s\S]*?)\*\*/g, "\x1B[1m$1\x1B[22m");
|
|
562
|
+
const mutedColor = themeColorSequence("muted", env);
|
|
563
|
+
const reset = "\x1B[39m";
|
|
564
|
+
result = result.replace(/`([^`\n]+)`/g, `${mutedColor}$1${reset}`);
|
|
565
|
+
result = result.replace(/(?<!\*)\*([^*\n]+?)\*(?!\*)/g, `\x1B[2m$1\x1B[22m`);
|
|
566
|
+
result = result.replace(/~~([^~\n]+?)~~/g, `\x1B[2m\x1B[9m$1\x1B[29m\x1B[22m`);
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// packages/cli/client/assistantOutput.ts
|
|
571
|
+
var STYLE = {
|
|
572
|
+
reset: "\x1B[0m",
|
|
573
|
+
bold: "\x1B[1m",
|
|
574
|
+
dim: "\x1B[2m"
|
|
575
|
+
};
|
|
576
|
+
function colorSeq(token, brightness) {
|
|
577
|
+
return themeColorSequence(token, process.env, brightness);
|
|
578
|
+
}
|
|
579
|
+
function splitTableCells(line) {
|
|
580
|
+
const trimmed = line.trim();
|
|
581
|
+
if (!trimmed.includes("|")) return [];
|
|
582
|
+
const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
583
|
+
return core.split("|").map((cell) => cell.trim()).filter(Boolean);
|
|
584
|
+
}
|
|
585
|
+
function isTableSeparator(line) {
|
|
586
|
+
const cells = splitTableCells(line);
|
|
587
|
+
return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
|
588
|
+
}
|
|
589
|
+
function isTableRow(line) {
|
|
590
|
+
const cells = splitTableCells(line);
|
|
591
|
+
return cells.length >= 2 && cells.some((cell) => cell.length > 0);
|
|
592
|
+
}
|
|
593
|
+
function isPipeWrappedTableRow(line) {
|
|
594
|
+
const trimmed = line.trim();
|
|
595
|
+
return trimmed.startsWith("|") && trimmed.endsWith("|") && isTableRow(line);
|
|
596
|
+
}
|
|
597
|
+
function isCodeFenceLine(line) {
|
|
598
|
+
return /^\s*```/.test(line);
|
|
599
|
+
}
|
|
600
|
+
function readFenceLanguage(line) {
|
|
601
|
+
const m = line.match(/^\s*```\s*([a-zA-Z0-9+#_-]*)/);
|
|
602
|
+
return m ? (m[1] ?? "").toLowerCase() : "";
|
|
603
|
+
}
|
|
604
|
+
function normalizeCodeLang(raw) {
|
|
605
|
+
switch (raw) {
|
|
606
|
+
case "js":
|
|
607
|
+
case "jsx":
|
|
608
|
+
case "ts":
|
|
609
|
+
case "tsx":
|
|
610
|
+
case "javascript":
|
|
611
|
+
case "typescript":
|
|
612
|
+
return "js";
|
|
613
|
+
case "py":
|
|
614
|
+
case "python":
|
|
615
|
+
return "py";
|
|
616
|
+
case "sh":
|
|
617
|
+
case "bash":
|
|
618
|
+
case "zsh":
|
|
619
|
+
case "shell":
|
|
620
|
+
case "console":
|
|
621
|
+
return "sh";
|
|
622
|
+
case "json":
|
|
623
|
+
return "json";
|
|
624
|
+
case "diff":
|
|
625
|
+
return "diff";
|
|
626
|
+
default:
|
|
627
|
+
return "unknown";
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
var KEYWORDS = {
|
|
631
|
+
js: /* @__PURE__ */ new Set([
|
|
632
|
+
"const",
|
|
633
|
+
"let",
|
|
634
|
+
"var",
|
|
635
|
+
"function",
|
|
636
|
+
"return",
|
|
637
|
+
"if",
|
|
638
|
+
"else",
|
|
639
|
+
"for",
|
|
640
|
+
"while",
|
|
641
|
+
"class",
|
|
642
|
+
"new",
|
|
643
|
+
"await",
|
|
644
|
+
"async",
|
|
645
|
+
"import",
|
|
646
|
+
"export",
|
|
647
|
+
"from",
|
|
648
|
+
"type",
|
|
649
|
+
"interface",
|
|
650
|
+
"extends",
|
|
651
|
+
"implements",
|
|
652
|
+
"null",
|
|
653
|
+
"undefined",
|
|
654
|
+
"true",
|
|
655
|
+
"false"
|
|
656
|
+
]),
|
|
657
|
+
py: /* @__PURE__ */ new Set([
|
|
658
|
+
"def",
|
|
659
|
+
"class",
|
|
660
|
+
"return",
|
|
661
|
+
"if",
|
|
662
|
+
"elif",
|
|
663
|
+
"else",
|
|
664
|
+
"for",
|
|
665
|
+
"while",
|
|
666
|
+
"import",
|
|
667
|
+
"from",
|
|
668
|
+
"as",
|
|
669
|
+
"with",
|
|
670
|
+
"try",
|
|
671
|
+
"except",
|
|
672
|
+
"finally",
|
|
673
|
+
"lambda",
|
|
674
|
+
"None",
|
|
675
|
+
"True",
|
|
676
|
+
"False",
|
|
677
|
+
"self"
|
|
678
|
+
]),
|
|
679
|
+
sh: /* @__PURE__ */ new Set([
|
|
680
|
+
"if",
|
|
681
|
+
"then",
|
|
682
|
+
"else",
|
|
683
|
+
"fi",
|
|
684
|
+
"for",
|
|
685
|
+
"do",
|
|
686
|
+
"done",
|
|
687
|
+
"while",
|
|
688
|
+
"case",
|
|
689
|
+
"esac",
|
|
690
|
+
"function",
|
|
691
|
+
"export",
|
|
692
|
+
"local",
|
|
693
|
+
"return",
|
|
694
|
+
"source",
|
|
695
|
+
"echo",
|
|
696
|
+
"cd"
|
|
697
|
+
]),
|
|
698
|
+
json: /* @__PURE__ */ new Set(["true", "false", "null"]),
|
|
699
|
+
diff: /* @__PURE__ */ new Set([])
|
|
700
|
+
};
|
|
701
|
+
function scanStringCommentRegions(line, lang) {
|
|
702
|
+
const regions = [];
|
|
703
|
+
const n = line.length;
|
|
704
|
+
let i = 0;
|
|
705
|
+
const commentMarkers = lang === "py" || lang === "sh" ? ["#"] : ["//"];
|
|
706
|
+
while (i < n) {
|
|
707
|
+
const ch = line[i];
|
|
708
|
+
if (ch === "'" || ch === '"' || lang === "js" && ch === "`") {
|
|
709
|
+
const quote = ch;
|
|
710
|
+
const start = i;
|
|
711
|
+
i += 1;
|
|
712
|
+
while (i < n) {
|
|
713
|
+
if (line[i] === "\\") {
|
|
714
|
+
i += 2;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (line[i] === quote) {
|
|
718
|
+
i += 1;
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
i += 1;
|
|
722
|
+
}
|
|
723
|
+
regions.push({ start, end: i, kind: "string" });
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
let matched = false;
|
|
727
|
+
for (const marker of commentMarkers) {
|
|
728
|
+
if (line.startsWith(marker, i)) {
|
|
729
|
+
regions.push({ start: i, end: n, kind: "comment" });
|
|
730
|
+
i = n;
|
|
731
|
+
matched = true;
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (matched) continue;
|
|
736
|
+
i += 1;
|
|
737
|
+
}
|
|
738
|
+
return regions;
|
|
739
|
+
}
|
|
740
|
+
function highlightCodeLine(line, lang, brightness) {
|
|
741
|
+
const info = colorSeq("info", brightness);
|
|
742
|
+
if (lang === "unknown") {
|
|
743
|
+
return `${info}${line}${STYLE.reset}`;
|
|
744
|
+
}
|
|
745
|
+
if (lang === "diff") {
|
|
746
|
+
const added = colorSeq("success", brightness);
|
|
747
|
+
const deleted = colorSeq("danger", brightness);
|
|
748
|
+
const hunk = colorSeq("info", brightness);
|
|
749
|
+
const context = colorSeq("chrome", brightness);
|
|
750
|
+
const reset2 = STYLE.reset;
|
|
751
|
+
let color = context;
|
|
752
|
+
if (line.startsWith("@@")) {
|
|
753
|
+
color = hunk;
|
|
754
|
+
} else if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
755
|
+
color = added;
|
|
756
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
757
|
+
color = deleted;
|
|
758
|
+
}
|
|
759
|
+
return `${color}${line}${reset2}`;
|
|
760
|
+
}
|
|
761
|
+
const regions = scanStringCommentRegions(line, lang);
|
|
762
|
+
const keywords = KEYWORDS[lang];
|
|
763
|
+
const accent = colorSeq("accent", brightness);
|
|
764
|
+
const success = colorSeq("success", brightness);
|
|
765
|
+
const warning = colorSeq("warning", brightness);
|
|
766
|
+
const chrome = colorSeq("chrome", brightness);
|
|
767
|
+
const dim = STYLE.dim;
|
|
768
|
+
const reset = STYLE.reset;
|
|
769
|
+
const out = [];
|
|
770
|
+
let cursor = 0;
|
|
771
|
+
for (const region of regions) {
|
|
772
|
+
if (region.start > cursor) {
|
|
773
|
+
out.push(emitPlainGap(line.slice(cursor, region.start), keywords, info, accent, warning, reset));
|
|
774
|
+
}
|
|
775
|
+
const text = line.slice(region.start, region.end);
|
|
776
|
+
if (region.kind === "string") {
|
|
777
|
+
out.push(`${success}${text}${reset}`);
|
|
778
|
+
} else {
|
|
779
|
+
out.push(`${chrome}${dim}${text}${reset}`);
|
|
780
|
+
}
|
|
781
|
+
cursor = region.end;
|
|
782
|
+
}
|
|
783
|
+
if (cursor < line.length) {
|
|
784
|
+
out.push(emitPlainGap(line.slice(cursor), keywords, info, accent, warning, reset));
|
|
785
|
+
}
|
|
786
|
+
return out.join("");
|
|
787
|
+
}
|
|
788
|
+
function emitPlainGap(text, keywords, info, accent, warning, reset) {
|
|
789
|
+
const parts = [];
|
|
790
|
+
const re = /([A-Za-z_]\w*)|(\d+(?:\.\d+)?)|([\s\S])/g;
|
|
791
|
+
let last = 0;
|
|
792
|
+
let m;
|
|
793
|
+
let started = false;
|
|
794
|
+
while ((m = re.exec(text)) !== null) {
|
|
795
|
+
if (!started) {
|
|
796
|
+
started = true;
|
|
797
|
+
}
|
|
798
|
+
if (m.index > last) {
|
|
799
|
+
parts.push(`${info}${text.slice(last, m.index)}`);
|
|
800
|
+
}
|
|
801
|
+
if (m[1] !== void 0) {
|
|
802
|
+
const word = m[1];
|
|
803
|
+
if (keywords.has(word)) {
|
|
804
|
+
parts.push(`${accent}${word}${reset}`);
|
|
805
|
+
} else {
|
|
806
|
+
parts.push(`${info}${word}`);
|
|
807
|
+
}
|
|
808
|
+
} else if (m[2] !== void 0) {
|
|
809
|
+
parts.push(`${warning}${m[2]}${reset}`);
|
|
810
|
+
} else {
|
|
811
|
+
parts.push(`${info}${m[3]}`);
|
|
812
|
+
}
|
|
813
|
+
last = re.lastIndex;
|
|
814
|
+
}
|
|
815
|
+
if (last < text.length) {
|
|
816
|
+
parts.push(`${info}${text.slice(last)}`);
|
|
817
|
+
}
|
|
818
|
+
return parts.join("");
|
|
819
|
+
}
|
|
820
|
+
function tableRowToBullet(line) {
|
|
821
|
+
const row = splitTableCells(line);
|
|
822
|
+
const label = row[0] ?? "";
|
|
823
|
+
const detail = row.slice(1).join(" \u2014 ").trim();
|
|
824
|
+
return detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`;
|
|
825
|
+
}
|
|
826
|
+
var UNORDERED_LIST_RE = /^(\s*)([-*+])\s+(.+)$/;
|
|
827
|
+
var ORDERED_LIST_RE = /^(\s*)(\d+)\.\s+(.+)$/;
|
|
828
|
+
var TASK_LIST_RE = /^(\s*)([-*+])\s+\[([ xX])\]\s+(.+)$/;
|
|
829
|
+
function normalizeListLine(line) {
|
|
830
|
+
const task = line.match(TASK_LIST_RE);
|
|
831
|
+
if (task) {
|
|
832
|
+
const checked = task[3] === "x" || task[3] === "X";
|
|
833
|
+
return `${task[1]}${checked ? "\u2611" : "\u2610"} ${task[4]}`;
|
|
834
|
+
}
|
|
835
|
+
const unordered = line.match(UNORDERED_LIST_RE);
|
|
836
|
+
if (unordered) {
|
|
837
|
+
return `${unordered[1]}\u2022 ${unordered[3]}`;
|
|
838
|
+
}
|
|
839
|
+
const ordered = line.match(ORDERED_LIST_RE);
|
|
840
|
+
if (ordered) {
|
|
841
|
+
return `${ordered[1]}${ordered[2]}. ${ordered[3]}`;
|
|
842
|
+
}
|
|
843
|
+
return line;
|
|
844
|
+
}
|
|
845
|
+
function convertMarkdownTablesForTerminal(text) {
|
|
846
|
+
const lines = text.split("\n");
|
|
847
|
+
const out = [];
|
|
848
|
+
let inFence = false;
|
|
849
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
850
|
+
const line = lines[index] ?? "";
|
|
851
|
+
if (isCodeFenceLine(line)) {
|
|
852
|
+
inFence = !inFence;
|
|
853
|
+
out.push(line);
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (inFence) {
|
|
857
|
+
out.push(line);
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
const next = lines[index + 1] ?? "";
|
|
861
|
+
if (isTableRow(line) && isTableSeparator(next)) {
|
|
862
|
+
index += 1;
|
|
863
|
+
while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
|
|
864
|
+
index += 1;
|
|
865
|
+
out.push(tableRowToBullet(lines[index] ?? ""));
|
|
866
|
+
}
|
|
867
|
+
if (out.length > 0 && out[out.length - 1] !== "") out.push("");
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (isPipeWrappedTableRow(line)) {
|
|
871
|
+
if (!isTableSeparator(line)) out.push(tableRowToBullet(line));
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
out.push(normalizeListLine(line));
|
|
875
|
+
}
|
|
876
|
+
return out.join("\n");
|
|
877
|
+
}
|
|
878
|
+
function polishAssistantStructure(text, options = {}) {
|
|
879
|
+
const converted = convertMarkdownTablesForTerminal(text).replace(/\r\n/g, "\n").replace(/\x00/g, "");
|
|
880
|
+
const lines = converted.split("\n");
|
|
881
|
+
let inFence = false;
|
|
882
|
+
const maskedLines = lines.map((line, index) => {
|
|
883
|
+
if (isCodeFenceLine(line)) {
|
|
884
|
+
inFence = !inFence;
|
|
885
|
+
return line;
|
|
886
|
+
}
|
|
887
|
+
if (inFence) {
|
|
888
|
+
return `\0F${index}\0`;
|
|
889
|
+
}
|
|
890
|
+
return line;
|
|
891
|
+
});
|
|
892
|
+
const afterHeading = maskedLines.join("\n").replace(/([^\n])\n(#{1,3} )/g, "$1\n\n$2").replace(/^(#{1,3} .+)\n(?!\n)/gm, "$1\n\n").replace(/\n{4,}/g, "\n\n\n");
|
|
893
|
+
const LIST_LIKE = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]/;
|
|
894
|
+
const headingLines = afterHeading.split("\n");
|
|
895
|
+
const breathed = [];
|
|
896
|
+
for (let i = 0; i < headingLines.length; i++) {
|
|
897
|
+
breathed.push(headingLines[i]);
|
|
898
|
+
const cur = headingLines[i];
|
|
899
|
+
const next = headingLines[i + 1];
|
|
900
|
+
if (cur === "" || next === void 0 || next === "") continue;
|
|
901
|
+
if (LIST_LIKE.test(cur) === LIST_LIKE.test(next)) continue;
|
|
902
|
+
breathed.push("");
|
|
903
|
+
}
|
|
904
|
+
const polishedMasked = breathed.join("\n");
|
|
905
|
+
const polished = polishedMasked.replace(
|
|
906
|
+
/\x00F(\d+)\x00/g,
|
|
907
|
+
(_, id) => lines[Number(id)] ?? ""
|
|
908
|
+
);
|
|
909
|
+
return options.trimEdges === false ? polished : polished.trim();
|
|
910
|
+
}
|
|
911
|
+
function renderMarkdownLink(match, text, url) {
|
|
912
|
+
const visible = `${text} (${url})`;
|
|
913
|
+
return `\x1B]8;;${url}\x1B\\${visible}\x1B]8;;\x1B\\`;
|
|
914
|
+
}
|
|
915
|
+
var MARKDOWN_LINK_RE = /\[([^\]]+)\]\(([^)\s]+)\)/g;
|
|
916
|
+
function styleInlineMarkdown(line, brightness) {
|
|
917
|
+
const inlineCode = colorSeq("muted", brightness);
|
|
918
|
+
const reset = STYLE.reset;
|
|
919
|
+
const bold = STYLE.bold;
|
|
920
|
+
const dim = STYLE.dim;
|
|
921
|
+
return line.replace(MARKDOWN_LINK_RE, (m, t2, u) => renderMarkdownLink(m, t2, u)).replace(/`([^`]+)`/g, `${inlineCode}$1${reset}`).replace(/\*\*(.+?)\*\*/g, `${bold}$1${reset}`).replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g, `${dim}$1${reset}`).replace(/~~([^~]+?)~~/g, `${dim}\x1B[9m$1\x1B[29m${reset}`);
|
|
922
|
+
}
|
|
923
|
+
function styleRichMarkdownLine(line, brightness) {
|
|
924
|
+
const heading = line.match(/^(#{1,3})\s+(.+)$/);
|
|
925
|
+
if (heading) {
|
|
926
|
+
const level = heading[1].length;
|
|
927
|
+
const title = heading[2];
|
|
928
|
+
if (level === 1) {
|
|
929
|
+
return `${STYLE.bold}\x1B[4m${colorSeq("accent", brightness)}${title}${STYLE.reset}`;
|
|
930
|
+
}
|
|
931
|
+
if (level === 2) {
|
|
932
|
+
return `${STYLE.bold}${colorSeq("warning", brightness)}${title}${STYLE.reset}`;
|
|
933
|
+
}
|
|
934
|
+
return `${STYLE.bold}${colorSeq("info", brightness)}${title}${STYLE.reset}`;
|
|
935
|
+
}
|
|
936
|
+
if (line.startsWith("\u8FDB\u5165 nolo-plan")) {
|
|
937
|
+
return `${colorSeq("chrome", brightness)}${STYLE.dim}${line}${STYLE.reset}`;
|
|
938
|
+
}
|
|
939
|
+
const blockquote = line.match(/^>\s?(.*)$/);
|
|
940
|
+
if (blockquote) {
|
|
941
|
+
const border = colorSeq("chrome", brightness);
|
|
942
|
+
const content = blockquote[1];
|
|
943
|
+
return `${border}\u2502${STYLE.reset} ${STYLE.dim}${content}${STYLE.reset}`;
|
|
944
|
+
}
|
|
945
|
+
if (/^---+$/.test(line.trim())) {
|
|
946
|
+
return `${colorSeq("chrome", brightness)}${"\u2500".repeat(Math.min(line.trim().length, 40))}${STYLE.reset}`;
|
|
947
|
+
}
|
|
948
|
+
const bullet = line.match(/^(\s*)(•)\s(.+)$/);
|
|
949
|
+
if (bullet) {
|
|
950
|
+
const styled = styleInlineMarkdown(bullet[3], brightness);
|
|
951
|
+
return `${bullet[1]}${colorSeq("accent", brightness)}\u2022${STYLE.reset} ${styled}`;
|
|
952
|
+
}
|
|
953
|
+
const ordered = line.match(/^(\s*)(\d+\.)\s(.+)$/);
|
|
954
|
+
if (ordered) {
|
|
955
|
+
const styled = styleInlineMarkdown(ordered[3], brightness);
|
|
956
|
+
return `${ordered[1]}${colorSeq("accent", brightness)}${ordered[2]}${STYLE.reset} ${styled}`;
|
|
957
|
+
}
|
|
958
|
+
const task = line.match(/^(\s*)(☐|☑)\s(.+)$/);
|
|
959
|
+
if (task) {
|
|
960
|
+
const styled = styleInlineMarkdown(task[3], brightness);
|
|
961
|
+
return `${task[1]}${colorSeq("accent", brightness)}${task[2]}${STYLE.reset} ${styled}`;
|
|
962
|
+
}
|
|
963
|
+
return styleInlineMarkdown(line, brightness);
|
|
964
|
+
}
|
|
965
|
+
function formatAssistantDisplay(text, options = {}) {
|
|
966
|
+
const brightness = resolveTuiBrightness();
|
|
967
|
+
const polished = polishAssistantStructure(text, options);
|
|
968
|
+
let inFence = false;
|
|
969
|
+
let fenceLang = "unknown";
|
|
970
|
+
return polished.split("\n").map((line) => {
|
|
971
|
+
if (isCodeFenceLine(line)) {
|
|
972
|
+
if (inFence) {
|
|
973
|
+
fenceLang = "unknown";
|
|
974
|
+
} else {
|
|
975
|
+
fenceLang = normalizeCodeLang(readFenceLanguage(line));
|
|
976
|
+
}
|
|
977
|
+
inFence = !inFence;
|
|
978
|
+
return `${STYLE.dim}${line}${STYLE.reset}`;
|
|
979
|
+
}
|
|
980
|
+
if (inFence) {
|
|
981
|
+
return highlightCodeLine(line, fenceLang, brightness);
|
|
982
|
+
}
|
|
983
|
+
return styleRichMarkdownLine(line, brightness);
|
|
984
|
+
}).join("\n");
|
|
985
|
+
}
|
|
986
|
+
function emitFormattedAssistantBlock(write, text, trailingNewline = false) {
|
|
987
|
+
if (!text) return;
|
|
988
|
+
write(formatAssistantDisplay(text, { trimEdges: false }));
|
|
989
|
+
if (trailingNewline) write("\n");
|
|
990
|
+
}
|
|
991
|
+
function classifyStreamLine(line) {
|
|
992
|
+
if (line === "") return "blank";
|
|
993
|
+
if (TASK_LIST_RE.test(line) || UNORDERED_LIST_RE.test(line) || ORDERED_LIST_RE.test(line) || /^\s*(?:•|☐|☑)\s/.test(line) || /^\s*[\u2460-\u2473]/.test(line)) {
|
|
994
|
+
return "list";
|
|
995
|
+
}
|
|
996
|
+
return "prose";
|
|
997
|
+
}
|
|
998
|
+
function needsListProseBreath(prev, next) {
|
|
999
|
+
if (prev === null || prev === "blank" || next === "blank") return false;
|
|
1000
|
+
const prevList = prev === "list";
|
|
1001
|
+
const nextList = next === "list";
|
|
1002
|
+
return prevList !== nextList;
|
|
1003
|
+
}
|
|
1004
|
+
function createRenderAwareStreamWriter(args) {
|
|
1005
|
+
const brightness = resolveTuiBrightness();
|
|
1006
|
+
let buffer = "";
|
|
1007
|
+
let inFence = false;
|
|
1008
|
+
let fenceLang = "unknown";
|
|
1009
|
+
let lastKind = null;
|
|
1010
|
+
const emitBreathIfNeeded = (nextKind) => {
|
|
1011
|
+
if (needsListProseBreath(lastKind, nextKind)) args.write("\n");
|
|
1012
|
+
};
|
|
1013
|
+
const flushCompleteBlocks = () => {
|
|
1014
|
+
while (buffer.includes("\n")) {
|
|
1015
|
+
const lines = buffer.split("\n");
|
|
1016
|
+
if (lines.length < 2) break;
|
|
1017
|
+
const firstLine = lines[0] ?? "";
|
|
1018
|
+
if (isCodeFenceLine(firstLine)) {
|
|
1019
|
+
if (inFence) {
|
|
1020
|
+
fenceLang = "unknown";
|
|
1021
|
+
} else {
|
|
1022
|
+
fenceLang = normalizeCodeLang(readFenceLanguage(firstLine));
|
|
1023
|
+
}
|
|
1024
|
+
inFence = !inFence;
|
|
1025
|
+
emitBreathIfNeeded("other");
|
|
1026
|
+
args.write(`${STYLE.dim}${firstLine}${STYLE.reset}
|
|
1027
|
+
`);
|
|
1028
|
+
lastKind = "other";
|
|
1029
|
+
buffer = lines.slice(1).join("\n");
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
if (inFence) {
|
|
1033
|
+
args.write(`${highlightCodeLine(firstLine, fenceLang, brightness)}
|
|
1034
|
+
`);
|
|
1035
|
+
lastKind = "other";
|
|
1036
|
+
buffer = lines.slice(1).join("\n");
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (isTableRow(firstLine)) {
|
|
1040
|
+
const nextLineComplete = lines.length > 2;
|
|
1041
|
+
if (!nextLineComplete) break;
|
|
1042
|
+
if (isTableSeparator(lines[1] ?? "")) {
|
|
1043
|
+
let end = 2;
|
|
1044
|
+
while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
|
|
1045
|
+
end += 1;
|
|
1046
|
+
}
|
|
1047
|
+
const tableComplete = end < lines.length - 1;
|
|
1048
|
+
if (!tableComplete) break;
|
|
1049
|
+
emitBreathIfNeeded("list");
|
|
1050
|
+
emitFormattedAssistantBlock(
|
|
1051
|
+
args.write,
|
|
1052
|
+
lines.slice(0, end).join("\n"),
|
|
1053
|
+
true
|
|
1054
|
+
);
|
|
1055
|
+
lastKind = "list";
|
|
1056
|
+
buffer = lines.slice(end).join("\n");
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const kind = classifyStreamLine(firstLine);
|
|
1061
|
+
emitBreathIfNeeded(kind);
|
|
1062
|
+
emitFormattedAssistantBlock(args.write, firstLine, true);
|
|
1063
|
+
lastKind = kind;
|
|
1064
|
+
buffer = lines.slice(1).join("\n");
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
return {
|
|
1068
|
+
push(chunk) {
|
|
1069
|
+
if (!chunk) return;
|
|
1070
|
+
buffer += chunk;
|
|
1071
|
+
flushCompleteBlocks();
|
|
1072
|
+
},
|
|
1073
|
+
flush() {
|
|
1074
|
+
if (!buffer) return;
|
|
1075
|
+
if (inFence) {
|
|
1076
|
+
args.write(buffer);
|
|
1077
|
+
} else {
|
|
1078
|
+
emitFormattedAssistantBlock(args.write, buffer);
|
|
1079
|
+
}
|
|
1080
|
+
buffer = "";
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// packages/cli/client/thinkingOutput.ts
|
|
1086
|
+
var THINK_OPEN = /<think>/i;
|
|
1087
|
+
var THINK_CLOSE = /<\/think>/i;
|
|
1088
|
+
var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
|
|
1089
|
+
function normalizeThinkingDisplayMode(raw, fallback = "hide") {
|
|
1090
|
+
const normalized = asTrimmedLowercaseString(raw);
|
|
1091
|
+
if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
|
|
1092
|
+
return "hide";
|
|
1093
|
+
}
|
|
1094
|
+
if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
|
|
1095
|
+
return "marker";
|
|
1096
|
+
}
|
|
1097
|
+
if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
|
|
1098
|
+
return "show";
|
|
1099
|
+
}
|
|
1100
|
+
return fallback;
|
|
1101
|
+
}
|
|
1102
|
+
function resolveThinkingDisplayMode(env = process.env) {
|
|
1103
|
+
return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
|
|
1104
|
+
}
|
|
1105
|
+
function collapseThinkingBlocks(text, mode = "hide") {
|
|
1106
|
+
if (mode === "show") return text;
|
|
1107
|
+
const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
|
|
1108
|
+
return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
|
|
1109
|
+
}
|
|
1110
|
+
function stripCollapsedThinkingMarkers(text) {
|
|
1111
|
+
return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
|
|
1112
|
+
}
|
|
1113
|
+
function formatAssistantTextForCli(text, mode = "hide") {
|
|
1114
|
+
const collapsed = collapseThinkingBlocks(text, mode);
|
|
1115
|
+
return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
|
|
1116
|
+
}
|
|
1117
|
+
function createThinkingEventSink(arg1, arg2) {
|
|
1118
|
+
let mode = "hide";
|
|
1119
|
+
let write = () => {
|
|
1120
|
+
};
|
|
1121
|
+
if (typeof arg1 === "function") {
|
|
1122
|
+
write = arg1;
|
|
1123
|
+
if (typeof arg2 === "string") {
|
|
1124
|
+
mode = arg2;
|
|
1125
|
+
}
|
|
1126
|
+
} else {
|
|
1127
|
+
mode = arg1;
|
|
1128
|
+
if (typeof arg2 === "function") {
|
|
1129
|
+
write = arg2;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (mode === "hide") {
|
|
1133
|
+
return {
|
|
1134
|
+
push(_chunk) {
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
if (mode === "show") {
|
|
1139
|
+
return {
|
|
1140
|
+
push(chunk) {
|
|
1141
|
+
if (chunk) write(chunk);
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
let markerEmitted = false;
|
|
1146
|
+
return {
|
|
1147
|
+
push(chunk) {
|
|
1148
|
+
if (!chunk || markerEmitted) return;
|
|
1149
|
+
write("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
|
|
1150
|
+
markerEmitted = true;
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function createThinkingAwareStreamFilter(write, mode = "hide") {
|
|
1155
|
+
if (mode === "show") {
|
|
1156
|
+
return {
|
|
1157
|
+
push(chunk) {
|
|
1158
|
+
write(chunk);
|
|
1159
|
+
},
|
|
1160
|
+
flush() {
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
let pending = "";
|
|
1165
|
+
let insideThink = false;
|
|
1166
|
+
let markerEmitted = false;
|
|
1167
|
+
const emit = (chunk) => {
|
|
1168
|
+
if (chunk) write(chunk);
|
|
1169
|
+
};
|
|
1170
|
+
const emitMarker = () => {
|
|
1171
|
+
if (mode !== "marker" || markerEmitted) return;
|
|
1172
|
+
emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
|
|
1173
|
+
markerEmitted = true;
|
|
1174
|
+
};
|
|
1175
|
+
const consume = (input) => {
|
|
1176
|
+
pending += input;
|
|
1177
|
+
while (pending.length > 0) {
|
|
1178
|
+
if (insideThink) {
|
|
1179
|
+
const closeMatch = pending.match(THINK_CLOSE);
|
|
1180
|
+
if (!closeMatch || closeMatch.index == null) {
|
|
1181
|
+
emitMarker();
|
|
1182
|
+
pending = "";
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
emitMarker();
|
|
1186
|
+
pending = pending.slice(closeMatch.index + closeMatch[0].length);
|
|
1187
|
+
insideThink = false;
|
|
1188
|
+
markerEmitted = false;
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
const openMatch = pending.match(THINK_OPEN);
|
|
1192
|
+
if (!openMatch || openMatch.index == null) {
|
|
1193
|
+
const nextMarker = pending.indexOf("<");
|
|
1194
|
+
if (nextMarker === -1) {
|
|
1195
|
+
const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
|
|
1196
|
+
emit(cleaned);
|
|
1197
|
+
pending = "";
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (nextMarker > 0) {
|
|
1201
|
+
const head = pending.slice(0, nextMarker);
|
|
1202
|
+
const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
|
|
1203
|
+
emit(cleaned);
|
|
1204
|
+
pending = pending.slice(nextMarker);
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (pending.length < 7) return;
|
|
1208
|
+
emit(pending[0]);
|
|
1209
|
+
pending = pending.slice(1);
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (openMatch.index > 0) {
|
|
1213
|
+
const head = pending.slice(0, openMatch.index);
|
|
1214
|
+
const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
|
|
1215
|
+
emit(cleaned);
|
|
1216
|
+
pending = pending.slice(openMatch.index);
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
pending = pending.slice(openMatch[0].length);
|
|
1220
|
+
insideThink = true;
|
|
1221
|
+
markerEmitted = false;
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
return {
|
|
1225
|
+
push(chunk) {
|
|
1226
|
+
consume(chunk);
|
|
1227
|
+
},
|
|
1228
|
+
flush() {
|
|
1229
|
+
if (insideThink) emitMarker();
|
|
1230
|
+
if (pending) {
|
|
1231
|
+
const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
|
|
1232
|
+
emit(cleaned);
|
|
1233
|
+
}
|
|
1234
|
+
pending = "";
|
|
1235
|
+
insideThink = false;
|
|
1236
|
+
markerEmitted = false;
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// packages/cli/client/formatReadPathTree.ts
|
|
1242
|
+
import { homedir as osHomedir } from "node:os";
|
|
1243
|
+
function clipPathAware(value, max = 72) {
|
|
1244
|
+
const compact = compactWhitespace(value);
|
|
1245
|
+
if (compact.length <= max) return compact;
|
|
1246
|
+
if (compact.includes(" ") || !compact.includes("/") && !compact.includes("\\")) {
|
|
1247
|
+
return clipCompactText(value, max, "\u2026");
|
|
1248
|
+
}
|
|
1249
|
+
const segments = compact.split(/[/\\]/);
|
|
1250
|
+
const filename = segments[segments.length - 1];
|
|
1251
|
+
if (filename.length >= max) {
|
|
1252
|
+
return clipCompactText(value, max, "\u2026");
|
|
1253
|
+
}
|
|
1254
|
+
const ELISION = "/\u2026/";
|
|
1255
|
+
let prefix = "";
|
|
1256
|
+
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
1257
|
+
const candidate = segments.slice(0, i + 1).join("/");
|
|
1258
|
+
if (candidate.length + ELISION.length + filename.length > max) break;
|
|
1259
|
+
prefix = candidate;
|
|
1260
|
+
}
|
|
1261
|
+
if (prefix) return `${prefix}${ELISION}${filename}`;
|
|
1262
|
+
const filenameOnly = `\u2026/${filename}`;
|
|
1263
|
+
if (filenameOnly.length <= max) return filenameOnly;
|
|
1264
|
+
return clipCompactText(value, max, "\u2026");
|
|
1265
|
+
}
|
|
1266
|
+
function formatHomePath(path, customHomedir) {
|
|
1267
|
+
if (!path) return "";
|
|
1268
|
+
const home = customHomedir || (typeof process !== "undefined" ? process.env?.HOME : void 0) || (typeof osHomedir === "function" ? osHomedir() : "");
|
|
1269
|
+
if (home && path.startsWith(home)) {
|
|
1270
|
+
return `~${path.slice(home.length)}`;
|
|
1271
|
+
}
|
|
1272
|
+
return path.replace(/^(\/Users\/[^\/]+\/|\/home\/[^\/]+\/)/, "~/");
|
|
1273
|
+
}
|
|
1274
|
+
function formatReadItemPath(rawPath, metadata, customHomedir) {
|
|
1275
|
+
const inputPath = rawPath || (typeof metadata?.path === "string" ? metadata.path : void 0) || (typeof metadata?.filePath === "string" ? metadata.filePath : void 0) || "file";
|
|
1276
|
+
let formattedPath = formatHomePath(inputPath, customHomedir);
|
|
1277
|
+
if (!formattedPath) return "file";
|
|
1278
|
+
let rangePart = "";
|
|
1279
|
+
const match = formattedPath.match(/(:[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)$/);
|
|
1280
|
+
if (match) {
|
|
1281
|
+
rangePart = match[1];
|
|
1282
|
+
formattedPath = formattedPath.slice(0, -rangePart.length);
|
|
1283
|
+
} else if (metadata && typeof metadata.startLine === "number" && typeof metadata.endLine === "number" && typeof metadata.totalLines === "number") {
|
|
1284
|
+
const { startLine, endLine, totalLines, truncated } = metadata;
|
|
1285
|
+
if (truncated || startLine !== 1 || endLine !== totalLines) {
|
|
1286
|
+
rangePart = `:${startLine}-${endLine}`;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const clippedPath = clipPathAware(formattedPath, 72);
|
|
1290
|
+
return `${clippedPath}${rangePart}`;
|
|
1291
|
+
}
|
|
1292
|
+
function formatReadTreeLines(items, customHomedir) {
|
|
1293
|
+
const count = items.length;
|
|
1294
|
+
const header = `Read (${count})`;
|
|
1295
|
+
const lines = items.map((item, index) => {
|
|
1296
|
+
const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1297
|
+
const pathWithRange = formatReadItemPath(item.path, item.metadata, customHomedir);
|
|
1298
|
+
return { connector, pathWithRange };
|
|
1299
|
+
});
|
|
1300
|
+
return { header, count, lines };
|
|
1301
|
+
}
|
|
1302
|
+
function formatSearchItemQuery(query, path, customHomedir) {
|
|
1303
|
+
let text = (query || "").trim();
|
|
1304
|
+
if (path) {
|
|
1305
|
+
const formattedPath = formatHomePath(path, customHomedir);
|
|
1306
|
+
if (formattedPath) {
|
|
1307
|
+
text = text ? `${text} in ${formattedPath}` : formattedPath;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (!text) return "search";
|
|
1311
|
+
return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
|
|
1312
|
+
}
|
|
1313
|
+
function formatSearchTreeLines(items, customHomedir) {
|
|
1314
|
+
const count = items.length;
|
|
1315
|
+
const header = `Search (${count})`;
|
|
1316
|
+
const lines = items.map((item, index) => {
|
|
1317
|
+
const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1318
|
+
const queryText = formatSearchItemQuery(item.query, item.path, customHomedir);
|
|
1319
|
+
return { connector, queryText };
|
|
1320
|
+
});
|
|
1321
|
+
return { header, count, lines };
|
|
1322
|
+
}
|
|
1323
|
+
function formatRunItemCommand(command, exitCode, timedOut) {
|
|
1324
|
+
let text = compactWhitespace(command || "");
|
|
1325
|
+
if (!text) text = "command";
|
|
1326
|
+
text = text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
|
|
1327
|
+
if (timedOut) return `${text} (timed out)`;
|
|
1328
|
+
if (typeof exitCode === "number" && exitCode !== 0) return `${text} (exit ${exitCode})`;
|
|
1329
|
+
return text;
|
|
1330
|
+
}
|
|
1331
|
+
function formatRunTreeLines(items) {
|
|
1332
|
+
const count = items.length;
|
|
1333
|
+
const header = `Run (${count})`;
|
|
1334
|
+
const lines = items.map((item, index) => {
|
|
1335
|
+
const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1336
|
+
const commandText = formatRunItemCommand(item.command, item.exitCode, item.timedOut);
|
|
1337
|
+
return { connector, commandText };
|
|
1338
|
+
});
|
|
1339
|
+
return { header, count, lines };
|
|
1340
|
+
}
|
|
1341
|
+
function formatFetchItemUrl(url) {
|
|
1342
|
+
const text = (url || "").trim();
|
|
1343
|
+
if (!text) return "webpage";
|
|
1344
|
+
return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
|
|
1345
|
+
}
|
|
1346
|
+
function formatFetchTreeLines(items) {
|
|
1347
|
+
const count = items.length;
|
|
1348
|
+
const header = `Fetch (${count})`;
|
|
1349
|
+
const lines = items.map((item, index) => {
|
|
1350
|
+
const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1351
|
+
const urlText = formatFetchItemUrl(item.url);
|
|
1352
|
+
return { connector, urlText };
|
|
1353
|
+
});
|
|
1354
|
+
return { header, count, lines };
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// packages/cli/tui/i18n.ts
|
|
1358
|
+
var ZH_PATTERNS = /^zh/i;
|
|
1359
|
+
function parseCliLocale(raw) {
|
|
1360
|
+
const normalized = raw?.trim().toLowerCase();
|
|
1361
|
+
if (!normalized) return null;
|
|
1362
|
+
if (ZH_PATTERNS.test(normalized)) return "zh";
|
|
1363
|
+
if (normalized === "en" || normalized.startsWith("en")) return "en";
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
function detectLocaleFromEnv(env) {
|
|
1367
|
+
const explicit = parseCliLocale(env.NOLO_LANG);
|
|
1368
|
+
if (explicit) return explicit;
|
|
1369
|
+
const candidates = [env.LC_ALL, env.LC_CTYPE, env.LANG].filter(Boolean);
|
|
1370
|
+
for (const candidate of candidates) {
|
|
1371
|
+
if (ZH_PATTERNS.test(candidate)) return "zh";
|
|
1372
|
+
if (candidate && !candidate.startsWith("C.") && !candidate.startsWith("POSIX")) {
|
|
1373
|
+
return "en";
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
function detectLocale(env) {
|
|
1379
|
+
return detectLocaleFromEnv(env) ?? "zh";
|
|
1380
|
+
}
|
|
1381
|
+
var currentLocale = detectLocale(process.env);
|
|
1382
|
+
function getCliLocale() {
|
|
1383
|
+
return currentLocale;
|
|
1384
|
+
}
|
|
1385
|
+
function setCliLocale(locale) {
|
|
1386
|
+
currentLocale = locale;
|
|
1387
|
+
}
|
|
1388
|
+
function initCliLocale(env = process.env) {
|
|
1389
|
+
currentLocale = detectLocale(env);
|
|
1390
|
+
}
|
|
1391
|
+
var STRINGS = {
|
|
1392
|
+
welcomeHint: {
|
|
1393
|
+
en: "Tell nolo what you want. Use /help for commands. Shift+Enter for newline.",
|
|
1394
|
+
zh: "\u544A\u8BC9 nolo \u4F60\u60F3\u8981\u4EC0\u4E48\u3002\u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\u3002Shift+Enter \u6362\u884C\u3002"
|
|
1395
|
+
},
|
|
1396
|
+
promptLabel: {
|
|
1397
|
+
en: "\u276F ",
|
|
1398
|
+
zh: "\u276F "
|
|
1399
|
+
},
|
|
1400
|
+
continueLabel: {
|
|
1401
|
+
en: "\u2502 ",
|
|
1402
|
+
zh: "\u2502 "
|
|
1403
|
+
},
|
|
1404
|
+
placeholder: {
|
|
1405
|
+
en: "Type a message or / for commands...",
|
|
1406
|
+
zh: "\u8F93\u5165\u6D88\u606F\uFF0C\u6216\u7528 / \u67E5\u770B\u547D\u4EE4\u2026"
|
|
1407
|
+
},
|
|
1408
|
+
newDialog: {
|
|
1409
|
+
en: "new dialog",
|
|
1410
|
+
zh: "\u65B0\u5BF9\u8BDD"
|
|
1411
|
+
},
|
|
1412
|
+
startedFreshDialog: {
|
|
1413
|
+
en: "Started a fresh dialog.",
|
|
1414
|
+
zh: "\u5DF2\u5F00\u59CB\u65B0\u5BF9\u8BDD\u3002"
|
|
1415
|
+
},
|
|
1416
|
+
bye: {
|
|
1417
|
+
en: "Bye.",
|
|
1418
|
+
zh: "\u518D\u89C1\u3002"
|
|
1419
|
+
},
|
|
1420
|
+
resumedDialogPrefix: {
|
|
1421
|
+
en: "Resumed dialog",
|
|
1422
|
+
zh: "\u5DF2\u6062\u590D\u5BF9\u8BDD"
|
|
1423
|
+
},
|
|
1424
|
+
resumeInvalidId: {
|
|
1425
|
+
en: "does not look like a dialog id. Use /history to pick one.",
|
|
1426
|
+
zh: "\u770B\u8D77\u6765\u4E0D\u662F dialog id\u3002\u7528 /history \u4ECE\u5217\u8868\u91CC\u9009\u4E00\u4E2A\u3002"
|
|
1427
|
+
},
|
|
1428
|
+
dialogResumeCancelled: {
|
|
1429
|
+
en: "Dialog resume cancelled.",
|
|
1430
|
+
zh: "\u5DF2\u53D6\u6D88\u6062\u590D\u5BF9\u8BDD\u3002"
|
|
1431
|
+
},
|
|
1432
|
+
agentSwitchCancelled: {
|
|
1433
|
+
en: "Agent switch cancelled.",
|
|
1434
|
+
zh: "\u5DF2\u53D6\u6D88\u5207\u6362 agent\u3002"
|
|
1435
|
+
},
|
|
1436
|
+
historyPickerTitle: {
|
|
1437
|
+
en: "Resume dialog (\u2191\u2193 Enter Esc)",
|
|
1438
|
+
zh: "\u6062\u590D\u5BF9\u8BDD\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u9009\u62E9 \xB7 Esc \u53D6\u6D88\uFF09"
|
|
1439
|
+
},
|
|
1440
|
+
noDialogsYet: {
|
|
1441
|
+
en: "No dialogs yet.",
|
|
1442
|
+
zh: "\u8FD8\u6CA1\u6709\u5386\u53F2\u5BF9\u8BDD\u3002"
|
|
1443
|
+
},
|
|
1444
|
+
langUsage: {
|
|
1445
|
+
en: "Usage: /lang <zh|en>",
|
|
1446
|
+
zh: "\u7528\u6CD5\uFF1A/lang <zh|en>"
|
|
1447
|
+
},
|
|
1448
|
+
stopHint: {
|
|
1449
|
+
en: "Esc to stop",
|
|
1450
|
+
zh: "Esc \u505C\u6B62\u56DE\u590D"
|
|
1451
|
+
},
|
|
1452
|
+
queuedHint: {
|
|
1453
|
+
en: "queued",
|
|
1454
|
+
zh: "\u6392\u961F"
|
|
1455
|
+
},
|
|
1456
|
+
turnStopped: {
|
|
1457
|
+
en: "Stopped this reply.",
|
|
1458
|
+
zh: "\u5DF2\u505C\u6B62\u672C\u6B21\u56DE\u590D\u3002"
|
|
1459
|
+
},
|
|
1460
|
+
turnFailed: {
|
|
1461
|
+
en: "This reply failed. Queued messages are kept \u2014 press Enter to resend.",
|
|
1462
|
+
zh: "\u672C\u6B21\u56DE\u590D\u51FA\u9519\u3002\u5DF2\u6392\u961F\u7684\u6D88\u606F\u4ECD\u4FDD\u7559\uFF0C\u6309 Enter \u53EF\u91CD\u65B0\u53D1\u9001\u3002"
|
|
1463
|
+
},
|
|
1464
|
+
quotaExhaustedHint: {
|
|
1465
|
+
en: "[nolo] This agent seems to have hit a quota/rate limit (HTTP 429).\nUse /agent to switch to another agent and keep going \u2014 the dialog context is preserved.",
|
|
1466
|
+
zh: "[nolo] \u5F53\u524D agent \u4F3C\u4E4E\u989D\u5EA6/\u901F\u7387\u53D7\u9650\uFF08429\uFF09\u3002\n\u53EF\u4EE5\u7528 /agent \u5207\u6362\u5230\u5176\u4ED6 agent \u540E\u7EE7\u7EED\uFF0C\u540C\u4E00\u5BF9\u8BDD\u4F1A\u4FDD\u7559\u4E0A\u4E0B\u6587\u3002"
|
|
1467
|
+
},
|
|
1468
|
+
copiedLastReply: {
|
|
1469
|
+
en: "Copied the last reply to the clipboard.",
|
|
1470
|
+
zh: "\u5DF2\u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F\u3002"
|
|
1471
|
+
},
|
|
1472
|
+
copyNothing: {
|
|
1473
|
+
en: "Nothing to copy yet.",
|
|
1474
|
+
zh: "\u8FD8\u6CA1\u6709\u53EF\u590D\u5236\u7684\u5185\u5BB9\u3002"
|
|
1475
|
+
},
|
|
1476
|
+
copyFailed: {
|
|
1477
|
+
en: "Copy failed",
|
|
1478
|
+
zh: "\u590D\u5236\u5931\u8D25"
|
|
1479
|
+
},
|
|
1480
|
+
copyUnavailable: {
|
|
1481
|
+
en: "Clipboard is unavailable in this environment. The last reply was printed above for manual copy.",
|
|
1482
|
+
zh: "\u5F53\u524D\u73AF\u5883\u6CA1\u6709\u53EF\u7528\u7684\u526A\u8D34\u677F\u3002\u6700\u540E\u4E00\u6761\u56DE\u590D\u5DF2\u6253\u5370\u5728\u4E0A\u65B9\uFF0C\u53EF\u624B\u52A8\u590D\u5236\u3002"
|
|
1483
|
+
},
|
|
1484
|
+
copyUsage: {
|
|
1485
|
+
en: "Usage: /copy or /copy view",
|
|
1486
|
+
zh: "\u7528\u6CD5\uFF1A/copy \u6216 /copy view"
|
|
1487
|
+
},
|
|
1488
|
+
copyViewTitle: {
|
|
1489
|
+
en: "Copy view \u2014 latest AI reply",
|
|
1490
|
+
zh: "\u590D\u5236\u89C6\u56FE \u2014 \u6700\u65B0 AI \u56DE\u590D"
|
|
1491
|
+
},
|
|
1492
|
+
copyViewHint: {
|
|
1493
|
+
en: "Drag to select; edge scrolling uses native terminal scrollback. Press Esc or Enter to return.",
|
|
1494
|
+
zh: "\u76F4\u63A5\u62D6\u9009\uFF0C\u62D6\u5230\u8FB9\u7F18\u53EF\u7528\u7EC8\u7AEF\u539F\u751F\u56DE\u6EDA\u8DE8\u5C4F\u9009\u62E9\u3002\u6309 Esc \u6216 Enter \u8FD4\u56DE\u3002"
|
|
1495
|
+
},
|
|
1496
|
+
historyNoToken: {
|
|
1497
|
+
en: "History requires an auth token. Run `nolo login` or set AUTH_TOKEN.",
|
|
1498
|
+
zh: "\u67E5\u770B\u5386\u53F2\u5BF9\u8BDD\u9700\u8981\u767B\u5F55\u51ED\u8BC1\u3002\u8BF7\u8FD0\u884C `nolo login` \u6216\u8BBE\u7F6E AUTH_TOKEN\u3002"
|
|
1499
|
+
},
|
|
1500
|
+
historyBadToken: {
|
|
1501
|
+
en: "Could not read a user id from AUTH_TOKEN. Run `nolo login` again.",
|
|
1502
|
+
zh: "\u65E0\u6CD5\u4ECE AUTH_TOKEN \u89E3\u6790\u51FA\u7528\u6237 id\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C `nolo login`\u3002"
|
|
1503
|
+
},
|
|
1504
|
+
mouseOn: {
|
|
1505
|
+
en: "Mouse mode on: wheel scrolls the transcript; hold Shift (or Option/Fn) to select text.",
|
|
1506
|
+
zh: "\u9F20\u6807\u6A21\u5F0F\u5DF2\u5F00\u542F\uFF1A\u6EDA\u8F6E\u6EDA\u52A8\u5BF9\u8BDD\u8BB0\u5F55\uFF1B\u6309\u4F4F Shift\uFF08\u6216 Option/Fn\uFF09\u53EF\u62D6\u9009\u590D\u5236\u3002"
|
|
1507
|
+
},
|
|
1508
|
+
mouseOff: {
|
|
1509
|
+
en: "Mouse mode off: drag to select/copy freely; scroll with PageUp/PageDown.",
|
|
1510
|
+
zh: "\u9F20\u6807\u6A21\u5F0F\u5DF2\u5173\u95ED\uFF1A\u53EF\u76F4\u63A5\u62D6\u9009\u590D\u5236\uFF1B\u7528 PageUp/PageDown \u6EDA\u52A8\u5BF9\u8BDD\u8BB0\u5F55\u3002"
|
|
1511
|
+
},
|
|
1512
|
+
mouseUsage: {
|
|
1513
|
+
en: "Usage: /mouse <on|off>",
|
|
1514
|
+
zh: "\u7528\u6CD5\uFF1A/mouse <on|off>"
|
|
1515
|
+
},
|
|
1516
|
+
langSwitched: {
|
|
1517
|
+
en: "Language switched to English.",
|
|
1518
|
+
zh: "\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587\u3002"
|
|
1519
|
+
},
|
|
1520
|
+
// --- Tool trace copy ------------------------------------------------------
|
|
1521
|
+
// The compact trace shows only status, never timing or output size: a line
|
|
1522
|
+
// count told the user nothing actionable and the ms figure read as noise.
|
|
1523
|
+
toolNeedsAction: {
|
|
1524
|
+
en: "needs action",
|
|
1525
|
+
zh: "\u5F85\u786E\u8BA4"
|
|
1526
|
+
},
|
|
1527
|
+
toolTimedOut: {
|
|
1528
|
+
en: "timed out",
|
|
1529
|
+
zh: "\u5DF2\u8D85\u65F6"
|
|
1530
|
+
},
|
|
1531
|
+
toolExitCode: {
|
|
1532
|
+
en: "exit",
|
|
1533
|
+
zh: "\u9000\u51FA\u7801"
|
|
1534
|
+
},
|
|
1535
|
+
toolFailed: {
|
|
1536
|
+
en: "failed",
|
|
1537
|
+
zh: "\u5931\u8D25"
|
|
1538
|
+
},
|
|
1539
|
+
// --- Dialog (picker / confirm) copy --------------------------------------
|
|
1540
|
+
// Key-hint wording is unified across select / multi-select / confirm so the
|
|
1541
|
+
// three dialogs read as one family: "<Label> <↑↓ move · Enter choose ·
|
|
1542
|
+
// Esc cancel> <count>". Connectors are "·" between keys, two spaces between
|
|
1543
|
+
// the label, hint, and count. zh uses full-width parentheses to match the
|
|
1544
|
+
// existing historyPickerTitle; en uses ASCII parentheses.
|
|
1545
|
+
dialogSelectLabel: {
|
|
1546
|
+
en: "Select",
|
|
1547
|
+
zh: "\u9009\u62E9"
|
|
1548
|
+
},
|
|
1549
|
+
dialogSelectHint: {
|
|
1550
|
+
en: "(\u2191\u2193 move \xB7 Enter choose \xB7 Esc cancel)",
|
|
1551
|
+
zh: "\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u9009\u62E9 \xB7 Esc \u53D6\u6D88\uFF09"
|
|
1552
|
+
},
|
|
1553
|
+
dialogMultiSelectLabel: {
|
|
1554
|
+
en: "Select",
|
|
1555
|
+
zh: "\u9009\u62E9"
|
|
1556
|
+
},
|
|
1557
|
+
dialogMultiSelectHint: {
|
|
1558
|
+
en: "(\u2191\u2193 move \xB7 Space toggle \xB7 Enter submit \xB7 Esc cancel)",
|
|
1559
|
+
zh: "\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Space \u5207\u6362 \xB7 Enter \u63D0\u4EA4 \xB7 Esc \u53D6\u6D88\uFF09"
|
|
1560
|
+
},
|
|
1561
|
+
dialogMultiSelectSelected: {
|
|
1562
|
+
en: "selected",
|
|
1563
|
+
zh: "\u5DF2\u9009"
|
|
1564
|
+
},
|
|
1565
|
+
dialogConfirmHint: {
|
|
1566
|
+
en: "(\u2191\u2193 move \xB7 Enter choose \xB7 Esc cancel)",
|
|
1567
|
+
zh: "\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u9009\u62E9 \xB7 Esc \u53D6\u6D88\uFF09"
|
|
1568
|
+
},
|
|
1569
|
+
dialogConfirmTitle: {
|
|
1570
|
+
en: "Confirm destructive shell command",
|
|
1571
|
+
zh: "\u786E\u8BA4\u6267\u884C\u7834\u574F\u6027 shell \u547D\u4EE4"
|
|
1572
|
+
},
|
|
1573
|
+
dialogConfirmBody: {
|
|
1574
|
+
en: "This command may delete or reset user content and needs explicit confirmation before it runs.",
|
|
1575
|
+
zh: "\u8BE5\u547D\u4EE4\u53EF\u80FD\u5220\u9664\u6216\u91CD\u7F6E\u7528\u6237\u5185\u5BB9\uFF0C\u9700\u8981\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u3002"
|
|
1576
|
+
},
|
|
1577
|
+
dialogConfirmCommandLabel: {
|
|
1578
|
+
en: "Command",
|
|
1579
|
+
zh: "\u547D\u4EE4"
|
|
1580
|
+
},
|
|
1581
|
+
dialogConfirmCommandTruncated: {
|
|
1582
|
+
en: "(truncated)",
|
|
1583
|
+
zh: "\uFF08\u5DF2\u622A\u65AD\uFF09"
|
|
1584
|
+
},
|
|
1585
|
+
dialogConfirmAllowLabel: {
|
|
1586
|
+
en: "Allow",
|
|
1587
|
+
zh: "\u5141\u8BB8"
|
|
1588
|
+
},
|
|
1589
|
+
dialogConfirmAllowDetail: {
|
|
1590
|
+
en: "execute this time",
|
|
1591
|
+
zh: "\u672C\u6B21\u6267\u884C"
|
|
1592
|
+
},
|
|
1593
|
+
dialogConfirmCancelLabel: {
|
|
1594
|
+
en: "Cancel",
|
|
1595
|
+
zh: "\u53D6\u6D88"
|
|
1596
|
+
},
|
|
1597
|
+
dialogConfirmCancelDetail: {
|
|
1598
|
+
en: "abort the operation",
|
|
1599
|
+
zh: "\u4E2D\u6B62\u64CD\u4F5C"
|
|
1600
|
+
},
|
|
1601
|
+
contextTitle: {
|
|
1602
|
+
en: "Workspace context",
|
|
1603
|
+
zh: "\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587"
|
|
1604
|
+
},
|
|
1605
|
+
contextNext: {
|
|
1606
|
+
en: "Next:",
|
|
1607
|
+
zh: "\u4E0B\u4E00\u6B65\uFF1A"
|
|
1608
|
+
},
|
|
1609
|
+
contextFieldAgent: {
|
|
1610
|
+
en: "agent",
|
|
1611
|
+
zh: "\u667A\u80FD\u4F53"
|
|
1612
|
+
},
|
|
1613
|
+
contextFieldTokens: {
|
|
1614
|
+
en: "tokens",
|
|
1615
|
+
zh: "\u4EE4\u724C"
|
|
1616
|
+
},
|
|
1617
|
+
contextFieldDialog: {
|
|
1618
|
+
en: "dialog",
|
|
1619
|
+
zh: "\u5BF9\u8BDD"
|
|
1620
|
+
},
|
|
1621
|
+
contextFieldDocs: {
|
|
1622
|
+
en: "docs",
|
|
1623
|
+
zh: "\u6587\u6863"
|
|
1624
|
+
},
|
|
1625
|
+
contextFieldSkills: {
|
|
1626
|
+
en: "skills",
|
|
1627
|
+
zh: "\u6280\u80FD"
|
|
1628
|
+
},
|
|
1629
|
+
contextFieldProfile: {
|
|
1630
|
+
en: "profile",
|
|
1631
|
+
zh: "\u914D\u7F6E"
|
|
1632
|
+
},
|
|
1633
|
+
contextFieldRuntime: {
|
|
1634
|
+
en: "runtime",
|
|
1635
|
+
zh: "\u8FD0\u884C\u65F6"
|
|
1636
|
+
},
|
|
1637
|
+
contextFieldTools: {
|
|
1638
|
+
en: "tools",
|
|
1639
|
+
zh: "\u5DE5\u5177"
|
|
1640
|
+
},
|
|
1641
|
+
contextFieldThinking: {
|
|
1642
|
+
en: "thinking",
|
|
1643
|
+
zh: "\u601D\u8003"
|
|
1644
|
+
},
|
|
1645
|
+
contextFieldServer: {
|
|
1646
|
+
en: "server",
|
|
1647
|
+
zh: "\u670D\u52A1\u7AEF"
|
|
1648
|
+
},
|
|
1649
|
+
contextNextAgents: {
|
|
1650
|
+
en: "see specialist shortcuts",
|
|
1651
|
+
zh: "\u67E5\u770B\u4E13\u7528\u667A\u80FD\u4F53\u5FEB\u6377\u65B9\u5F0F"
|
|
1652
|
+
},
|
|
1653
|
+
contextNextDoc: {
|
|
1654
|
+
en: "add working context",
|
|
1655
|
+
zh: "\u6DFB\u52A0\u5DE5\u4F5C\u4E0A\u4E0B\u6587"
|
|
1656
|
+
},
|
|
1657
|
+
contextNextSkill: {
|
|
1658
|
+
en: "attach a skill to this workspace",
|
|
1659
|
+
zh: "\u4E3A\u8BE5\u5DE5\u4F5C\u533A\u6302\u8F7D\u6280\u80FD"
|
|
1660
|
+
},
|
|
1661
|
+
contextNextNew: {
|
|
1662
|
+
en: "start a clean dialog",
|
|
1663
|
+
zh: "\u5F00\u59CB\u4E00\u4E2A\u5E72\u51C0\u7684\u5BF9\u8BDD"
|
|
1664
|
+
},
|
|
1665
|
+
agentsTitle: {
|
|
1666
|
+
en: "Agents:",
|
|
1667
|
+
zh: "\u667A\u80FD\u4F53\uFF1A"
|
|
1668
|
+
},
|
|
1669
|
+
agentsTip: {
|
|
1670
|
+
en: "Tip: run /switch for the full picker, or /switch list for your private agents too.",
|
|
1671
|
+
zh: "\u63D0\u793A\uFF1A\u7528 /switch \u6253\u5F00\u5B8C\u6574\u9009\u62E9\u5668\uFF0C\u6216 /switch list \u8FDE\u4F60\u7684\u79C1\u6709\u667A\u80FD\u4F53\u4E00\u8D77\u5217\u51FA\u3002"
|
|
1672
|
+
},
|
|
1673
|
+
helpText: {
|
|
1674
|
+
en: [
|
|
1675
|
+
"Commands:",
|
|
1676
|
+
" /help Show this help",
|
|
1677
|
+
" /new Clear screen and start a fresh dialog",
|
|
1678
|
+
" /compact Compact current dialog and fork a new one",
|
|
1679
|
+
" /context Show workspace context and next actions",
|
|
1680
|
+
" /runtime <mode> Use auto, local, or server runtime",
|
|
1681
|
+
" /tools <mode> Control tool trace: hide, compact, verbose",
|
|
1682
|
+
" /thinking <mode> Control thinking output: hide, marker, show",
|
|
1683
|
+
" /switch Pick an agent interactively (\u2191\u2193, Enter)",
|
|
1684
|
+
" /switch list List agents as text",
|
|
1685
|
+
" /switch <name> Switch directly by name, alias, or key (alias: /agent)",
|
|
1686
|
+
" /agents List platform agent shortcuts",
|
|
1687
|
+
" /history Pick a recent dialog to resume (\u2191\u2193, Enter)",
|
|
1688
|
+
" /resume <dialogId> Resume a dialog directly by id",
|
|
1689
|
+
" /lang <zh|en> Switch interface language",
|
|
1690
|
+
" /copy Copy the last reply to the clipboard",
|
|
1691
|
+
" /copy view Open native scrollback for partial selection (Ctrl+O)",
|
|
1692
|
+
" /mouse <on|off> Toggle mouse mode (off = drag to select text)",
|
|
1693
|
+
" /doc List attached docs",
|
|
1694
|
+
" /doc attach <doc> Attach a doc to this workspace",
|
|
1695
|
+
" /skill List attached skills",
|
|
1696
|
+
" /skill attach <ref> Attach a skill (dbKey, name, or SKILL.md path)",
|
|
1697
|
+
" /skill detach <ref> Detach a skill",
|
|
1698
|
+
" /skill clear Detach all skills",
|
|
1699
|
+
" /customize Describe how you want to tune nolo",
|
|
1700
|
+
" /tasks List background process tasks (aliases: /jobs, /procs)",
|
|
1701
|
+
" /stop <pid|all> Stop background process tasks",
|
|
1702
|
+
" /login Show login/profile hint",
|
|
1703
|
+
" /profile Show active profile",
|
|
1704
|
+
" /update Update the nolo CLI install",
|
|
1705
|
+
" /version Show version/update hint",
|
|
1706
|
+
" /exit Leave the workspace",
|
|
1707
|
+
"",
|
|
1708
|
+
"You can also type normally. nolo routes simple read/status requests to CLI commands and sends the rest to the current agent."
|
|
1709
|
+
].join("\n"),
|
|
1710
|
+
zh: [
|
|
1711
|
+
"\u547D\u4EE4\uFF1A",
|
|
1712
|
+
" /help \u663E\u793A\u672C\u5E2E\u52A9",
|
|
1713
|
+
" /new \u6E05\u5C4F\u5E76\u5F00\u59CB\u65B0\u5BF9\u8BDD",
|
|
1714
|
+
" /compact \u538B\u7F29\u5F53\u524D\u5BF9\u8BDD\u5E76\u5206\u53C9\u51FA\u65B0\u5BF9\u8BDD",
|
|
1715
|
+
" /context \u67E5\u770B\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587\u4E0E\u540E\u7EED\u64CD\u4F5C",
|
|
1716
|
+
" /runtime <mode> \u5207\u6362 runtime\uFF1Aauto\u3001local\u3001server",
|
|
1717
|
+
" /tools <mode> \u5DE5\u5177\u8F68\u8FF9\u663E\u793A\uFF1Ahide\u3001compact\u3001verbose",
|
|
1718
|
+
" /thinking <mode> \u601D\u8003\u8FC7\u7A0B\u663E\u793A\uFF1Ahide\u3001marker\u3001show",
|
|
1719
|
+
" /switch \u4EA4\u4E92\u5F0F\u9009\u62E9 agent\uFF08\u2191\u2193 \u79FB\u52A8\uFF0CEnter \u786E\u8BA4\uFF09",
|
|
1720
|
+
" /switch list \u4EE5\u6587\u672C\u5217\u51FA\u5168\u90E8 agent",
|
|
1721
|
+
" /switch <name> \u6309\u540D\u79F0\u3001\u522B\u540D\u6216 key \u76F4\u63A5\u5207\u6362\uFF08\u522B\u540D\uFF1A/agent\uFF09",
|
|
1722
|
+
" /agents \u5217\u51FA\u5E73\u53F0 agent \u5FEB\u6377\u65B9\u5F0F",
|
|
1723
|
+
" /history \u4ECE\u6700\u8FD1\u5BF9\u8BDD\u4E2D\u9009\u62E9\u5E76\u6062\u590D\uFF08\u2191\u2193\uFF0CEnter\uFF09",
|
|
1724
|
+
" /resume <dialogId> \u6309 id \u76F4\u63A5\u6062\u590D\u5BF9\u8BDD",
|
|
1725
|
+
" /lang <zh|en> \u5207\u6362\u754C\u9762\u8BED\u8A00",
|
|
1726
|
+
" /copy \u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F",
|
|
1727
|
+
" /copy view \u6253\u5F00\u539F\u751F\u56DE\u6EDA\u8FDB\u884C\u8DE8\u5C4F\u62D6\u9009\uFF08Ctrl+O\uFF09",
|
|
1728
|
+
" /mouse <on|off> \u5207\u6362\u9F20\u6807\u6A21\u5F0F\uFF08off \u540E\u53EF\u76F4\u63A5\u62D6\u9009\u6587\u672C\uFF09",
|
|
1729
|
+
" /doc \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6587\u6863",
|
|
1730
|
+
" /doc attach <doc> \u6302\u8F7D\u6587\u6863\u5230\u5F53\u524D\u5DE5\u4F5C\u533A",
|
|
1731
|
+
" /skill \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6280\u80FD",
|
|
1732
|
+
" /skill attach <ref> \u6302\u8F7D\u6280\u80FD\uFF08dbKey\u3001\u540D\u79F0\u6216 SKILL.md \u8DEF\u5F84\uFF09",
|
|
1733
|
+
" /skill detach <ref> \u5378\u8F7D\u6307\u5B9A\u6280\u80FD",
|
|
1734
|
+
" /skill clear \u5378\u8F7D\u5168\u90E8\u6280\u80FD",
|
|
1735
|
+
" /customize \u63CF\u8FF0\u4F60\u60F3\u600E\u4E48\u8C03\u6559 nolo",
|
|
1736
|
+
" /tasks \u5217\u51FA\u540E\u53F0\u5B50\u8FDB\u7A0B\u4EFB\u52A1\uFF08\u522B\u540D\uFF1A/jobs, /procs\uFF09",
|
|
1737
|
+
" /stop <pid|all> \u505C\u6B62\u6307\u5B9A\u7684\u540E\u53F0\u4EFB\u52A1",
|
|
1738
|
+
" /login \u67E5\u770B\u767B\u5F55 / \u914D\u7F6E\u63D0\u793A",
|
|
1739
|
+
" /profile \u67E5\u770B\u5F53\u524D\u914D\u7F6E\u73AF\u5883",
|
|
1740
|
+
" /update \u66F4\u65B0 nolo CLI",
|
|
1741
|
+
" /version \u67E5\u770B\u7248\u672C\u4E0E\u66F4\u65B0\u63D0\u793A",
|
|
1742
|
+
" /exit \u9000\u51FA\u5DE5\u4F5C\u533A",
|
|
1743
|
+
"",
|
|
1744
|
+
"\u4E5F\u53EF\u4EE5\u76F4\u63A5\u8F93\u5165\u81EA\u7136\u8BED\u8A00\u3002\u7B80\u5355\u7684\u8BFB\u53D6/\u72B6\u6001\u8BF7\u6C42\u4F1A\u8D70 CLI \u547D\u4EE4\uFF0C\u5176\u4F59\u4EA4\u7ED9\u5F53\u524D agent\u3002"
|
|
1745
|
+
].join("\n")
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
function t(key) {
|
|
1749
|
+
return STRINGS[key][currentLocale];
|
|
1750
|
+
}
|
|
1751
|
+
var TOOL_LABELS = {
|
|
1752
|
+
// Local workspace tools (packages/agent-runtime/localWorkspaceTools.ts)
|
|
1753
|
+
readFile: { en: "Read", zh: "\u8BFB\u53D6" },
|
|
1754
|
+
writeFile: { en: "Write", zh: "\u5199\u5165" },
|
|
1755
|
+
editFile: { en: "Edit", zh: "\u7F16\u8F91" },
|
|
1756
|
+
listFiles: { en: "List", zh: "\u5217\u51FA" },
|
|
1757
|
+
searchFiles: { en: "Search", zh: "\u641C\u7D22" },
|
|
1758
|
+
globFiles: { en: "Glob", zh: "\u5339\u914D" },
|
|
1759
|
+
execShell: { en: "Run", zh: "\u6267\u884C" },
|
|
1760
|
+
runCommand: { en: "Run", zh: "\u6267\u884C" },
|
|
1761
|
+
captureVisualState: { en: "Capture", zh: "\u622A\u5C4F" },
|
|
1762
|
+
// Workspace / diagnostics
|
|
1763
|
+
searchWorkspace: { en: "Search workspace", zh: "\u641C\u7D22\u5DE5\u4F5C\u533A" },
|
|
1764
|
+
cliDoctor: { en: "Doctor", zh: "\u81EA\u68C0" },
|
|
1765
|
+
cliWhoami: { en: "Whoami", zh: "\u67E5\u770B\u8EAB\u4EFD" },
|
|
1766
|
+
checkEnv: { en: "Check env", zh: "\u68C0\u67E5\u73AF\u5883" },
|
|
1767
|
+
configure: { en: "Configure", zh: "\u914D\u7F6E" },
|
|
1768
|
+
notifyUser: { en: "Notify", zh: "\u901A\u77E5" },
|
|
1769
|
+
// Docs / dialogs / spaces
|
|
1770
|
+
readDoc: { en: "Read doc", zh: "\u8BFB\u53D6\u6587\u6863" },
|
|
1771
|
+
createDoc: { en: "Create doc", zh: "\u65B0\u5EFA\u6587\u6863" },
|
|
1772
|
+
updateDoc: { en: "Update doc", zh: "\u66F4\u65B0\u6587\u6863" },
|
|
1773
|
+
readDialog: { en: "Read dialog", zh: "\u8BFB\u53D6\u5BF9\u8BDD" },
|
|
1774
|
+
listDialogs: { en: "List dialogs", zh: "\u5217\u51FA\u5BF9\u8BDD" },
|
|
1775
|
+
queryDialogsBySubjectRef: { en: "Query dialogs", zh: "\u67E5\u8BE2\u5BF9\u8BDD" },
|
|
1776
|
+
searchDialogMessages: { en: "Search messages", zh: "\u641C\u7D22\u6D88\u606F" },
|
|
1777
|
+
listSpaces: { en: "List spaces", zh: "\u5217\u51FA\u7A7A\u95F4" },
|
|
1778
|
+
readSpace: { en: "Read space", zh: "\u8BFB\u53D6\u7A7A\u95F4" },
|
|
1779
|
+
// Tables
|
|
1780
|
+
createTable: { en: "Create table", zh: "\u521B\u5EFA\u8868" },
|
|
1781
|
+
queryTableRows: { en: "Query rows", zh: "\u67E5\u8BE2\u8868\u884C" },
|
|
1782
|
+
addTableRow: { en: "Add row", zh: "\u65B0\u589E\u8868\u884C" },
|
|
1783
|
+
addTableRows: { en: "Add rows", zh: "\u65B0\u589E\u8868\u884C" },
|
|
1784
|
+
updateTableRow: { en: "Update row", zh: "\u66F4\u65B0\u8868\u884C" },
|
|
1785
|
+
deleteTableRow: { en: "Delete row", zh: "\u5220\u9664\u8868\u884C" },
|
|
1786
|
+
// Web
|
|
1787
|
+
fetchWebpage: { en: "Fetch page", zh: "\u6293\u53D6\u7F51\u9875" },
|
|
1788
|
+
readPage: { en: "Read page", zh: "\u8BFB\u53D6\u7F51\u9875" },
|
|
1789
|
+
exaSearch: { en: "Web search", zh: "\u8054\u7F51\u641C\u7D22" },
|
|
1790
|
+
// Skill loading
|
|
1791
|
+
loadSkill: { en: "Used Skill", zh: "\u4F7F\u7528\u6280\u80FD" }
|
|
1792
|
+
};
|
|
1793
|
+
function toolLabel(name) {
|
|
1794
|
+
return TOOL_LABELS[name]?.[currentLocale] ?? name;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// packages/cli/client/toolOutput.ts
|
|
1798
|
+
function normalizeToolDisplayMode(raw, fallback = "compact") {
|
|
1799
|
+
const normalized = asTrimmedLowercaseString(raw);
|
|
1800
|
+
if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
|
|
1801
|
+
return "hide";
|
|
1802
|
+
}
|
|
1803
|
+
if (normalized === "verbose" || normalized === "debug" || normalized === "trace" || normalized === "full") {
|
|
1804
|
+
return "verbose";
|
|
1805
|
+
}
|
|
1806
|
+
if (normalized === "compact" || normalized === "minimal" || normalized === "short") {
|
|
1807
|
+
return "compact";
|
|
1808
|
+
}
|
|
1809
|
+
return fallback;
|
|
1810
|
+
}
|
|
1811
|
+
function resolveToolDisplayMode(env = process.env) {
|
|
1812
|
+
const legacyTrace = asTrimmedLowercaseString(env.NOLO_TRACE_TOOLS);
|
|
1813
|
+
if (legacyTrace === "0" || legacyTrace === "false" || legacyTrace === "off") {
|
|
1814
|
+
return "hide";
|
|
1815
|
+
}
|
|
1816
|
+
if (legacyTrace === "verbose" || legacyTrace === "full") {
|
|
1817
|
+
return "verbose";
|
|
1818
|
+
}
|
|
1819
|
+
return normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact");
|
|
1820
|
+
}
|
|
1821
|
+
function shouldEmitToolEvents(mode) {
|
|
1822
|
+
return mode !== "hide";
|
|
1823
|
+
}
|
|
1824
|
+
function clip(value, max = 72) {
|
|
1825
|
+
return clipCompactText(value, max, "\u2026");
|
|
1826
|
+
}
|
|
1827
|
+
function formatActiveToolLabel(event) {
|
|
1828
|
+
const toolName = event.toolName || "tool";
|
|
1829
|
+
const args = clipPathAware(event.argumentsPreview ?? "");
|
|
1830
|
+
const label = toolLabel(toolName);
|
|
1831
|
+
return args ? `${label} ${args}` : label;
|
|
1832
|
+
}
|
|
1833
|
+
function compactResultHint(event) {
|
|
1834
|
+
const gate = readActionGate(event.metadata?.actionGate);
|
|
1835
|
+
if (gate) {
|
|
1836
|
+
const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
|
|
1837
|
+
const command = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? "";
|
|
1838
|
+
const detail = command.trim() ? command : gate.title;
|
|
1839
|
+
return { inline: `${t("toolNeedsAction")}: ${clip(detail, 120)}` };
|
|
1840
|
+
}
|
|
1841
|
+
if (event.metadata?.timedOut) return { inline: t("toolTimedOut") };
|
|
1842
|
+
if (event.toolName === "readFile" && event.metadata) {
|
|
1843
|
+
const range = readReadFileRange(event.metadata);
|
|
1844
|
+
if (range) return { inline: range };
|
|
1845
|
+
}
|
|
1846
|
+
if (event.toolName === "editFile" && event.metadata) {
|
|
1847
|
+
const detail = formatEditFileSnippet(event.metadata);
|
|
1848
|
+
if (detail) return { inline: "", detail };
|
|
1849
|
+
}
|
|
1850
|
+
const summary = event.summary;
|
|
1851
|
+
if (!summary) return { inline: "" };
|
|
1852
|
+
const exitMatch = summary.match(/exit=(\d+)/);
|
|
1853
|
+
if (exitMatch && exitMatch[1] !== "0") return { inline: `${t("toolExitCode")} ${exitMatch[1]}` };
|
|
1854
|
+
return { inline: "" };
|
|
1855
|
+
}
|
|
1856
|
+
function readReadFileRange(metadata) {
|
|
1857
|
+
const startLine = metadata.startLine;
|
|
1858
|
+
const endLine = metadata.endLine;
|
|
1859
|
+
const totalLines = metadata.totalLines;
|
|
1860
|
+
const truncated = metadata.truncated;
|
|
1861
|
+
if (typeof startLine !== "number" || typeof endLine !== "number" || typeof totalLines !== "number") {
|
|
1862
|
+
return void 0;
|
|
1863
|
+
}
|
|
1864
|
+
if (!truncated && startLine === 1 && endLine === totalLines) return void 0;
|
|
1865
|
+
return `${startLine}-${endLine}/${totalLines}`;
|
|
1866
|
+
}
|
|
1867
|
+
var EDIT_SNIPPET_MAX_LINES = 5;
|
|
1868
|
+
var EDIT_SNIPPET_MAX_WIDTH = 96;
|
|
1869
|
+
function formatEditFileSnippet(metadata) {
|
|
1870
|
+
const oldSnippet = typeof metadata.oldSnippet === "string" ? metadata.oldSnippet : void 0;
|
|
1871
|
+
const newSnippet = typeof metadata.newSnippet === "string" ? metadata.newSnippet : void 0;
|
|
1872
|
+
if (!oldSnippet && !newSnippet) return void 0;
|
|
1873
|
+
const lines = [];
|
|
1874
|
+
if (oldSnippet) {
|
|
1875
|
+
for (const line of snippetLines(oldSnippet)) lines.push(`- ${line}`);
|
|
1876
|
+
}
|
|
1877
|
+
if (newSnippet) {
|
|
1878
|
+
for (const line of snippetLines(newSnippet)) lines.push(`+ ${line}`);
|
|
1879
|
+
}
|
|
1880
|
+
return lines.length ? lines.join("\n") : void 0;
|
|
1881
|
+
}
|
|
1882
|
+
function snippetLines(snippet) {
|
|
1883
|
+
const lines = snippet.split(/\r?\n/).filter((line) => line.length > 0);
|
|
1884
|
+
const shown = lines.slice(0, EDIT_SNIPPET_MAX_LINES);
|
|
1885
|
+
return shown.map((line) => line.length > EDIT_SNIPPET_MAX_WIDTH ? `${line.slice(0, EDIT_SNIPPET_MAX_WIDTH)}\u2026` : line);
|
|
1886
|
+
}
|
|
1887
|
+
function isFailedToolResult(event) {
|
|
1888
|
+
const exitCode = event.metadata?.exitCode;
|
|
1889
|
+
if (event.metadata?.actionGate) return false;
|
|
1890
|
+
if (typeof exitCode === "number" && exitCode !== 0) return true;
|
|
1891
|
+
return Boolean(event.metadata?.timedOut);
|
|
1892
|
+
}
|
|
1893
|
+
function isNeedsActionToolResult(event) {
|
|
1894
|
+
return Boolean(event.metadata?.actionGate);
|
|
1895
|
+
}
|
|
1896
|
+
function parseUiAskChoiceForCli(event) {
|
|
1897
|
+
if (event.toolName !== "ui_ask_choice" && !event.metadata?.uiAskChoice) {
|
|
1898
|
+
return null;
|
|
1899
|
+
}
|
|
1900
|
+
const parsed = parseUiAskChoiceContent(event.content);
|
|
1901
|
+
if (!parsed) return null;
|
|
1902
|
+
const question = typeof parsed.question === "string" ? parsed.question.trim() : "";
|
|
1903
|
+
const choices = (Array.isArray(parsed.choices) ? parsed.choices : []).filter((c) => Boolean(c && typeof c === "object")).map((c) => ({
|
|
1904
|
+
label: String(c.label ?? "").trim(),
|
|
1905
|
+
userMessage: typeof c.userMessage === "string" ? c.userMessage : void 0
|
|
1906
|
+
})).filter((c) => c.label.length > 0);
|
|
1907
|
+
if (!question || choices.length === 0) return null;
|
|
1908
|
+
return { question, choices };
|
|
1909
|
+
}
|
|
1910
|
+
function formatUiAskChoiceBlock(event, colorEnabled) {
|
|
1911
|
+
const parsed = parseUiAskChoiceForCli(event);
|
|
1912
|
+
if (!parsed) return null;
|
|
1913
|
+
const lines = [];
|
|
1914
|
+
lines.push("");
|
|
1915
|
+
if (colorEnabled) {
|
|
1916
|
+
lines.push(`${themeText("\u2753 ", "info", true)}${styleCliText(parsed.question, "cyan", true)}`);
|
|
1917
|
+
} else {
|
|
1918
|
+
lines.push(`\u2753 ${parsed.question}`);
|
|
1919
|
+
}
|
|
1920
|
+
parsed.choices.forEach((choice, i) => {
|
|
1921
|
+
const num = String(i + 1);
|
|
1922
|
+
const label = choice.label;
|
|
1923
|
+
if (colorEnabled) {
|
|
1924
|
+
lines.push(
|
|
1925
|
+
` ${themeText(num + ".", "chrome", true)} ${label}`
|
|
1926
|
+
);
|
|
1927
|
+
} else {
|
|
1928
|
+
lines.push(` ${num}. ${label}`);
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
const hint = "\u8BF7\u8F93\u5165\u5E8F\u53F7\u9009\u62E9\uFF0C\u6216\u76F4\u63A5\u56DE\u590D\uFF1A";
|
|
1932
|
+
if (colorEnabled) {
|
|
1933
|
+
lines.push(` ${dimCliText(hint, true)}`);
|
|
1934
|
+
} else {
|
|
1935
|
+
lines.push(` ${hint}`);
|
|
1936
|
+
}
|
|
1937
|
+
return `${lines.join("\n")}
|
|
1938
|
+
`;
|
|
1939
|
+
}
|
|
1940
|
+
function formatToolTraceLine(text, colorEnabled, accent = "none") {
|
|
1941
|
+
if (!colorEnabled) return `${text}
|
|
1942
|
+
`;
|
|
1943
|
+
if (accent === "error") {
|
|
1944
|
+
return `${themeText(text, "danger", true)}
|
|
1945
|
+
`;
|
|
1946
|
+
}
|
|
1947
|
+
return `${dimCliText(text, true)}
|
|
1948
|
+
`;
|
|
1949
|
+
}
|
|
1950
|
+
function formatVerboseToolEvent(event, colorEnabled) {
|
|
1951
|
+
const round = event.round + 1;
|
|
1952
|
+
const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
|
|
1953
|
+
if (event.type === "tool-call") {
|
|
1954
|
+
return formatToolTraceLine(`[nolo:tool] #${round} -> ${event.toolName}${detail}`, colorEnabled);
|
|
1955
|
+
}
|
|
1956
|
+
if (event.type === "tool-error") {
|
|
1957
|
+
const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
|
|
1958
|
+
return formatToolTraceLine(
|
|
1959
|
+
`[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}`,
|
|
1960
|
+
colorEnabled,
|
|
1961
|
+
"error"
|
|
1962
|
+
);
|
|
1963
|
+
}
|
|
1964
|
+
const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
|
|
1965
|
+
if (event.type === "tool-result" && (event.toolName === "ui_ask_choice" || event.metadata?.uiAskChoice)) {
|
|
1966
|
+
const block = formatUiAskChoiceBlock(event, colorEnabled);
|
|
1967
|
+
if (block) return `${formatToolTraceLine(`[nolo:tool] #${round} <- ${event.toolName}${elapsed}`, colorEnabled)}${block}`;
|
|
1968
|
+
}
|
|
1969
|
+
const summary = event.summary ? ` ${event.summary}` : "";
|
|
1970
|
+
return formatToolTraceLine(
|
|
1971
|
+
`[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}`,
|
|
1972
|
+
colorEnabled
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
function isReadToolName(name) {
|
|
1976
|
+
if (!name) return false;
|
|
1977
|
+
return name === "read" || name === "readFile" || name === "read_file" || name === "readWorkspaceFile";
|
|
1978
|
+
}
|
|
1979
|
+
function isSearchToolName(name) {
|
|
1980
|
+
if (!name) return false;
|
|
1981
|
+
return name === "grep" || name === "searchFiles" || name === "search_files" || name === "globFiles" || name === "glob_files" || name === "glob" || name === "searchWorkspace" || name === "search";
|
|
1982
|
+
}
|
|
1983
|
+
function isRunToolName(name) {
|
|
1984
|
+
if (!name) return false;
|
|
1985
|
+
return name === "execShell" || name === "runCommand" || name === "run_command" || name === "launchProcess";
|
|
1986
|
+
}
|
|
1987
|
+
function isFetchToolName(name) {
|
|
1988
|
+
if (!name) return false;
|
|
1989
|
+
return name === "fetchWebpage" || name === "fetch_webpage";
|
|
1990
|
+
}
|
|
1991
|
+
function isRunResultFoldable(event) {
|
|
1992
|
+
return Boolean(event.metadata?.actionGate) === false;
|
|
1993
|
+
}
|
|
1994
|
+
function formatSearchTreeBlockForCli(items, colorEnabled) {
|
|
1995
|
+
if (items.length === 0) return "";
|
|
1996
|
+
const { count, lines } = formatSearchTreeLines(items);
|
|
1997
|
+
if (!colorEnabled) {
|
|
1998
|
+
const headerLine2 = `\u2022 Search (${count})
|
|
1999
|
+
`;
|
|
2000
|
+
const treeLines2 = lines.map((l) => ` ${l.connector}${l.queryText}`).join("\n");
|
|
2001
|
+
return `${headerLine2}${treeLines2}
|
|
2002
|
+
`;
|
|
2003
|
+
}
|
|
2004
|
+
const bullet = themeText("\u2022", "chrome", true);
|
|
2005
|
+
const title = styleCliText("Search", "bold", true);
|
|
2006
|
+
const countText = themeText(`(${count})`, "muted", true);
|
|
2007
|
+
const headerLine = `${bullet} ${title} ${countText}
|
|
2008
|
+
`;
|
|
2009
|
+
const treeLines = lines.map((l) => {
|
|
2010
|
+
const connector = themeText(` ${l.connector}`, "chrome", true);
|
|
2011
|
+
const queryText = themeText(l.queryText, "muted", true);
|
|
2012
|
+
return `${connector}${queryText}`;
|
|
2013
|
+
}).join("\n");
|
|
2014
|
+
return `${headerLine}${treeLines}
|
|
2015
|
+
`;
|
|
2016
|
+
}
|
|
2017
|
+
function formatReadTreeBlockForCli(items, colorEnabled) {
|
|
2018
|
+
if (items.length === 0) return "";
|
|
2019
|
+
const { count, lines } = formatReadTreeLines(items);
|
|
2020
|
+
if (!colorEnabled) {
|
|
2021
|
+
const headerLine2 = `\u2022 Read (${count})
|
|
2022
|
+
`;
|
|
2023
|
+
const treeLines2 = lines.map((l) => ` ${l.connector}${l.pathWithRange}`).join("\n");
|
|
2024
|
+
return `${headerLine2}${treeLines2}
|
|
2025
|
+
`;
|
|
2026
|
+
}
|
|
2027
|
+
const bullet = themeText("\u2022", "chrome", true);
|
|
2028
|
+
const title = styleCliText("Read", "bold", true);
|
|
2029
|
+
const countText = themeText(`(${count})`, "muted", true);
|
|
2030
|
+
const headerLine = `${bullet} ${title} ${countText}
|
|
2031
|
+
`;
|
|
2032
|
+
const treeLines = lines.map((l) => {
|
|
2033
|
+
const connector = themeText(` ${l.connector}`, "chrome", true);
|
|
2034
|
+
const pathText = themeText(l.pathWithRange, "muted", true);
|
|
2035
|
+
return `${connector}${pathText}`;
|
|
2036
|
+
}).join("\n");
|
|
2037
|
+
return `${headerLine}${treeLines}
|
|
2038
|
+
`;
|
|
2039
|
+
}
|
|
2040
|
+
function formatRunTreeBlockForCli(items, colorEnabled) {
|
|
2041
|
+
if (items.length === 0) return "";
|
|
2042
|
+
const { count, lines } = formatRunTreeLines(items);
|
|
2043
|
+
if (!colorEnabled) {
|
|
2044
|
+
const headerLine2 = `\u2022 Run (${count})
|
|
2045
|
+
`;
|
|
2046
|
+
const treeLines2 = lines.map((l) => ` ${l.connector}${l.commandText}`).join("\n");
|
|
2047
|
+
return `${headerLine2}${treeLines2}
|
|
2048
|
+
`;
|
|
2049
|
+
}
|
|
2050
|
+
const bullet = themeText("\u2022", "chrome", true);
|
|
2051
|
+
const title = styleCliText("Run", "bold", true);
|
|
2052
|
+
const countText = themeText(`(${count})`, "muted", true);
|
|
2053
|
+
const headerLine = `${bullet} ${title} ${countText}
|
|
2054
|
+
`;
|
|
2055
|
+
const treeLines = lines.map((l) => {
|
|
2056
|
+
const connector = themeText(` ${l.connector}`, "chrome", true);
|
|
2057
|
+
const commandText = themeText(l.commandText, "muted", true);
|
|
2058
|
+
return `${connector}${commandText}`;
|
|
2059
|
+
}).join("\n");
|
|
2060
|
+
return `${headerLine}${treeLines}
|
|
2061
|
+
`;
|
|
2062
|
+
}
|
|
2063
|
+
function formatFetchTreeBlockForCli(items, colorEnabled) {
|
|
2064
|
+
if (items.length === 0) return "";
|
|
2065
|
+
const { count, lines } = formatFetchTreeLines(items);
|
|
2066
|
+
if (!colorEnabled) {
|
|
2067
|
+
const headerLine2 = `\u2022 Fetch (${count})
|
|
2068
|
+
`;
|
|
2069
|
+
const treeLines2 = lines.map((l) => ` ${l.connector}${l.urlText}`).join("\n");
|
|
2070
|
+
return `${headerLine2}${treeLines2}
|
|
2071
|
+
`;
|
|
2072
|
+
}
|
|
2073
|
+
const bullet = themeText("\u2022", "chrome", true);
|
|
2074
|
+
const title = styleCliText("Fetch", "bold", true);
|
|
2075
|
+
const countText = themeText(`(${count})`, "muted", true);
|
|
2076
|
+
const headerLine = `${bullet} ${title} ${countText}
|
|
2077
|
+
`;
|
|
2078
|
+
const treeLines = lines.map((l) => {
|
|
2079
|
+
const connector = themeText(` ${l.connector}`, "chrome", true);
|
|
2080
|
+
const urlText = themeText(l.urlText, "muted", true);
|
|
2081
|
+
return `${connector}${urlText}`;
|
|
2082
|
+
}).join("\n");
|
|
2083
|
+
return `${headerLine}${treeLines}
|
|
2084
|
+
`;
|
|
2085
|
+
}
|
|
2086
|
+
function resolveLoadSkillName(event) {
|
|
2087
|
+
const metaName = typeof event.metadata?.name === "string" ? event.metadata.name : void 0;
|
|
2088
|
+
if (metaName) return metaName;
|
|
2089
|
+
const argName = event.argumentsPreview?.trim();
|
|
2090
|
+
if (argName && !argName.startsWith("{")) return argName;
|
|
2091
|
+
const content = typeof event.content === "string" ? event.content : "";
|
|
2092
|
+
const match = content.match(/Skill "([^"]+)" loaded inline/);
|
|
2093
|
+
if (match?.[1]) return match[1];
|
|
2094
|
+
return argName || "skill";
|
|
2095
|
+
}
|
|
2096
|
+
function formatCompactToolLine(event, pending, colorEnabled) {
|
|
2097
|
+
const toolName = event.toolName || pending?.toolName || "tool";
|
|
2098
|
+
if (isReadToolName(toolName) && event.type === "tool-result") {
|
|
2099
|
+
const rawPath = (typeof event.metadata?.path === "string" ? event.metadata.path : void 0) || (typeof event.metadata?.filePath === "string" ? event.metadata.filePath : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
|
|
2100
|
+
return formatReadTreeBlockForCli([{ path: rawPath, metadata: event.metadata }], colorEnabled);
|
|
2101
|
+
}
|
|
2102
|
+
if (isSearchToolName(toolName) && event.type === "tool-result") {
|
|
2103
|
+
const rawQuery = (typeof event.metadata?.query === "string" ? event.metadata.query : void 0) || (typeof event.metadata?.pattern === "string" ? event.metadata.pattern : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
|
|
2104
|
+
const rawPath = typeof event.metadata?.path === "string" ? event.metadata.path : void 0;
|
|
2105
|
+
return formatSearchTreeBlockForCli([{ query: rawQuery, path: rawPath }], colorEnabled);
|
|
2106
|
+
}
|
|
2107
|
+
if (isFetchToolName(toolName) && event.type === "tool-result") {
|
|
2108
|
+
const rawUrl = (typeof event.metadata?.url === "string" ? event.metadata.url : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
|
|
2109
|
+
return formatFetchTreeBlockForCli([{ url: rawUrl }], colorEnabled);
|
|
2110
|
+
}
|
|
2111
|
+
if (isRunToolName(toolName) && event.type === "tool-result" && isRunResultFoldable(event)) {
|
|
2112
|
+
const rawCommand = (typeof event.metadata?.command === "string" ? event.metadata.command : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
|
|
2113
|
+
const exitCode = typeof event.metadata?.exitCode === "number" ? event.metadata.exitCode : void 0;
|
|
2114
|
+
const timedOut = Boolean(event.metadata?.timedOut);
|
|
2115
|
+
return formatRunTreeBlockForCli([{ command: rawCommand, exitCode, timedOut }], colorEnabled);
|
|
2116
|
+
}
|
|
2117
|
+
const rawArgs = clipPathAware(event.argumentsPreview || pending?.argumentsPreview || "", 72);
|
|
2118
|
+
const rawLabel = toolLabel(toolName);
|
|
2119
|
+
const label = rawArgs ? `${rawLabel} ${rawArgs}` : rawLabel;
|
|
2120
|
+
if (event.type === "tool-error") {
|
|
2121
|
+
const message = clip(event.message ?? t("toolFailed"), 96);
|
|
2122
|
+
return formatToolTraceLine(`\u25B8 ${label} \u2717 ${message}`, colorEnabled, "error");
|
|
2123
|
+
}
|
|
2124
|
+
if (event.type === "tool-result" && toolName === "loadSkill") {
|
|
2125
|
+
const skillName = resolveLoadSkillName(event);
|
|
2126
|
+
const content = typeof event.content === "string" ? event.content : "";
|
|
2127
|
+
const failed2 = /^Skill\s+"[^"]*"\s+not found/.test(content) || isFailedToolResult(event);
|
|
2128
|
+
if (failed2) {
|
|
2129
|
+
const message = clip(content.split("\n")[0] || t("toolFailed"), 96);
|
|
2130
|
+
return formatToolTraceLine(`\u25B8 Used Skill (${skillName}) \u2717 ${message}`, colorEnabled, "error");
|
|
2131
|
+
}
|
|
2132
|
+
const resultLine = `Skill "${skillName}" loaded inline. Follow its instructions.`;
|
|
2133
|
+
if (!colorEnabled) {
|
|
2134
|
+
return `\u25CF Used Skill (${skillName})
|
|
2135
|
+
${resultLine}
|
|
2136
|
+
`;
|
|
2137
|
+
}
|
|
2138
|
+
const bullet = themeText("\u25CF", "success", true);
|
|
2139
|
+
const labelPart = themeText("Used Skill", "muted", true);
|
|
2140
|
+
const namePart = themeText(`(${skillName})`, "chrome", true);
|
|
2141
|
+
const detail = themeText(` ${resultLine}`, "muted", true);
|
|
2142
|
+
return `${bullet} ${labelPart} ${namePart}
|
|
2143
|
+
${detail}
|
|
2144
|
+
`;
|
|
2145
|
+
}
|
|
2146
|
+
if (event.type === "tool-result" && (event.toolName === "ui_ask_choice" || event.metadata?.uiAskChoice)) {
|
|
2147
|
+
const block = formatUiAskChoiceBlock(event, colorEnabled);
|
|
2148
|
+
if (block) return block;
|
|
2149
|
+
}
|
|
2150
|
+
const hint = compactResultHint(event);
|
|
2151
|
+
const suffix = hint.inline ? ` ${hint.inline}` : "";
|
|
2152
|
+
const failed = isFailedToolResult(event);
|
|
2153
|
+
const marker = failed ? "\u2717" : isNeedsActionToolResult(event) ? "!" : "\u2713";
|
|
2154
|
+
let mainLine;
|
|
2155
|
+
if (!colorEnabled) {
|
|
2156
|
+
mainLine = `\u25B8 ${label} ${marker}${suffix}
|
|
2157
|
+
`;
|
|
2158
|
+
} else {
|
|
2159
|
+
const chromePointer = themeText("\u25B8", "chrome", true);
|
|
2160
|
+
const mutedLabel = themeText(rawLabel, "muted", true);
|
|
2161
|
+
const argsPart = rawArgs ? ` ${dimCliText(rawArgs, true)}` : "";
|
|
2162
|
+
const statusToken = failed ? themeText("\u2717", "danger", true) : isNeedsActionToolResult(event) ? themeText("!", "warning", true) : themeText("\u2713", "success", true);
|
|
2163
|
+
const suffixPart = hint.inline ? ` ${dimCliText(hint.inline, true)}` : "";
|
|
2164
|
+
mainLine = `${chromePointer} ${mutedLabel}${argsPart} ${statusToken}${suffixPart}
|
|
2165
|
+
`;
|
|
2166
|
+
}
|
|
2167
|
+
if (!hint.detail) return mainLine;
|
|
2168
|
+
const detailLines = hint.detail.split("\n").map((line) => formatEditDetailLine(line, colorEnabled)).join("");
|
|
2169
|
+
return `${mainLine}${detailLines}`;
|
|
2170
|
+
}
|
|
2171
|
+
function formatEditDetailLine(line, colorEnabled) {
|
|
2172
|
+
const marker = line.slice(0, 2);
|
|
2173
|
+
const rest = line.slice(2);
|
|
2174
|
+
if (!colorEnabled) return ` ${line}
|
|
2175
|
+
`;
|
|
2176
|
+
const diff = diffLineSequences();
|
|
2177
|
+
if (!diff) {
|
|
2178
|
+
const color = marker === "- " ? "red" : marker === "+ " ? "green" : void 0;
|
|
2179
|
+
if (color) {
|
|
2180
|
+
return ` ${styleCliText(marker, color, true)}${dimCliText(rest, true)}
|
|
2181
|
+
`;
|
|
2182
|
+
}
|
|
2183
|
+
return ` ${dimCliText(line, true)}
|
|
2184
|
+
`;
|
|
2185
|
+
}
|
|
2186
|
+
const RESET2 = "\x1B[0m";
|
|
2187
|
+
if (marker === "- ") {
|
|
2188
|
+
return ` ${diff.removed.bg}${diff.removed.fg}- ${rest}${RESET2}
|
|
2189
|
+
`;
|
|
2190
|
+
}
|
|
2191
|
+
if (marker === "+ ") {
|
|
2192
|
+
return ` ${diff.added.bg}${diff.added.fg}+ ${rest}${RESET2}
|
|
2193
|
+
`;
|
|
2194
|
+
}
|
|
2195
|
+
return ` ${dimCliText(line, true)}
|
|
2196
|
+
`;
|
|
2197
|
+
}
|
|
2198
|
+
function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
|
|
2199
|
+
const pending = /* @__PURE__ */ new Map();
|
|
2200
|
+
let readBuffer = [];
|
|
2201
|
+
let searchBuffer = [];
|
|
2202
|
+
let runBuffer = [];
|
|
2203
|
+
let fetchBuffer = [];
|
|
2204
|
+
const flushBuffers = () => {
|
|
2205
|
+
let out = "";
|
|
2206
|
+
if (readBuffer.length > 0) {
|
|
2207
|
+
const items = readBuffer.map((evt) => {
|
|
2208
|
+
const call = pending.get(evt.toolCallId);
|
|
2209
|
+
const rawPath = (typeof evt.metadata?.path === "string" ? evt.metadata.path : void 0) || (typeof evt.metadata?.filePath === "string" ? evt.metadata.filePath : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
|
|
2210
|
+
return { path: rawPath, metadata: evt.metadata };
|
|
2211
|
+
});
|
|
2212
|
+
for (const evt of readBuffer) pending.delete(evt.toolCallId);
|
|
2213
|
+
readBuffer = [];
|
|
2214
|
+
out += formatReadTreeBlockForCli(items, colorEnabled);
|
|
2215
|
+
}
|
|
2216
|
+
if (searchBuffer.length > 0) {
|
|
2217
|
+
const items = searchBuffer.map((evt) => {
|
|
2218
|
+
const call = pending.get(evt.toolCallId);
|
|
2219
|
+
const rawQuery = (typeof evt.metadata?.query === "string" ? evt.metadata.query : void 0) || (typeof evt.metadata?.pattern === "string" ? evt.metadata.pattern : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
|
|
2220
|
+
const rawPath = typeof evt.metadata?.path === "string" ? evt.metadata.path : void 0;
|
|
2221
|
+
return { query: rawQuery, path: rawPath };
|
|
2222
|
+
});
|
|
2223
|
+
for (const evt of searchBuffer) pending.delete(evt.toolCallId);
|
|
2224
|
+
searchBuffer = [];
|
|
2225
|
+
out += formatSearchTreeBlockForCli(items, colorEnabled);
|
|
2226
|
+
}
|
|
2227
|
+
if (runBuffer.length > 0) {
|
|
2228
|
+
const items = runBuffer.map((evt) => {
|
|
2229
|
+
const call = pending.get(evt.toolCallId);
|
|
2230
|
+
const rawCommand = (typeof evt.metadata?.command === "string" ? evt.metadata.command : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
|
|
2231
|
+
const exitCode = typeof evt.metadata?.exitCode === "number" ? evt.metadata.exitCode : void 0;
|
|
2232
|
+
const timedOut = Boolean(evt.metadata?.timedOut);
|
|
2233
|
+
return { command: rawCommand, exitCode, timedOut };
|
|
2234
|
+
});
|
|
2235
|
+
for (const evt of runBuffer) pending.delete(evt.toolCallId);
|
|
2236
|
+
runBuffer = [];
|
|
2237
|
+
out += formatRunTreeBlockForCli(items, colorEnabled);
|
|
2238
|
+
}
|
|
2239
|
+
if (fetchBuffer.length > 0) {
|
|
2240
|
+
const items = fetchBuffer.map((evt) => {
|
|
2241
|
+
const call = pending.get(evt.toolCallId);
|
|
2242
|
+
const rawUrl = (typeof evt.metadata?.url === "string" ? evt.metadata.url : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
|
|
2243
|
+
return { url: rawUrl };
|
|
2244
|
+
});
|
|
2245
|
+
for (const evt of fetchBuffer) pending.delete(evt.toolCallId);
|
|
2246
|
+
fetchBuffer = [];
|
|
2247
|
+
out += formatFetchTreeBlockForCli(items, colorEnabled);
|
|
2248
|
+
}
|
|
2249
|
+
return out;
|
|
2250
|
+
};
|
|
2251
|
+
const formatter = (event) => {
|
|
2252
|
+
if (mode === "hide") return "";
|
|
2253
|
+
if (mode === "verbose") return formatVerboseToolEvent(event, colorEnabled);
|
|
2254
|
+
if (isReadToolName(event.toolName)) {
|
|
2255
|
+
if (event.type === "tool-call") {
|
|
2256
|
+
pending.set(event.toolCallId, {
|
|
2257
|
+
toolName: event.toolName,
|
|
2258
|
+
argumentsPreview: event.argumentsPreview
|
|
2259
|
+
});
|
|
2260
|
+
return "";
|
|
2261
|
+
}
|
|
2262
|
+
if (event.type === "tool-result") {
|
|
2263
|
+
readBuffer.push(event);
|
|
2264
|
+
return "";
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
if (isSearchToolName(event.toolName)) {
|
|
2268
|
+
if (event.type === "tool-call") {
|
|
2269
|
+
pending.set(event.toolCallId, {
|
|
2270
|
+
toolName: event.toolName,
|
|
2271
|
+
argumentsPreview: event.argumentsPreview
|
|
2272
|
+
});
|
|
2273
|
+
return "";
|
|
2274
|
+
}
|
|
2275
|
+
if (event.type === "tool-result") {
|
|
2276
|
+
searchBuffer.push(event);
|
|
2277
|
+
return "";
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
if (isRunToolName(event.toolName)) {
|
|
2281
|
+
if (event.type === "tool-result" && !isRunResultFoldable(event)) {
|
|
2282
|
+
const flushed2 = flushBuffers();
|
|
2283
|
+
const call2 = pending.get(event.toolCallId);
|
|
2284
|
+
pending.delete(event.toolCallId);
|
|
2285
|
+
return `${flushed2}${formatCompactToolLine(event, call2, colorEnabled)}`;
|
|
2286
|
+
}
|
|
2287
|
+
if (event.type === "tool-call") {
|
|
2288
|
+
pending.set(event.toolCallId, {
|
|
2289
|
+
toolName: event.toolName,
|
|
2290
|
+
argumentsPreview: event.argumentsPreview
|
|
2291
|
+
});
|
|
2292
|
+
return "";
|
|
2293
|
+
}
|
|
2294
|
+
if (event.type === "tool-result") {
|
|
2295
|
+
runBuffer.push(event);
|
|
2296
|
+
return "";
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
if (isFetchToolName(event.toolName)) {
|
|
2300
|
+
if (event.type === "tool-call") {
|
|
2301
|
+
pending.set(event.toolCallId, {
|
|
2302
|
+
toolName: event.toolName,
|
|
2303
|
+
argumentsPreview: event.argumentsPreview
|
|
2304
|
+
});
|
|
2305
|
+
return "";
|
|
2306
|
+
}
|
|
2307
|
+
if (event.type === "tool-result") {
|
|
2308
|
+
fetchBuffer.push(event);
|
|
2309
|
+
return "";
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
const flushed = flushBuffers();
|
|
2313
|
+
if (event.type === "tool-call") {
|
|
2314
|
+
pending.set(event.toolCallId, {
|
|
2315
|
+
toolName: event.toolName,
|
|
2316
|
+
argumentsPreview: event.argumentsPreview
|
|
2317
|
+
});
|
|
2318
|
+
return flushed;
|
|
2319
|
+
}
|
|
2320
|
+
const call = pending.get(event.toolCallId);
|
|
2321
|
+
pending.delete(event.toolCallId);
|
|
2322
|
+
return `${flushed}${formatCompactToolLine(event, call, colorEnabled)}`;
|
|
2323
|
+
};
|
|
2324
|
+
formatter.flush = flushBuffers;
|
|
2325
|
+
return formatter;
|
|
2326
|
+
}
|
|
2327
|
+
function createSseToolEventAdapter(onEvent) {
|
|
2328
|
+
let round = 0;
|
|
2329
|
+
let callIndex = 0;
|
|
2330
|
+
let pendingCalls = [];
|
|
2331
|
+
const emit = (event) => {
|
|
2332
|
+
onEvent?.(event);
|
|
2333
|
+
return event;
|
|
2334
|
+
};
|
|
2335
|
+
return {
|
|
2336
|
+
onToolStart(payload) {
|
|
2337
|
+
const calls = Array.isArray(payload) ? payload : payload?.calls ?? [];
|
|
2338
|
+
pendingCalls = [];
|
|
2339
|
+
const events = [];
|
|
2340
|
+
for (const name of calls) {
|
|
2341
|
+
callIndex++;
|
|
2342
|
+
const toolCallId = `sse-call-${callIndex}`;
|
|
2343
|
+
const toolName = name || "tool";
|
|
2344
|
+
pendingCalls.push({ toolCallId, toolName });
|
|
2345
|
+
const event = {
|
|
2346
|
+
type: "tool-call",
|
|
2347
|
+
toolCallId,
|
|
2348
|
+
toolName,
|
|
2349
|
+
round
|
|
2350
|
+
};
|
|
2351
|
+
events.push(emit(event));
|
|
2352
|
+
}
|
|
2353
|
+
return events;
|
|
2354
|
+
},
|
|
2355
|
+
onToolResult(payload) {
|
|
2356
|
+
const pending = payload.toolCallId ? pendingCalls.find((p) => p.toolCallId === payload.toolCallId) : pendingCalls.shift();
|
|
2357
|
+
if (pending && payload.toolCallId) {
|
|
2358
|
+
pendingCalls = pendingCalls.filter((p) => p.toolCallId !== payload.toolCallId);
|
|
2359
|
+
}
|
|
2360
|
+
const toolCallId = payload.toolCallId || pending?.toolCallId || `sse-call-${callIndex}`;
|
|
2361
|
+
const toolName = payload.toolName || pending?.toolName || "tool";
|
|
2362
|
+
const rawContent = typeof payload.content === "string" ? payload.content : "";
|
|
2363
|
+
const summary = rawContent ? clipCompactText(rawContent, 120, "\u2026") : void 0;
|
|
2364
|
+
const event = {
|
|
2365
|
+
type: "tool-result",
|
|
2366
|
+
toolCallId,
|
|
2367
|
+
toolName,
|
|
2368
|
+
summary,
|
|
2369
|
+
metadata: payload.metadata,
|
|
2370
|
+
round
|
|
2371
|
+
};
|
|
2372
|
+
return emit(event);
|
|
2373
|
+
},
|
|
2374
|
+
onToolEnd() {
|
|
2375
|
+
round++;
|
|
2376
|
+
pendingCalls = [];
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// packages/cli/client/agentRunSpinner.ts
|
|
2382
|
+
function formatElapsed(totalSeconds) {
|
|
2383
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
2384
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2385
|
+
const seconds = totalSeconds % 60;
|
|
2386
|
+
if (minutes < 60) return `${minutes}m ${seconds}s`;
|
|
2387
|
+
const hours = Math.floor(minutes / 60);
|
|
2388
|
+
const mins = minutes % 60;
|
|
2389
|
+
return `${hours}h ${mins}m`;
|
|
2390
|
+
}
|
|
2391
|
+
var Spinner = class {
|
|
2392
|
+
constructor(output, text, silent = false) {
|
|
2393
|
+
this.output = output;
|
|
2394
|
+
this.text = text;
|
|
2395
|
+
this.silent = silent;
|
|
2396
|
+
const explicit = output.isTTY;
|
|
2397
|
+
this.isTTY = typeof explicit === "boolean" ? explicit : Boolean(process.stdout.isTTY);
|
|
2398
|
+
}
|
|
2399
|
+
timer = null;
|
|
2400
|
+
startTime = 0;
|
|
2401
|
+
frameIndex = 0;
|
|
2402
|
+
frames = ["\xB7", "~", "\u2248", "\u223F", "\u2248", "~"];
|
|
2403
|
+
isTTY;
|
|
2404
|
+
start() {
|
|
2405
|
+
if (this.silent) {
|
|
2406
|
+
this.startTime = Date.now();
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (!this.isTTY) {
|
|
2410
|
+
this.output.write(`
|
|
2411
|
+
${this.text}
|
|
2412
|
+
`);
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
if (this.timer) {
|
|
2416
|
+
clearInterval(this.timer);
|
|
2417
|
+
this.timer = null;
|
|
2418
|
+
}
|
|
2419
|
+
this.frameIndex = 0;
|
|
2420
|
+
this.startTime = Date.now();
|
|
2421
|
+
this.output.write("\x1B[?25l");
|
|
2422
|
+
this.output.write(this.renderLine(this.frames[0]));
|
|
2423
|
+
this.timer = setInterval(() => {
|
|
2424
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
2425
|
+
this.output.write(`\r${this.renderLine(this.frames[this.frameIndex])}`);
|
|
2426
|
+
}, 80);
|
|
2427
|
+
}
|
|
2428
|
+
stop() {
|
|
2429
|
+
const wasActive = this.timer !== null;
|
|
2430
|
+
if (this.timer) {
|
|
2431
|
+
clearInterval(this.timer);
|
|
2432
|
+
this.timer = null;
|
|
2433
|
+
}
|
|
2434
|
+
if (this.silent) return;
|
|
2435
|
+
if (this.isTTY && wasActive) {
|
|
2436
|
+
this.output.write("\r\x1B[K");
|
|
2437
|
+
this.output.write("\x1B[?25h");
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
show(text) {
|
|
2441
|
+
this.text = text;
|
|
2442
|
+
if (this.silent) {
|
|
2443
|
+
if (!this.startTime) {
|
|
2444
|
+
this.startTime = Date.now();
|
|
2445
|
+
}
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
if (!this.isTTY) return;
|
|
2449
|
+
if (!this.timer) {
|
|
2450
|
+
this.start();
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
this.frameIndex = 0;
|
|
2454
|
+
this.startTime = Date.now();
|
|
2455
|
+
this.output.write(`\r${this.renderLine(this.frames[0])}`);
|
|
2456
|
+
}
|
|
2457
|
+
renderLine(frame) {
|
|
2458
|
+
const elapsedSeconds = Math.max(
|
|
2459
|
+
0,
|
|
2460
|
+
Math.floor((Date.now() - this.startTime) / 1e3)
|
|
2461
|
+
);
|
|
2462
|
+
return `${themeColorSequence("accent")}${frame}\x1B[39m ${this.text} (${formatElapsed(elapsedSeconds)})`;
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
|
|
2466
|
+
// packages/cli/client/agentRunOutput.ts
|
|
2467
|
+
function formatAssistantResponseForCli(text, options) {
|
|
2468
|
+
const thinkingMode = resolveThinkingDisplayMode(options.env);
|
|
2469
|
+
return formatAssistantDisplay(
|
|
2470
|
+
formatAssistantTextForCli(text, thinkingMode)
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
function resolveAgentEventMode(options) {
|
|
2474
|
+
if (options.eventsMode === "jsonl") return "jsonl";
|
|
2475
|
+
return options.env.NOLO_AGENT_EVENTS === "jsonl" ? "jsonl" : "text";
|
|
2476
|
+
}
|
|
2477
|
+
function formatToolJsonEvent(event) {
|
|
2478
|
+
return `${JSON.stringify({
|
|
2479
|
+
schemaVersion: 1,
|
|
2480
|
+
type: event.type,
|
|
2481
|
+
round: event.round + 1,
|
|
2482
|
+
tool: event.toolName,
|
|
2483
|
+
toolCallId: event.toolCallId,
|
|
2484
|
+
...event.argumentsPreview ? { argsPreview: event.argumentsPreview } : {},
|
|
2485
|
+
...typeof event.elapsedMs === "number" ? { elapsedMs: event.elapsedMs } : {},
|
|
2486
|
+
...event.summary ? { summary: event.summary } : {},
|
|
2487
|
+
...event.message ? { message: event.message } : {},
|
|
2488
|
+
...event.metadata ? { metadata: event.metadata } : {}
|
|
2489
|
+
})}
|
|
2490
|
+
`;
|
|
2491
|
+
}
|
|
2492
|
+
function createCliTurnOutput(params) {
|
|
2493
|
+
const { options } = params;
|
|
2494
|
+
const workingLabel = params.workingLabel ?? `${options.agentName} -> working`;
|
|
2495
|
+
const spinner = params.spinner ?? new Spinner(options.output, workingLabel, Boolean(options.activityReporter));
|
|
2496
|
+
const toolDisplayMode = resolveToolDisplayMode(options.env);
|
|
2497
|
+
const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
|
|
2498
|
+
const formatToolEvent = createToolEventFormatter(toolDisplayMode);
|
|
2499
|
+
const eventMode = resolveAgentEventMode(options);
|
|
2500
|
+
let streamedAssistantText = false;
|
|
2501
|
+
let everStreamedAnyText = false;
|
|
2502
|
+
let printedAssistantLabel = false;
|
|
2503
|
+
const thinkingMode = resolveThinkingDisplayMode(options.env);
|
|
2504
|
+
const renderWriter = createRenderAwareStreamWriter({
|
|
2505
|
+
write: (chunk) => options.output.write(chunk)
|
|
2506
|
+
});
|
|
2507
|
+
const writeVisibleAssistantChunk = (chunk) => {
|
|
2508
|
+
if (!chunk) return;
|
|
2509
|
+
spinner.stop();
|
|
2510
|
+
options.activityReporter?.(null);
|
|
2511
|
+
if (!printedAssistantLabel) {
|
|
2512
|
+
options.output.write(`
|
|
2513
|
+
${options.agentName} > `);
|
|
2514
|
+
printedAssistantLabel = true;
|
|
2515
|
+
}
|
|
2516
|
+
streamedAssistantText = true;
|
|
2517
|
+
everStreamedAnyText = true;
|
|
2518
|
+
renderWriter.push(chunk);
|
|
2519
|
+
};
|
|
2520
|
+
const thinkingFilter = createThinkingAwareStreamFilter(
|
|
2521
|
+
writeVisibleAssistantChunk,
|
|
2522
|
+
thinkingMode
|
|
2523
|
+
);
|
|
2524
|
+
const thinkingSink = createThinkingEventSink((chunk) => {
|
|
2525
|
+
spinner.stop();
|
|
2526
|
+
options.activityReporter?.(null);
|
|
2527
|
+
options.output.write(chunk);
|
|
2528
|
+
}, thinkingMode);
|
|
2529
|
+
const handleToolEvent = (event) => {
|
|
2530
|
+
if (!traceLocalTools) return;
|
|
2531
|
+
const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
|
|
2532
|
+
if (eventMode === "jsonl") {
|
|
2533
|
+
options.output.write(chunk);
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
if (event.type === "tool-call") {
|
|
2537
|
+
thinkingFilter.flush();
|
|
2538
|
+
renderWriter.flush();
|
|
2539
|
+
}
|
|
2540
|
+
spinner.stop();
|
|
2541
|
+
options.activityReporter?.(null);
|
|
2542
|
+
if (event.type === "tool-call" && streamedAssistantText) {
|
|
2543
|
+
options.output.write("\n");
|
|
2544
|
+
streamedAssistantText = false;
|
|
2545
|
+
}
|
|
2546
|
+
if (chunk) {
|
|
2547
|
+
options.output.write(chunk);
|
|
2548
|
+
}
|
|
2549
|
+
if (toolDisplayMode === "compact" && event.type === "tool-call") {
|
|
2550
|
+
const activeLabel = formatActiveToolLabel(event);
|
|
2551
|
+
spinner.show(activeLabel);
|
|
2552
|
+
options.activityReporter?.(activeLabel);
|
|
2553
|
+
}
|
|
2554
|
+
};
|
|
2555
|
+
return {
|
|
2556
|
+
spinner,
|
|
2557
|
+
thinkingMode,
|
|
2558
|
+
toolDisplayMode,
|
|
2559
|
+
traceLocalTools,
|
|
2560
|
+
eventMode,
|
|
2561
|
+
pushText(chunk) {
|
|
2562
|
+
thinkingFilter.push(chunk);
|
|
2563
|
+
},
|
|
2564
|
+
pushThinking(chunk) {
|
|
2565
|
+
thinkingSink.push(chunk);
|
|
2566
|
+
},
|
|
2567
|
+
handleToolEvent,
|
|
2568
|
+
showWorking(label) {
|
|
2569
|
+
const activeLabel = label ?? workingLabel;
|
|
2570
|
+
spinner.show(activeLabel);
|
|
2571
|
+
options.activityReporter?.(activeLabel);
|
|
2572
|
+
},
|
|
2573
|
+
finish(fallbackContent) {
|
|
2574
|
+
spinner.stop();
|
|
2575
|
+
options.activityReporter?.(null);
|
|
2576
|
+
const pendingToolOutput = formatToolEvent.flush ? formatToolEvent.flush() : "";
|
|
2577
|
+
if (pendingToolOutput) {
|
|
2578
|
+
options.output.write(pendingToolOutput);
|
|
2579
|
+
}
|
|
2580
|
+
if (streamedAssistantText) {
|
|
2581
|
+
thinkingFilter.flush();
|
|
2582
|
+
renderWriter.flush();
|
|
2583
|
+
options.output.write("\n");
|
|
2584
|
+
} else if (everStreamedAnyText) {
|
|
2585
|
+
thinkingFilter.flush();
|
|
2586
|
+
options.output.write("\n");
|
|
2587
|
+
} else {
|
|
2588
|
+
const content = fallbackContent ? formatAssistantResponseForCli(fallbackContent.trim(), options) : "";
|
|
2589
|
+
if (content) {
|
|
2590
|
+
options.output.write(`
|
|
2591
|
+
${options.agentName} > ${content}
|
|
2592
|
+
`);
|
|
2593
|
+
} else {
|
|
2594
|
+
options.output.write(`
|
|
2595
|
+
${options.agentName} > (no text response)
|
|
2596
|
+
`);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// packages/cli/client/agentRunStream.ts
|
|
2604
|
+
async function readStreamingAgentRun(options, res, existingSpinner) {
|
|
2605
|
+
const reader = res.body?.getReader();
|
|
2606
|
+
if (!reader) {
|
|
2607
|
+
existingSpinner?.stop();
|
|
2608
|
+
options.output.write(
|
|
2609
|
+
"[nolo] Agent stream response did not include a readable body.\n"
|
|
2610
|
+
);
|
|
2611
|
+
return { exitCode: 1 };
|
|
2612
|
+
}
|
|
2613
|
+
options.abortSignal?.addEventListener(
|
|
2614
|
+
"abort",
|
|
2615
|
+
() => {
|
|
2616
|
+
reader.cancel("user-stop").catch(() => {
|
|
2617
|
+
});
|
|
2618
|
+
},
|
|
2619
|
+
{ once: true }
|
|
2620
|
+
);
|
|
2621
|
+
const decoder = new TextDecoder();
|
|
2622
|
+
const turnOutput = createCliTurnOutput({
|
|
2623
|
+
options,
|
|
2624
|
+
workingLabel: `${options.agentName} -> working`,
|
|
2625
|
+
spinner: existingSpinner
|
|
2626
|
+
});
|
|
2627
|
+
const sseAdapter = createSseToolEventAdapter((evt) => {
|
|
2628
|
+
turnOutput.handleToolEvent(evt);
|
|
2629
|
+
});
|
|
2630
|
+
let buffer = "";
|
|
2631
|
+
let content = "";
|
|
2632
|
+
let dialogId;
|
|
2633
|
+
let usage;
|
|
2634
|
+
const handlePayload = (payload) => {
|
|
2635
|
+
if (typeof payload?.dialogId === "string" && payload.dialogId.trim()) {
|
|
2636
|
+
dialogId = payload.dialogId;
|
|
2637
|
+
}
|
|
2638
|
+
if (payload?.error || payload?.type === "error") {
|
|
2639
|
+
throw new Error(
|
|
2640
|
+
String(payload.error || payload.message || "Agent stream failed")
|
|
2641
|
+
);
|
|
2642
|
+
}
|
|
2643
|
+
if (payload?.type === "done") {
|
|
2644
|
+
usage = payload.usage;
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
if (payload?.type === "dialog" || payload?.type === "status") {
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
if (payload?.type === "turn_warning") {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
if (payload?.type === "thinking") {
|
|
2654
|
+
const thinkChunk = typeof payload.content === "string" ? payload.content : typeof payload.chunk === "string" ? payload.chunk : "";
|
|
2655
|
+
if (thinkChunk) {
|
|
2656
|
+
turnOutput.pushThinking(thinkChunk);
|
|
2657
|
+
}
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
if (payload?.type === "tool_start") {
|
|
2661
|
+
sseAdapter.onToolStart(payload.calls ?? payload);
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
if (payload?.type === "tool_result") {
|
|
2665
|
+
sseAdapter.onToolResult(payload);
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
if (payload?.type === "tool_end") {
|
|
2669
|
+
sseAdapter.onToolEnd();
|
|
2670
|
+
turnOutput.showWorking();
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
const chunk = payload?.type === "text" ? payload.content : typeof payload?.chunk === "string" ? payload.chunk : "";
|
|
2674
|
+
if (!chunk) return;
|
|
2675
|
+
content += chunk;
|
|
2676
|
+
turnOutput.pushText(chunk);
|
|
2677
|
+
};
|
|
2678
|
+
try {
|
|
2679
|
+
while (true) {
|
|
2680
|
+
const { done, value } = await reader.read();
|
|
2681
|
+
if (done) break;
|
|
2682
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2683
|
+
const events = buffer.split("\n\n");
|
|
2684
|
+
buffer = events.pop() ?? "";
|
|
2685
|
+
for (const event of events) {
|
|
2686
|
+
const dataLines = event.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
|
|
2687
|
+
for (const raw of dataLines) {
|
|
2688
|
+
handlePayload(JSON.parse(raw));
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
if (buffer.trim()) {
|
|
2693
|
+
const raw = buffer.split("\n").find((line) => line.startsWith("data:"))?.slice(5).trim();
|
|
2694
|
+
if (raw) handlePayload(JSON.parse(raw));
|
|
2695
|
+
}
|
|
2696
|
+
} catch (error) {
|
|
2697
|
+
turnOutput.spinner.stop();
|
|
2698
|
+
if (options.abortSignal?.aborted) {
|
|
2699
|
+
return {
|
|
2700
|
+
exitCode: 0,
|
|
2701
|
+
...dialogId ? { dialogId } : {},
|
|
2702
|
+
streamInterrupted: true
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2705
|
+
const message = toErrorMessage(error);
|
|
2706
|
+
if (dialogId) {
|
|
2707
|
+
options.output.write(
|
|
2708
|
+
`
|
|
2709
|
+
[nolo] Agent stream transport interrupted after dialog ${dialogId} was created: ${message}
|
|
2710
|
+
`
|
|
2711
|
+
);
|
|
2712
|
+
options.output.write(
|
|
2713
|
+
"[nolo] The agent run may still finish on the server; read the dialog before retrying.\n"
|
|
2714
|
+
);
|
|
2715
|
+
return { exitCode: 0, dialogId, streamInterrupted: true };
|
|
2716
|
+
}
|
|
2717
|
+
options.output.write(`
|
|
2718
|
+
[nolo] Agent stream failed: ${message}
|
|
2719
|
+
`);
|
|
2720
|
+
return { exitCode: 1 };
|
|
2721
|
+
}
|
|
2722
|
+
turnOutput.finish(content);
|
|
2723
|
+
const usageText = formatUsage(usage, dialogId);
|
|
2724
|
+
if (usageText && shouldShowUsage(options.env))
|
|
2725
|
+
options.output.write(`${usageText}
|
|
2726
|
+
`);
|
|
2727
|
+
return {
|
|
2728
|
+
exitCode: 0,
|
|
2729
|
+
...dialogId ? { dialogId } : {},
|
|
2730
|
+
turnTokens: buildTurnTokenUsage(usage, options.agentKey)
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
// packages/cli/client/agentRunTypes.ts
|
|
2735
|
+
function resolveAuthToken2(env) {
|
|
2736
|
+
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
2737
|
+
}
|
|
2738
|
+
function isMachineBoundLocalhostCustomProvider(agentConfig) {
|
|
2739
|
+
const machineId = agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object" ? String(agentConfig.runtimeBinding.machineId ?? "").trim() : "";
|
|
2740
|
+
const providerUrl = asTrimmedString(agentConfig?.customProviderUrl);
|
|
2741
|
+
if (!machineId || !providerUrl) return false;
|
|
2742
|
+
return isLoopbackUrl(providerUrl);
|
|
2743
|
+
}
|
|
2744
|
+
function resolveBoundMachineId(agentConfig) {
|
|
2745
|
+
return agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object" ? String(agentConfig.runtimeBinding.machineId ?? "").trim() : "";
|
|
2746
|
+
}
|
|
2747
|
+
var CLI_PROVIDER_NAMES = /* @__PURE__ */ new Set([
|
|
2748
|
+
"agy",
|
|
2749
|
+
"claude",
|
|
2750
|
+
"codex",
|
|
2751
|
+
"copilot",
|
|
2752
|
+
"gemini",
|
|
2753
|
+
"grok",
|
|
2754
|
+
"kimi",
|
|
2755
|
+
"opencode",
|
|
2756
|
+
"qoder"
|
|
2757
|
+
]);
|
|
2758
|
+
function isCliProviderAgentConfig(agentConfig) {
|
|
2759
|
+
const cliProvider = asTrimmedLowercaseString(agentConfig?.cliProvider);
|
|
2760
|
+
const provider = asTrimmedLowercaseString(agentConfig?.provider);
|
|
2761
|
+
return Boolean(cliProvider) || CLI_PROVIDER_NAMES.has(provider);
|
|
2762
|
+
}
|
|
2763
|
+
async function detectCurrentMachineId(env) {
|
|
2764
|
+
const fromEnv = (env.NOLO_CURRENT_MACHINE_ID || env.NOLO_MACHINE_ID || "").trim();
|
|
2765
|
+
if (fromEnv) return fromEnv;
|
|
2766
|
+
try {
|
|
2767
|
+
const { detectMachineInfo } = await import("./machineInfo-M6KB6HZN.js");
|
|
2768
|
+
const machine = await detectMachineInfo({ probeLaunchable: true });
|
|
2769
|
+
return asOptionalTrimmedString(machine?.machineId);
|
|
2770
|
+
} catch {
|
|
2771
|
+
return void 0;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
// packages/cli/client/agentRunPlatformTools.ts
|
|
2776
|
+
var SERVER_PLATFORM_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
2777
|
+
"addTableRow",
|
|
2778
|
+
"addTableRows",
|
|
2779
|
+
"deleteTableRow",
|
|
2780
|
+
"deleteTableRows",
|
|
2781
|
+
"updateTableRow",
|
|
2782
|
+
"updateTableRows"
|
|
2783
|
+
]);
|
|
2784
|
+
var KNOWN_SERVER_PLATFORM_AGENT_KEYS = /* @__PURE__ */ new Set([NOLO_PROJECT_MANAGER_AGENT_KEY]);
|
|
2785
|
+
var KNOWN_SERVER_PLATFORM_AGENT_ALIASES = /* @__PURE__ */ new Set([
|
|
2786
|
+
"code-review",
|
|
2787
|
+
"frontend",
|
|
2788
|
+
"frontend-agent",
|
|
2789
|
+
"frontend-implementer",
|
|
2790
|
+
"full-stack",
|
|
2791
|
+
"fullstack",
|
|
2792
|
+
"nolo code review",
|
|
2793
|
+
"nolo fullstack",
|
|
2794
|
+
"nolo project manager",
|
|
2795
|
+
"nolo reviewer",
|
|
2796
|
+
"nolo-code-review",
|
|
2797
|
+
"nolo-fullstack",
|
|
2798
|
+
"nolo-pm",
|
|
2799
|
+
"nolo-project-manager",
|
|
2800
|
+
"nolo-reviewer",
|
|
2801
|
+
"pm",
|
|
2802
|
+
"project-manager",
|
|
2803
|
+
"review",
|
|
2804
|
+
"reviewer"
|
|
2805
|
+
]);
|
|
2806
|
+
function findServerPlatformTools(toolNames) {
|
|
2807
|
+
if (!Array.isArray(toolNames)) return [];
|
|
2808
|
+
return toolNames.filter(
|
|
2809
|
+
(toolName) => SERVER_PLATFORM_TOOL_NAMES.has(toolName)
|
|
2810
|
+
);
|
|
2811
|
+
}
|
|
2812
|
+
function resolveServerPlatformToolNames(agentConfig) {
|
|
2813
|
+
return findServerPlatformTools([
|
|
2814
|
+
...Array.isArray(agentConfig?.toolNames) ? agentConfig.toolNames : [],
|
|
2815
|
+
...Array.isArray(agentConfig?.runtimeToolPolicy?.agentTools) ? agentConfig.runtimeToolPolicy.agentTools : []
|
|
2816
|
+
]);
|
|
2817
|
+
}
|
|
2818
|
+
function isKnownServerPlatformAgent(options) {
|
|
2819
|
+
if (KNOWN_SERVER_PLATFORM_AGENT_KEYS.has(options.agentKey)) return true;
|
|
2820
|
+
const normalizedKey = normalizeAgentHandle(options.agentKey);
|
|
2821
|
+
return Boolean(
|
|
2822
|
+
normalizedKey && KNOWN_SERVER_PLATFORM_AGENT_ALIASES.has(normalizedKey)
|
|
2823
|
+
);
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
// packages/cli/client/agentRun.ts
|
|
2827
|
+
import { ulid } from "ulid";
|
|
2828
|
+
async function loadRunLocalAgentTurn() {
|
|
2829
|
+
const { runLocalAgentTurn } = await import("./agentRuntimeLocal-ZYEIPTY4.js");
|
|
2830
|
+
return runLocalAgentTurn;
|
|
2831
|
+
}
|
|
2832
|
+
async function resolveCurrentMachineId(options) {
|
|
2833
|
+
return options.currentMachineIdResolver ? options.currentMachineIdResolver(options.env) : detectCurrentMachineId(options.env);
|
|
2834
|
+
}
|
|
2835
|
+
function resolveRequestedRuntimeMode(options) {
|
|
2836
|
+
const envMode = options.env.NOLO_RUNTIME_MODE;
|
|
2837
|
+
if (options.runtimeMode) return options.runtimeMode;
|
|
2838
|
+
if (envMode === "local" || envMode === "server" || envMode === "auto")
|
|
2839
|
+
return envMode;
|
|
2840
|
+
return "auto";
|
|
2841
|
+
}
|
|
2842
|
+
function buildDefaultLocalRuntimeAdapter(options) {
|
|
2843
|
+
return createCliLocalRuntimeAdapter({
|
|
2844
|
+
env: options.env,
|
|
2845
|
+
fetchImpl: options.fetchImpl,
|
|
2846
|
+
cwd: options.localRuntimeCwd,
|
|
2847
|
+
output: options.output,
|
|
2848
|
+
...options.confirmDestructiveAction ? { confirmDestructiveAction: options.confirmDestructiveAction } : {},
|
|
2849
|
+
...options.requestUserChoice ? { requestUserChoice: options.requestUserChoice } : {}
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
function resolveLocalRuntimeAdapter(options) {
|
|
2853
|
+
return options.localRuntimeAdapter || options.localRuntimeAdapterFactory?.(options.env, {
|
|
2854
|
+
cwd: options.localRuntimeCwd
|
|
2855
|
+
}) || buildDefaultLocalRuntimeAdapter(options);
|
|
2856
|
+
}
|
|
2857
|
+
function wrapAdapterEphemeral(adapter) {
|
|
2858
|
+
return {
|
|
2859
|
+
...adapter,
|
|
2860
|
+
loadDialogHistory: async () => [],
|
|
2861
|
+
saveTurn: async (input) => ({
|
|
2862
|
+
dialogId: input.continueDialogId ?? "ephemeral"
|
|
2863
|
+
})
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function applyEphemeralIfRequested(options, adapter) {
|
|
2867
|
+
return options.ephemeral ? wrapAdapterEphemeral(adapter) : adapter;
|
|
2868
|
+
}
|
|
2869
|
+
function wrapLoadAgentConfigWithModelOverride(adapter, targetAgentKey, override) {
|
|
2870
|
+
return {
|
|
2871
|
+
...adapter,
|
|
2872
|
+
loadAgentConfig: async (agentRef) => {
|
|
2873
|
+
const config = await adapter.loadAgentConfig(agentRef);
|
|
2874
|
+
if (!config || agentRef !== targetAgentKey) return config;
|
|
2875
|
+
const baseRecord = config.rawRecord ?? config;
|
|
2876
|
+
return resolveAgentRuntimeConfigFromRecord(
|
|
2877
|
+
agentRef,
|
|
2878
|
+
applyModelLayerOverride(baseRecord, override)
|
|
2879
|
+
);
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
async function shouldSkipAutoLocalForServerPlatformTools(options) {
|
|
2884
|
+
if (isBuiltinNoloAgentRef(options.agentKey)) return false;
|
|
2885
|
+
if (options.localRuntimeCwd) {
|
|
2886
|
+
return false;
|
|
2887
|
+
}
|
|
2888
|
+
const knownServerPlatformAgent = isKnownServerPlatformAgent(options);
|
|
2889
|
+
const adapter = resolveLocalRuntimeAdapter(options);
|
|
2890
|
+
if (!adapter) return knownServerPlatformAgent;
|
|
2891
|
+
let agentConfig;
|
|
2892
|
+
try {
|
|
2893
|
+
agentConfig = await adapter.loadAgentConfig(options.agentKey);
|
|
2894
|
+
} catch {
|
|
2895
|
+
if (knownServerPlatformAgent) {
|
|
2896
|
+
options.output.write(
|
|
2897
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. Use --local explicitly to force local workspace tools.
|
|
2898
|
+
`
|
|
2899
|
+
);
|
|
2900
|
+
return true;
|
|
2901
|
+
}
|
|
2902
|
+
return false;
|
|
2903
|
+
}
|
|
2904
|
+
if (isCliProviderAgentConfig(agentConfig)) {
|
|
2905
|
+
const boundMachineId = resolveBoundMachineId(agentConfig);
|
|
2906
|
+
if (!boundMachineId) return false;
|
|
2907
|
+
const currentMachineId = (await resolveCurrentMachineId(options))?.trim() || "";
|
|
2908
|
+
if (currentMachineId && currentMachineId === boundMachineId) return false;
|
|
2909
|
+
options.output.write(
|
|
2910
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is bound to ${boundMachineId}` + (currentMachineId ? ` and this machine is ${currentMachineId}.` : ".") + " Use --local explicitly to force the current machine.\n"
|
|
2911
|
+
);
|
|
2912
|
+
return true;
|
|
2913
|
+
}
|
|
2914
|
+
if (knownServerPlatformAgent) {
|
|
2915
|
+
options.output.write(
|
|
2916
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. Use --local explicitly to force local workspace tools.
|
|
2917
|
+
`
|
|
2918
|
+
);
|
|
2919
|
+
return true;
|
|
2920
|
+
}
|
|
2921
|
+
if (isMachineBoundLocalhostCustomProvider(agentConfig)) {
|
|
2922
|
+
options.output.write(
|
|
2923
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a machine-bound localhost custom provider. Use --local explicitly to force the current machine.
|
|
2924
|
+
`
|
|
2925
|
+
);
|
|
2926
|
+
return true;
|
|
2927
|
+
}
|
|
2928
|
+
const serverTools = resolveServerPlatformToolNames(agentConfig);
|
|
2929
|
+
if (serverTools.length === 0) return false;
|
|
2930
|
+
options.output.write(
|
|
2931
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} declares server platform tools (${serverTools.join(", ")}). Use --local explicitly to force local workspace tools.
|
|
2932
|
+
`
|
|
2933
|
+
);
|
|
2934
|
+
return true;
|
|
2935
|
+
}
|
|
2936
|
+
function buildUserInputContent(message, imageUrls = []) {
|
|
2937
|
+
if (imageUrls.length === 0) return message;
|
|
2938
|
+
return [
|
|
2939
|
+
...message.trim() ? [{ type: "text", text: message }] : [],
|
|
2940
|
+
...imageUrls.map((url) => ({
|
|
2941
|
+
type: "image_url",
|
|
2942
|
+
image_url: { url }
|
|
2943
|
+
}))
|
|
2944
|
+
];
|
|
2945
|
+
}
|
|
2946
|
+
function buildSubjectRefs(options) {
|
|
2947
|
+
const refs = [];
|
|
2948
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2949
|
+
const pushRef = (ref) => {
|
|
2950
|
+
const kind = ref.kind.trim();
|
|
2951
|
+
const id = ref.id.trim();
|
|
2952
|
+
const role = ref.role?.trim();
|
|
2953
|
+
if (!kind || !id) return;
|
|
2954
|
+
const key = `${kind}\0${id}\0${role ?? ""}`;
|
|
2955
|
+
if (seen.has(key)) return;
|
|
2956
|
+
seen.add(key);
|
|
2957
|
+
refs.push({ kind, id, ...role ? { role } : {} });
|
|
2958
|
+
};
|
|
2959
|
+
for (const ref of options.subjectRefs ?? []) pushRef(ref);
|
|
2960
|
+
if (options.subjectDialogKey) {
|
|
2961
|
+
pushRef({
|
|
2962
|
+
kind: "dialog",
|
|
2963
|
+
id: options.subjectDialogKey,
|
|
2964
|
+
role: "subject"
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
if (options.taskEvidence?.rowDbKey) {
|
|
2968
|
+
pushRef({
|
|
2969
|
+
kind: "table-row",
|
|
2970
|
+
id: options.taskEvidence.rowDbKey,
|
|
2971
|
+
role: "task"
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
for (const artifactId of options.taskEvidence?.artifactIds ?? []) {
|
|
2975
|
+
pushRef({
|
|
2976
|
+
kind: "artifact",
|
|
2977
|
+
id: artifactId,
|
|
2978
|
+
role: "evidence"
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
return refs.length ? refs : void 0;
|
|
2982
|
+
}
|
|
2983
|
+
function isMissingLocalAgentConfigError(error, agentRef) {
|
|
2984
|
+
return Boolean(
|
|
2985
|
+
error && typeof error === "object" && "code" in error && error.code === LOCAL_AGENT_CONFIG_MISSING_CODE && error.agentRef === agentRef
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
function shouldAttemptAutoLocal(options) {
|
|
2989
|
+
if (options.localRuntimeAdapter || options.localRuntimeAdapterFactory)
|
|
2990
|
+
return true;
|
|
2991
|
+
if (options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" && isBuiltinNoloAgentRef(options.agentKey) && resolveAuthToken2(options.env)) {
|
|
2992
|
+
return true;
|
|
2993
|
+
}
|
|
2994
|
+
if (options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" && resolveAuthToken2(options.env) && !isKnownServerPlatformAgent(options)) {
|
|
2995
|
+
return true;
|
|
2996
|
+
}
|
|
2997
|
+
return Boolean(
|
|
2998
|
+
options.env.NOLO_LOCAL_OPENAI_API_KEY || options.env.OPENAI_API_KEY || options.env.NOLO_LOCAL_OPENAI_BASE_URL || options.env.OPENAI_BASE_URL || options.env.NOLO_LOCAL_AGENT_KEY
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
function buildTransportErrorHint(serverUrl, error) {
|
|
3002
|
+
const endpoint = `${serverUrl}/api/agent/run`;
|
|
3003
|
+
const reason = toErrorMessage(error);
|
|
3004
|
+
let detail = `[nolo] Could not reach ${endpoint}.
|
|
3005
|
+
Reason: ${reason}
|
|
3006
|
+
`;
|
|
3007
|
+
try {
|
|
3008
|
+
const parsed = new URL(serverUrl);
|
|
3009
|
+
if (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost") {
|
|
3010
|
+
detail += "If you meant local dev, start the local API first.\nOtherwise set NOLO_SERVER to a reachable server, or re-run `nolo login --server https://nolo.chat`.\n";
|
|
3011
|
+
return detail;
|
|
3012
|
+
}
|
|
3013
|
+
} catch {
|
|
3014
|
+
}
|
|
3015
|
+
detail += "Check NOLO_SERVER / BASE_URL and make sure the configured server is reachable.\n";
|
|
3016
|
+
return detail;
|
|
3017
|
+
}
|
|
3018
|
+
async function readAgentRunFailureMetadata(res) {
|
|
3019
|
+
const data = await res.clone().json().catch(() => ({}));
|
|
3020
|
+
return {
|
|
3021
|
+
...asOptionalTrimmedString(data?.dialogId) ? { dialogId: asOptionalTrimmedString(data?.dialogId) } : {}
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
async function runHttpAgentTurn(options, authToken) {
|
|
3025
|
+
const spinner = new Spinner(
|
|
3026
|
+
options.output,
|
|
3027
|
+
`${options.agentName} -> working`,
|
|
3028
|
+
// 与 createCliTurnOutput 的兜底构造一致:传入 activityReporter 时
|
|
3029
|
+
// Spinner 静默,避免 TUI docked 活动行与 spinner 帧重复 live 指示。
|
|
3030
|
+
Boolean(options.activityReporter)
|
|
3031
|
+
);
|
|
3032
|
+
spinner.start();
|
|
3033
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
3034
|
+
const subjectRefs = buildSubjectRefs(options);
|
|
3035
|
+
const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter(
|
|
3036
|
+
(key) => key.trim()
|
|
3037
|
+
);
|
|
3038
|
+
const allowedToolNames = options.allowedToolNames?.filter(
|
|
3039
|
+
(name) => name.trim()
|
|
3040
|
+
);
|
|
3041
|
+
const blockedToolNames = options.blockedToolNames?.filter(
|
|
3042
|
+
(name) => name.trim()
|
|
3043
|
+
);
|
|
3044
|
+
const shouldStream = !options.noStream && !options.background;
|
|
3045
|
+
const buildRequestBody = (stream) => JSON.stringify({
|
|
3046
|
+
agentKey: options.agentKey,
|
|
3047
|
+
userInput: buildUserInputContent(
|
|
3048
|
+
options.extraContextBlocks?.length ? [...options.extraContextBlocks, "", options.message].join("\n") : options.message,
|
|
3049
|
+
options.imageUrls
|
|
3050
|
+
),
|
|
3051
|
+
runtimeContext: {
|
|
3052
|
+
surface: "cli",
|
|
3053
|
+
host: "terminal",
|
|
3054
|
+
runtime: "bun",
|
|
3055
|
+
entrypoint: "nolo-cli",
|
|
3056
|
+
capabilities: ["text-io", "streaming", "slash-commands"],
|
|
3057
|
+
...subjectRefs ? { subjectRefs } : {},
|
|
3058
|
+
...allowedChildAgentKeys?.length ? { allowedChildAgentKeys } : {},
|
|
3059
|
+
...blockedToolNames?.length ? { blockedToolNames } : {},
|
|
3060
|
+
...allowedToolNames?.length ? { allowedToolNames } : {}
|
|
3061
|
+
},
|
|
3062
|
+
...options.continueDialogId ? { continueDialogId: options.continueDialogId } : {},
|
|
3063
|
+
...options.spaceId ? { spaceId: options.spaceId } : {},
|
|
3064
|
+
...options.category ? { category: options.category } : {},
|
|
3065
|
+
...options.inheritedFromDialogKey ? { inheritedFromDialogKey: options.inheritedFromDialogKey } : {},
|
|
3066
|
+
...options.parentDialogId ? { parentDialogId: options.parentDialogId } : {},
|
|
3067
|
+
...options.background ? { background: true } : {},
|
|
3068
|
+
...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
|
|
3069
|
+
...options.modelOverride ? { runtimeOptions: { quickChatModelOverride: options.modelOverride } } : {},
|
|
3070
|
+
stream
|
|
3071
|
+
});
|
|
3072
|
+
const postAgentRun = (stream) => fetchImpl(`${options.serverUrl}/api/agent/run`, {
|
|
3073
|
+
method: "POST",
|
|
3074
|
+
headers: {
|
|
3075
|
+
Authorization: `Bearer ${authToken}`,
|
|
3076
|
+
"Content-Type": "application/json"
|
|
3077
|
+
},
|
|
3078
|
+
body: buildRequestBody(stream),
|
|
3079
|
+
...options.abortSignal ? { signal: options.abortSignal } : {}
|
|
3080
|
+
});
|
|
3081
|
+
let res;
|
|
3082
|
+
try {
|
|
3083
|
+
res = await postAgentRun(shouldStream);
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
spinner.stop();
|
|
3086
|
+
if (options.abortSignal?.aborted) {
|
|
3087
|
+
return { exitCode: 0, streamInterrupted: true };
|
|
3088
|
+
}
|
|
3089
|
+
options.output.write(buildTransportErrorHint(options.serverUrl, error));
|
|
3090
|
+
return { exitCode: 1 };
|
|
3091
|
+
}
|
|
3092
|
+
if (shouldStream && isGatewayHttpStatus(res.status)) {
|
|
3093
|
+
const failureMeta = await readAgentRunFailureMetadata(res);
|
|
3094
|
+
if (!failureMeta.dialogId) {
|
|
3095
|
+
spinner.stop();
|
|
3096
|
+
options.output.write(
|
|
3097
|
+
`[nolo] streaming request returned HTTP ${res.status}; retrying once without streaming.
|
|
3098
|
+
`
|
|
3099
|
+
);
|
|
3100
|
+
spinner.start();
|
|
3101
|
+
try {
|
|
3102
|
+
res = await postAgentRun(false);
|
|
3103
|
+
} catch (error) {
|
|
3104
|
+
spinner.stop();
|
|
3105
|
+
options.output.write(buildTransportErrorHint(options.serverUrl, error));
|
|
3106
|
+
return { exitCode: 1 };
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
const contentType = res.headers.get("content-type") || "";
|
|
3111
|
+
if (contentType.includes("text/event-stream") && res.body) {
|
|
3112
|
+
const result = await readStreamingAgentRun(options, res, spinner);
|
|
3113
|
+
return result;
|
|
3114
|
+
}
|
|
3115
|
+
spinner.stop();
|
|
3116
|
+
let data = {};
|
|
3117
|
+
try {
|
|
3118
|
+
data = await res.json();
|
|
3119
|
+
} catch {
|
|
3120
|
+
data = {};
|
|
3121
|
+
}
|
|
3122
|
+
if (!res.ok) {
|
|
3123
|
+
options.output.write(`[nolo] Agent request failed: HTTP ${res.status}
|
|
3124
|
+
`);
|
|
3125
|
+
const errorText = asTrimmedString(data?.error);
|
|
3126
|
+
const messageText = asTrimmedString(data?.message);
|
|
3127
|
+
const reasonText = asTrimmedString(data?.reason);
|
|
3128
|
+
const codeText = asTrimmedString(data?.code);
|
|
3129
|
+
const dialogIdText = asTrimmedString(data?.dialogId);
|
|
3130
|
+
if (errorText || messageText) {
|
|
3131
|
+
options.output.write(`${errorText || messageText}
|
|
3132
|
+
`);
|
|
3133
|
+
if (messageText && messageText !== errorText) {
|
|
3134
|
+
options.output.write(`${messageText}
|
|
3135
|
+
`);
|
|
3136
|
+
}
|
|
3137
|
+
if (codeText && codeText !== errorText && codeText !== messageText) {
|
|
3138
|
+
options.output.write(`code=${codeText}
|
|
3139
|
+
`);
|
|
3140
|
+
}
|
|
3141
|
+
if (reasonText && reasonText !== errorText && reasonText !== messageText) {
|
|
3142
|
+
options.output.write(`reason=${reasonText}
|
|
3143
|
+
`);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
if (dialogIdText) {
|
|
3147
|
+
options.output.write(`[nolo] failed dialog: ${dialogIdText}
|
|
3148
|
+
`);
|
|
3149
|
+
options.output.write(
|
|
3150
|
+
`[nolo] continue with: nolo agent run ${options.agentKey} --continue ${dialogIdText} --msg "retry"
|
|
3151
|
+
`
|
|
3152
|
+
);
|
|
3153
|
+
}
|
|
3154
|
+
return dialogIdText ? { exitCode: 1, dialogId: dialogIdText } : { exitCode: 1 };
|
|
3155
|
+
}
|
|
3156
|
+
const content = formatAssistantResponseForCli(
|
|
3157
|
+
String(data?.content ?? data?.message ?? ""),
|
|
3158
|
+
options
|
|
3159
|
+
);
|
|
3160
|
+
if (content) {
|
|
3161
|
+
options.output.write(`
|
|
3162
|
+
${options.agentName} > ${content}
|
|
3163
|
+
`);
|
|
3164
|
+
} else {
|
|
3165
|
+
options.output.write(`
|
|
3166
|
+
${options.agentName} > (no text response)
|
|
3167
|
+
`);
|
|
3168
|
+
}
|
|
3169
|
+
const usage = formatUsage(data?.usage, data?.dialogId);
|
|
3170
|
+
if (usage && shouldShowUsage(options.env)) options.output.write(`${usage}
|
|
3171
|
+
`);
|
|
3172
|
+
return {
|
|
3173
|
+
exitCode: 0,
|
|
3174
|
+
...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {},
|
|
3175
|
+
turnTokens: buildTurnTokenUsage(
|
|
3176
|
+
data?.usage,
|
|
3177
|
+
typeof data?.model === "string" ? data.model : options.agentKey
|
|
3178
|
+
)
|
|
3179
|
+
};
|
|
3180
|
+
}
|
|
3181
|
+
async function runInjectedLocalAgentTurn(options) {
|
|
3182
|
+
return runLocalAgentTurnForCli(options, { reportFailure: true });
|
|
3183
|
+
}
|
|
3184
|
+
async function refreshMissingLocalAgentConfig(options) {
|
|
3185
|
+
const adapter = resolveLocalRuntimeAdapter(options);
|
|
3186
|
+
if (!adapter) return false;
|
|
3187
|
+
const agentConfig = await adapter.loadAgentConfig(options.agentKey);
|
|
3188
|
+
return Boolean(agentConfig);
|
|
3189
|
+
}
|
|
3190
|
+
async function runLocalAgentTurnForCli(options, settings) {
|
|
3191
|
+
const resolvedBaseAdapter = resolveLocalRuntimeAdapter(options);
|
|
3192
|
+
const baseAdapter = (() => {
|
|
3193
|
+
let adapter2 = resolvedBaseAdapter && options.modelOverride ? wrapLoadAgentConfigWithModelOverride(
|
|
3194
|
+
resolvedBaseAdapter,
|
|
3195
|
+
options.agentKey,
|
|
3196
|
+
options.modelOverride
|
|
3197
|
+
) : resolvedBaseAdapter;
|
|
3198
|
+
if (adapter2) adapter2 = applyEphemeralIfRequested(options, adapter2);
|
|
3199
|
+
return adapter2;
|
|
3200
|
+
})();
|
|
3201
|
+
if (!baseAdapter) {
|
|
3202
|
+
options.output.write(
|
|
3203
|
+
"[nolo] Local runtime was requested but no local runtime adapter is available.\n"
|
|
3204
|
+
);
|
|
3205
|
+
return { exitCode: 1 };
|
|
3206
|
+
}
|
|
3207
|
+
const subjectRefs = buildSubjectRefs(options);
|
|
3208
|
+
const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter(
|
|
3209
|
+
(key) => key.trim()
|
|
3210
|
+
);
|
|
3211
|
+
const allowedToolNames = options.allowedToolNames?.filter(
|
|
3212
|
+
(name) => name.trim()
|
|
3213
|
+
);
|
|
3214
|
+
const blockedToolNames = options.blockedToolNames?.filter(
|
|
3215
|
+
(name) => name.trim()
|
|
3216
|
+
);
|
|
3217
|
+
const runtimeContext = subjectRefs || allowedChildAgentKeys?.length || allowedToolNames?.length || blockedToolNames?.length || options.parentWakeOnTerminal ? {
|
|
3218
|
+
...subjectRefs ? { subjectRefs } : {},
|
|
3219
|
+
...allowedChildAgentKeys?.length ? { allowedChildAgentKeys } : {},
|
|
3220
|
+
...allowedToolNames?.length ? { allowedToolNames } : {},
|
|
3221
|
+
...blockedToolNames?.length ? { blockedToolNames } : {},
|
|
3222
|
+
...options.parentWakeOnTerminal ? { parentWakeOnTerminal: true } : {},
|
|
3223
|
+
...options.parentDialogId ? { parentThreadId: options.parentDialogId } : {}
|
|
3224
|
+
} : void 0;
|
|
3225
|
+
const currentDialogId = options.continueDialogId ?? ulid();
|
|
3226
|
+
const runChildTurn = async (input) => {
|
|
3227
|
+
const { runLocalAgentTurn } = await import("./agentRuntimeLocal-ZYEIPTY4.js");
|
|
3228
|
+
return runLocalAgentTurn(input);
|
|
3229
|
+
};
|
|
3230
|
+
const createFreshChildBaseAdapter = () => {
|
|
3231
|
+
const fresh = options.localRuntimeAdapterFactory?.(options.env, {
|
|
3232
|
+
cwd: options.localRuntimeCwd
|
|
3233
|
+
}) ?? buildDefaultLocalRuntimeAdapter(options);
|
|
3234
|
+
return applyEphemeralIfRequested(options, fresh);
|
|
3235
|
+
};
|
|
3236
|
+
const withLocalDelegation = (args) => {
|
|
3237
|
+
let adapter2;
|
|
3238
|
+
const callAgentExecutor = createCliCallAgentToolExecutor(
|
|
3239
|
+
{
|
|
3240
|
+
env: options.env,
|
|
3241
|
+
fetchImpl: options.fetchImpl,
|
|
3242
|
+
cwd: options.localRuntimeCwd,
|
|
3243
|
+
output: options.output
|
|
3244
|
+
},
|
|
3245
|
+
{
|
|
3246
|
+
createChildAdapter: (child) => withLocalDelegation({
|
|
3247
|
+
base: createFreshChildBaseAdapter(),
|
|
3248
|
+
dialogId: child.dialogId,
|
|
3249
|
+
spaceId: child.spaceId,
|
|
3250
|
+
runtimeContext: child.runtimeContext
|
|
3251
|
+
}),
|
|
3252
|
+
runChildTurn,
|
|
3253
|
+
dialogId: args.dialogId,
|
|
3254
|
+
spaceId: args.spaceId,
|
|
3255
|
+
runtimeContext: args.runtimeContext
|
|
3256
|
+
}
|
|
3257
|
+
);
|
|
3258
|
+
adapter2 = {
|
|
3259
|
+
...args.base,
|
|
3260
|
+
executeTool: async (call) => {
|
|
3261
|
+
if (call.name === "callAgent") {
|
|
3262
|
+
return callAgentExecutor(call);
|
|
3263
|
+
}
|
|
3264
|
+
return args.base.executeTool(call);
|
|
3265
|
+
}
|
|
3266
|
+
};
|
|
3267
|
+
return adapter2;
|
|
3268
|
+
};
|
|
3269
|
+
const adapter = withLocalDelegation({
|
|
3270
|
+
base: baseAdapter,
|
|
3271
|
+
dialogId: currentDialogId,
|
|
3272
|
+
spaceId: options.spaceId,
|
|
3273
|
+
runtimeContext
|
|
3274
|
+
});
|
|
3275
|
+
const workingLabel = `${options.agentName} -> working locally`;
|
|
3276
|
+
const turnOutput = createCliTurnOutput({
|
|
3277
|
+
options,
|
|
3278
|
+
workingLabel
|
|
3279
|
+
});
|
|
3280
|
+
turnOutput.spinner.start();
|
|
3281
|
+
try {
|
|
3282
|
+
const runLocalAgentTurn = await loadRunLocalAgentTurn();
|
|
3283
|
+
const result = await runLocalAgentTurn({
|
|
3284
|
+
adapter,
|
|
3285
|
+
agentRef: options.agentKey,
|
|
3286
|
+
input: buildUserInputContent(options.message, options.imageUrls),
|
|
3287
|
+
continueDialogId: currentDialogId,
|
|
3288
|
+
spaceId: options.spaceId,
|
|
3289
|
+
category: options.category,
|
|
3290
|
+
inheritedFromDialogKey: options.inheritedFromDialogKey,
|
|
3291
|
+
parentDialogId: options.parentDialogId,
|
|
3292
|
+
background: options.background,
|
|
3293
|
+
noStream: options.noStream,
|
|
3294
|
+
...runtimeContext ? { runtimeContext } : {},
|
|
3295
|
+
...options.extraContextBlocks?.length ? { contextBlocks: options.extraContextBlocks } : {},
|
|
3296
|
+
...options.contextBlockScopes?.length ? { contextBlockScopes: options.contextBlockScopes } : {},
|
|
3297
|
+
...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
|
|
3298
|
+
...options.actionGateHandler ? { onActionGate: options.actionGateHandler } : {},
|
|
3299
|
+
onLoopEvent: (event) => {
|
|
3300
|
+
if (event.kind === "llm-start") {
|
|
3301
|
+
turnOutput.showWorking();
|
|
3302
|
+
}
|
|
3303
|
+
if (event.kind === "image-downgraded") {
|
|
3304
|
+
options.output.write(
|
|
3305
|
+
"[nolo] \u5F53\u524D agent \u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C\u5DF2\u7528\u5360\u4F4D\u6587\u672C\u66FF\u4EE3\u3002\u8981\u5B8C\u6574\u56FE\u7247\u7406\u89E3\u53EF /switch \u5230 Kimi K2.6\u3002\n"
|
|
3306
|
+
);
|
|
3307
|
+
}
|
|
3308
|
+
options.onLoopEvent?.(event);
|
|
3309
|
+
},
|
|
3310
|
+
...options.abortSignal ? { abortSignal: options.abortSignal } : {},
|
|
3311
|
+
...turnOutput.traceLocalTools ? {
|
|
3312
|
+
onToolEvent: (event) => {
|
|
3313
|
+
turnOutput.handleToolEvent(event);
|
|
3314
|
+
}
|
|
3315
|
+
} : {},
|
|
3316
|
+
...!options.noStream ? {
|
|
3317
|
+
onTextDelta: (chunk) => {
|
|
3318
|
+
turnOutput.pushText(chunk);
|
|
3319
|
+
}
|
|
3320
|
+
} : {}
|
|
3321
|
+
});
|
|
3322
|
+
turnOutput.finish(result.content);
|
|
3323
|
+
return {
|
|
3324
|
+
exitCode: 0,
|
|
3325
|
+
dialogId: result.dialogId,
|
|
3326
|
+
turnTokens: buildTurnTokenUsage(result.usage, result.model)
|
|
3327
|
+
};
|
|
3328
|
+
} catch (error) {
|
|
3329
|
+
turnOutput.spinner.stop();
|
|
3330
|
+
if (error?.code === LOCAL_TURN_ABORTED_CODE || options.abortSignal?.aborted) {
|
|
3331
|
+
const stoppedDialogId = error?.dialogId;
|
|
3332
|
+
return {
|
|
3333
|
+
exitCode: 0,
|
|
3334
|
+
streamInterrupted: true,
|
|
3335
|
+
...stoppedDialogId ? { dialogId: stoppedDialogId } : {}
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
if (settings.reportFailure) {
|
|
3339
|
+
options.output.write(
|
|
3340
|
+
`[nolo] Local agent run failed: ${toErrorMessage(error)}
|
|
3341
|
+
`
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
return { exitCode: 1, localError: error };
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
async function runAgentTurn(options) {
|
|
3348
|
+
const authToken = resolveAuthToken2(options.env);
|
|
3349
|
+
const runtimeMode = resolveRequestedRuntimeMode(options);
|
|
3350
|
+
if (runtimeMode === "local") {
|
|
3351
|
+
return runInjectedLocalAgentTurn(options);
|
|
3352
|
+
}
|
|
3353
|
+
if (runtimeMode === "auto" && shouldAttemptAutoLocal(options)) {
|
|
3354
|
+
const skipLocal = await shouldSkipAutoLocalForServerPlatformTools(options);
|
|
3355
|
+
if (!skipLocal) {
|
|
3356
|
+
const localResult = await runLocalAgentTurnForCli(options, {
|
|
3357
|
+
reportFailure: false
|
|
3358
|
+
});
|
|
3359
|
+
if (localResult.exitCode === 0) {
|
|
3360
|
+
return {
|
|
3361
|
+
exitCode: localResult.exitCode,
|
|
3362
|
+
...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
|
|
3363
|
+
...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {},
|
|
3364
|
+
...localResult.streamInterrupted ? { streamInterrupted: localResult.streamInterrupted } : {}
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
|
|
3368
|
+
options.output.write(
|
|
3369
|
+
`[nolo] Local agent config was missing; refreshing local config and retrying local once.
|
|
3370
|
+
`
|
|
3371
|
+
);
|
|
3372
|
+
try {
|
|
3373
|
+
const refreshed = await refreshMissingLocalAgentConfig(options);
|
|
3374
|
+
if (refreshed) {
|
|
3375
|
+
const retriedLocalResult = await runLocalAgentTurnForCli(options, {
|
|
3376
|
+
reportFailure: false
|
|
3377
|
+
});
|
|
3378
|
+
if (retriedLocalResult.exitCode === 0) {
|
|
3379
|
+
return {
|
|
3380
|
+
exitCode: retriedLocalResult.exitCode,
|
|
3381
|
+
...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {},
|
|
3382
|
+
...retriedLocalResult.turnTokens ? { turnTokens: retriedLocalResult.turnTokens } : {}
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
} catch {
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
const localErrorMessage = localResult.localError ? toErrorMessage(localResult.localError) : "local runtime failed";
|
|
3390
|
+
options.output.write(
|
|
3391
|
+
`[nolo] auto runtime: local run unavailable (${localErrorMessage}). Not falling back to server. Fix the local credential/config and retry, or use --server to run on the server explicitly.
|
|
3392
|
+
`
|
|
3393
|
+
);
|
|
3394
|
+
return {
|
|
3395
|
+
exitCode: 1,
|
|
3396
|
+
...localResult.dialogId ? { dialogId: localResult.dialogId } : {}
|
|
3397
|
+
};
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
if (!authToken) {
|
|
3401
|
+
options.output.write(
|
|
3402
|
+
"[nolo] This install needs an auth token before it can talk to agents.\nRun `nolo login`, or set AUTH_TOKEN / NOLO_SERVER for non-interactive runs.\n"
|
|
3403
|
+
);
|
|
3404
|
+
return { exitCode: 1 };
|
|
3405
|
+
}
|
|
3406
|
+
if (options.ephemeral) {
|
|
3407
|
+
options.output.write(
|
|
3408
|
+
"[nolo] --ephemeral/--memory-only is only effective with the local runtime (--local); the server stores dialogs on its own and cannot run memory-only.\n"
|
|
3409
|
+
);
|
|
3410
|
+
}
|
|
3411
|
+
return runHttpAgentTurn(options, authToken);
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
// packages/cli/agentRunCommand.ts
|
|
3415
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
3416
|
+
import { join as join2 } from "node:path";
|
|
3417
|
+
|
|
3418
|
+
// packages/agent-runtime/quickChatIntentCore.ts
|
|
3419
|
+
var INTENT_MODEL = "deepseek-v4-flash";
|
|
3420
|
+
var INTENT_PROVIDER = "deepseek";
|
|
3421
|
+
var QUICK_CHAT_INTENT_TIMEOUT_MS = 4e3;
|
|
3422
|
+
var SHORT_GREETING_MAX_LENGTH = 20;
|
|
3423
|
+
var SHORT_GREETING_PATTERN = /^(?:hi|hello|hey|hiya|yo|sup|howdy|嗨|哈喽|哈囉|你好|您好|在吗|在嘛|喂|早上好|中午好|下午好|晚上好|拜拜|再见|谢谢|thanks|thx|ok|好的|收到|了解)[!.。,、~~]?$/iu;
|
|
3424
|
+
function isShortGreeting(text) {
|
|
3425
|
+
const trimmed = text.trim();
|
|
3426
|
+
if (!trimmed) return false;
|
|
3427
|
+
if (trimmed.length > SHORT_GREETING_MAX_LENGTH) return false;
|
|
3428
|
+
return SHORT_GREETING_PATTERN.test(trimmed);
|
|
3429
|
+
}
|
|
3430
|
+
var COMPLEXITY_SIMPLE_MAX_LENGTH = 80;
|
|
3431
|
+
var COMPLEXITY_MEDIUM_MAX_LENGTH = 500;
|
|
3432
|
+
var COMPLEXITY_KEYWORDS = /为什么|如何|分析|对比|比较|设计|架构|实现|原理|推导|评价|区别|关系|影响|优化|重构/;
|
|
3433
|
+
var COMPLEXITY_CODE_BLOCK = /```[\s\S]*```/;
|
|
3434
|
+
function estimateComplexity(text) {
|
|
3435
|
+
const trimmed = text.trim();
|
|
3436
|
+
const len = trimmed.length;
|
|
3437
|
+
if (len > COMPLEXITY_MEDIUM_MAX_LENGTH) return "complex";
|
|
3438
|
+
if (COMPLEXITY_CODE_BLOCK.test(trimmed)) return "complex";
|
|
3439
|
+
if (len > COMPLEXITY_SIMPLE_MAX_LENGTH) return "medium";
|
|
3440
|
+
if (COMPLEXITY_KEYWORDS.test(trimmed)) return "medium";
|
|
3441
|
+
return "simple";
|
|
3442
|
+
}
|
|
3443
|
+
var TIER_DESCRIPTIONS = {
|
|
3444
|
+
flash: "\u5FEB\u901F\u7B80\u5355\uFF1A\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587",
|
|
3445
|
+
balanced: "\u5E73\u8861\u63A8\u7406\uFF1A\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u4EE3\u7801\u3001\u63A8\u7406",
|
|
3446
|
+
quality: "\u9AD8\u8D28\u91CF\u6DF1\u5EA6\uFF1A\u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790"
|
|
3447
|
+
};
|
|
3448
|
+
function buildQuickChatIntentSystemPrompt(tierAgents) {
|
|
3449
|
+
const agentList = tierAgents.map((t2) => ` - "${t2.agentKey}"\uFF1A${t2.description}`).join("\n");
|
|
3450
|
+
return `\u4F60\u662F\u4E00\u4E2A\u610F\u56FE\u8DEF\u7531\u5668\u3002\u6839\u636E\u7528\u6237\u6D88\u606F\uFF0C\u5224\u65AD\u5E94\u8BE5\u4EA4\u7ED9\u54EA\u4E2A agent \u5904\u7406\u3002
|
|
3451
|
+
|
|
3452
|
+
\u53EF\u9009 agent \u5217\u8868\uFF08agentKey \u4E0E\u63CF\u8FF0\uFF09\uFF1A
|
|
3453
|
+
${agentList}
|
|
3454
|
+
|
|
3455
|
+
\u5224\u65AD\u539F\u5219\uFF08\u9ED8\u8BA4\u504F\u5411\u901A\u7528\u6863\u4F4D\uFF1B\u4E13\u804C agent \u5728\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF1B**\u7F16\u7801/\u672C\u5730\u4ED3\u5E93\u4EFB\u52A1\u8D70\u901A\u7528\u6863 + needsWorkspace=true**\uFF09\uFF1A
|
|
3456
|
+
- \u9ED8\u8BA4\u60C5\u51B5\u4E0B\uFF0C**\u9664\u975E\u7528\u6237\u660E\u786E\u8868\u8FBE\u8981\u5EFA\u5E94\u7528/\u5EFA Agent/\u63D0\u4EA4\u53CD\u9988/\u591A Agent \u7F16\u6392**\uFF0C\u5426\u5219\u9009\u901A\u7528\u6863\u4F4D\uFF08flash/balanced/quality\uFF09\uFF1A
|
|
3457
|
+
- \u7B80\u5355\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587 \u2192 \u9009 flash \u6863
|
|
3458
|
+
- \u9700\u8981\u63A8\u7406\u3001\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u666E\u901A\u4EE3\u7801\u89E3\u91CA/\u67B6\u6784\u95EE\u7B54 \u2192 \u9009 balanced \u6863
|
|
3459
|
+
- \u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790 \u2192 \u9009 quality \u6863
|
|
3460
|
+
- **\u660E\u786E\u7F16\u7801\u4EFB\u52A1\uFF08\u5B9E\u73B0\u529F\u80FD\u3001\u4FEE bug\u3001refactor\u3001\u5199/\u8865\u6D4B\u8BD5\u3001\u8DD1 build/tests/ci\uFF09\u6216\u67E5\u770B\u672C\u673A\u4ED3\u5E93/\u4EE3\u7801\u72B6\u6001\uFF08git/\u5206\u652F/commit/\u672C\u5730\u4E0E\u8FDC\u7A0B\u5DEE\u8DDD/\u672A\u63D0\u4EA4\u6539\u52A8\u7B49\uFF09** \u2192 \u9009 balanced \u6216 quality \u6863\uFF08\u6309\u590D\u6742\u5EA6\uFF1B**\u4E0D\u8981\u9009 flash**\uFF09\uFF0C\u4E14 needsWorkspace=true
|
|
3461
|
+
- \u4E13\u804C agent \u8DEF\u7531\u89C4\u5219\uFF08\u6709\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF09\uFF1A
|
|
3462
|
+
- \u8981\u63D0\u4EA4/\u8BB0\u5F55 bug\u3001\u4F53\u9A8C\u95EE\u9898\u3001\u6570\u636E\u5F02\u5E38\u3001\u4EA7\u54C1\u5EFA\u8BAE\uFF0C\u6216\u53E3\u8BED\u5982\u300C\u60F3\u53CD\u9988\u4E00\u4E9B\u95EE\u9898\u300D\u300C\u53CD\u9988\u4E00\u4E0B\u300D\u2192 \u9009\u610F\u89C1\u53CD\u9988\u4E13\u804C agent
|
|
3463
|
+
- \u8981\u65B0\u5EFA\u6216\u5B9A\u5236 AI/Agent/\u667A\u80FD\u4F53\u52A9\u624B\uFF08\u542B\u914D\u7F6E\u5DE5\u5177\u3001\u77E5\u8BC6\u3001\u786E\u8BA4\u521B\u5EFA\uFF09\u2192 \u9009\u521B\u5EFA Agent \u4E13\u804C agent
|
|
3464
|
+
- \u8981\u505A/\u6539/\u53D1\u5E03\u7F51\u7AD9\u3001\u7F51\u9875\u3001\u5C0F\u5E94\u7528\u3001\u9884\u7EA6\u9875\u3001\u535A\u5BA2\u3001\u770B\u677F \u2192 \u9009\u5E94\u7528\u6784\u5EFA\u4E13\u804C agent
|
|
3465
|
+
- \u8981\u6D3E\u591A\u4E2A agent\u3001\u591A agent \u5E76\u884C\u3001\u62C6\u6210\u4EFB\u52A1\u540E\u53F0\u6267\u884C\u3001\u591A\u4E2A agent review\u3001\u8BA9\u4E0D\u540C agent \u8BA8\u8BBA/\u4F1A\u5546 \u2192 \u9009\u591A Agent \u7F16\u6392\u4E13\u804C agent\uFF08\u663E\u5F0F multi-agent \u4F18\u5148\uFF09
|
|
3466
|
+
- **\u62FF\u4E0D\u51C6\u65F6**\uFF1A\u7EAF\u95F2\u804A\u9009 flash\uFF1B**\u82E5\u6CBE\u8FB9\u672C\u5730\u4ED3\u5E93/\u5F53\u524D\u4EE3\u7801\u72B6\u6001\uFF0C\u9009 balanced/quality + needsWorkspace=true**\u3002
|
|
3467
|
+
|
|
3468
|
+
\u540C\u65F6\u5224\u65AD\u672C\u8F6E\u662F\u5426\u9700\u8981\u6CE8\u5165\u300C\u5DE5\u4F5C\u533A\u53EA\u8BFB\u5DE5\u5177\u300D\uFF08listFiles/globFiles/searchFiles/readFile/execShell \u7B49\uFF09\uFF1A
|
|
3469
|
+
- needsWorkspace=true \u7684\u573A\u666F\uFF1A\u7528\u6237\u8981\u8BFB/\u6539/\u5206\u6790\u6587\u4EF6\u6216\u4EE3\u7801\u3001\u63D0\u5230\u4E86\u6587\u4EF6\u8DEF\u5F84/\u6269\u5C55\u540D\uFF08.md/.csv/.ts \u7B49\uFF09\u3001\u63D0\u5230\u5DE5\u4F5C\u533A/\u4EE3\u7801\u5E93/\u4ED3\u5E93/\u76EE\u5F55/git/\u5206\u652F\u3001\u8981\u770B\u5F53\u524D\u4EE3\u7801\u6216\u4ED3\u5E93\u72B6\u6001\u3001\u672C\u5730\u641C\u7D22/\u67E5\u770B\u4EE3\u7801\u3002
|
|
3470
|
+
- needsWorkspace=false \u7684\u573A\u666F\uFF1A\u901A\u7528\u77E5\u8BC6\u95EE\u7B54\uFF08\u5982\u300C\u5E7F\u4E1C\u7ECF\u6D4E\u6570\u636E\u300D\u300C\u5199\u4E00\u9996\u8BD7\u300D\uFF09\u3001\u7EAF\u95F2\u804A\u95EE\u5019\u3001\u7FFB\u8BD1\u3001\u4E0D\u6D89\u53CA\u672C\u5730\u6587\u4EF6/\u4ED3\u5E93\u3002
|
|
3471
|
+
- \u6F0F\u5224\uFF08\u7528\u6237\u8981\u6587\u4EF6\u800C\u6CA1\u5E26\u5DE5\u5177\uFF09\u6BD4\u8BEF\u5224\uFF08\u767D\u641C\u51E0\u8F6E\uFF09\u4EE3\u4EF7\u66F4\u9AD8\uFF0C\u5B81\u53EF\u504F\u5BBD\u5224 true\u3002
|
|
3472
|
+
|
|
3473
|
+
\u5728\u7ED9\u51FA agentKey \u4E4B\u524D\uFF0C\u5148\u5728\u5FC3\u91CC\u533A\u5206\u5019\u9009\u6863\u4F4D\u7684\u8FB9\u754C\u518D\u7ED9\u7F6E\u4FE1\u5EA6\uFF1Abalanced \u4E0E quality \u7684\u8FB9\u754C\u5728\u4E8E\u590D\u6742\u5EA6/\u7BC7\u5E45/\u662F\u5426\u9700\u8981\u6DF1\u5EA6\u5206\u6790\uFF0C\u62FF\u4E0D\u51C6\u5C31\u7ED9\u4F4E confidence\u3002
|
|
3474
|
+
|
|
3475
|
+
\u540C\u65F6\u5224\u65AD\u672C\u8F6E\u6D88\u606F\u662F\u5426\u9700\u8981\u6302\u8F7D\u5BF9\u8C61\u64CD\u4F5C\u6280\u80FD\uFF08skills \u6570\u7EC4\uFF0C\u672A\u547D\u4E2D\u7ED9 []\uFF09\uFF1A
|
|
3476
|
+
- \u7528\u6237\u8981\u65B0\u5EFA/\u6574\u7406/\u5F55\u5165/\u6279\u91CF\u5904\u7406\u8868\u683C\u6570\u636E\uFF08\u5EFA\u8868\u3001\u52A0\u884C\u3001\u6539\u5217\u3001\u6309\u6761\u4EF6\u6574\u7406\u6570\u636E\uFF09\u2192 skills \u542B "table"
|
|
3477
|
+
- \u7528\u6237\u8981\u5199/\u6DA6\u8272/\u6539\u5199/\u7EED\u5199/\u6392\u7248\u4E00\u7BC7\u6587\u6863\u6216\u6587\u7AE0 \u2192 skills \u542B "doc"
|
|
3478
|
+
- \u7528\u6237\u8981\u5199/\u6539/\u5B9E\u73B0/\u4FEE bug/refactor/\u8865\u6D4B\u8BD5/\u5BA1\u67E5\u5E76\u4FEE\u6539\u4EE3\u7801 \u2192 skills \u542B "code"\uFF08\u4EC5\u95EE\u901A\u7528\u77E5\u8BC6\u3001\u7EAF\u95F2\u804A\u3001\u53EA\u67E5\u770B git/\u4ED3\u5E93\u72B6\u6001\u800C\u4E0D\u4FEE\u6539\u4EE3\u7801\u65F6\u4E0D\u6302 "code"\uFF09
|
|
3479
|
+
- \u666E\u901A\u95EE\u7B54/\u7EAF\u95F2\u804A/\u5EFA\u5E94\u7528 \u2192 skills \u4E3A []
|
|
3480
|
+
|
|
3481
|
+
\u53EA\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF1A
|
|
3482
|
+
{"confidence": <0\u52301\u4E4B\u95F4\u7684\u5C0F\u6570\uFF0C\u8868\u793A\u4F60\u5BF9\u8FD9\u4E2A\u5206\u7C7B\u5224\u65AD\u7684\u628A\u63E1\u7A0B\u5EA6>, "agentKey": "<\u4ECE\u5217\u8868\u4E2D\u9009\u62E9\u7684 agentKey>", "needsWorkspace": <true \u6216 false>, "skills": <\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u5143\u7D20\u53EA\u80FD\u662F "table" | "doc" | "code"\uFF0C\u672A\u547D\u4E2D\u7ED9 []>}`;
|
|
3483
|
+
}
|
|
3484
|
+
function parseQuickChatIntentResult(content, tierAgents) {
|
|
3485
|
+
try {
|
|
3486
|
+
const parsed = JSON.parse(content.trim());
|
|
3487
|
+
if (typeof parsed !== "object" || parsed === null || !("agentKey" in parsed)) return null;
|
|
3488
|
+
const key = parsed.agentKey;
|
|
3489
|
+
if (typeof key !== "string") return null;
|
|
3490
|
+
if (!tierAgents.some((t2) => t2.agentKey === key)) return null;
|
|
3491
|
+
const rawNeedsWorkspace = parsed.needsWorkspace;
|
|
3492
|
+
const needsWorkspace = rawNeedsWorkspace === true;
|
|
3493
|
+
const rawConfidence = parsed.confidence;
|
|
3494
|
+
const confidence = typeof rawConfidence === "number" && Number.isFinite(rawConfidence) && rawConfidence >= 0 && rawConfidence <= 1 ? rawConfidence : void 0;
|
|
3495
|
+
const rawSkills = parsed.skills;
|
|
3496
|
+
let skills;
|
|
3497
|
+
if (Array.isArray(rawSkills)) {
|
|
3498
|
+
const filtered = [...new Set(rawSkills)].filter(
|
|
3499
|
+
(s) => s === "table" || s === "doc" || s === "code"
|
|
3500
|
+
);
|
|
3501
|
+
if (filtered.length > 0) skills = filtered;
|
|
3502
|
+
}
|
|
3503
|
+
return { agentKey: key, needsWorkspace, confidence, skills };
|
|
3504
|
+
} catch {
|
|
3505
|
+
return null;
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
// packages/cli/client/autoModelRouter.ts
|
|
3510
|
+
var CLI_AUTO_TIER_AGENT_KEYS = {
|
|
3511
|
+
flash: PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
|
|
3512
|
+
balanced: PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
|
|
3513
|
+
quality: PUBLIC_GLM_52_AGENT_KEY
|
|
3514
|
+
};
|
|
3515
|
+
var CLI_IMAGE_AGENT_KEY = PUBLIC_KIMI_K26_IMAGE_AGENT_KEY;
|
|
3516
|
+
var CLI_AUTO_TIER_AGENT_KEY_TABLE = {
|
|
3517
|
+
[CLI_AUTO_TIER_AGENT_KEYS.flash]: true,
|
|
3518
|
+
[CLI_AUTO_TIER_AGENT_KEYS.balanced]: true,
|
|
3519
|
+
[CLI_AUTO_TIER_AGENT_KEYS.quality]: true
|
|
3520
|
+
};
|
|
3521
|
+
var buildRouteOptions = () => ["flash", "balanced", "quality"].map((tier) => ({
|
|
3522
|
+
tier,
|
|
3523
|
+
agentKey: CLI_AUTO_TIER_AGENT_KEYS[tier],
|
|
3524
|
+
description: TIER_DESCRIPTIONS[tier]
|
|
3525
|
+
}));
|
|
3526
|
+
var tierForAgentKey = (agentKey) => {
|
|
3527
|
+
for (const tier of ["flash", "balanced", "quality"]) {
|
|
3528
|
+
if (CLI_AUTO_TIER_AGENT_KEYS[tier] === agentKey) return tier;
|
|
3529
|
+
}
|
|
3530
|
+
return null;
|
|
3531
|
+
};
|
|
3532
|
+
function resolveCliAutoFallbackTier(text) {
|
|
3533
|
+
switch (estimateComplexity(text)) {
|
|
3534
|
+
case "complex":
|
|
3535
|
+
return "quality";
|
|
3536
|
+
case "medium":
|
|
3537
|
+
return "balanced";
|
|
3538
|
+
case "simple":
|
|
3539
|
+
default:
|
|
3540
|
+
return "flash";
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
async function classifyCliAutoRoute(text, options) {
|
|
3544
|
+
const timeoutMs = options.timeoutMs ?? QUICK_CHAT_INTENT_TIMEOUT_MS;
|
|
3545
|
+
const fallbackTier = resolveCliAutoFallbackTier(text);
|
|
3546
|
+
const fallback = () => ({
|
|
3547
|
+
agentKey: CLI_AUTO_TIER_AGENT_KEYS[fallbackTier],
|
|
3548
|
+
tier: fallbackTier,
|
|
3549
|
+
classified: false
|
|
3550
|
+
});
|
|
3551
|
+
if (!text.trim()) return fallback();
|
|
3552
|
+
if (isShortGreeting(text)) {
|
|
3553
|
+
return {
|
|
3554
|
+
agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
|
|
3555
|
+
tier: "flash",
|
|
3556
|
+
classified: true
|
|
3557
|
+
};
|
|
3558
|
+
}
|
|
3559
|
+
if (!options.authToken) return fallback();
|
|
3560
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
3561
|
+
const controller = new AbortController();
|
|
3562
|
+
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
3563
|
+
try {
|
|
3564
|
+
const routeOptions = buildRouteOptions();
|
|
3565
|
+
const messages = [
|
|
3566
|
+
{ role: "system", content: buildQuickChatIntentSystemPrompt(routeOptions) },
|
|
3567
|
+
{ role: "user", content: text }
|
|
3568
|
+
];
|
|
3569
|
+
const { url, init } = buildPlatformChatCompletionRequest({
|
|
3570
|
+
providerConfig: {
|
|
3571
|
+
serverUrl: options.serverUrl,
|
|
3572
|
+
authToken: options.authToken,
|
|
3573
|
+
agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
|
|
3574
|
+
model: INTENT_MODEL,
|
|
3575
|
+
provider: INTENT_PROVIDER,
|
|
3576
|
+
endpoint: resolvePlatformChatCompletionsEndpoint(INTENT_PROVIDER) ?? "",
|
|
3577
|
+
requestOptions: {}
|
|
3578
|
+
},
|
|
3579
|
+
messages,
|
|
3580
|
+
stream: false
|
|
3581
|
+
});
|
|
3582
|
+
const response = await fetchImpl(url, {
|
|
3583
|
+
...init,
|
|
3584
|
+
signal: controller.signal
|
|
3585
|
+
});
|
|
3586
|
+
if (!response.ok) return fallback();
|
|
3587
|
+
const data = parsePlatformChatCompletionData(await response.text());
|
|
3588
|
+
const content = data?.choices?.[0]?.message?.content;
|
|
3589
|
+
if (typeof content !== "string" || !content.trim()) return fallback();
|
|
3590
|
+
const parsed = parseQuickChatIntentResult(content, routeOptions);
|
|
3591
|
+
if (!parsed) return fallback();
|
|
3592
|
+
const tier = tierForAgentKey(parsed.agentKey) ?? fallbackTier;
|
|
3593
|
+
return {
|
|
3594
|
+
agentKey: parsed.agentKey,
|
|
3595
|
+
tier,
|
|
3596
|
+
classified: true,
|
|
3597
|
+
confidence: parsed.confidence,
|
|
3598
|
+
needsWorkspace: parsed.needsWorkspace,
|
|
3599
|
+
skills: parsed.skills
|
|
3600
|
+
};
|
|
3601
|
+
} catch {
|
|
3602
|
+
return fallback();
|
|
3603
|
+
} finally {
|
|
3604
|
+
clearTimeout(timeoutHandle);
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
// packages/cli/agentRunPrompts.ts
|
|
3609
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3610
|
+
import { dirname, extname, isAbsolute, join, relative as relativePath, resolve, sep } from "node:path";
|
|
3611
|
+
function workflowRefToCandidatePath(cwd, ref) {
|
|
3612
|
+
const normalized = ref.trim();
|
|
3613
|
+
if (!normalized) return "";
|
|
3614
|
+
if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
|
|
3615
|
+
const directPath = resolve(cwd, normalized);
|
|
3616
|
+
if (existsSync(directPath)) return directPath;
|
|
3617
|
+
}
|
|
3618
|
+
const fileName = normalized.replace(/[^a-zA-Z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3619
|
+
return resolve(cwd, "docs", "workflows", `${fileName}.md`);
|
|
3620
|
+
}
|
|
3621
|
+
var SKILL_SEARCH_DIRS = [
|
|
3622
|
+
".agents/skills",
|
|
3623
|
+
"docs/skills"
|
|
3624
|
+
];
|
|
3625
|
+
function skillRefToCandidatePath(cwd, ref) {
|
|
3626
|
+
const normalized = ref.trim();
|
|
3627
|
+
if (!normalized) return "";
|
|
3628
|
+
if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
|
|
3629
|
+
const directPath = resolve(cwd, normalized);
|
|
3630
|
+
if (existsSync(directPath)) return directPath;
|
|
3631
|
+
}
|
|
3632
|
+
const fileName = normalized.replace(/[^a-zA-Z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3633
|
+
for (const dir of SKILL_SEARCH_DIRS) {
|
|
3634
|
+
if (dir === "docs/skills") {
|
|
3635
|
+
const flatPath = resolve(cwd, dir, `${fileName}.md`);
|
|
3636
|
+
if (existsSync(flatPath)) return flatPath;
|
|
3637
|
+
} else {
|
|
3638
|
+
const skillMdPath = resolve(cwd, dir, fileName, "SKILL.md");
|
|
3639
|
+
if (existsSync(skillMdPath)) return skillMdPath;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
return resolve(cwd, "docs", "skills", `${fileName}.md`);
|
|
3643
|
+
}
|
|
3644
|
+
async function resolveWorkflowReference(ref, cwd = process.cwd()) {
|
|
3645
|
+
const path = workflowRefToCandidatePath(cwd, ref);
|
|
3646
|
+
if (!path || !existsSync(path)) {
|
|
3647
|
+
throw new Error(`Workflow reference not found: ${ref}`);
|
|
3648
|
+
}
|
|
3649
|
+
const markdown = readFileSync(path, "utf8");
|
|
3650
|
+
const parsed = parseSkillDocProtocol(markdown);
|
|
3651
|
+
return {
|
|
3652
|
+
ref,
|
|
3653
|
+
content: parsed.content,
|
|
3654
|
+
...parsed.meta?.workflowConfig ? { config: parsed.meta.workflowConfig } : {}
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
function inlineRelativeRefs(body, skillDir) {
|
|
3658
|
+
const refPattern = /\[([^\]]+)\]\((\.\/[^)]+|[^)]+\/[^)]+)\)/g;
|
|
3659
|
+
const normalizedSkillDir = resolve(skillDir) + sep;
|
|
3660
|
+
return body.replace(refPattern, (match, label, relPath) => {
|
|
3661
|
+
const cleaned = relPath.trim();
|
|
3662
|
+
if (!cleaned.startsWith("./") && !cleaned.startsWith("../")) return match;
|
|
3663
|
+
const absPath = resolve(skillDir, cleaned);
|
|
3664
|
+
const relative = relativePath(normalizedSkillDir, absPath);
|
|
3665
|
+
if (relative.startsWith("..") || isAbsolute(relative)) return match;
|
|
3666
|
+
if (!existsSync(absPath) || !absPath.endsWith(".md")) return match;
|
|
3667
|
+
const refContent = readFileSync(absPath, "utf8");
|
|
3668
|
+
return `
|
|
3669
|
+
|
|
3670
|
+
### ${label}
|
|
3671
|
+
|
|
3672
|
+
${refContent}
|
|
3673
|
+
|
|
3674
|
+
`;
|
|
3675
|
+
});
|
|
3676
|
+
}
|
|
3677
|
+
function listSkillScripts(skillDir) {
|
|
3678
|
+
const scriptsDir = join(skillDir, "scripts");
|
|
3679
|
+
if (!existsSync(scriptsDir) || !statSync(scriptsDir).isDirectory()) return null;
|
|
3680
|
+
const entries = readdirSync(scriptsDir).filter((e) => !e.startsWith("."));
|
|
3681
|
+
if (entries.length === 0) return null;
|
|
3682
|
+
return [
|
|
3683
|
+
"",
|
|
3684
|
+
"--- Available scripts (use execShell/readFile to access) ---",
|
|
3685
|
+
...entries.map((e) => `- scripts/${e}`)
|
|
3686
|
+
].join("\n");
|
|
3687
|
+
}
|
|
3688
|
+
async function resolveSkillReference(ref, options = {}) {
|
|
3689
|
+
const isDbKey = /^(page|doc)-[0-9a-z]+-/i.test(ref);
|
|
3690
|
+
let markdown = "";
|
|
3691
|
+
let skillDir;
|
|
3692
|
+
let filePath;
|
|
3693
|
+
if (isDbKey) {
|
|
3694
|
+
if (!options.readDbRecord) {
|
|
3695
|
+
throw new Error("skill dbKey requires server access");
|
|
3696
|
+
}
|
|
3697
|
+
const record = await options.readDbRecord(ref);
|
|
3698
|
+
const contentText = record?.content ?? record?.prompt ?? record?.text;
|
|
3699
|
+
if (contentText === void 0 || contentText === null || contentText === "") {
|
|
3700
|
+
throw new Error(`Skill reference has no content: ${ref}`);
|
|
3701
|
+
}
|
|
3702
|
+
markdown = String(contentText);
|
|
3703
|
+
} else {
|
|
3704
|
+
const cwd = options.cwd ?? process.cwd();
|
|
3705
|
+
const path = skillRefToCandidatePath(cwd, ref);
|
|
3706
|
+
if (!path || !existsSync(path)) {
|
|
3707
|
+
throw new Error(`Skill reference not found: ${ref}`);
|
|
3708
|
+
}
|
|
3709
|
+
markdown = readFileSync(path, "utf8");
|
|
3710
|
+
filePath = path;
|
|
3711
|
+
skillDir = dirname(path);
|
|
3712
|
+
}
|
|
3713
|
+
const parsed = parseSkillDocProtocol(markdown);
|
|
3714
|
+
const name = parsed.meta?.skillConfig?.name;
|
|
3715
|
+
const promptPatch = parsed.meta?.skillConfig?.promptPatch;
|
|
3716
|
+
const externalParsed = parseExternalSkillMarkdown(markdown);
|
|
3717
|
+
const allowedTools = externalParsed.allowedTools;
|
|
3718
|
+
const effectiveName = name ?? externalParsed.name;
|
|
3719
|
+
let effectiveContent = parsed.content;
|
|
3720
|
+
if (skillDir) {
|
|
3721
|
+
effectiveContent = inlineRelativeRefs(effectiveContent, skillDir);
|
|
3722
|
+
const scriptsHint = listSkillScripts(skillDir);
|
|
3723
|
+
if (scriptsHint) effectiveContent += scriptsHint;
|
|
3724
|
+
}
|
|
3725
|
+
return {
|
|
3726
|
+
ref,
|
|
3727
|
+
content: effectiveContent,
|
|
3728
|
+
...effectiveName ? { name: effectiveName } : {},
|
|
3729
|
+
...promptPatch ? { promptPatch } : {},
|
|
3730
|
+
...allowedTools && allowedTools.length > 0 ? { allowedTools } : {},
|
|
3731
|
+
...skillDir ? { skillDir } : {}
|
|
3732
|
+
};
|
|
3733
|
+
}
|
|
3734
|
+
function buildSkillContextBlocks(skills) {
|
|
3735
|
+
if (!skills || !skills.length) return [];
|
|
3736
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3737
|
+
const blocks = [];
|
|
3738
|
+
for (const skill of skills) {
|
|
3739
|
+
const dedupKey = skill.name ?? skill.ref;
|
|
3740
|
+
if (seen.has(dedupKey)) continue;
|
|
3741
|
+
seen.add(dedupKey);
|
|
3742
|
+
const header = `## ${dedupKey}`;
|
|
3743
|
+
const patch = skill.promptPatch ? `${skill.promptPatch}
|
|
3744
|
+
` : "";
|
|
3745
|
+
blocks.push(`${header}
|
|
3746
|
+
${patch}${skill.content}`);
|
|
3747
|
+
}
|
|
3748
|
+
return blocks;
|
|
3749
|
+
}
|
|
3750
|
+
function prependWorkflowReferencePrompt(message, workflow) {
|
|
3751
|
+
if (!workflow) return message;
|
|
3752
|
+
const config = workflow.config;
|
|
3753
|
+
return [
|
|
3754
|
+
"AI-native workflow reference:",
|
|
3755
|
+
"- This reference is guidance for the agent, not a central workflow engine.",
|
|
3756
|
+
`- ref: ${workflow.ref}`,
|
|
3757
|
+
...config?.id ? [`- id: ${config.id}`] : [],
|
|
3758
|
+
...config?.name ? [`- name: ${config.name}`] : [],
|
|
3759
|
+
...config?.defaultAgent ? [`- suggested defaultAgent: ${config.defaultAgent}`] : [],
|
|
3760
|
+
...config?.inputs?.length ? [`- inputs: ${config.inputs.join(", ")}`] : [],
|
|
3761
|
+
...config?.recommendedTools?.length ? [`- recommendedTools: ${config.recommendedTools.join(", ")}`] : [],
|
|
3762
|
+
...config?.requiredTools?.length ? [`- requiredTools: ${config.requiredTools.join(", ")}`] : [],
|
|
3763
|
+
...config?.requiredOutputs?.length ? [`- requiredOutputs: ${config.requiredOutputs.join(", ")}`] : [],
|
|
3764
|
+
...config?.gates?.length ? [`- gates: ${config.gates.join(", ")}`] : [],
|
|
3765
|
+
...config?.contextStrategy ? [`- contextStrategy: ${config.contextStrategy}`] : [],
|
|
3766
|
+
...config?.failureProtocol ? [`- failureProtocol: ${config.failureProtocol}`] : [],
|
|
3767
|
+
"",
|
|
3768
|
+
"Reference body:",
|
|
3769
|
+
workflow.content,
|
|
3770
|
+
"",
|
|
3771
|
+
"User task:",
|
|
3772
|
+
message
|
|
3773
|
+
].join("\n");
|
|
3774
|
+
}
|
|
3775
|
+
function prependFeatureWorktreeInstruction(message, enabled) {
|
|
3776
|
+
if (!enabled) return message;
|
|
3777
|
+
return [
|
|
3778
|
+
"Local execution rule:",
|
|
3779
|
+
"- You are running in the current git checkout with shell access.",
|
|
3780
|
+
"- For read-only checks, smoke tests, or answering questions, stay in the current directory.",
|
|
3781
|
+
"- Before developing a new feature or making non-trivial code changes, create a separate git worktree yourself with git worktree and do the edits there.",
|
|
3782
|
+
"- Commit and push only when the user explicitly asks or the task requires it.",
|
|
3783
|
+
"",
|
|
3784
|
+
"User task:",
|
|
3785
|
+
message
|
|
3786
|
+
].join("\n");
|
|
3787
|
+
}
|
|
3788
|
+
function prependSubjectDialogMarker(message, subjectDialogKey) {
|
|
3789
|
+
if (!subjectDialogKey) return message;
|
|
3790
|
+
return [
|
|
3791
|
+
`Subject dialog for this run: ${subjectDialogKey}`,
|
|
3792
|
+
"If the user asks to evaluate the referenced dialog, call readDialog with this id/key first.",
|
|
3793
|
+
"",
|
|
3794
|
+
message
|
|
3795
|
+
].join("\n");
|
|
3796
|
+
}
|
|
3797
|
+
function normalizeCliImageInput(input) {
|
|
3798
|
+
if (/^(https?:|data:|file:)/i.test(input)) return input;
|
|
3799
|
+
const absolutePath = resolve(input);
|
|
3800
|
+
if (!existsSync(absolutePath)) return input;
|
|
3801
|
+
const base64 = readFileSync(absolutePath).toString("base64");
|
|
3802
|
+
return `data:${imageMimeTypeForPath(absolutePath)};base64,${base64}`;
|
|
3803
|
+
}
|
|
3804
|
+
function imageMimeTypeForPath(path) {
|
|
3805
|
+
switch (extname(path).toLowerCase()) {
|
|
3806
|
+
case ".jpg":
|
|
3807
|
+
case ".jpeg":
|
|
3808
|
+
return "image/jpeg";
|
|
3809
|
+
case ".webp":
|
|
3810
|
+
return "image/webp";
|
|
3811
|
+
case ".gif":
|
|
3812
|
+
return "image/gif";
|
|
3813
|
+
default:
|
|
3814
|
+
return "image/png";
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
// packages/cli/agentRunLocalWorkspace.ts
|
|
3819
|
+
async function runGitForLocalSummary(args, deps = {}) {
|
|
3820
|
+
const spawn = deps.spawnProcess ?? spawnProcess;
|
|
3821
|
+
const read = deps.readPipeText ?? readPipeText;
|
|
3822
|
+
const proc = spawn({
|
|
3823
|
+
cmd: ["git", ...args.gitArgs],
|
|
3824
|
+
cwd: args.cwd,
|
|
3825
|
+
stdout: "pipe",
|
|
3826
|
+
stderr: "pipe"
|
|
3827
|
+
});
|
|
3828
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
3829
|
+
read(proc.stdout),
|
|
3830
|
+
read(proc.stderr),
|
|
3831
|
+
proc.exited
|
|
3832
|
+
]);
|
|
3833
|
+
if (exitCode !== 0) {
|
|
3834
|
+
throw new Error((stderr || stdout || `git ${args.gitArgs.join(" ")} failed`).trim());
|
|
3835
|
+
}
|
|
3836
|
+
return stdout.trim();
|
|
3837
|
+
}
|
|
3838
|
+
async function inspectLocalRunWorkspace(cwd, deps = {}) {
|
|
3839
|
+
const run = (gitArgs) => runGitForLocalSummary({ cwd, gitArgs }, deps);
|
|
3840
|
+
const status = await run(["status", "--short"]);
|
|
3841
|
+
const rawCommit = await run(["log", "-1", "--format=%h%x00%s"]).catch(() => "");
|
|
3842
|
+
const [hash, subject] = rawCommit.split("\0");
|
|
3843
|
+
return {
|
|
3844
|
+
cwd,
|
|
3845
|
+
clean: status.trim() === "",
|
|
3846
|
+
status,
|
|
3847
|
+
...hash ? { commit: { hash, subject: subject ?? "" } } : {}
|
|
3848
|
+
};
|
|
3849
|
+
}
|
|
3850
|
+
function shouldPrintLocalRunSummary(args) {
|
|
3851
|
+
return Boolean(args.localRuntimeCwd && args.parsed.runtimeMode === "local");
|
|
3852
|
+
}
|
|
3853
|
+
function formatLocalRunSummary(args) {
|
|
3854
|
+
return [
|
|
3855
|
+
"",
|
|
3856
|
+
"[nolo] local run summary",
|
|
3857
|
+
`workspace: ${args.inspection.cwd}`,
|
|
3858
|
+
...args.dialogId ? [`dialog: ${args.dialogId}`] : [],
|
|
3859
|
+
...args.inspection.commit ? [`commit: ${args.inspection.commit.hash} ${args.inspection.commit.subject}`] : [],
|
|
3860
|
+
`dirty: ${args.inspection.clean ? "clean" : "dirty"}`,
|
|
3861
|
+
...!args.inspection.clean && args.inspection.status.trim() ? [`status:
|
|
3862
|
+
${args.inspection.status.trim()}`] : [],
|
|
3863
|
+
""
|
|
3864
|
+
].join("\n");
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
// packages/ai/memory/overlay.ts
|
|
3868
|
+
var KIND_TITLES = {
|
|
3869
|
+
episodic: "Episodic",
|
|
3870
|
+
semantic: "Semantic",
|
|
3871
|
+
procedural: "Procedural"
|
|
3872
|
+
};
|
|
3873
|
+
var normalizeDisplayContent = (item) => {
|
|
3874
|
+
const normalized = item.content.trim().replace(/^(你要记住|请记住|以后记住|记住)[,,。.\s::]*/u, "").replace(/[。!?!?]+$/u, "").trim();
|
|
3875
|
+
const stripPrefix = (prefix) => normalized.startsWith(prefix) ? normalized.slice(prefix.length).trim() : normalized;
|
|
3876
|
+
if (item.kind === "semantic") {
|
|
3877
|
+
if (item.tags?.includes("understanding-memory")) {
|
|
3878
|
+
switch (item.facet) {
|
|
3879
|
+
case "unfinished":
|
|
3880
|
+
return `\u7528\u6237\u4ECD\u672A\u5B9A\u4E0B\uFF1A${stripPrefix("\u8FD8\u6CA1\u51B3\u5B9A")}`;
|
|
3881
|
+
case "tension":
|
|
3882
|
+
return `\u7528\u6237\u5F53\u524D\u5728\u6743\u8861\uFF1A${stripPrefix("\u5728\u6743\u8861")}`;
|
|
3883
|
+
case "preference":
|
|
3884
|
+
return `\u7528\u6237\u5F53\u524D\u66F4\u5728\u610F\uFF1A${stripPrefix("\u66F4\u5728\u610F")}`;
|
|
3885
|
+
case "style":
|
|
3886
|
+
return `\u7528\u6237\u4E92\u52A8\u504F\u597D\uFF1A${stripPrefix("\u66F4\u559C\u6B22")}`;
|
|
3887
|
+
case "goal":
|
|
3888
|
+
return `\u7528\u6237\u5F53\u524D\u76EE\u6807\uFF1A${stripPrefix("\u60F3\u63A8\u8FDB")}`;
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
return `\u7528\u6237\u957F\u671F\u504F\u597D/\u4E8B\u5B9E\uFF1A${normalized}`;
|
|
3892
|
+
}
|
|
3893
|
+
if (item.kind === "episodic" && (item.patternKey === "explicit-remember" || item.patternKey === "agent-remember")) {
|
|
3894
|
+
return `\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u8BB0\u4F4F\uFF1A${normalized}`;
|
|
3895
|
+
}
|
|
3896
|
+
if (item.kind === "episodic" && item.tags?.includes("understanding-memory")) {
|
|
3897
|
+
switch (item.facet) {
|
|
3898
|
+
case "unfinished":
|
|
3899
|
+
return `\u6700\u8FD1\u4E00\u6B21\u5BF9\u8BDD\u663E\u793A\uFF1A\u7528\u6237\u8FD8\u6CA1\u5B9A\u4E0B ${stripPrefix("\u8FD8\u6CA1\u51B3\u5B9A")}`;
|
|
3900
|
+
case "tension":
|
|
3901
|
+
return `\u6700\u8FD1\u4E00\u6B21\u5BF9\u8BDD\u663E\u793A\uFF1A\u7528\u6237\u8FD8\u5728\u6743\u8861 ${stripPrefix("\u5728\u6743\u8861")}`;
|
|
3902
|
+
case "preference":
|
|
3903
|
+
return `\u6700\u8FD1\u4E00\u6B21\u5BF9\u8BDD\u663E\u793A\uFF1A\u7528\u6237\u66F4\u5728\u610F ${stripPrefix("\u66F4\u5728\u610F")}`;
|
|
3904
|
+
case "style":
|
|
3905
|
+
return `\u6700\u8FD1\u4E00\u6B21\u5BF9\u8BDD\u663E\u793A\uFF1A\u7528\u6237\u66F4\u504F\u597D ${stripPrefix("\u66F4\u559C\u6B22")}`;
|
|
3906
|
+
case "goal":
|
|
3907
|
+
return `\u6700\u8FD1\u4E00\u6B21\u5BF9\u8BDD\u663E\u793A\uFF1A\u7528\u6237\u60F3\u63A8\u8FDB ${stripPrefix("\u60F3\u63A8\u8FDB")}`;
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
return normalized;
|
|
3911
|
+
};
|
|
3912
|
+
var buildMemoryOverlay = (items) => {
|
|
3913
|
+
if (items.length === 0) return null;
|
|
3914
|
+
const byKind = {
|
|
3915
|
+
episodic: [],
|
|
3916
|
+
semantic: [],
|
|
3917
|
+
procedural: []
|
|
3918
|
+
};
|
|
3919
|
+
for (const item of items) {
|
|
3920
|
+
byKind[item.kind].push(item);
|
|
3921
|
+
}
|
|
3922
|
+
const sections = Object.entries(byKind).filter(([, list]) => list.length > 0).map(([kind, list]) => {
|
|
3923
|
+
const lines = list.slice(0, 3).map((item) => `- ${normalizeDisplayContent(item)}`);
|
|
3924
|
+
return [`[${KIND_TITLES[kind]}]`, ...lines].join("\n");
|
|
3925
|
+
});
|
|
3926
|
+
return [
|
|
3927
|
+
"--- Memory Overlay ---",
|
|
3928
|
+
"\u4EE5\u4E0B\u662F\u5F53\u524D\u8BF7\u6C42\u53EF\u7528\u7684\u8BB0\u5FC6\u4E8B\u5B9E\uFF1B\u76F8\u5173\u65F6\u76F4\u63A5\u57FA\u4E8E\u8FD9\u4E9B\u8BB0\u5FC6\u56DE\u7B54\u3002",
|
|
3929
|
+
"\u7528\u6237\u5F53\u524D\u8F93\u5165\u4F18\u5148\u4E8E\u8BB0\u5FC6\uFF1B\u8BB0\u5FC6\u53EA\u8F85\u52A9\u7406\u89E3\uFF0C\u4E0D\u8981\u6C42\u8FCE\u5408\u3002",
|
|
3930
|
+
"",
|
|
3931
|
+
...sections
|
|
3932
|
+
].join("\n");
|
|
3933
|
+
};
|
|
3934
|
+
|
|
3935
|
+
// packages/ai/memory/rank.ts
|
|
3936
|
+
var CJK_SEQUENCE_REGEX = /[\u3400-\u4dbf\u4e00-\u9fff]+/g;
|
|
3937
|
+
var MEMORY_IDENTIFIER_REGEX = /\b(?:memory|noise)-[a-z0-9][a-z0-9-]*/gi;
|
|
3938
|
+
var buildCjkTokens = (text) => {
|
|
3939
|
+
const sequences = text.match(CJK_SEQUENCE_REGEX) ?? [];
|
|
3940
|
+
const tokens = [];
|
|
3941
|
+
for (const sequence of sequences) {
|
|
3942
|
+
if (sequence.length >= 2) {
|
|
3943
|
+
tokens.push(sequence);
|
|
3944
|
+
}
|
|
3945
|
+
for (let index = 0; index < sequence.length - 1; index += 1) {
|
|
3946
|
+
tokens.push(sequence.slice(index, index + 2));
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
return tokens;
|
|
3950
|
+
};
|
|
3951
|
+
var tokenize = (text) => {
|
|
3952
|
+
const normalized = text.toLowerCase();
|
|
3953
|
+
const wordTokens = normalized.split(/[\s,.;:!?()[\]{}"'`,。!?:;、]+/).map((token) => token.trim()).filter((token) => token.length >= 2);
|
|
3954
|
+
return [...wordTokens, ...buildCjkTokens(normalized)];
|
|
3955
|
+
};
|
|
3956
|
+
var keywordScore = (query, item) => {
|
|
3957
|
+
const queryTokens = new Set(tokenize(query));
|
|
3958
|
+
if (queryTokens.size === 0) return 0;
|
|
3959
|
+
const haystack = /* @__PURE__ */ new Set([
|
|
3960
|
+
...tokenize(item.content),
|
|
3961
|
+
...(item.tags ?? []).map((tag) => tag.toLowerCase()),
|
|
3962
|
+
item.patternKey?.toLowerCase() ?? ""
|
|
3963
|
+
]);
|
|
3964
|
+
let matches = 0;
|
|
3965
|
+
for (const token of queryTokens) {
|
|
3966
|
+
if (haystack.has(token)) matches += 1;
|
|
3967
|
+
}
|
|
3968
|
+
return Math.min(1, matches / Math.max(2, queryTokens.size));
|
|
3969
|
+
};
|
|
3970
|
+
var identifierScore = (query, item) => {
|
|
3971
|
+
const identifiers = query.match(MEMORY_IDENTIFIER_REGEX) ?? [];
|
|
3972
|
+
if (identifiers.length === 0) return 0;
|
|
3973
|
+
const content = item.content.toLowerCase();
|
|
3974
|
+
const matches = identifiers.filter(
|
|
3975
|
+
(identifier) => content.includes(identifier.toLowerCase())
|
|
3976
|
+
).length;
|
|
3977
|
+
return matches / identifiers.length;
|
|
3978
|
+
};
|
|
3979
|
+
var activationScore = (item, nowMs) => {
|
|
3980
|
+
const lastActivatedMs = Date.parse(item.lastActivatedAt || item.createdAt);
|
|
3981
|
+
const ageDays = Math.max(0, (nowMs - lastActivatedMs) / 864e5);
|
|
3982
|
+
const recency = 1 / (1 + ageDays / 7);
|
|
3983
|
+
const reinforcement = Math.min(1, Math.log1p(item.activationCount ?? 0) / 3);
|
|
3984
|
+
return 0.7 * recency + 0.3 * reinforcement;
|
|
3985
|
+
};
|
|
3986
|
+
var creationRecencyScore = (item, nowMs) => {
|
|
3987
|
+
const createdMs = Date.parse(item.createdAt);
|
|
3988
|
+
const ageDays = Math.max(0, (nowMs - createdMs) / 864e5);
|
|
3989
|
+
return 1 / (1 + ageDays / 7);
|
|
3990
|
+
};
|
|
3991
|
+
var typeFitScore = (item) => {
|
|
3992
|
+
if (item.kind === "episodic") return 0.75;
|
|
3993
|
+
if (item.kind === "semantic") return 0.9;
|
|
3994
|
+
return 0.85;
|
|
3995
|
+
};
|
|
3996
|
+
var understandingScore = (item) => {
|
|
3997
|
+
if (!item.tags?.includes("understanding-memory")) return 0;
|
|
3998
|
+
if (item.facet === "unfinished") return 1;
|
|
3999
|
+
if (item.facet === "tension") return 0.95;
|
|
4000
|
+
if (item.facet === "preference") return 0.85;
|
|
4001
|
+
if (item.facet === "style") return 0.75;
|
|
4002
|
+
return 0.8;
|
|
4003
|
+
};
|
|
4004
|
+
var pathScore = (item, context) => {
|
|
4005
|
+
let score = 0;
|
|
4006
|
+
if (context?.currentOwner && item.ownerType === context.currentOwner.ownerType && item.ownerId === context.currentOwner.ownerId) {
|
|
4007
|
+
score += 0.65;
|
|
4008
|
+
}
|
|
4009
|
+
if (context?.currentSubject && item.subjectType === context.currentSubject.subjectType && item.subjectId === context.currentSubject.subjectId) {
|
|
4010
|
+
score += 0.85;
|
|
4011
|
+
}
|
|
4012
|
+
return Math.min(1, score);
|
|
4013
|
+
};
|
|
4014
|
+
var scoreMemoryItem = (item, query, nowMs = Date.now(), context) => {
|
|
4015
|
+
const queryMatch = keywordScore(query, item);
|
|
4016
|
+
const identifierMatch = identifierScore(query, item);
|
|
4017
|
+
const activation = activationScore(item, nowMs);
|
|
4018
|
+
const createdRecency = creationRecencyScore(item, nowMs);
|
|
4019
|
+
const importance = item.importance ?? 0;
|
|
4020
|
+
const confidence = item.confidence ?? 0;
|
|
4021
|
+
const typeFit = typeFitScore(item);
|
|
4022
|
+
const understanding = understandingScore(item);
|
|
4023
|
+
const path = pathScore(item, context);
|
|
4024
|
+
return 0.25 * queryMatch + 0.2 * identifierMatch + 0.2 * path + 0.09 * activation + 0.16 * createdRecency + 0.06 * importance + 0.03 * confidence + 0.02 * typeFit + 0.01 * understanding;
|
|
4025
|
+
};
|
|
4026
|
+
var rankMemoryCandidates = (items, query, context) => {
|
|
4027
|
+
const nowMs = Date.now();
|
|
4028
|
+
return [...items].sort((a, b) => {
|
|
4029
|
+
const aExplicit = a.sourceKind === "explicit-user-directive";
|
|
4030
|
+
const bExplicit = b.sourceKind === "explicit-user-directive";
|
|
4031
|
+
if (aExplicit !== bExplicit) return bExplicit ? 1 : -1;
|
|
4032
|
+
if (aExplicit) {
|
|
4033
|
+
return Date.parse(b.createdAt) - Date.parse(a.createdAt);
|
|
4034
|
+
}
|
|
4035
|
+
return scoreMemoryItem(b, query, nowMs, context) - scoreMemoryItem(a, query, nowMs, context);
|
|
4036
|
+
});
|
|
4037
|
+
};
|
|
4038
|
+
|
|
4039
|
+
// packages/ai/memory/runtime.ts
|
|
4040
|
+
var COLD_STORAGE_CONFIDENCE = 0.3;
|
|
4041
|
+
var normalizeSelectedContent = (text) => text.trim().replace(/^(你要记住|请记住|以后记住|记住)[,,。.\s::]*/u, "").replace(/[。!?!?]+$/u, "").trim();
|
|
4042
|
+
var STACK_TERMS = [
|
|
4043
|
+
"typescript",
|
|
4044
|
+
"javascript",
|
|
4045
|
+
"python",
|
|
4046
|
+
"rust",
|
|
4047
|
+
"go",
|
|
4048
|
+
"golang",
|
|
4049
|
+
"java",
|
|
4050
|
+
"kotlin",
|
|
4051
|
+
"swift",
|
|
4052
|
+
"ruby",
|
|
4053
|
+
"php"
|
|
4054
|
+
];
|
|
4055
|
+
var stackAliases = {
|
|
4056
|
+
golang: "go"
|
|
4057
|
+
};
|
|
4058
|
+
var normalizeStackTerm = (term) => stackAliases[term] ?? term;
|
|
4059
|
+
var stackTermsIn = (text) => {
|
|
4060
|
+
const lower = text.toLowerCase();
|
|
4061
|
+
const terms = /* @__PURE__ */ new Set();
|
|
4062
|
+
for (const term of STACK_TERMS) {
|
|
4063
|
+
const pattern = term.length <= 2 ? new RegExp(`\\b${term}\\b`, "i") : new RegExp(term, "i");
|
|
4064
|
+
if (pattern.test(lower)) {
|
|
4065
|
+
terms.add(normalizeStackTerm(term));
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return terms;
|
|
4069
|
+
};
|
|
4070
|
+
var conflictsWithCurrentStack = (item, userInput) => {
|
|
4071
|
+
const currentStacks = stackTermsIn(userInput);
|
|
4072
|
+
if (currentStacks.size === 0) return false;
|
|
4073
|
+
const memoryStacks = stackTermsIn(item.content);
|
|
4074
|
+
if (memoryStacks.size === 0) return false;
|
|
4075
|
+
for (const stack of memoryStacks) {
|
|
4076
|
+
if (!currentStacks.has(stack)) return true;
|
|
4077
|
+
}
|
|
4078
|
+
return false;
|
|
4079
|
+
};
|
|
4080
|
+
var INTERACTION_PREFERENCE_TERMS = [
|
|
4081
|
+
"\u56DE\u7B54\u504F\u597D",
|
|
4082
|
+
"\u8F93\u51FA\u504F\u597D",
|
|
4083
|
+
"\u56DE\u590D\u504F\u597D",
|
|
4084
|
+
"\u56DE\u7B54\u7ED3\u6784",
|
|
4085
|
+
"\u8F93\u51FA\u7ED3\u6784",
|
|
4086
|
+
"\u56DE\u590D\u7ED3\u6784",
|
|
4087
|
+
"\u5148\u7ED9\u7ED3\u8BBA",
|
|
4088
|
+
"\u518D\u5217\u98CE\u9669",
|
|
4089
|
+
"\u6700\u540E\u7ED9\u8BC1\u636E",
|
|
4090
|
+
"\u8BED\u6C14",
|
|
4091
|
+
"\u98CE\u683C",
|
|
4092
|
+
"\u79F0\u547C",
|
|
4093
|
+
"\u53EB\u6211"
|
|
4094
|
+
];
|
|
4095
|
+
var isInteractionPreferenceMemory = (item) => {
|
|
4096
|
+
if (item.facet === "style") return true;
|
|
4097
|
+
const content = normalizeSelectedContent(item.content);
|
|
4098
|
+
return INTERACTION_PREFERENCE_TERMS.some((term) => content.includes(term));
|
|
4099
|
+
};
|
|
4100
|
+
var isOffCurrentPath = (item, context) => {
|
|
4101
|
+
const currentOwner = context?.currentOwner;
|
|
4102
|
+
const currentSubject = context?.currentSubject;
|
|
4103
|
+
if (currentSubject && (item.subjectType === "project" || item.subjectType === "space") && item.subjectType === currentSubject.subjectType && item.subjectId !== currentSubject.subjectId) {
|
|
4104
|
+
return true;
|
|
4105
|
+
}
|
|
4106
|
+
if (currentOwner?.ownerType === "space" && item.ownerType === "user" && (item.subjectType === "project" || item.subjectType === "space") && item.subjectId !== currentOwner.ownerId) {
|
|
4107
|
+
return true;
|
|
4108
|
+
}
|
|
4109
|
+
return false;
|
|
4110
|
+
};
|
|
4111
|
+
var selectRuntimeMemoryItems = (items, context, userInput = "") => {
|
|
4112
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4113
|
+
const unique = items.filter((item) => {
|
|
4114
|
+
if ((item.confidence ?? 0) < COLD_STORAGE_CONFIDENCE) return false;
|
|
4115
|
+
if (isOffCurrentPath(item, context)) return false;
|
|
4116
|
+
if (conflictsWithCurrentStack(item, userInput)) return false;
|
|
4117
|
+
const key = `${item.kind}:${normalizeSelectedContent(item.content).toLowerCase()}`;
|
|
4118
|
+
if (seen.has(key)) return false;
|
|
4119
|
+
seen.add(key);
|
|
4120
|
+
return true;
|
|
4121
|
+
});
|
|
4122
|
+
const selected = [];
|
|
4123
|
+
const selectedIds = /* @__PURE__ */ new Set();
|
|
4124
|
+
const add = (item) => {
|
|
4125
|
+
if (!item || selectedIds.has(item.id) || selected.length >= 4) return;
|
|
4126
|
+
selected.push(item);
|
|
4127
|
+
selectedIds.add(item.id);
|
|
4128
|
+
};
|
|
4129
|
+
add(
|
|
4130
|
+
unique.find(
|
|
4131
|
+
(item) => context?.currentSubject && item.subjectType === context.currentSubject.subjectType && item.subjectId === context.currentSubject.subjectId
|
|
4132
|
+
)
|
|
4133
|
+
);
|
|
4134
|
+
add(unique.find(isInteractionPreferenceMemory));
|
|
4135
|
+
add(
|
|
4136
|
+
unique.find(
|
|
4137
|
+
(item) => context?.currentOwner && item.ownerType === context.currentOwner.ownerType && item.ownerId === context.currentOwner.ownerId
|
|
4138
|
+
)
|
|
4139
|
+
);
|
|
4140
|
+
add(unique.find((item) => item.ownerType === "user"));
|
|
4141
|
+
add(unique.find((item) => item.ownerType === "space"));
|
|
4142
|
+
add(unique.find((item) => item.kind === "procedural"));
|
|
4143
|
+
for (const item of unique) {
|
|
4144
|
+
add(item);
|
|
4145
|
+
}
|
|
4146
|
+
return selected;
|
|
4147
|
+
};
|
|
4148
|
+
var resolveMemoryRuntime = async (input) => {
|
|
4149
|
+
const owners = chooseMemoryOwners({
|
|
4150
|
+
userId: input.userId,
|
|
4151
|
+
spaceId: input.spaceId
|
|
4152
|
+
});
|
|
4153
|
+
if (owners.length === 0) {
|
|
4154
|
+
return { selectedItems: [], promptBlock: null };
|
|
4155
|
+
}
|
|
4156
|
+
const policy = resolveAgentMemoryPolicy({ agentKey: input.agentKey });
|
|
4157
|
+
const candidates = await loadMemoryCandidatesFromDb(input.db, {
|
|
4158
|
+
owners,
|
|
4159
|
+
subjects: buildMemorySubjectsForAgent({
|
|
4160
|
+
userId: input.userId,
|
|
4161
|
+
spaceId: input.spaceId,
|
|
4162
|
+
agentKey: input.agentKey,
|
|
4163
|
+
policy
|
|
4164
|
+
}),
|
|
4165
|
+
kinds: ["episodic", "semantic", "procedural"],
|
|
4166
|
+
ownerLimit: 20,
|
|
4167
|
+
ownerFallback: policy.ownerFallback
|
|
4168
|
+
});
|
|
4169
|
+
const rankContext = {
|
|
4170
|
+
currentOwner: input.spaceId ? { ownerType: "space", ownerId: input.spaceId } : input.userId ? { ownerType: "user", ownerId: input.userId } : null,
|
|
4171
|
+
currentSubject: input.spaceId ? { subjectType: "space", subjectId: input.spaceId } : input.userId ? { subjectType: "user", subjectId: input.userId } : null
|
|
4172
|
+
};
|
|
4173
|
+
const ranked = selectRuntimeMemoryItems(
|
|
4174
|
+
rankMemoryCandidates(candidates, input.userInput, rankContext),
|
|
4175
|
+
rankContext,
|
|
4176
|
+
input.userInput
|
|
4177
|
+
);
|
|
4178
|
+
if (ranked.length === 0) {
|
|
4179
|
+
return { selectedItems: [], promptBlock: null };
|
|
4180
|
+
}
|
|
4181
|
+
await touchMemoryItemsInDb(input.db, ranked);
|
|
4182
|
+
return {
|
|
4183
|
+
selectedItems: ranked,
|
|
4184
|
+
promptBlock: buildMemoryOverlay(ranked)
|
|
4185
|
+
};
|
|
4186
|
+
};
|
|
4187
|
+
|
|
4188
|
+
// packages/cli/agentRunCommand.ts
|
|
4189
|
+
function resolvePositiveMs(value, fallback) {
|
|
4190
|
+
return parsePositiveFiniteNumberOrFallback(value, fallback);
|
|
4191
|
+
}
|
|
4192
|
+
async function resolveAgentRunAgentKey(args) {
|
|
4193
|
+
if (!args.agentInput.trim() || args.agentInput.startsWith("agent-")) {
|
|
4194
|
+
return void 0;
|
|
4195
|
+
}
|
|
4196
|
+
const db = await getReadableCliDb(args.output);
|
|
4197
|
+
const resolved = await resolveAgentRecordFromHybridStore({
|
|
4198
|
+
agentInput: args.agentInput,
|
|
4199
|
+
cliArgs: args.cliArgs,
|
|
4200
|
+
env: args.env,
|
|
4201
|
+
db,
|
|
4202
|
+
fetchImpl: fetch
|
|
4203
|
+
});
|
|
4204
|
+
return resolved?.agentKey;
|
|
4205
|
+
}
|
|
4206
|
+
async function runAgentRunCommand(args, deps) {
|
|
4207
|
+
const env = deps.env ?? process.env;
|
|
4208
|
+
const output = deps.output ?? process.stdout;
|
|
4209
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
4210
|
+
writeUsage(output, deps.commandPath);
|
|
4211
|
+
return 0;
|
|
4212
|
+
}
|
|
4213
|
+
const parsed = parseAgentRunArgs(args, { commandPath: deps.commandPath });
|
|
4214
|
+
if (!parsed) {
|
|
4215
|
+
writeUsage(output, deps.commandPath);
|
|
4216
|
+
return 1;
|
|
4217
|
+
}
|
|
4218
|
+
const runner = deps.runner ?? runAgentTurn;
|
|
4219
|
+
const nowMs = () => deps.now ? deps.now().getTime() : Date.now();
|
|
4220
|
+
const childRunId = env.NOLO_AGENT_RUN_ID;
|
|
4221
|
+
const hasRegistry = typeof childRunId === "string" && childRunId.length > 0;
|
|
4222
|
+
const processExit = deps.processExit ?? ((code) => process.exit(code));
|
|
4223
|
+
const stallTimeoutMs = resolvePositiveMs(
|
|
4224
|
+
deps.stallTimeoutMs ?? env.NOLO_LOCAL_RUN_STALL_TIMEOUT_MS,
|
|
4225
|
+
5 * 6e4
|
|
4226
|
+
);
|
|
4227
|
+
let timedOut = false;
|
|
4228
|
+
let stalled = false;
|
|
4229
|
+
let timeoutTimer;
|
|
4230
|
+
let stallTimer;
|
|
4231
|
+
const activityTracker = hasRegistry ? createRunActivityTracker(childRunId, {
|
|
4232
|
+
env,
|
|
4233
|
+
homedir: deps.homedir,
|
|
4234
|
+
fs: deps.fs,
|
|
4235
|
+
now: deps.now
|
|
4236
|
+
}) : void 0;
|
|
4237
|
+
const clearWatchdogs = () => {
|
|
4238
|
+
if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
|
|
4239
|
+
if (stallTimer !== void 0) clearInterval(stallTimer);
|
|
4240
|
+
activityTracker?.flush();
|
|
4241
|
+
activityTracker?.dispose();
|
|
4242
|
+
};
|
|
4243
|
+
let rejectOnWatchdogFailure;
|
|
4244
|
+
const watchdogFailure = hasRegistry ? new Promise((_, reject) => {
|
|
4245
|
+
rejectOnWatchdogFailure = reject;
|
|
4246
|
+
}) : void 0;
|
|
4247
|
+
const raceWithWatchdog = (work) => watchdogFailure ? Promise.race([work, watchdogFailure]) : work;
|
|
4248
|
+
const exitFromWatchdog = (code) => {
|
|
4249
|
+
try {
|
|
4250
|
+
processExit(code);
|
|
4251
|
+
} catch {
|
|
4252
|
+
}
|
|
4253
|
+
};
|
|
4254
|
+
if (hasRegistry) {
|
|
4255
|
+
stallTimer = setInterval(() => {
|
|
4256
|
+
if (timedOut || stalled) return;
|
|
4257
|
+
const activity = activityTracker?.getActivity();
|
|
4258
|
+
const lastEventAtMs = activity ? new Date(activity.lastEventAt).getTime() : nowMs();
|
|
4259
|
+
const elapsedSinceEvent = nowMs() - lastEventAtMs;
|
|
4260
|
+
if (activity?.inFlight?.kind === "tool") {
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
if (activity?.inFlight?.kind === "llm" && elapsedSinceEvent < stallTimeoutMs) {
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
if (elapsedSinceEvent >= stallTimeoutMs) {
|
|
4267
|
+
stalled = true;
|
|
4268
|
+
clearWatchdogs();
|
|
4269
|
+
const note = `stalled: no progress for ${stallTimeoutMs}ms`;
|
|
4270
|
+
output.write(`[nolo] local run ${note}
|
|
4271
|
+
`);
|
|
4272
|
+
(deps.finalizeRunRecord ?? finalizeRunRecord)(
|
|
4273
|
+
childRunId,
|
|
4274
|
+
{ status: "failed", note },
|
|
4275
|
+
{ env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
|
|
4276
|
+
);
|
|
4277
|
+
rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
|
|
4278
|
+
exitFromWatchdog(1);
|
|
4279
|
+
}
|
|
4280
|
+
}, Math.min(stallTimeoutMs, 1e4));
|
|
4281
|
+
if (typeof parsed.timeoutMs === "number" && parsed.timeoutMs > 0) {
|
|
4282
|
+
timeoutTimer = setTimeout(() => {
|
|
4283
|
+
timedOut = true;
|
|
4284
|
+
clearWatchdogs();
|
|
4285
|
+
const note = `timed out after ${parsed.timeoutMs}ms`;
|
|
4286
|
+
output.write(`[nolo] local run ${note}
|
|
4287
|
+
`);
|
|
4288
|
+
(deps.finalizeRunRecord ?? finalizeRunRecord)(
|
|
4289
|
+
childRunId,
|
|
4290
|
+
{ status: "timeout", note },
|
|
4291
|
+
{ env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
|
|
4292
|
+
);
|
|
4293
|
+
rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
|
|
4294
|
+
exitFromWatchdog(124);
|
|
4295
|
+
}, parsed.timeoutMs);
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
const wantsFallbackSuggestions = parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0;
|
|
4299
|
+
const rawAgentInput = resolveRawAgentInput(args, deps.commandPath) ?? parsed.agentKey;
|
|
4300
|
+
const fullstackLocal = isFullstackCodingAgentRef(rawAgentInput, parsed.agentKey);
|
|
4301
|
+
const explicitLocalCli = isLocalCliAgentKey(parsed.agentKey);
|
|
4302
|
+
const isLocalRun = parsed.runtimeMode === "local" || !parsed.runtimeMode && (fullstackLocal || explicitLocalCli || args.includes("--dangerously-allow-shell"));
|
|
4303
|
+
if (wantsFallbackSuggestions && !isLocalRun) {
|
|
4304
|
+
output.write("[nolo] --fallback-agent (suggestions) is only supported for local CLI runs (use --local)\n");
|
|
4305
|
+
return 1;
|
|
4306
|
+
}
|
|
4307
|
+
let effectiveMessage = parsed.message;
|
|
4308
|
+
if (wantsFallbackSuggestions) {
|
|
4309
|
+
effectiveMessage = `${parsed.message}
|
|
4310
|
+
|
|
4311
|
+
[Quota fallback context for agent decision: If this execution for ${parsed.agentKey} hits subscription quota, YOU (the agent) must decide the next agent to use. Do not rely on automatic wrapper. Reason based on the specific task (complexity, length, domain), agent strengths, cost, previous attempt results. Suggested alternatives: ${parsed.fallbackAgentKeys.join(", ")}. Update the task row with attempt history and dispatch the chosen next (via new agent run or callAgent({ background: true })).]`;
|
|
4312
|
+
}
|
|
4313
|
+
const resolvedAgentKey = parsed.runtimeMode !== "local" ? await (deps.resolveAgentRunAgentKey ?? resolveAgentRunAgentKey)({
|
|
4314
|
+
agentInput: resolveRawAgentInput(args, deps.commandPath) ?? parsed.agentKey,
|
|
4315
|
+
cliArgs: args,
|
|
4316
|
+
env,
|
|
4317
|
+
output
|
|
4318
|
+
}).catch(() => void 0) : void 0;
|
|
4319
|
+
const agentKey = resolvedAgentKey ?? parsed.agentKey;
|
|
4320
|
+
let effectiveAgentKey = agentKey;
|
|
4321
|
+
let modelOverride = null;
|
|
4322
|
+
const autoRouteRequested = deps.commandPath?.join(" ") === "chat" && !parsed.continueDialogId && (args.includes("--auto-route") || env.NOLO_AUTO_ROUTE === "1") && env.NOLO_AUTO_ROUTE !== "0";
|
|
4323
|
+
if (autoRouteRequested) {
|
|
4324
|
+
const authToken = resolveAuthToken(args, env);
|
|
4325
|
+
const serverUrl = resolveServerUrl(env);
|
|
4326
|
+
const hasExplicitAgent = Boolean(readFlagValue(args, "--agent"));
|
|
4327
|
+
const route = await classifyCliAutoRoute(effectiveMessage, {
|
|
4328
|
+
serverUrl,
|
|
4329
|
+
authToken
|
|
4330
|
+
});
|
|
4331
|
+
if (hasExplicitAgent) {
|
|
4332
|
+
modelOverride = buildModelLayerOverride(
|
|
4333
|
+
await readDbRecord({
|
|
4334
|
+
dbKey: agentKey,
|
|
4335
|
+
authToken,
|
|
4336
|
+
serverUrl,
|
|
4337
|
+
fetchImpl: fetch
|
|
4338
|
+
}).catch(() => null)
|
|
4339
|
+
);
|
|
4340
|
+
}
|
|
4341
|
+
if (hasExplicitAgent && !modelOverride) {
|
|
4342
|
+
output.write(
|
|
4343
|
+
"[nolo] auto-route: \u8986\u76D6\u6E90 agent \u8BFB\u53D6\u5931\u8D25\uFF0C\u6309\u539F\u6837\u76F4\u8DD1\u6240\u9009 agent\u3002\n"
|
|
4344
|
+
);
|
|
4345
|
+
} else {
|
|
4346
|
+
effectiveAgentKey = route.agentKey;
|
|
4347
|
+
output.write(
|
|
4348
|
+
`[nolo] auto \u2192 ${route.tier}${modelOverride ? ` (model: ${agentKey})` : ""}
|
|
4349
|
+
`
|
|
4350
|
+
);
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
let workflowReference;
|
|
4354
|
+
if (parsed.workflowRef) {
|
|
4355
|
+
try {
|
|
4356
|
+
workflowReference = await (deps.resolveWorkflowReference ?? resolveWorkflowReference)(
|
|
4357
|
+
parsed.workflowRef
|
|
4358
|
+
);
|
|
4359
|
+
} catch (error) {
|
|
4360
|
+
output.write(`[nolo] ${toErrorMessage(error)}
|
|
4361
|
+
`);
|
|
4362
|
+
return 1;
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
let skillReferences;
|
|
4366
|
+
let skillAllowedToolOverride;
|
|
4367
|
+
if (parsed.skillRefs?.length) {
|
|
4368
|
+
const skills = [];
|
|
4369
|
+
const authToken = resolveAuthToken(args, env);
|
|
4370
|
+
const serverUrl = resolveServerUrl(env);
|
|
4371
|
+
for (const ref of parsed.skillRefs) {
|
|
4372
|
+
try {
|
|
4373
|
+
const resolved = await resolveSkillReference(ref, {
|
|
4374
|
+
cwd: parsed.cwd,
|
|
4375
|
+
readDbRecord: async (dbKey) => {
|
|
4376
|
+
return readDbRecord({
|
|
4377
|
+
dbKey,
|
|
4378
|
+
authToken,
|
|
4379
|
+
serverUrl,
|
|
4380
|
+
fetchImpl: fetch
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
});
|
|
4384
|
+
skills.push(resolved);
|
|
4385
|
+
} catch (error) {
|
|
4386
|
+
output.write(`[nolo] ${toErrorMessage(error)}
|
|
4387
|
+
`);
|
|
4388
|
+
return 1;
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
skillReferences = skills;
|
|
4392
|
+
const skillToolLists = skills.map((s) => s.allowedTools).filter((t2) => !!t2 && t2.length > 0);
|
|
4393
|
+
if (skillToolLists.length > 0) {
|
|
4394
|
+
const skillAllowed = skillToolLists.reduce(
|
|
4395
|
+
(acc, tools) => acc.filter((t2) => tools.includes(t2))
|
|
4396
|
+
);
|
|
4397
|
+
if (skillAllowed.length === 0) {
|
|
4398
|
+
output.write(`[nolo] warning: attached skills declare incompatible allowed-tools; no tool restriction enforced
|
|
4399
|
+
`);
|
|
4400
|
+
}
|
|
4401
|
+
skillAllowedToolOverride = parsed.allowedToolNames?.length ? parsed.allowedToolNames.filter((t2) => skillAllowed.includes(t2)) : skillAllowed;
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
let localRuntimeCwd = parsed.cwd;
|
|
4405
|
+
if (!localRuntimeCwd && parsed.runtimeMode === "local") {
|
|
4406
|
+
localRuntimeCwd = process.cwd();
|
|
4407
|
+
}
|
|
4408
|
+
const runEnv = buildLocalRunEnv({
|
|
4409
|
+
env,
|
|
4410
|
+
allowShell: parsed.allowShell
|
|
4411
|
+
});
|
|
4412
|
+
if (parsed.background && isLocalRun) {
|
|
4413
|
+
const { runId, pid, logPath } = await (deps.spawnLocalBackgroundRun ?? spawnLocalBackgroundRun)(
|
|
4414
|
+
{
|
|
4415
|
+
rawArgs: args,
|
|
4416
|
+
commandPath: deps.commandPath,
|
|
4417
|
+
cliEntrypointPath: deps.cliEntrypointPath,
|
|
4418
|
+
agentKey,
|
|
4419
|
+
cwd: localRuntimeCwd,
|
|
4420
|
+
msgFile: readFlagValue(args, "--msg-file"),
|
|
4421
|
+
message: parsed.message,
|
|
4422
|
+
timeoutMs: parsed.timeoutMs,
|
|
4423
|
+
output
|
|
4424
|
+
},
|
|
4425
|
+
{
|
|
4426
|
+
env,
|
|
4427
|
+
homedir: deps.homedir,
|
|
4428
|
+
spawn: deps.spawn,
|
|
4429
|
+
fs: deps.fs,
|
|
4430
|
+
now: deps.now,
|
|
4431
|
+
generateRunId: deps.generateRunId
|
|
4432
|
+
}
|
|
4433
|
+
);
|
|
4434
|
+
output.write(`[nolo] runId=${runId}
|
|
4435
|
+
`);
|
|
4436
|
+
output.write(`[nolo] pid=${pid ?? "-"}
|
|
4437
|
+
`);
|
|
4438
|
+
output.write(`[nolo] log=${logPath}
|
|
4439
|
+
`);
|
|
4440
|
+
output.write(`[nolo] status: nolo agent status ${runId}
|
|
4441
|
+
`);
|
|
4442
|
+
output.write(`[nolo] stop: nolo agent stop ${runId}
|
|
4443
|
+
`);
|
|
4444
|
+
return 0;
|
|
4445
|
+
}
|
|
4446
|
+
const cliCwd = parsed.cwd ?? process.cwd();
|
|
4447
|
+
const extraContextBlocks = [];
|
|
4448
|
+
const AGENTS_MD_MAX = 8192;
|
|
4449
|
+
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
4450
|
+
const agentsMdPath = join2(cliCwd, name);
|
|
4451
|
+
if (existsSync2(agentsMdPath)) {
|
|
4452
|
+
try {
|
|
4453
|
+
let mdContent = readFileSync2(agentsMdPath, "utf8").trim();
|
|
4454
|
+
if (!mdContent) continue;
|
|
4455
|
+
if (Buffer.byteLength(mdContent, "utf8") > AGENTS_MD_MAX) {
|
|
4456
|
+
mdContent = Buffer.from(mdContent, "utf8").subarray(0, AGENTS_MD_MAX).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
|
|
4457
|
+
}
|
|
4458
|
+
extraContextBlocks.push(`--- \u9879\u76EE\u6307\u4EE4\uFF08${name}\uFF09---
|
|
4459
|
+
${mdContent}`);
|
|
4460
|
+
break;
|
|
4461
|
+
} catch {
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
extraContextBlocks.push(...buildSkillContextBlocks(skillReferences));
|
|
4466
|
+
const skillDiscoveryBlock = buildSkillDiscoveryContextBlock(cliCwd);
|
|
4467
|
+
if (skillDiscoveryBlock) {
|
|
4468
|
+
extraContextBlocks.push(skillDiscoveryBlock);
|
|
4469
|
+
}
|
|
4470
|
+
let memoryPromptBlock = null;
|
|
4471
|
+
if (!deps.memoryRecallDisabled) {
|
|
4472
|
+
const memoryServerUrl = resolveServerUrl(env);
|
|
4473
|
+
const memoryAuthToken = resolveAuthToken(args, env);
|
|
4474
|
+
const memoryAgentKey = effectiveAgentKey;
|
|
4475
|
+
const memorySpaceId = parsed.spaceId ?? void 0;
|
|
4476
|
+
try {
|
|
4477
|
+
if (memoryAuthToken && memoryServerUrl) {
|
|
4478
|
+
const memoryResponse = await fetch(`${memoryServerUrl}/api/memory/query`, {
|
|
4479
|
+
method: "POST",
|
|
4480
|
+
headers: {
|
|
4481
|
+
"Content-Type": "application/json",
|
|
4482
|
+
Authorization: `Bearer ${memoryAuthToken}`
|
|
4483
|
+
},
|
|
4484
|
+
body: JSON.stringify({
|
|
4485
|
+
agentKey: memoryAgentKey,
|
|
4486
|
+
userInput: effectiveMessage,
|
|
4487
|
+
...memorySpaceId ? { spaceId: memorySpaceId } : {}
|
|
4488
|
+
}),
|
|
4489
|
+
signal: AbortSignal.timeout(5e3)
|
|
4490
|
+
});
|
|
4491
|
+
if (memoryResponse.ok) {
|
|
4492
|
+
const memoryPayload = await memoryResponse.json().catch(() => null);
|
|
4493
|
+
const block = typeof memoryPayload?.promptBlock === "string" ? memoryPayload.promptBlock.trim() : "";
|
|
4494
|
+
memoryPromptBlock = block || null;
|
|
4495
|
+
} else {
|
|
4496
|
+
console.warn("[cli-memory] memory query returned non-ok status:", memoryResponse.status);
|
|
4497
|
+
}
|
|
4498
|
+
} else {
|
|
4499
|
+
const localDb = await getDefaultCliLocalRuntimeDb({ env });
|
|
4500
|
+
const machineId = resolveMachineId();
|
|
4501
|
+
const resolution = await resolveMemoryRuntime({
|
|
4502
|
+
db: localDb,
|
|
4503
|
+
userId: machineId,
|
|
4504
|
+
agentKey: memoryAgentKey,
|
|
4505
|
+
userInput: effectiveMessage,
|
|
4506
|
+
...memorySpaceId ? { spaceId: memorySpaceId } : {}
|
|
4507
|
+
});
|
|
4508
|
+
memoryPromptBlock = resolution.promptBlock;
|
|
4509
|
+
}
|
|
4510
|
+
} catch (memoryError) {
|
|
4511
|
+
console.warn("[cli-memory] memory recall failed, omitting memory layer:", memoryError);
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
const memoryOverlayLayer = buildMemoryOverlayLayer({ promptBlock: memoryPromptBlock });
|
|
4515
|
+
const memoryUseGuidanceLayer = buildMemoryUseGuidanceLayer({ promptBlock: memoryPromptBlock });
|
|
4516
|
+
const contextBlockScopes = [];
|
|
4517
|
+
const agentsMdMarker = "--- \u9879\u76EE\u6307\u4EE4\uFF08";
|
|
4518
|
+
for (const block of extraContextBlocks) {
|
|
4519
|
+
if (block.startsWith(agentsMdMarker)) {
|
|
4520
|
+
contextBlockScopes.push({ content: block, cacheScope: "session" });
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
if (memoryUseGuidanceLayer?.content?.trim()) {
|
|
4524
|
+
contextBlockScopes.push({ content: memoryUseGuidanceLayer.content, cacheScope: "session" });
|
|
4525
|
+
}
|
|
4526
|
+
for (const block of extraContextBlocks) {
|
|
4527
|
+
if (!block.startsWith(agentsMdMarker)) {
|
|
4528
|
+
contextBlockScopes.push({ content: block, cacheScope: "turn" });
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
if (memoryOverlayLayer?.content?.trim()) {
|
|
4532
|
+
contextBlockScopes.push({ content: memoryOverlayLayer.content, cacheScope: "turn" });
|
|
4533
|
+
}
|
|
4534
|
+
const buildRunOptions = (targetAgentKey) => ({
|
|
4535
|
+
agentName: targetAgentKey,
|
|
4536
|
+
agentKey: targetAgentKey,
|
|
4537
|
+
serverUrl: resolveServerUrl(env),
|
|
4538
|
+
message: prependWorkflowReferencePrompt(
|
|
4539
|
+
prependSubjectDialogMarker(
|
|
4540
|
+
prependFeatureWorktreeInstruction(
|
|
4541
|
+
effectiveMessage,
|
|
4542
|
+
parsed.injectFeatureWorktreeInstruction
|
|
4543
|
+
),
|
|
4544
|
+
parsed.subjectDialogKey
|
|
4545
|
+
),
|
|
4546
|
+
workflowReference
|
|
4547
|
+
),
|
|
4548
|
+
imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
|
|
4549
|
+
scriptDir: deps.scriptDir,
|
|
4550
|
+
env: runEnv,
|
|
4551
|
+
output,
|
|
4552
|
+
...deps.localRuntimeAdapterFactory ? { localRuntimeAdapterFactory: deps.localRuntimeAdapterFactory } : {},
|
|
4553
|
+
...localRuntimeCwd ? { localRuntimeCwd } : {},
|
|
4554
|
+
...parsed.runtimeMode ? { runtimeMode: parsed.runtimeMode } : {},
|
|
4555
|
+
...parsed.continueDialogId ? { continueDialogId: parsed.continueDialogId } : {},
|
|
4556
|
+
...parsed.spaceId ? { spaceId: parsed.spaceId } : {},
|
|
4557
|
+
...parsed.category ? { category: parsed.category } : {},
|
|
4558
|
+
...parsed.inheritedFromDialogKey ? { inheritedFromDialogKey: parsed.inheritedFromDialogKey } : {},
|
|
4559
|
+
...parsed.parentDialogId ? { parentDialogId: parsed.parentDialogId } : {},
|
|
4560
|
+
...parsed.parentWakeOnTerminal ? { parentWakeOnTerminal: true } : {},
|
|
4561
|
+
...parsed.subjectDialogKey ? { subjectDialogKey: parsed.subjectDialogKey } : {},
|
|
4562
|
+
...parsed.subjectRefs?.length ? { subjectRefs: parsed.subjectRefs } : {},
|
|
4563
|
+
...parsed.allowedChildAgentKeys?.length ? { allowedChildAgentKeys: parsed.allowedChildAgentKeys } : {},
|
|
4564
|
+
...skillAllowedToolOverride !== void 0 ? { allowedToolNames: skillAllowedToolOverride } : parsed.allowedToolNames?.length ? { allowedToolNames: parsed.allowedToolNames } : {},
|
|
4565
|
+
...parsed.blockedToolNames?.length ? { blockedToolNames: parsed.blockedToolNames } : {},
|
|
4566
|
+
...activityTracker ? { onLoopEvent: activityTracker.onLoopEvent } : {},
|
|
4567
|
+
background: parsed.background,
|
|
4568
|
+
noStream: parsed.noStream,
|
|
4569
|
+
ephemeral: parsed.ephemeral,
|
|
4570
|
+
...typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {},
|
|
4571
|
+
traceTools: parsed.traceTools,
|
|
4572
|
+
...parsed.eventsMode ? { eventsMode: parsed.eventsMode } : {},
|
|
4573
|
+
...parsed.taskEvidence ? { taskEvidence: parsed.taskEvidence } : {},
|
|
4574
|
+
...extraContextBlocks.length > 0 ? { extraContextBlocks } : {},
|
|
4575
|
+
...contextBlockScopes.length > 0 ? { contextBlockScopes } : {}
|
|
4576
|
+
});
|
|
4577
|
+
let result = await raceWithWatchdog(
|
|
4578
|
+
runner({
|
|
4579
|
+
...buildRunOptions(effectiveAgentKey),
|
|
4580
|
+
...modelOverride ? { modelOverride } : {}
|
|
4581
|
+
})
|
|
4582
|
+
);
|
|
4583
|
+
clearWatchdogs();
|
|
4584
|
+
if (result.exitCode !== 0 && isQuotaExhaustedError(result.localError) && isLocalRun && parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0) {
|
|
4585
|
+
const fallbackAgentKey = parsed.fallbackAgentKeys[0];
|
|
4586
|
+
output.write(
|
|
4587
|
+
`[nolo] quota exhausted on ${agentKey}, falling back to ${fallbackAgentKey}
|
|
4588
|
+
`
|
|
4589
|
+
);
|
|
4590
|
+
const fallbackResult = await raceWithWatchdog(runner(buildRunOptions(fallbackAgentKey)));
|
|
4591
|
+
clearWatchdogs();
|
|
4592
|
+
if (fallbackResult.exitCode === 0) {
|
|
4593
|
+
result = fallbackResult;
|
|
4594
|
+
} else {
|
|
4595
|
+
result = fallbackResult;
|
|
4596
|
+
output.write(
|
|
4597
|
+
`[nolo] fallback to ${fallbackAgentKey} also failed: ${fallbackResult.localError instanceof Error ? fallbackResult.localError.message : String(fallbackResult.localError ?? "unknown error")}
|
|
4598
|
+
`
|
|
4599
|
+
);
|
|
4600
|
+
}
|
|
4601
|
+
} else if (result.localError instanceof CliProviderQuotaError) {
|
|
4602
|
+
output.write(`
|
|
4603
|
+
[nolo] Quota limit hit for ${parsed.agentKey} (CliProviderQuotaError).
|
|
4604
|
+
`);
|
|
4605
|
+
if (parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0) {
|
|
4606
|
+
output.write(`Suggested alternatives for your decision: ${parsed.fallbackAgentKeys.join(", ")}
|
|
4607
|
+
`);
|
|
4608
|
+
}
|
|
4609
|
+
output.write(`As the orchestrating agent, decide next based on task and re-dispatch (update task state with this attempt).
|
|
4610
|
+
`);
|
|
4611
|
+
}
|
|
4612
|
+
if (result.dialogId) {
|
|
4613
|
+
if (parsed.background) {
|
|
4614
|
+
output.write(`
|
|
4615
|
+
[nolo] background dialog ${result.dialogId}
|
|
4616
|
+
`);
|
|
4617
|
+
output.write(`[nolo] read: nolo dialog read ${result.dialogId}
|
|
4618
|
+
`);
|
|
4619
|
+
} else {
|
|
4620
|
+
output.write(`
|
|
4621
|
+
[nolo] dialog ${result.dialogId}
|
|
4622
|
+
`);
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
if (shouldPrintLocalRunSummary({ parsed, localRuntimeCwd })) {
|
|
4626
|
+
try {
|
|
4627
|
+
const inspect = deps.inspectLocalRunWorkspace ?? inspectLocalRunWorkspace;
|
|
4628
|
+
const inspection = await inspect(localRuntimeCwd);
|
|
4629
|
+
output.write(formatLocalRunSummary({
|
|
4630
|
+
dialogId: result.dialogId,
|
|
4631
|
+
inspection
|
|
4632
|
+
}));
|
|
4633
|
+
} catch (error) {
|
|
4634
|
+
output.write(
|
|
4635
|
+
`
|
|
4636
|
+
[nolo] local run summary unavailable: ${toErrorMessage(error)}
|
|
4637
|
+
`
|
|
4638
|
+
);
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
if (typeof childRunId === "string" && childRunId.length > 0) {
|
|
4642
|
+
const finalStatus = result.exitCode === 0 ? "done" : "failed";
|
|
4643
|
+
await (deps.finalizeRunRecord ?? finalizeRunRecord)(
|
|
4644
|
+
childRunId,
|
|
4645
|
+
{
|
|
4646
|
+
status: finalStatus,
|
|
4647
|
+
exitCode: result.exitCode,
|
|
4648
|
+
dialogId: result.dialogId
|
|
4649
|
+
},
|
|
4650
|
+
{
|
|
4651
|
+
env,
|
|
4652
|
+
homedir: deps.homedir,
|
|
4653
|
+
fs: deps.fs,
|
|
4654
|
+
now: deps.now
|
|
4655
|
+
}
|
|
4656
|
+
);
|
|
4657
|
+
}
|
|
4658
|
+
return result.exitCode;
|
|
4659
|
+
}
|
|
4660
|
+
var QUOTA_ERROR_PATTERNS = [
|
|
4661
|
+
/429/,
|
|
4662
|
+
/quota/i,
|
|
4663
|
+
/rate\s*limit/i,
|
|
4664
|
+
/too\s*many\s*requests/i,
|
|
4665
|
+
/额度/,
|
|
4666
|
+
/上限/,
|
|
4667
|
+
/用尽/,
|
|
4668
|
+
/CliProviderQuotaError/i
|
|
4669
|
+
];
|
|
4670
|
+
function isQuotaExhaustedError(error) {
|
|
4671
|
+
if (!error) return false;
|
|
4672
|
+
if (error instanceof CliProviderQuotaError) return true;
|
|
4673
|
+
if (error instanceof Error) {
|
|
4674
|
+
return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error.message));
|
|
4675
|
+
}
|
|
4676
|
+
if (typeof error === "string") {
|
|
4677
|
+
return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error));
|
|
4678
|
+
}
|
|
4679
|
+
const maybeStatus = error.status;
|
|
4680
|
+
if (typeof maybeStatus === "number" && maybeStatus === 429) return true;
|
|
4681
|
+
const maybeStatusCode = error.statusCode;
|
|
4682
|
+
if (typeof maybeStatusCode === "number" && maybeStatusCode === 429) return true;
|
|
4683
|
+
return false;
|
|
4684
|
+
}
|
|
4685
|
+
|
|
4686
|
+
export {
|
|
4687
|
+
formatTokenCount,
|
|
4688
|
+
renderTokenStatus,
|
|
4689
|
+
resolveCliColorEnabled,
|
|
4690
|
+
dimCliText,
|
|
4691
|
+
THEME_PALETTES,
|
|
4692
|
+
getActiveThemeName,
|
|
4693
|
+
setActiveThemeName,
|
|
4694
|
+
getActiveBrightness,
|
|
4695
|
+
setActiveBrightness,
|
|
4696
|
+
getActiveDensity,
|
|
4697
|
+
setActiveDensity,
|
|
4698
|
+
resolveTuiBrightness,
|
|
4699
|
+
surfaceBackgroundSequence,
|
|
4700
|
+
themeColorSequence,
|
|
4701
|
+
themeText,
|
|
4702
|
+
highlightMarkdown,
|
|
4703
|
+
formatAssistantDisplay,
|
|
4704
|
+
normalizeThinkingDisplayMode,
|
|
4705
|
+
parseCliLocale,
|
|
4706
|
+
getCliLocale,
|
|
4707
|
+
setCliLocale,
|
|
4708
|
+
initCliLocale,
|
|
4709
|
+
t,
|
|
4710
|
+
normalizeToolDisplayMode,
|
|
4711
|
+
runAgentTurn,
|
|
4712
|
+
CLI_IMAGE_AGENT_KEY,
|
|
4713
|
+
CLI_AUTO_TIER_AGENT_KEY_TABLE,
|
|
4714
|
+
classifyCliAutoRoute,
|
|
4715
|
+
resolveWorkflowReference,
|
|
4716
|
+
resolveSkillReference,
|
|
4717
|
+
buildSkillContextBlocks,
|
|
4718
|
+
prependSubjectDialogMarker,
|
|
4719
|
+
normalizeCliImageInput,
|
|
4720
|
+
inspectLocalRunWorkspace,
|
|
4721
|
+
runAgentRunCommand,
|
|
4722
|
+
isQuotaExhaustedError
|
|
4723
|
+
};
|