@ai-setting/roy-agent-cli 1.6.42 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/roy-agent.js +247 -40
- package/dist/index.js +247 -40
- package/dist/roy-agent-linux-x64/bin/roy-agent +0 -0
- package/package.json +3 -2
package/dist/bin/roy-agent.js
CHANGED
|
@@ -9461,7 +9461,7 @@ class CliQuietModeService {
|
|
|
9461
9461
|
// package.json
|
|
9462
9462
|
var package_default = {
|
|
9463
9463
|
name: "@ai-setting/roy-agent-cli",
|
|
9464
|
-
version: "1.
|
|
9464
|
+
version: "1.7.0",
|
|
9465
9465
|
type: "module",
|
|
9466
9466
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9467
9467
|
main: "./dist/index.js",
|
|
@@ -9510,7 +9510,8 @@ var package_default = {
|
|
|
9510
9510
|
"vscode-html-languageserver-bin": "^1.4.0",
|
|
9511
9511
|
"vscode-json-languageserver": "^1.3.4",
|
|
9512
9512
|
yargs: "^17.7.2",
|
|
9513
|
-
"zod-to-json-schema": "^3.25.2"
|
|
9513
|
+
"zod-to-json-schema": "^3.25.2",
|
|
9514
|
+
"@earendil-works/pi-ai": "^0.74.2"
|
|
9514
9515
|
},
|
|
9515
9516
|
devDependencies: {
|
|
9516
9517
|
"@types/node": "^20.10.0",
|
|
@@ -13380,6 +13381,9 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
13380
13381
|
showToolCalls: args.toolCalls,
|
|
13381
13382
|
showToolResults: args.toolResults
|
|
13382
13383
|
}, traceId, mergedContext);
|
|
13384
|
+
if (result && !queryExecutor.getFullText()) {
|
|
13385
|
+
console.log(result);
|
|
13386
|
+
}
|
|
13383
13387
|
repl.setLastUsage(queryExecutor.getLastUsage());
|
|
13384
13388
|
return result;
|
|
13385
13389
|
},
|
|
@@ -13518,7 +13522,7 @@ import React6 from "react";
|
|
|
13518
13522
|
import { render } from "ink";
|
|
13519
13523
|
|
|
13520
13524
|
// src/RunTui.tsx
|
|
13521
|
-
import { useState as useState6, useCallback as useCallback4, useRef as useRef2 } from "react";
|
|
13525
|
+
import { useState as useState6, useCallback as useCallback4, useRef as useRef2, useEffect as useEffect5 } from "react";
|
|
13522
13526
|
import { Box as Box6, Text as Text8, useApp as useApp2 } from "ink";
|
|
13523
13527
|
|
|
13524
13528
|
// src/tui/App.tsx
|
|
@@ -13830,7 +13834,7 @@ function useStreamingAccumulator() {
|
|
|
13830
13834
|
let snapshot = "";
|
|
13831
13835
|
setStreamingText((prev) => {
|
|
13832
13836
|
snapshot = prev;
|
|
13833
|
-
return
|
|
13837
|
+
return "";
|
|
13834
13838
|
});
|
|
13835
13839
|
return snapshot;
|
|
13836
13840
|
}, []);
|
|
@@ -13839,7 +13843,7 @@ function useStreamingAccumulator() {
|
|
|
13839
13843
|
let snapshot = "";
|
|
13840
13844
|
setStreamingText((prev) => {
|
|
13841
13845
|
snapshot = prev;
|
|
13842
|
-
return
|
|
13846
|
+
return "";
|
|
13843
13847
|
});
|
|
13844
13848
|
return snapshot;
|
|
13845
13849
|
}, []);
|
|
@@ -13930,7 +13934,8 @@ var RunScreen = ({
|
|
|
13930
13934
|
workdir,
|
|
13931
13935
|
maxLines: initialMaxLines,
|
|
13932
13936
|
markdownEnabled: markdownEnabled2,
|
|
13933
|
-
onExit
|
|
13937
|
+
onExit,
|
|
13938
|
+
onRegisterHandlers
|
|
13934
13939
|
}) => {
|
|
13935
13940
|
const [history, setHistory] = useState6([]);
|
|
13936
13941
|
const [errorMessage, setErrorMessage] = useState6(null);
|
|
@@ -13941,11 +13946,60 @@ var RunScreen = ({
|
|
|
13941
13946
|
const { exit } = useApp2();
|
|
13942
13947
|
const userTurnCountRef = useRef2(0);
|
|
13943
13948
|
const assistantTurnCountRef = useRef2(0);
|
|
13949
|
+
const isProcessingRef = useRef2(0);
|
|
13950
|
+
const executeQueryRef = useRef2(null);
|
|
13944
13951
|
const width = process.stdout.columns ?? 80;
|
|
13945
13952
|
const doExit = useCallback4(() => {
|
|
13946
13953
|
onExit?.();
|
|
13947
13954
|
exit();
|
|
13948
13955
|
}, [onExit, exit]);
|
|
13956
|
+
const executeQuery = useCallback4(async (message) => {
|
|
13957
|
+
isProcessingRef.current += 1;
|
|
13958
|
+
stream.start();
|
|
13959
|
+
try {
|
|
13960
|
+
let responseText;
|
|
13961
|
+
if (streamHandleQuery) {
|
|
13962
|
+
responseText = await streamHandleQuery(message, (chunk) => stream.append(chunk));
|
|
13963
|
+
} else {
|
|
13964
|
+
responseText = await handleQuery(message);
|
|
13965
|
+
}
|
|
13966
|
+
const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
|
|
13967
|
+
const finalText = truncateForTerminal(rendered, maxLines);
|
|
13968
|
+
stream.finish();
|
|
13969
|
+
setHistory((h) => [...h, { role: "assistant", content: finalText }]);
|
|
13970
|
+
assistantTurnCountRef.current += 1;
|
|
13971
|
+
} catch (err) {
|
|
13972
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13973
|
+
stream.fail(err instanceof Error ? err : new Error(msg));
|
|
13974
|
+
setErrorMessage(msg);
|
|
13975
|
+
setHistory((h) => [
|
|
13976
|
+
...h,
|
|
13977
|
+
{ role: "tool", content: `❌ Error: ${msg}` }
|
|
13978
|
+
]);
|
|
13979
|
+
} finally {
|
|
13980
|
+
isProcessingRef.current -= 1;
|
|
13981
|
+
}
|
|
13982
|
+
}, [handleQuery, markdownEnabled2, maxLines, stream, streamHandleQuery, width]);
|
|
13983
|
+
useEffect5(() => {
|
|
13984
|
+
executeQueryRef.current = executeQuery;
|
|
13985
|
+
}, [executeQuery]);
|
|
13986
|
+
useEffect5(() => {
|
|
13987
|
+
if (!onRegisterHandlers)
|
|
13988
|
+
return;
|
|
13989
|
+
const handleNotification = async (message, _envEvent) => {
|
|
13990
|
+
setHistory((h) => [
|
|
13991
|
+
...h,
|
|
13992
|
+
{ role: "tool", content: `\uD83D\uDCE2 ${message}` }
|
|
13993
|
+
]);
|
|
13994
|
+
if (executeQueryRef.current) {
|
|
13995
|
+
await executeQueryRef.current(message);
|
|
13996
|
+
}
|
|
13997
|
+
};
|
|
13998
|
+
const isIdle = () => {
|
|
13999
|
+
return isProcessingRef.current === 0;
|
|
14000
|
+
};
|
|
14001
|
+
onRegisterHandlers({ onNotification: handleNotification, isIdle });
|
|
14002
|
+
}, [onRegisterHandlers]);
|
|
13949
14003
|
const handleSubmit = useCallback4(async (raw) => {
|
|
13950
14004
|
const trimmed = raw.trim();
|
|
13951
14005
|
if (!trimmed)
|
|
@@ -14007,38 +14061,14 @@ var RunScreen = ({
|
|
|
14007
14061
|
setHistory((h) => [...h, { role: "user", content: trimmed }]);
|
|
14008
14062
|
setUserHistory((uh) => [trimmed, ...uh]);
|
|
14009
14063
|
userTurnCountRef.current += 1;
|
|
14010
|
-
|
|
14011
|
-
try {
|
|
14012
|
-
let responseText;
|
|
14013
|
-
if (streamHandleQuery) {
|
|
14014
|
-
responseText = await streamHandleQuery(trimmed, (chunk) => stream.append(chunk));
|
|
14015
|
-
} else {
|
|
14016
|
-
responseText = await handleQuery(trimmed);
|
|
14017
|
-
}
|
|
14018
|
-
const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
|
|
14019
|
-
const finalText = truncateForTerminal(rendered, maxLines);
|
|
14020
|
-
stream.finish();
|
|
14021
|
-
setHistory((h) => [...h, { role: "assistant", content: finalText }]);
|
|
14022
|
-
assistantTurnCountRef.current += 1;
|
|
14023
|
-
} catch (err) {
|
|
14024
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
14025
|
-
stream.fail(err instanceof Error ? err : new Error(msg));
|
|
14026
|
-
setErrorMessage(msg);
|
|
14027
|
-
setHistory((h) => [
|
|
14028
|
-
...h,
|
|
14029
|
-
{ role: "tool", content: `❌ Error: ${msg}` }
|
|
14030
|
-
]);
|
|
14031
|
-
}
|
|
14064
|
+
await executeQuery(trimmed);
|
|
14032
14065
|
}, [
|
|
14033
14066
|
doExit,
|
|
14034
14067
|
exit,
|
|
14035
|
-
|
|
14036
|
-
markdownEnabled2,
|
|
14068
|
+
executeQuery,
|
|
14037
14069
|
maxLines,
|
|
14038
14070
|
model,
|
|
14039
|
-
|
|
14040
|
-
streamHandleQuery,
|
|
14041
|
-
width
|
|
14071
|
+
workdir
|
|
14042
14072
|
]);
|
|
14043
14073
|
const handleCancel = useCallback4(() => {
|
|
14044
14074
|
doExit();
|
|
@@ -14081,7 +14111,7 @@ var RunTui_default = RunScreen;
|
|
|
14081
14111
|
|
|
14082
14112
|
// src/commands/run.ts
|
|
14083
14113
|
init_markdown();
|
|
14084
|
-
async function loadHandleQuery(options) {
|
|
14114
|
+
async function loadHandleQuery(options, hooks) {
|
|
14085
14115
|
const quiet = CliQuietModeService.getInstance().isQuiet();
|
|
14086
14116
|
const output = new OutputService2;
|
|
14087
14117
|
output.configure({ quiet });
|
|
@@ -14098,10 +14128,126 @@ async function loadHandleQuery(options) {
|
|
|
14098
14128
|
if (!env) {
|
|
14099
14129
|
throw new Error("Failed to create environment");
|
|
14100
14130
|
}
|
|
14101
|
-
|
|
14102
|
-
|
|
14103
|
-
|
|
14131
|
+
const sessionComponent = envService.getSession();
|
|
14132
|
+
if (!sessionComponent) {
|
|
14133
|
+
throw new Error("Failed to create session component");
|
|
14134
|
+
}
|
|
14135
|
+
const sessionManager = new SessionManager({
|
|
14136
|
+
sessionComponent,
|
|
14137
|
+
output,
|
|
14138
|
+
quiet: true
|
|
14139
|
+
});
|
|
14140
|
+
const sessionResult = await sessionManager.init(options.session, options.continue);
|
|
14141
|
+
const sessionId = sessionResult.sessionId;
|
|
14142
|
+
const queryExecutor = new QueryExecutor({
|
|
14143
|
+
env,
|
|
14144
|
+
sessionComponent,
|
|
14145
|
+
output,
|
|
14146
|
+
quiet: true,
|
|
14147
|
+
silent: true
|
|
14148
|
+
});
|
|
14149
|
+
let agentContext = undefined;
|
|
14150
|
+
if (options.agent && options.agent !== "default") {
|
|
14151
|
+
const agentComponent = env.getComponent("agent");
|
|
14152
|
+
const validation = validateActAgent(agentComponent, options.agent);
|
|
14153
|
+
if (!validation.valid) {
|
|
14154
|
+
output.error(validation.message);
|
|
14155
|
+
output.info("提示: roy-agent agents list");
|
|
14156
|
+
process.exit(1);
|
|
14157
|
+
}
|
|
14158
|
+
agentContext = buildActAgentContext(options.agent);
|
|
14159
|
+
}
|
|
14160
|
+
let eventSourceSystemPrompt;
|
|
14161
|
+
const rawEventSourceIds = options.eventSource;
|
|
14162
|
+
const eventSourceIds = Array.isArray(rawEventSourceIds) ? rawEventSourceIds.filter((s) => !!s) : rawEventSourceIds ? [rawEventSourceIds] : [];
|
|
14163
|
+
if (eventSourceIds.length > 0) {
|
|
14164
|
+
const eventSourceComponent = env.getComponent("event-source");
|
|
14165
|
+
if (!eventSourceComponent || typeof eventSourceComponent.startSource !== "function") {
|
|
14166
|
+
output.error("❌ 未找到事件源组件或 startSource 方法");
|
|
14167
|
+
output.info("请确保 EventSourceComponent 已正确注册");
|
|
14168
|
+
process.exit(1);
|
|
14169
|
+
}
|
|
14170
|
+
const summary = await startEventSources(eventSourceIds, eventSourceComponent, output);
|
|
14171
|
+
const preloadedPrompts = collectEventSourceSystemPrompts(eventSourceIds, eventSourceComponent);
|
|
14172
|
+
eventSourceSystemPrompt = preloadedPrompts.length > 0 ? joinResolvedSystemPrompts(preloadedPrompts) : undefined;
|
|
14173
|
+
if (eventSourceIds.length === 1 && summary.failedCount > 0) {
|
|
14174
|
+
process.exit(1);
|
|
14175
|
+
}
|
|
14176
|
+
}
|
|
14177
|
+
const llmComponent = envService.getLLM();
|
|
14178
|
+
queryExecutor.initStreamOutput(llmComponent ?? null);
|
|
14179
|
+
const handleQuery = async (message) => {
|
|
14180
|
+
const mergedContext = {
|
|
14181
|
+
...agentContext,
|
|
14182
|
+
model: options.model,
|
|
14183
|
+
eventSourceSystemPrompt,
|
|
14184
|
+
metadata: {
|
|
14185
|
+
...agentContext?.metadata ?? {},
|
|
14186
|
+
defaultAgentName: options.defaultAgentName
|
|
14187
|
+
}
|
|
14188
|
+
};
|
|
14189
|
+
const result = await queryExecutor.execute(message, sessionId, {
|
|
14190
|
+
showReasoning: options.reasoning ?? true,
|
|
14191
|
+
showToolCalls: options.toolCalls ?? true,
|
|
14192
|
+
showToolResults: options.toolResults ?? false
|
|
14193
|
+
}, undefined, mergedContext);
|
|
14194
|
+
return String(result ?? "");
|
|
14195
|
+
};
|
|
14196
|
+
const taskEventHandler = new TaskEventHandler({
|
|
14197
|
+
env,
|
|
14198
|
+
enabled: true
|
|
14199
|
+
});
|
|
14200
|
+
const eventHandler = new EventHandler({
|
|
14201
|
+
env,
|
|
14202
|
+
eventTypes: ["task.background.*", "event-source.event.*"],
|
|
14203
|
+
formatter: new EventMessageFormatter({ prefix: "[通知]" }),
|
|
14204
|
+
isIdle: hooks?.isIdle ?? (() => true),
|
|
14205
|
+
sessionComponent,
|
|
14206
|
+
onEvent: async (_message) => {},
|
|
14207
|
+
onEventWithEnv: async (formattedEvent) => {
|
|
14208
|
+
if (hooks?.onNotification) {
|
|
14209
|
+
await hooks.onNotification(formattedEvent.message, formattedEvent.envEvent);
|
|
14210
|
+
}
|
|
14211
|
+
}
|
|
14212
|
+
});
|
|
14213
|
+
return {
|
|
14214
|
+
handleQuery,
|
|
14215
|
+
envService,
|
|
14216
|
+
env,
|
|
14217
|
+
sessionId,
|
|
14218
|
+
queryExecutor,
|
|
14219
|
+
agentContext,
|
|
14220
|
+
eventSourceSystemPrompt,
|
|
14221
|
+
sessionComponent,
|
|
14222
|
+
eventHandler,
|
|
14223
|
+
taskEventHandler
|
|
14224
|
+
};
|
|
14225
|
+
}
|
|
14226
|
+
function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
|
|
14227
|
+
return async (message, onChunk) => {
|
|
14228
|
+
queryExecutor.setChunkCallback((event) => {
|
|
14229
|
+
if (event.type === "llm.text") {
|
|
14230
|
+
if (event.delta) {
|
|
14231
|
+
onChunk(event.delta);
|
|
14232
|
+
} else if (event.content) {
|
|
14233
|
+
onChunk(event.content);
|
|
14234
|
+
}
|
|
14235
|
+
}
|
|
14104
14236
|
});
|
|
14237
|
+
const mergedContext = {
|
|
14238
|
+
...agentContext,
|
|
14239
|
+
model: options.model,
|
|
14240
|
+
eventSourceSystemPrompt,
|
|
14241
|
+
metadata: {
|
|
14242
|
+
...agentContext?.metadata ?? {},
|
|
14243
|
+
defaultAgentName: options.defaultAgentName
|
|
14244
|
+
}
|
|
14245
|
+
};
|
|
14246
|
+
const result = await queryExecutor.execute(message, sessionId, {
|
|
14247
|
+
showReasoning: options.reasoning ?? true,
|
|
14248
|
+
showToolCalls: options.toolCalls ?? true,
|
|
14249
|
+
showToolResults: options.toolResults ?? false
|
|
14250
|
+
}, undefined, mergedContext);
|
|
14105
14251
|
return String(result ?? "");
|
|
14106
14252
|
};
|
|
14107
14253
|
}
|
|
@@ -14141,6 +14287,41 @@ function createRunCommand() {
|
|
|
14141
14287
|
}).option("env", {
|
|
14142
14288
|
type: "string",
|
|
14143
14289
|
describe: "Environment name"
|
|
14290
|
+
}).option("session", {
|
|
14291
|
+
type: "string",
|
|
14292
|
+
describe: "指定会话 ID(注: -s 已被 --system-prompt 占用)"
|
|
14293
|
+
}).option("continue", {
|
|
14294
|
+
alias: "c",
|
|
14295
|
+
type: "boolean",
|
|
14296
|
+
describe: "继续上次会话",
|
|
14297
|
+
default: false
|
|
14298
|
+
}).option("reasoning", {
|
|
14299
|
+
alias: "r",
|
|
14300
|
+
type: "boolean",
|
|
14301
|
+
describe: "显示 AI 思考过程",
|
|
14302
|
+
default: true
|
|
14303
|
+
}).option("tool-calls", {
|
|
14304
|
+
type: "boolean",
|
|
14305
|
+
describe: "显示工具调用",
|
|
14306
|
+
default: true
|
|
14307
|
+
}).option("tool-results", {
|
|
14308
|
+
type: "boolean",
|
|
14309
|
+
describe: "显示工具执行结果",
|
|
14310
|
+
default: false
|
|
14311
|
+
}).option("event-source", {
|
|
14312
|
+
alias: "es",
|
|
14313
|
+
type: "array",
|
|
14314
|
+
string: true,
|
|
14315
|
+
describe: "启动时一并启动的事件源 ID(可多次指定,支持 --es a --es b)"
|
|
14316
|
+
}).option("agent", {
|
|
14317
|
+
alias: "a",
|
|
14318
|
+
type: "string",
|
|
14319
|
+
describe: "指定使用的 agent (如 explore, general)"
|
|
14320
|
+
}).option("default-agent-name", {
|
|
14321
|
+
alias: "dan",
|
|
14322
|
+
type: "string",
|
|
14323
|
+
describe: "Default agent 名字(默认 Roy,可被覆盖)",
|
|
14324
|
+
default: "Roy"
|
|
14144
14325
|
}),
|
|
14145
14326
|
handler: async (argv) => {
|
|
14146
14327
|
if (argv.markdown === false) {
|
|
@@ -14149,18 +14330,44 @@ function createRunCommand() {
|
|
|
14149
14330
|
setMarkdownEnabled(true);
|
|
14150
14331
|
}
|
|
14151
14332
|
const maxLines = Number.parseInt(argv.maxLines ?? "500", 10);
|
|
14152
|
-
const
|
|
14333
|
+
const runState = {
|
|
14334
|
+
isIdle: () => true,
|
|
14335
|
+
onNotification: () => {}
|
|
14336
|
+
};
|
|
14337
|
+
const {
|
|
14338
|
+
handleQuery,
|
|
14339
|
+
queryExecutor,
|
|
14340
|
+
sessionId,
|
|
14341
|
+
agentContext,
|
|
14342
|
+
eventSourceSystemPrompt,
|
|
14343
|
+
eventHandler,
|
|
14344
|
+
taskEventHandler
|
|
14345
|
+
} = await loadHandleQuery(argv, {
|
|
14346
|
+
isIdle: () => runState.isIdle(),
|
|
14347
|
+
onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
|
|
14348
|
+
});
|
|
14349
|
+
const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
|
|
14350
|
+
const stopEventHandler = eventHandler.start();
|
|
14351
|
+
const stopTaskEventHandler = taskEventHandler.start();
|
|
14153
14352
|
const { waitUntilExit } = render(React6.createElement(RunTui_default, {
|
|
14154
14353
|
handleQuery,
|
|
14354
|
+
streamHandleQuery,
|
|
14155
14355
|
model: argv.model,
|
|
14156
14356
|
workdir: argv.workdir,
|
|
14157
14357
|
maxLines,
|
|
14158
14358
|
markdownEnabled: argv.markdown !== false,
|
|
14359
|
+
onRegisterHandlers: (handlers) => {
|
|
14360
|
+
runState.onNotification = handlers.onNotification;
|
|
14361
|
+
runState.isIdle = handlers.isIdle;
|
|
14362
|
+
},
|
|
14159
14363
|
onExit: () => {}
|
|
14160
14364
|
}), { exitOnCtrlC: true });
|
|
14161
14365
|
try {
|
|
14162
14366
|
await waitUntilExit();
|
|
14163
14367
|
} finally {
|
|
14368
|
+
stopEventHandler();
|
|
14369
|
+
stopTaskEventHandler();
|
|
14370
|
+
queryExecutor.dispose();
|
|
14164
14371
|
process.exit(0);
|
|
14165
14372
|
}
|
|
14166
14373
|
}
|
|
@@ -14173,7 +14380,7 @@ import React8 from "react";
|
|
|
14173
14380
|
import { render as render2 } from "ink";
|
|
14174
14381
|
|
|
14175
14382
|
// src/ChatTui.tsx
|
|
14176
|
-
import { useEffect as
|
|
14383
|
+
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
14177
14384
|
import { Box as Box7, useApp as useApp3 } from "ink";
|
|
14178
14385
|
init_markdown();
|
|
14179
14386
|
import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
|
|
@@ -14192,7 +14399,7 @@ var ChatScreen = ({
|
|
|
14192
14399
|
const stream = useStreamingAccumulator();
|
|
14193
14400
|
const { exit } = useApp3();
|
|
14194
14401
|
const width = process.stdout.columns ?? 80;
|
|
14195
|
-
|
|
14402
|
+
useEffect6(() => {
|
|
14196
14403
|
let cancelled = false;
|
|
14197
14404
|
stream.start();
|
|
14198
14405
|
(async () => {
|
package/dist/index.js
CHANGED
|
@@ -9460,7 +9460,7 @@ class CliQuietModeService {
|
|
|
9460
9460
|
// package.json
|
|
9461
9461
|
var package_default = {
|
|
9462
9462
|
name: "@ai-setting/roy-agent-cli",
|
|
9463
|
-
version: "1.
|
|
9463
|
+
version: "1.7.0",
|
|
9464
9464
|
type: "module",
|
|
9465
9465
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9466
9466
|
main: "./dist/index.js",
|
|
@@ -9509,7 +9509,8 @@ var package_default = {
|
|
|
9509
9509
|
"vscode-html-languageserver-bin": "^1.4.0",
|
|
9510
9510
|
"vscode-json-languageserver": "^1.3.4",
|
|
9511
9511
|
yargs: "^17.7.2",
|
|
9512
|
-
"zod-to-json-schema": "^3.25.2"
|
|
9512
|
+
"zod-to-json-schema": "^3.25.2",
|
|
9513
|
+
"@earendil-works/pi-ai": "^0.74.2"
|
|
9513
9514
|
},
|
|
9514
9515
|
devDependencies: {
|
|
9515
9516
|
"@types/node": "^20.10.0",
|
|
@@ -13379,6 +13380,9 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
13379
13380
|
showToolCalls: args.toolCalls,
|
|
13380
13381
|
showToolResults: args.toolResults
|
|
13381
13382
|
}, traceId, mergedContext);
|
|
13383
|
+
if (result && !queryExecutor.getFullText()) {
|
|
13384
|
+
console.log(result);
|
|
13385
|
+
}
|
|
13382
13386
|
repl.setLastUsage(queryExecutor.getLastUsage());
|
|
13383
13387
|
return result;
|
|
13384
13388
|
},
|
|
@@ -13517,7 +13521,7 @@ import React6 from "react";
|
|
|
13517
13521
|
import { render } from "ink";
|
|
13518
13522
|
|
|
13519
13523
|
// src/RunTui.tsx
|
|
13520
|
-
import { useState as useState6, useCallback as useCallback4, useRef as useRef2 } from "react";
|
|
13524
|
+
import { useState as useState6, useCallback as useCallback4, useRef as useRef2, useEffect as useEffect5 } from "react";
|
|
13521
13525
|
import { Box as Box6, Text as Text8, useApp as useApp2 } from "ink";
|
|
13522
13526
|
|
|
13523
13527
|
// src/tui/App.tsx
|
|
@@ -13829,7 +13833,7 @@ function useStreamingAccumulator() {
|
|
|
13829
13833
|
let snapshot = "";
|
|
13830
13834
|
setStreamingText((prev) => {
|
|
13831
13835
|
snapshot = prev;
|
|
13832
|
-
return
|
|
13836
|
+
return "";
|
|
13833
13837
|
});
|
|
13834
13838
|
return snapshot;
|
|
13835
13839
|
}, []);
|
|
@@ -13838,7 +13842,7 @@ function useStreamingAccumulator() {
|
|
|
13838
13842
|
let snapshot = "";
|
|
13839
13843
|
setStreamingText((prev) => {
|
|
13840
13844
|
snapshot = prev;
|
|
13841
|
-
return
|
|
13845
|
+
return "";
|
|
13842
13846
|
});
|
|
13843
13847
|
return snapshot;
|
|
13844
13848
|
}, []);
|
|
@@ -13929,7 +13933,8 @@ var RunScreen = ({
|
|
|
13929
13933
|
workdir,
|
|
13930
13934
|
maxLines: initialMaxLines,
|
|
13931
13935
|
markdownEnabled: markdownEnabled2,
|
|
13932
|
-
onExit
|
|
13936
|
+
onExit,
|
|
13937
|
+
onRegisterHandlers
|
|
13933
13938
|
}) => {
|
|
13934
13939
|
const [history, setHistory] = useState6([]);
|
|
13935
13940
|
const [errorMessage, setErrorMessage] = useState6(null);
|
|
@@ -13940,11 +13945,60 @@ var RunScreen = ({
|
|
|
13940
13945
|
const { exit } = useApp2();
|
|
13941
13946
|
const userTurnCountRef = useRef2(0);
|
|
13942
13947
|
const assistantTurnCountRef = useRef2(0);
|
|
13948
|
+
const isProcessingRef = useRef2(0);
|
|
13949
|
+
const executeQueryRef = useRef2(null);
|
|
13943
13950
|
const width = process.stdout.columns ?? 80;
|
|
13944
13951
|
const doExit = useCallback4(() => {
|
|
13945
13952
|
onExit?.();
|
|
13946
13953
|
exit();
|
|
13947
13954
|
}, [onExit, exit]);
|
|
13955
|
+
const executeQuery = useCallback4(async (message) => {
|
|
13956
|
+
isProcessingRef.current += 1;
|
|
13957
|
+
stream.start();
|
|
13958
|
+
try {
|
|
13959
|
+
let responseText;
|
|
13960
|
+
if (streamHandleQuery) {
|
|
13961
|
+
responseText = await streamHandleQuery(message, (chunk) => stream.append(chunk));
|
|
13962
|
+
} else {
|
|
13963
|
+
responseText = await handleQuery(message);
|
|
13964
|
+
}
|
|
13965
|
+
const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
|
|
13966
|
+
const finalText = truncateForTerminal(rendered, maxLines);
|
|
13967
|
+
stream.finish();
|
|
13968
|
+
setHistory((h) => [...h, { role: "assistant", content: finalText }]);
|
|
13969
|
+
assistantTurnCountRef.current += 1;
|
|
13970
|
+
} catch (err) {
|
|
13971
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13972
|
+
stream.fail(err instanceof Error ? err : new Error(msg));
|
|
13973
|
+
setErrorMessage(msg);
|
|
13974
|
+
setHistory((h) => [
|
|
13975
|
+
...h,
|
|
13976
|
+
{ role: "tool", content: `❌ Error: ${msg}` }
|
|
13977
|
+
]);
|
|
13978
|
+
} finally {
|
|
13979
|
+
isProcessingRef.current -= 1;
|
|
13980
|
+
}
|
|
13981
|
+
}, [handleQuery, markdownEnabled2, maxLines, stream, streamHandleQuery, width]);
|
|
13982
|
+
useEffect5(() => {
|
|
13983
|
+
executeQueryRef.current = executeQuery;
|
|
13984
|
+
}, [executeQuery]);
|
|
13985
|
+
useEffect5(() => {
|
|
13986
|
+
if (!onRegisterHandlers)
|
|
13987
|
+
return;
|
|
13988
|
+
const handleNotification = async (message, _envEvent) => {
|
|
13989
|
+
setHistory((h) => [
|
|
13990
|
+
...h,
|
|
13991
|
+
{ role: "tool", content: `\uD83D\uDCE2 ${message}` }
|
|
13992
|
+
]);
|
|
13993
|
+
if (executeQueryRef.current) {
|
|
13994
|
+
await executeQueryRef.current(message);
|
|
13995
|
+
}
|
|
13996
|
+
};
|
|
13997
|
+
const isIdle = () => {
|
|
13998
|
+
return isProcessingRef.current === 0;
|
|
13999
|
+
};
|
|
14000
|
+
onRegisterHandlers({ onNotification: handleNotification, isIdle });
|
|
14001
|
+
}, [onRegisterHandlers]);
|
|
13948
14002
|
const handleSubmit = useCallback4(async (raw) => {
|
|
13949
14003
|
const trimmed = raw.trim();
|
|
13950
14004
|
if (!trimmed)
|
|
@@ -14006,38 +14060,14 @@ var RunScreen = ({
|
|
|
14006
14060
|
setHistory((h) => [...h, { role: "user", content: trimmed }]);
|
|
14007
14061
|
setUserHistory((uh) => [trimmed, ...uh]);
|
|
14008
14062
|
userTurnCountRef.current += 1;
|
|
14009
|
-
|
|
14010
|
-
try {
|
|
14011
|
-
let responseText;
|
|
14012
|
-
if (streamHandleQuery) {
|
|
14013
|
-
responseText = await streamHandleQuery(trimmed, (chunk) => stream.append(chunk));
|
|
14014
|
-
} else {
|
|
14015
|
-
responseText = await handleQuery(trimmed);
|
|
14016
|
-
}
|
|
14017
|
-
const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
|
|
14018
|
-
const finalText = truncateForTerminal(rendered, maxLines);
|
|
14019
|
-
stream.finish();
|
|
14020
|
-
setHistory((h) => [...h, { role: "assistant", content: finalText }]);
|
|
14021
|
-
assistantTurnCountRef.current += 1;
|
|
14022
|
-
} catch (err) {
|
|
14023
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
14024
|
-
stream.fail(err instanceof Error ? err : new Error(msg));
|
|
14025
|
-
setErrorMessage(msg);
|
|
14026
|
-
setHistory((h) => [
|
|
14027
|
-
...h,
|
|
14028
|
-
{ role: "tool", content: `❌ Error: ${msg}` }
|
|
14029
|
-
]);
|
|
14030
|
-
}
|
|
14063
|
+
await executeQuery(trimmed);
|
|
14031
14064
|
}, [
|
|
14032
14065
|
doExit,
|
|
14033
14066
|
exit,
|
|
14034
|
-
|
|
14035
|
-
markdownEnabled2,
|
|
14067
|
+
executeQuery,
|
|
14036
14068
|
maxLines,
|
|
14037
14069
|
model,
|
|
14038
|
-
|
|
14039
|
-
streamHandleQuery,
|
|
14040
|
-
width
|
|
14070
|
+
workdir
|
|
14041
14071
|
]);
|
|
14042
14072
|
const handleCancel = useCallback4(() => {
|
|
14043
14073
|
doExit();
|
|
@@ -14080,7 +14110,7 @@ var RunTui_default = RunScreen;
|
|
|
14080
14110
|
|
|
14081
14111
|
// src/commands/run.ts
|
|
14082
14112
|
init_markdown();
|
|
14083
|
-
async function loadHandleQuery(options) {
|
|
14113
|
+
async function loadHandleQuery(options, hooks) {
|
|
14084
14114
|
const quiet = CliQuietModeService.getInstance().isQuiet();
|
|
14085
14115
|
const output = new OutputService2;
|
|
14086
14116
|
output.configure({ quiet });
|
|
@@ -14097,10 +14127,126 @@ async function loadHandleQuery(options) {
|
|
|
14097
14127
|
if (!env) {
|
|
14098
14128
|
throw new Error("Failed to create environment");
|
|
14099
14129
|
}
|
|
14100
|
-
|
|
14101
|
-
|
|
14102
|
-
|
|
14130
|
+
const sessionComponent = envService.getSession();
|
|
14131
|
+
if (!sessionComponent) {
|
|
14132
|
+
throw new Error("Failed to create session component");
|
|
14133
|
+
}
|
|
14134
|
+
const sessionManager = new SessionManager({
|
|
14135
|
+
sessionComponent,
|
|
14136
|
+
output,
|
|
14137
|
+
quiet: true
|
|
14138
|
+
});
|
|
14139
|
+
const sessionResult = await sessionManager.init(options.session, options.continue);
|
|
14140
|
+
const sessionId = sessionResult.sessionId;
|
|
14141
|
+
const queryExecutor = new QueryExecutor({
|
|
14142
|
+
env,
|
|
14143
|
+
sessionComponent,
|
|
14144
|
+
output,
|
|
14145
|
+
quiet: true,
|
|
14146
|
+
silent: true
|
|
14147
|
+
});
|
|
14148
|
+
let agentContext = undefined;
|
|
14149
|
+
if (options.agent && options.agent !== "default") {
|
|
14150
|
+
const agentComponent = env.getComponent("agent");
|
|
14151
|
+
const validation = validateActAgent(agentComponent, options.agent);
|
|
14152
|
+
if (!validation.valid) {
|
|
14153
|
+
output.error(validation.message);
|
|
14154
|
+
output.info("提示: roy-agent agents list");
|
|
14155
|
+
process.exit(1);
|
|
14156
|
+
}
|
|
14157
|
+
agentContext = buildActAgentContext(options.agent);
|
|
14158
|
+
}
|
|
14159
|
+
let eventSourceSystemPrompt;
|
|
14160
|
+
const rawEventSourceIds = options.eventSource;
|
|
14161
|
+
const eventSourceIds = Array.isArray(rawEventSourceIds) ? rawEventSourceIds.filter((s) => !!s) : rawEventSourceIds ? [rawEventSourceIds] : [];
|
|
14162
|
+
if (eventSourceIds.length > 0) {
|
|
14163
|
+
const eventSourceComponent = env.getComponent("event-source");
|
|
14164
|
+
if (!eventSourceComponent || typeof eventSourceComponent.startSource !== "function") {
|
|
14165
|
+
output.error("❌ 未找到事件源组件或 startSource 方法");
|
|
14166
|
+
output.info("请确保 EventSourceComponent 已正确注册");
|
|
14167
|
+
process.exit(1);
|
|
14168
|
+
}
|
|
14169
|
+
const summary = await startEventSources(eventSourceIds, eventSourceComponent, output);
|
|
14170
|
+
const preloadedPrompts = collectEventSourceSystemPrompts(eventSourceIds, eventSourceComponent);
|
|
14171
|
+
eventSourceSystemPrompt = preloadedPrompts.length > 0 ? joinResolvedSystemPrompts(preloadedPrompts) : undefined;
|
|
14172
|
+
if (eventSourceIds.length === 1 && summary.failedCount > 0) {
|
|
14173
|
+
process.exit(1);
|
|
14174
|
+
}
|
|
14175
|
+
}
|
|
14176
|
+
const llmComponent = envService.getLLM();
|
|
14177
|
+
queryExecutor.initStreamOutput(llmComponent ?? null);
|
|
14178
|
+
const handleQuery = async (message) => {
|
|
14179
|
+
const mergedContext = {
|
|
14180
|
+
...agentContext,
|
|
14181
|
+
model: options.model,
|
|
14182
|
+
eventSourceSystemPrompt,
|
|
14183
|
+
metadata: {
|
|
14184
|
+
...agentContext?.metadata ?? {},
|
|
14185
|
+
defaultAgentName: options.defaultAgentName
|
|
14186
|
+
}
|
|
14187
|
+
};
|
|
14188
|
+
const result = await queryExecutor.execute(message, sessionId, {
|
|
14189
|
+
showReasoning: options.reasoning ?? true,
|
|
14190
|
+
showToolCalls: options.toolCalls ?? true,
|
|
14191
|
+
showToolResults: options.toolResults ?? false
|
|
14192
|
+
}, undefined, mergedContext);
|
|
14193
|
+
return String(result ?? "");
|
|
14194
|
+
};
|
|
14195
|
+
const taskEventHandler = new TaskEventHandler({
|
|
14196
|
+
env,
|
|
14197
|
+
enabled: true
|
|
14198
|
+
});
|
|
14199
|
+
const eventHandler = new EventHandler({
|
|
14200
|
+
env,
|
|
14201
|
+
eventTypes: ["task.background.*", "event-source.event.*"],
|
|
14202
|
+
formatter: new EventMessageFormatter({ prefix: "[通知]" }),
|
|
14203
|
+
isIdle: hooks?.isIdle ?? (() => true),
|
|
14204
|
+
sessionComponent,
|
|
14205
|
+
onEvent: async (_message) => {},
|
|
14206
|
+
onEventWithEnv: async (formattedEvent) => {
|
|
14207
|
+
if (hooks?.onNotification) {
|
|
14208
|
+
await hooks.onNotification(formattedEvent.message, formattedEvent.envEvent);
|
|
14209
|
+
}
|
|
14210
|
+
}
|
|
14211
|
+
});
|
|
14212
|
+
return {
|
|
14213
|
+
handleQuery,
|
|
14214
|
+
envService,
|
|
14215
|
+
env,
|
|
14216
|
+
sessionId,
|
|
14217
|
+
queryExecutor,
|
|
14218
|
+
agentContext,
|
|
14219
|
+
eventSourceSystemPrompt,
|
|
14220
|
+
sessionComponent,
|
|
14221
|
+
eventHandler,
|
|
14222
|
+
taskEventHandler
|
|
14223
|
+
};
|
|
14224
|
+
}
|
|
14225
|
+
function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
|
|
14226
|
+
return async (message, onChunk) => {
|
|
14227
|
+
queryExecutor.setChunkCallback((event) => {
|
|
14228
|
+
if (event.type === "llm.text") {
|
|
14229
|
+
if (event.delta) {
|
|
14230
|
+
onChunk(event.delta);
|
|
14231
|
+
} else if (event.content) {
|
|
14232
|
+
onChunk(event.content);
|
|
14233
|
+
}
|
|
14234
|
+
}
|
|
14103
14235
|
});
|
|
14236
|
+
const mergedContext = {
|
|
14237
|
+
...agentContext,
|
|
14238
|
+
model: options.model,
|
|
14239
|
+
eventSourceSystemPrompt,
|
|
14240
|
+
metadata: {
|
|
14241
|
+
...agentContext?.metadata ?? {},
|
|
14242
|
+
defaultAgentName: options.defaultAgentName
|
|
14243
|
+
}
|
|
14244
|
+
};
|
|
14245
|
+
const result = await queryExecutor.execute(message, sessionId, {
|
|
14246
|
+
showReasoning: options.reasoning ?? true,
|
|
14247
|
+
showToolCalls: options.toolCalls ?? true,
|
|
14248
|
+
showToolResults: options.toolResults ?? false
|
|
14249
|
+
}, undefined, mergedContext);
|
|
14104
14250
|
return String(result ?? "");
|
|
14105
14251
|
};
|
|
14106
14252
|
}
|
|
@@ -14140,6 +14286,41 @@ function createRunCommand() {
|
|
|
14140
14286
|
}).option("env", {
|
|
14141
14287
|
type: "string",
|
|
14142
14288
|
describe: "Environment name"
|
|
14289
|
+
}).option("session", {
|
|
14290
|
+
type: "string",
|
|
14291
|
+
describe: "指定会话 ID(注: -s 已被 --system-prompt 占用)"
|
|
14292
|
+
}).option("continue", {
|
|
14293
|
+
alias: "c",
|
|
14294
|
+
type: "boolean",
|
|
14295
|
+
describe: "继续上次会话",
|
|
14296
|
+
default: false
|
|
14297
|
+
}).option("reasoning", {
|
|
14298
|
+
alias: "r",
|
|
14299
|
+
type: "boolean",
|
|
14300
|
+
describe: "显示 AI 思考过程",
|
|
14301
|
+
default: true
|
|
14302
|
+
}).option("tool-calls", {
|
|
14303
|
+
type: "boolean",
|
|
14304
|
+
describe: "显示工具调用",
|
|
14305
|
+
default: true
|
|
14306
|
+
}).option("tool-results", {
|
|
14307
|
+
type: "boolean",
|
|
14308
|
+
describe: "显示工具执行结果",
|
|
14309
|
+
default: false
|
|
14310
|
+
}).option("event-source", {
|
|
14311
|
+
alias: "es",
|
|
14312
|
+
type: "array",
|
|
14313
|
+
string: true,
|
|
14314
|
+
describe: "启动时一并启动的事件源 ID(可多次指定,支持 --es a --es b)"
|
|
14315
|
+
}).option("agent", {
|
|
14316
|
+
alias: "a",
|
|
14317
|
+
type: "string",
|
|
14318
|
+
describe: "指定使用的 agent (如 explore, general)"
|
|
14319
|
+
}).option("default-agent-name", {
|
|
14320
|
+
alias: "dan",
|
|
14321
|
+
type: "string",
|
|
14322
|
+
describe: "Default agent 名字(默认 Roy,可被覆盖)",
|
|
14323
|
+
default: "Roy"
|
|
14143
14324
|
}),
|
|
14144
14325
|
handler: async (argv) => {
|
|
14145
14326
|
if (argv.markdown === false) {
|
|
@@ -14148,18 +14329,44 @@ function createRunCommand() {
|
|
|
14148
14329
|
setMarkdownEnabled(true);
|
|
14149
14330
|
}
|
|
14150
14331
|
const maxLines = Number.parseInt(argv.maxLines ?? "500", 10);
|
|
14151
|
-
const
|
|
14332
|
+
const runState = {
|
|
14333
|
+
isIdle: () => true,
|
|
14334
|
+
onNotification: () => {}
|
|
14335
|
+
};
|
|
14336
|
+
const {
|
|
14337
|
+
handleQuery,
|
|
14338
|
+
queryExecutor,
|
|
14339
|
+
sessionId,
|
|
14340
|
+
agentContext,
|
|
14341
|
+
eventSourceSystemPrompt,
|
|
14342
|
+
eventHandler,
|
|
14343
|
+
taskEventHandler
|
|
14344
|
+
} = await loadHandleQuery(argv, {
|
|
14345
|
+
isIdle: () => runState.isIdle(),
|
|
14346
|
+
onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
|
|
14347
|
+
});
|
|
14348
|
+
const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
|
|
14349
|
+
const stopEventHandler = eventHandler.start();
|
|
14350
|
+
const stopTaskEventHandler = taskEventHandler.start();
|
|
14152
14351
|
const { waitUntilExit } = render(React6.createElement(RunTui_default, {
|
|
14153
14352
|
handleQuery,
|
|
14353
|
+
streamHandleQuery,
|
|
14154
14354
|
model: argv.model,
|
|
14155
14355
|
workdir: argv.workdir,
|
|
14156
14356
|
maxLines,
|
|
14157
14357
|
markdownEnabled: argv.markdown !== false,
|
|
14358
|
+
onRegisterHandlers: (handlers) => {
|
|
14359
|
+
runState.onNotification = handlers.onNotification;
|
|
14360
|
+
runState.isIdle = handlers.isIdle;
|
|
14361
|
+
},
|
|
14158
14362
|
onExit: () => {}
|
|
14159
14363
|
}), { exitOnCtrlC: true });
|
|
14160
14364
|
try {
|
|
14161
14365
|
await waitUntilExit();
|
|
14162
14366
|
} finally {
|
|
14367
|
+
stopEventHandler();
|
|
14368
|
+
stopTaskEventHandler();
|
|
14369
|
+
queryExecutor.dispose();
|
|
14163
14370
|
process.exit(0);
|
|
14164
14371
|
}
|
|
14165
14372
|
}
|
|
@@ -14172,7 +14379,7 @@ import React8 from "react";
|
|
|
14172
14379
|
import { render as render2 } from "ink";
|
|
14173
14380
|
|
|
14174
14381
|
// src/ChatTui.tsx
|
|
14175
|
-
import { useEffect as
|
|
14382
|
+
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
14176
14383
|
import { Box as Box7, useApp as useApp3 } from "ink";
|
|
14177
14384
|
init_markdown();
|
|
14178
14385
|
import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
|
|
@@ -14191,7 +14398,7 @@ var ChatScreen = ({
|
|
|
14191
14398
|
const stream = useStreamingAccumulator();
|
|
14192
14399
|
const { exit } = useApp3();
|
|
14193
14400
|
const width = process.stdout.columns ?? 80;
|
|
14194
|
-
|
|
14401
|
+
useEffect6(() => {
|
|
14195
14402
|
let cancelled = false;
|
|
14196
14403
|
stream.start();
|
|
14197
14404
|
(async () => {
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-setting/roy-agent-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for roy-agent - Non-interactive command execution",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"vscode-html-languageserver-bin": "^1.4.0",
|
|
50
50
|
"vscode-json-languageserver": "^1.3.4",
|
|
51
51
|
"yargs": "^17.7.2",
|
|
52
|
-
"zod-to-json-schema": "^3.25.2"
|
|
52
|
+
"zod-to-json-schema": "^3.25.2",
|
|
53
|
+
"@earendil-works/pi-ai": "^0.74.2"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@types/node": "^20.10.0",
|