@ai-setting/roy-agent-cli 1.7.17 → 1.7.19

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.
@@ -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.7.17",
9464
+ version: "1.7.19",
9465
9465
  type: "module",
9466
9466
  description: "CLI for roy-agent - Non-interactive command execution",
9467
9467
  main: "./dist/index.js",
@@ -11294,7 +11294,8 @@ var TaskEventTypes = {
11294
11294
  TASK_COMPLETED: "task.background.completed",
11295
11295
  TASK_FAILED: "task.background.failed",
11296
11296
  TASK_TIMEOUT: "task.background.timeout",
11297
- TASK_STOPPED: "task.background.stopped"
11297
+ TASK_STOPPED: "task.background.stopped",
11298
+ TASK_ASK_USER: "task.background.ask_user"
11298
11299
  };
11299
11300
  var EventSourceEventTypes = {
11300
11301
  EVENT_SOURCE_STARTED: "event-source.started",
@@ -11324,6 +11325,8 @@ class EventMessageFormatter {
11324
11325
  return this.formatTaskTimeout(payload);
11325
11326
  case TaskEventTypes.TASK_STOPPED:
11326
11327
  return this.formatTaskStopped(payload);
11328
+ case TaskEventTypes.TASK_ASK_USER:
11329
+ return this.formatTaskAskUser(payload);
11327
11330
  case EventSourceEventTypes.EVENT_SOURCE_STARTED:
11328
11331
  return this.formatEventSourceStarted(payload);
11329
11332
  case EventSourceEventTypes.EVENT_SOURCE_STOPPED:
@@ -11431,6 +11434,34 @@ Task #${taskId}`;
11431
11434
  message += this.appendSubSessionId(payload);
11432
11435
  return message;
11433
11436
  }
11437
+ formatTaskAskUser(payload) {
11438
+ const desc = payload.description || "未命名任务";
11439
+ const bgProcessId = payload.backgroundTaskId || "";
11440
+ const subSessionId = payload.subSessionId || "";
11441
+ const query = payload.query || "(空问题)";
11442
+ let message = `${this.prefix} ❓ 后台子代理「${desc}」请求用户输入`;
11443
+ if (bgProcessId) {
11444
+ message += `
11445
+ bgprocess: ${bgProcessId}`;
11446
+ }
11447
+ if (subSessionId) {
11448
+ message += `
11449
+ subSessionId: ${subSessionId}`;
11450
+ }
11451
+ message += `
11452
+
11453
+ ${query}`;
11454
+ if (payload.hints && payload.hints.length > 0) {
11455
+ message += `
11456
+
11457
+ [\uD83E\uDD16 Agent 操作提示]`;
11458
+ for (const hint of payload.hints) {
11459
+ message += `
11460
+ ${hint}`;
11461
+ }
11462
+ }
11463
+ return message;
11464
+ }
11434
11465
  appendSubSessionId(payload) {
11435
11466
  if (payload.subSessionId) {
11436
11467
  return `
@@ -12108,6 +12139,52 @@ async function shutdownInteractiveSteps(deps) {
12108
12139
  }
12109
12140
  }
12110
12141
 
12142
+ // src/commands/lifecycle.ts
12143
+ var DEFAULT_LOGGER = {
12144
+ warn: (msg) => console.error(`[shutdown] ${msg}`)
12145
+ };
12146
+ function registerShutdownHandlers(cleanup, options = {}) {
12147
+ const logger5 = options.logger ?? DEFAULT_LOGGER;
12148
+ const exitCode = options.exitCode ?? 0;
12149
+ const shutdownMessage = options.shutdownMessage ?? "Cleaning up...";
12150
+ let shuttingDown = false;
12151
+ const shutdown = async (signal) => {
12152
+ if (shuttingDown) {
12153
+ return;
12154
+ }
12155
+ shuttingDown = true;
12156
+ try {
12157
+ logger5.debug?.(`[${signal}] ${shutdownMessage}`);
12158
+ } catch {}
12159
+ if (cleanup) {
12160
+ try {
12161
+ await cleanup();
12162
+ } catch (err) {
12163
+ const message = err instanceof Error ? err.message : String(err);
12164
+ try {
12165
+ logger5.warn(`Cleanup failed: ${message}`);
12166
+ } catch {
12167
+ try {
12168
+ process.stderr.write(`[shutdown] Cleanup failed: ${message}
12169
+ `);
12170
+ } catch {}
12171
+ }
12172
+ }
12173
+ }
12174
+ try {
12175
+ process.exit(exitCode);
12176
+ } catch {}
12177
+ };
12178
+ process.on("SIGINT", shutdown);
12179
+ process.on("SIGTERM", shutdown);
12180
+ return {
12181
+ dispose: () => {
12182
+ process.removeListener("SIGINT", shutdown);
12183
+ process.removeListener("SIGTERM", shutdown);
12184
+ }
12185
+ };
12186
+ }
12187
+
12111
12188
  // src/commands/interactive.ts
12112
12189
  import { AskUserError, cascadeStopSessionTree } from "@ai-setting/roy-agent-core";
12113
12190
 
@@ -12793,10 +12870,23 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
12793
12870
  }
12794
12871
  }
12795
12872
  const agentFromEvent = envEvent.metadata?.agent;
12873
+ let envEventForAgent = envEvent;
12874
+ const eventReplyChannel = envEvent?.metadata?.replyChannel;
12875
+ if (eventReplyChannel) {
12876
+ this.lastReplyChannel = eventReplyChannel;
12877
+ } else if (this.lastReplyChannel) {
12878
+ envEventForAgent = {
12879
+ ...envEvent,
12880
+ metadata: {
12881
+ ...envEvent?.metadata ?? {},
12882
+ replyChannel: this.lastReplyChannel
12883
+ }
12884
+ };
12885
+ }
12796
12886
  this.currentAgentContext = {
12797
12887
  agentType: agentFromEvent,
12798
12888
  pluginEnabled,
12799
- envEvent,
12889
+ envEvent: envEventForAgent,
12800
12890
  plugins,
12801
12891
  eventSessionId,
12802
12892
  systemPrompt: envEvent.metadata?.systemPrompt
@@ -12804,6 +12894,7 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
12804
12894
  await this.executeInternal(message, { showPrompt: true });
12805
12895
  }
12806
12896
  currentAgentContext = undefined;
12897
+ lastReplyChannel = undefined;
12807
12898
  isIdle() {
12808
12899
  return !this.isExecuting;
12809
12900
  }
@@ -13453,6 +13544,22 @@ function createInteractiveCommand(externalEnvService) {
13453
13544
  });
13454
13545
  }
13455
13546
  }, env);
13547
+ registerShutdownHandlers(async () => {
13548
+ await shutdownInteractiveSteps({
13549
+ memoryPluginDispose,
13550
+ memoryPluginInstance,
13551
+ lspManagerDispose,
13552
+ pluginAdapterDispose,
13553
+ eventHandler,
13554
+ queryExecutor,
13555
+ env,
13556
+ envService,
13557
+ shouldDisposeEnvService,
13558
+ output,
13559
+ logger: logger5,
13560
+ console
13561
+ });
13562
+ });
13456
13563
  const eventHandler = new EventHandler({
13457
13564
  env,
13458
13565
  eventTypes: ["task.background.*", "event-source.event.*"],
@@ -13682,9 +13789,9 @@ var App = ({
13682
13789
  }, undefined, true, undefined, this),
13683
13790
  /* @__PURE__ */ jsxDEV6(Static2, {
13684
13791
  items: history,
13685
- children: (item) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13792
+ children: (item, index) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13686
13793
  item
13687
- }, `${item.role}:${item.content}`, false, undefined, this)
13794
+ }, item.id ?? `${item.role}:${index}`, false, undefined, this)
13688
13795
  }, undefined, false, undefined, this),
13689
13796
  status === "thinking" ? /* @__PURE__ */ jsxDEV6(Box4, {
13690
13797
  marginBottom: 1,
@@ -13965,6 +14072,7 @@ var RunScreen = ({
13965
14072
  const assistantTurnCountRef = useRef2(0);
13966
14073
  const isProcessingRef = useRef2(0);
13967
14074
  const executeQueryRef = useRef2(null);
14075
+ const historyIdRef = useRef2(0);
13968
14076
  const width = process.stdout.columns ?? 80;
13969
14077
  const doExit = useCallback4(() => {
13970
14078
  onExit?.();
@@ -13983,15 +14091,20 @@ var RunScreen = ({
13983
14091
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
13984
14092
  const finalText = truncateForTerminal(rendered, maxLines);
13985
14093
  stream.finish();
13986
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14094
+ const assistantId = `h${++historyIdRef.current}`;
14095
+ setHistory((h) => [
14096
+ ...h,
14097
+ { role: "assistant", content: finalText, id: assistantId }
14098
+ ]);
13987
14099
  assistantTurnCountRef.current += 1;
13988
14100
  } catch (err) {
13989
14101
  const msg = err instanceof Error ? err.message : String(err);
13990
14102
  stream.fail(err instanceof Error ? err : new Error(msg));
13991
14103
  setErrorMessage(msg);
14104
+ const errorId = `h${++historyIdRef.current}`;
13992
14105
  setHistory((h) => [
13993
14106
  ...h,
13994
- { role: "tool", content: `❌ Error: ${msg}` }
14107
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
13995
14108
  ]);
13996
14109
  } finally {
13997
14110
  isProcessingRef.current -= 1;
@@ -14004,9 +14117,10 @@ var RunScreen = ({
14004
14117
  if (!onRegisterHandlers)
14005
14118
  return;
14006
14119
  const handleNotification = async (message, _envEvent) => {
14120
+ const notifId = `h${++historyIdRef.current}`;
14007
14121
  setHistory((h) => [
14008
14122
  ...h,
14009
- { role: "tool", content: `\uD83D\uDCE2 ${message}` }
14123
+ { role: "tool", content: `\uD83D\uDCE2 ${message}`, id: notifId }
14010
14124
  ]);
14011
14125
  if (executeQueryRef.current) {
14012
14126
  await executeQueryRef.current(message);
@@ -14030,10 +14144,12 @@ var RunScreen = ({
14030
14144
  };
14031
14145
  const result = dispatchSlashCommand(trimmed, slashCtx);
14032
14146
  if (result.kind === "exit") {
14147
+ const userId2 = `h${++historyIdRef.current}`;
14148
+ const toolId = `h${++historyIdRef.current}`;
14033
14149
  setHistory((h) => [
14034
14150
  ...h,
14035
- { role: "user", content: trimmed },
14036
- { role: "tool", content: "\uD83D\uDC4B Goodbye!" }
14151
+ { role: "user", content: trimmed, id: userId2 },
14152
+ { role: "tool", content: "\uD83D\uDC4B Goodbye!", id: toolId }
14037
14153
  ]);
14038
14154
  doExit();
14039
14155
  return;
@@ -14047,35 +14163,46 @@ var RunScreen = ({
14047
14163
  setErrorMessage(null);
14048
14164
  return;
14049
14165
  }
14166
+ const userId2 = `h${++historyIdRef.current}`;
14167
+ const toolId = `h${++historyIdRef.current}`;
14050
14168
  setHistory((h) => [
14051
14169
  ...h,
14052
- { role: "user", content: trimmed },
14053
- { role: "tool", content: result.message }
14170
+ { role: "user", content: trimmed, id: userId2 },
14171
+ { role: "tool", content: result.message, id: toolId }
14054
14172
  ]);
14055
14173
  return;
14056
14174
  }
14057
14175
  if (result.kind === "unknown") {
14176
+ const userId2 = `h${++historyIdRef.current}`;
14177
+ const toolId = `h${++historyIdRef.current}`;
14058
14178
  setHistory((h) => [
14059
14179
  ...h,
14060
- { role: "user", content: trimmed },
14061
- { role: "tool", content: `⚠ ${result.message}` }
14180
+ { role: "user", content: trimmed, id: userId2 },
14181
+ { role: "tool", content: `⚠ ${result.message}`, id: toolId }
14062
14182
  ]);
14063
14183
  return;
14064
14184
  }
14065
14185
  if (result.kind === "set-max-lines") {
14066
14186
  setMaxLines(result.value);
14187
+ const userId2 = `h${++historyIdRef.current}`;
14188
+ const toolId = `h${++historyIdRef.current}`;
14067
14189
  setHistory((h) => [
14068
14190
  ...h,
14069
- { role: "user", content: trimmed },
14191
+ { role: "user", content: trimmed, id: userId2 },
14070
14192
  {
14071
14193
  role: "tool",
14072
- content: `max-lines set to ${result.value}`
14194
+ content: `max-lines set to ${result.value}`,
14195
+ id: toolId
14073
14196
  }
14074
14197
  ]);
14075
14198
  return;
14076
14199
  }
14077
14200
  setErrorMessage(null);
14078
- setHistory((h) => [...h, { role: "user", content: trimmed }]);
14201
+ const userId = `h${++historyIdRef.current}`;
14202
+ setHistory((h) => [
14203
+ ...h,
14204
+ { role: "user", content: trimmed, id: userId }
14205
+ ]);
14079
14206
  setUserHistory((uh) => [trimmed, ...uh]);
14080
14207
  userTurnCountRef.current += 1;
14081
14208
  await executeQuery(trimmed);
@@ -14128,6 +14255,89 @@ var RunTui_default = RunScreen;
14128
14255
 
14129
14256
  // src/commands/run.ts
14130
14257
  init_markdown();
14258
+
14259
+ // src/commands/run-shutdown.ts
14260
+ import { TempFileManager as TempFileManager3 } from "@ai-setting/roy-agent-core/utils/temp-file-manager";
14261
+ async function shutdownRunSteps(deps) {
14262
+ const {
14263
+ lspManagerDispose,
14264
+ pluginAdapterDispose,
14265
+ stopEventHandler,
14266
+ stopTaskEventHandler,
14267
+ queryExecutor,
14268
+ env,
14269
+ envService,
14270
+ shouldDisposeEnvService,
14271
+ output,
14272
+ logger: logger6
14273
+ } = deps;
14274
+ try {
14275
+ stopEventHandler();
14276
+ } catch (err) {
14277
+ logger6?.warn?.(`[Shutdown] EventHandler.stop failed: ${err}`);
14278
+ }
14279
+ try {
14280
+ stopTaskEventHandler();
14281
+ } catch (err) {
14282
+ logger6?.warn?.(`[Shutdown] TaskEventHandler.stop failed: ${err}`);
14283
+ }
14284
+ if (lspManagerDispose) {
14285
+ try {
14286
+ await lspManagerDispose();
14287
+ } catch (err) {
14288
+ logger6?.warn?.(`[Shutdown] LSP Manager dispose failed: ${err}`);
14289
+ }
14290
+ }
14291
+ if (pluginAdapterDispose) {
14292
+ try {
14293
+ await pluginAdapterDispose();
14294
+ } catch (err) {
14295
+ logger6?.warn?.(`[Shutdown] PluginAdapter dispose failed: ${err}`);
14296
+ }
14297
+ }
14298
+ try {
14299
+ queryExecutor.dispose();
14300
+ } catch (err) {
14301
+ logger6?.warn?.(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
14302
+ }
14303
+ try {
14304
+ const eventSourceComponent = env.getComponent("event-source");
14305
+ if (eventSourceComponent) {
14306
+ const sources = eventSourceComponent.list?.();
14307
+ if (sources && sources.length > 0) {
14308
+ for (const source of sources) {
14309
+ const status = eventSourceComponent.getStatus?.(source.id);
14310
+ logger6?.debug?.(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
14311
+ if (status !== "stopped" && status !== "created") {
14312
+ try {
14313
+ await eventSourceComponent.stopSource(source.id);
14314
+ output?.info?.(`✓ 已停止事件源: ${source.name} (${status})`);
14315
+ } catch (error) {
14316
+ output?.warn?.(`⚠️ 停止事件源 ${source.name} 失败: ${error}`);
14317
+ }
14318
+ }
14319
+ }
14320
+ }
14321
+ }
14322
+ } catch (err) {
14323
+ logger6?.warn?.(`[Shutdown] EventSource cleanup failed: ${err}`);
14324
+ }
14325
+ if (shouldDisposeEnvService) {
14326
+ try {
14327
+ await envService.dispose();
14328
+ } catch (err) {
14329
+ logger6?.warn?.(`[Shutdown] envService.dispose failed: ${err}`);
14330
+ }
14331
+ }
14332
+ try {
14333
+ await TempFileManager3.getInstance().cleanupAll();
14334
+ logger6?.debug?.("[Shutdown] TempFileManager cleaned up");
14335
+ } catch (err) {
14336
+ logger6?.warn?.(`[Shutdown] TempFileManager cleanup failed: ${err}`);
14337
+ }
14338
+ }
14339
+
14340
+ // src/commands/run.ts
14131
14341
  async function loadHandleQuery(options, hooks) {
14132
14342
  const quiet = CliQuietModeService.getInstance().isQuiet();
14133
14343
  const output = new OutputService2;
@@ -14237,7 +14447,8 @@ async function loadHandleQuery(options, hooks) {
14237
14447
  eventSourceSystemPrompt,
14238
14448
  sessionComponent,
14239
14449
  eventHandler,
14240
- taskEventHandler
14450
+ taskEventHandler,
14451
+ sessionManager
14241
14452
  };
14242
14453
  }
14243
14454
  function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
@@ -14353,16 +14564,33 @@ function createRunCommand() {
14353
14564
  };
14354
14565
  const {
14355
14566
  handleQuery,
14567
+ envService,
14568
+ env,
14356
14569
  queryExecutor,
14357
14570
  sessionId,
14358
14571
  agentContext,
14359
14572
  eventSourceSystemPrompt,
14360
14573
  eventHandler,
14361
- taskEventHandler
14574
+ taskEventHandler,
14575
+ sessionManager: _sessionManager
14362
14576
  } = await loadHandleQuery(argv, {
14363
14577
  isIdle: () => runState.isIdle(),
14364
14578
  onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
14365
14579
  });
14580
+ const shutdownHandle = registerShutdownHandlers(async () => {
14581
+ await shutdownRunSteps({
14582
+ lspManagerDispose: null,
14583
+ pluginAdapterDispose: null,
14584
+ stopEventHandler,
14585
+ stopTaskEventHandler,
14586
+ queryExecutor,
14587
+ env,
14588
+ envService,
14589
+ shouldDisposeEnvService: true,
14590
+ output: undefined,
14591
+ logger: undefined
14592
+ });
14593
+ });
14366
14594
  const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
14367
14595
  const stopEventHandler = eventHandler.start();
14368
14596
  const stopTaskEventHandler = taskEventHandler.start();
@@ -14382,9 +14610,19 @@ function createRunCommand() {
14382
14610
  try {
14383
14611
  await waitUntilExit();
14384
14612
  } finally {
14385
- stopEventHandler();
14386
- stopTaskEventHandler();
14387
- queryExecutor.dispose();
14613
+ shutdownHandle.dispose();
14614
+ await shutdownRunSteps({
14615
+ lspManagerDispose: null,
14616
+ pluginAdapterDispose: null,
14617
+ stopEventHandler,
14618
+ stopTaskEventHandler,
14619
+ queryExecutor,
14620
+ env,
14621
+ envService,
14622
+ shouldDisposeEnvService: true,
14623
+ output: undefined,
14624
+ logger: undefined
14625
+ });
14388
14626
  process.exit(0);
14389
14627
  }
14390
14628
  }
@@ -14397,7 +14635,7 @@ import React8 from "react";
14397
14635
  import { render as render2 } from "ink";
14398
14636
 
14399
14637
  // src/ChatTui.tsx
14400
- import { useEffect as useEffect6, useState as useState7 } from "react";
14638
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
14401
14639
  import { Box as Box7, useApp as useApp3 } from "ink";
14402
14640
  init_markdown();
14403
14641
  import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
@@ -14411,8 +14649,9 @@ var ChatScreen = ({
14411
14649
  markdownEnabled: markdownEnabled2
14412
14650
  }) => {
14413
14651
  const [history, setHistory] = useState7([
14414
- { role: "user", content: message }
14652
+ { role: "user", content: message, id: "h-init-1" }
14415
14653
  ]);
14654
+ const historyIdRef = useRef3(1);
14416
14655
  const stream = useStreamingAccumulator();
14417
14656
  const { exit } = useApp3();
14418
14657
  const width = process.stdout.columns ?? 80;
@@ -14432,16 +14671,21 @@ var ChatScreen = ({
14432
14671
  stream.finish();
14433
14672
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
14434
14673
  const finalText = truncateForTerminal(rendered, maxLines);
14435
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14674
+ const assistantId = `h${++historyIdRef.current}`;
14675
+ setHistory((h) => [
14676
+ ...h,
14677
+ { role: "assistant", content: finalText, id: assistantId }
14678
+ ]);
14436
14679
  setTimeout(() => exit(), 100);
14437
14680
  } catch (err) {
14438
14681
  if (cancelled)
14439
14682
  return;
14440
14683
  const msg = err instanceof Error ? err.message : String(err);
14441
14684
  stream.fail(err instanceof Error ? err : new Error(msg));
14685
+ const errorId = `h${++historyIdRef.current}`;
14442
14686
  setHistory((h) => [
14443
14687
  ...h,
14444
- { role: "tool", content: `❌ Error: ${msg}` }
14688
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
14445
14689
  ]);
14446
14690
  setTimeout(() => exit(), 100);
14447
14691
  }
@@ -23495,7 +23739,7 @@ var WorkflowAddCommand = {
23495
23739
  process.exit(1);
23496
23740
  }
23497
23741
  if (!agentComponent.getAgent("workflow-extract")) {
23498
- output.error("Built-in 'workflow-extract' agent is not available. This usually means the environment was not fully initialized.");
23742
+ output.error("Built-in 'workflow-extract' agent is not available. ");
23499
23743
  process.exit(1);
23500
23744
  }
23501
23745
  const prompt = `## 任务
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.7.17",
9463
+ version: "1.7.19",
9464
9464
  type: "module",
9465
9465
  description: "CLI for roy-agent - Non-interactive command execution",
9466
9466
  main: "./dist/index.js",
@@ -11293,7 +11293,8 @@ var TaskEventTypes = {
11293
11293
  TASK_COMPLETED: "task.background.completed",
11294
11294
  TASK_FAILED: "task.background.failed",
11295
11295
  TASK_TIMEOUT: "task.background.timeout",
11296
- TASK_STOPPED: "task.background.stopped"
11296
+ TASK_STOPPED: "task.background.stopped",
11297
+ TASK_ASK_USER: "task.background.ask_user"
11297
11298
  };
11298
11299
  var EventSourceEventTypes = {
11299
11300
  EVENT_SOURCE_STARTED: "event-source.started",
@@ -11323,6 +11324,8 @@ class EventMessageFormatter {
11323
11324
  return this.formatTaskTimeout(payload);
11324
11325
  case TaskEventTypes.TASK_STOPPED:
11325
11326
  return this.formatTaskStopped(payload);
11327
+ case TaskEventTypes.TASK_ASK_USER:
11328
+ return this.formatTaskAskUser(payload);
11326
11329
  case EventSourceEventTypes.EVENT_SOURCE_STARTED:
11327
11330
  return this.formatEventSourceStarted(payload);
11328
11331
  case EventSourceEventTypes.EVENT_SOURCE_STOPPED:
@@ -11430,6 +11433,34 @@ Task #${taskId}`;
11430
11433
  message += this.appendSubSessionId(payload);
11431
11434
  return message;
11432
11435
  }
11436
+ formatTaskAskUser(payload) {
11437
+ const desc = payload.description || "未命名任务";
11438
+ const bgProcessId = payload.backgroundTaskId || "";
11439
+ const subSessionId = payload.subSessionId || "";
11440
+ const query = payload.query || "(空问题)";
11441
+ let message = `${this.prefix} ❓ 后台子代理「${desc}」请求用户输入`;
11442
+ if (bgProcessId) {
11443
+ message += `
11444
+ bgprocess: ${bgProcessId}`;
11445
+ }
11446
+ if (subSessionId) {
11447
+ message += `
11448
+ subSessionId: ${subSessionId}`;
11449
+ }
11450
+ message += `
11451
+
11452
+ ${query}`;
11453
+ if (payload.hints && payload.hints.length > 0) {
11454
+ message += `
11455
+
11456
+ [\uD83E\uDD16 Agent 操作提示]`;
11457
+ for (const hint of payload.hints) {
11458
+ message += `
11459
+ ${hint}`;
11460
+ }
11461
+ }
11462
+ return message;
11463
+ }
11433
11464
  appendSubSessionId(payload) {
11434
11465
  if (payload.subSessionId) {
11435
11466
  return `
@@ -12107,6 +12138,52 @@ async function shutdownInteractiveSteps(deps) {
12107
12138
  }
12108
12139
  }
12109
12140
 
12141
+ // src/commands/lifecycle.ts
12142
+ var DEFAULT_LOGGER = {
12143
+ warn: (msg) => console.error(`[shutdown] ${msg}`)
12144
+ };
12145
+ function registerShutdownHandlers(cleanup, options = {}) {
12146
+ const logger5 = options.logger ?? DEFAULT_LOGGER;
12147
+ const exitCode = options.exitCode ?? 0;
12148
+ const shutdownMessage = options.shutdownMessage ?? "Cleaning up...";
12149
+ let shuttingDown = false;
12150
+ const shutdown = async (signal) => {
12151
+ if (shuttingDown) {
12152
+ return;
12153
+ }
12154
+ shuttingDown = true;
12155
+ try {
12156
+ logger5.debug?.(`[${signal}] ${shutdownMessage}`);
12157
+ } catch {}
12158
+ if (cleanup) {
12159
+ try {
12160
+ await cleanup();
12161
+ } catch (err) {
12162
+ const message = err instanceof Error ? err.message : String(err);
12163
+ try {
12164
+ logger5.warn(`Cleanup failed: ${message}`);
12165
+ } catch {
12166
+ try {
12167
+ process.stderr.write(`[shutdown] Cleanup failed: ${message}
12168
+ `);
12169
+ } catch {}
12170
+ }
12171
+ }
12172
+ }
12173
+ try {
12174
+ process.exit(exitCode);
12175
+ } catch {}
12176
+ };
12177
+ process.on("SIGINT", shutdown);
12178
+ process.on("SIGTERM", shutdown);
12179
+ return {
12180
+ dispose: () => {
12181
+ process.removeListener("SIGINT", shutdown);
12182
+ process.removeListener("SIGTERM", shutdown);
12183
+ }
12184
+ };
12185
+ }
12186
+
12110
12187
  // src/commands/interactive.ts
12111
12188
  import { AskUserError, cascadeStopSessionTree } from "@ai-setting/roy-agent-core";
12112
12189
 
@@ -12792,10 +12869,23 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
12792
12869
  }
12793
12870
  }
12794
12871
  const agentFromEvent = envEvent.metadata?.agent;
12872
+ let envEventForAgent = envEvent;
12873
+ const eventReplyChannel = envEvent?.metadata?.replyChannel;
12874
+ if (eventReplyChannel) {
12875
+ this.lastReplyChannel = eventReplyChannel;
12876
+ } else if (this.lastReplyChannel) {
12877
+ envEventForAgent = {
12878
+ ...envEvent,
12879
+ metadata: {
12880
+ ...envEvent?.metadata ?? {},
12881
+ replyChannel: this.lastReplyChannel
12882
+ }
12883
+ };
12884
+ }
12795
12885
  this.currentAgentContext = {
12796
12886
  agentType: agentFromEvent,
12797
12887
  pluginEnabled,
12798
- envEvent,
12888
+ envEvent: envEventForAgent,
12799
12889
  plugins,
12800
12890
  eventSessionId,
12801
12891
  systemPrompt: envEvent.metadata?.systemPrompt
@@ -12803,6 +12893,7 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
12803
12893
  await this.executeInternal(message, { showPrompt: true });
12804
12894
  }
12805
12895
  currentAgentContext = undefined;
12896
+ lastReplyChannel = undefined;
12806
12897
  isIdle() {
12807
12898
  return !this.isExecuting;
12808
12899
  }
@@ -13452,6 +13543,22 @@ function createInteractiveCommand(externalEnvService) {
13452
13543
  });
13453
13544
  }
13454
13545
  }, env);
13546
+ registerShutdownHandlers(async () => {
13547
+ await shutdownInteractiveSteps({
13548
+ memoryPluginDispose,
13549
+ memoryPluginInstance,
13550
+ lspManagerDispose,
13551
+ pluginAdapterDispose,
13552
+ eventHandler,
13553
+ queryExecutor,
13554
+ env,
13555
+ envService,
13556
+ shouldDisposeEnvService,
13557
+ output,
13558
+ logger: logger5,
13559
+ console
13560
+ });
13561
+ });
13455
13562
  const eventHandler = new EventHandler({
13456
13563
  env,
13457
13564
  eventTypes: ["task.background.*", "event-source.event.*"],
@@ -13681,9 +13788,9 @@ var App = ({
13681
13788
  }, undefined, true, undefined, this),
13682
13789
  /* @__PURE__ */ jsxDEV6(Static2, {
13683
13790
  items: history,
13684
- children: (item) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13791
+ children: (item, index) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13685
13792
  item
13686
- }, `${item.role}:${item.content}`, false, undefined, this)
13793
+ }, item.id ?? `${item.role}:${index}`, false, undefined, this)
13687
13794
  }, undefined, false, undefined, this),
13688
13795
  status === "thinking" ? /* @__PURE__ */ jsxDEV6(Box4, {
13689
13796
  marginBottom: 1,
@@ -13964,6 +14071,7 @@ var RunScreen = ({
13964
14071
  const assistantTurnCountRef = useRef2(0);
13965
14072
  const isProcessingRef = useRef2(0);
13966
14073
  const executeQueryRef = useRef2(null);
14074
+ const historyIdRef = useRef2(0);
13967
14075
  const width = process.stdout.columns ?? 80;
13968
14076
  const doExit = useCallback4(() => {
13969
14077
  onExit?.();
@@ -13982,15 +14090,20 @@ var RunScreen = ({
13982
14090
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
13983
14091
  const finalText = truncateForTerminal(rendered, maxLines);
13984
14092
  stream.finish();
13985
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14093
+ const assistantId = `h${++historyIdRef.current}`;
14094
+ setHistory((h) => [
14095
+ ...h,
14096
+ { role: "assistant", content: finalText, id: assistantId }
14097
+ ]);
13986
14098
  assistantTurnCountRef.current += 1;
13987
14099
  } catch (err) {
13988
14100
  const msg = err instanceof Error ? err.message : String(err);
13989
14101
  stream.fail(err instanceof Error ? err : new Error(msg));
13990
14102
  setErrorMessage(msg);
14103
+ const errorId = `h${++historyIdRef.current}`;
13991
14104
  setHistory((h) => [
13992
14105
  ...h,
13993
- { role: "tool", content: `❌ Error: ${msg}` }
14106
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
13994
14107
  ]);
13995
14108
  } finally {
13996
14109
  isProcessingRef.current -= 1;
@@ -14003,9 +14116,10 @@ var RunScreen = ({
14003
14116
  if (!onRegisterHandlers)
14004
14117
  return;
14005
14118
  const handleNotification = async (message, _envEvent) => {
14119
+ const notifId = `h${++historyIdRef.current}`;
14006
14120
  setHistory((h) => [
14007
14121
  ...h,
14008
- { role: "tool", content: `\uD83D\uDCE2 ${message}` }
14122
+ { role: "tool", content: `\uD83D\uDCE2 ${message}`, id: notifId }
14009
14123
  ]);
14010
14124
  if (executeQueryRef.current) {
14011
14125
  await executeQueryRef.current(message);
@@ -14029,10 +14143,12 @@ var RunScreen = ({
14029
14143
  };
14030
14144
  const result = dispatchSlashCommand(trimmed, slashCtx);
14031
14145
  if (result.kind === "exit") {
14146
+ const userId2 = `h${++historyIdRef.current}`;
14147
+ const toolId = `h${++historyIdRef.current}`;
14032
14148
  setHistory((h) => [
14033
14149
  ...h,
14034
- { role: "user", content: trimmed },
14035
- { role: "tool", content: "\uD83D\uDC4B Goodbye!" }
14150
+ { role: "user", content: trimmed, id: userId2 },
14151
+ { role: "tool", content: "\uD83D\uDC4B Goodbye!", id: toolId }
14036
14152
  ]);
14037
14153
  doExit();
14038
14154
  return;
@@ -14046,35 +14162,46 @@ var RunScreen = ({
14046
14162
  setErrorMessage(null);
14047
14163
  return;
14048
14164
  }
14165
+ const userId2 = `h${++historyIdRef.current}`;
14166
+ const toolId = `h${++historyIdRef.current}`;
14049
14167
  setHistory((h) => [
14050
14168
  ...h,
14051
- { role: "user", content: trimmed },
14052
- { role: "tool", content: result.message }
14169
+ { role: "user", content: trimmed, id: userId2 },
14170
+ { role: "tool", content: result.message, id: toolId }
14053
14171
  ]);
14054
14172
  return;
14055
14173
  }
14056
14174
  if (result.kind === "unknown") {
14175
+ const userId2 = `h${++historyIdRef.current}`;
14176
+ const toolId = `h${++historyIdRef.current}`;
14057
14177
  setHistory((h) => [
14058
14178
  ...h,
14059
- { role: "user", content: trimmed },
14060
- { role: "tool", content: `⚠ ${result.message}` }
14179
+ { role: "user", content: trimmed, id: userId2 },
14180
+ { role: "tool", content: `⚠ ${result.message}`, id: toolId }
14061
14181
  ]);
14062
14182
  return;
14063
14183
  }
14064
14184
  if (result.kind === "set-max-lines") {
14065
14185
  setMaxLines(result.value);
14186
+ const userId2 = `h${++historyIdRef.current}`;
14187
+ const toolId = `h${++historyIdRef.current}`;
14066
14188
  setHistory((h) => [
14067
14189
  ...h,
14068
- { role: "user", content: trimmed },
14190
+ { role: "user", content: trimmed, id: userId2 },
14069
14191
  {
14070
14192
  role: "tool",
14071
- content: `max-lines set to ${result.value}`
14193
+ content: `max-lines set to ${result.value}`,
14194
+ id: toolId
14072
14195
  }
14073
14196
  ]);
14074
14197
  return;
14075
14198
  }
14076
14199
  setErrorMessage(null);
14077
- setHistory((h) => [...h, { role: "user", content: trimmed }]);
14200
+ const userId = `h${++historyIdRef.current}`;
14201
+ setHistory((h) => [
14202
+ ...h,
14203
+ { role: "user", content: trimmed, id: userId }
14204
+ ]);
14078
14205
  setUserHistory((uh) => [trimmed, ...uh]);
14079
14206
  userTurnCountRef.current += 1;
14080
14207
  await executeQuery(trimmed);
@@ -14127,6 +14254,89 @@ var RunTui_default = RunScreen;
14127
14254
 
14128
14255
  // src/commands/run.ts
14129
14256
  init_markdown();
14257
+
14258
+ // src/commands/run-shutdown.ts
14259
+ import { TempFileManager as TempFileManager3 } from "@ai-setting/roy-agent-core/utils/temp-file-manager";
14260
+ async function shutdownRunSteps(deps) {
14261
+ const {
14262
+ lspManagerDispose,
14263
+ pluginAdapterDispose,
14264
+ stopEventHandler,
14265
+ stopTaskEventHandler,
14266
+ queryExecutor,
14267
+ env,
14268
+ envService,
14269
+ shouldDisposeEnvService,
14270
+ output,
14271
+ logger: logger6
14272
+ } = deps;
14273
+ try {
14274
+ stopEventHandler();
14275
+ } catch (err) {
14276
+ logger6?.warn?.(`[Shutdown] EventHandler.stop failed: ${err}`);
14277
+ }
14278
+ try {
14279
+ stopTaskEventHandler();
14280
+ } catch (err) {
14281
+ logger6?.warn?.(`[Shutdown] TaskEventHandler.stop failed: ${err}`);
14282
+ }
14283
+ if (lspManagerDispose) {
14284
+ try {
14285
+ await lspManagerDispose();
14286
+ } catch (err) {
14287
+ logger6?.warn?.(`[Shutdown] LSP Manager dispose failed: ${err}`);
14288
+ }
14289
+ }
14290
+ if (pluginAdapterDispose) {
14291
+ try {
14292
+ await pluginAdapterDispose();
14293
+ } catch (err) {
14294
+ logger6?.warn?.(`[Shutdown] PluginAdapter dispose failed: ${err}`);
14295
+ }
14296
+ }
14297
+ try {
14298
+ queryExecutor.dispose();
14299
+ } catch (err) {
14300
+ logger6?.warn?.(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
14301
+ }
14302
+ try {
14303
+ const eventSourceComponent = env.getComponent("event-source");
14304
+ if (eventSourceComponent) {
14305
+ const sources = eventSourceComponent.list?.();
14306
+ if (sources && sources.length > 0) {
14307
+ for (const source of sources) {
14308
+ const status = eventSourceComponent.getStatus?.(source.id);
14309
+ logger6?.debug?.(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
14310
+ if (status !== "stopped" && status !== "created") {
14311
+ try {
14312
+ await eventSourceComponent.stopSource(source.id);
14313
+ output?.info?.(`✓ 已停止事件源: ${source.name} (${status})`);
14314
+ } catch (error) {
14315
+ output?.warn?.(`⚠️ 停止事件源 ${source.name} 失败: ${error}`);
14316
+ }
14317
+ }
14318
+ }
14319
+ }
14320
+ }
14321
+ } catch (err) {
14322
+ logger6?.warn?.(`[Shutdown] EventSource cleanup failed: ${err}`);
14323
+ }
14324
+ if (shouldDisposeEnvService) {
14325
+ try {
14326
+ await envService.dispose();
14327
+ } catch (err) {
14328
+ logger6?.warn?.(`[Shutdown] envService.dispose failed: ${err}`);
14329
+ }
14330
+ }
14331
+ try {
14332
+ await TempFileManager3.getInstance().cleanupAll();
14333
+ logger6?.debug?.("[Shutdown] TempFileManager cleaned up");
14334
+ } catch (err) {
14335
+ logger6?.warn?.(`[Shutdown] TempFileManager cleanup failed: ${err}`);
14336
+ }
14337
+ }
14338
+
14339
+ // src/commands/run.ts
14130
14340
  async function loadHandleQuery(options, hooks) {
14131
14341
  const quiet = CliQuietModeService.getInstance().isQuiet();
14132
14342
  const output = new OutputService2;
@@ -14236,7 +14446,8 @@ async function loadHandleQuery(options, hooks) {
14236
14446
  eventSourceSystemPrompt,
14237
14447
  sessionComponent,
14238
14448
  eventHandler,
14239
- taskEventHandler
14449
+ taskEventHandler,
14450
+ sessionManager
14240
14451
  };
14241
14452
  }
14242
14453
  function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
@@ -14352,16 +14563,33 @@ function createRunCommand() {
14352
14563
  };
14353
14564
  const {
14354
14565
  handleQuery,
14566
+ envService,
14567
+ env,
14355
14568
  queryExecutor,
14356
14569
  sessionId,
14357
14570
  agentContext,
14358
14571
  eventSourceSystemPrompt,
14359
14572
  eventHandler,
14360
- taskEventHandler
14573
+ taskEventHandler,
14574
+ sessionManager: _sessionManager
14361
14575
  } = await loadHandleQuery(argv, {
14362
14576
  isIdle: () => runState.isIdle(),
14363
14577
  onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
14364
14578
  });
14579
+ const shutdownHandle = registerShutdownHandlers(async () => {
14580
+ await shutdownRunSteps({
14581
+ lspManagerDispose: null,
14582
+ pluginAdapterDispose: null,
14583
+ stopEventHandler,
14584
+ stopTaskEventHandler,
14585
+ queryExecutor,
14586
+ env,
14587
+ envService,
14588
+ shouldDisposeEnvService: true,
14589
+ output: undefined,
14590
+ logger: undefined
14591
+ });
14592
+ });
14365
14593
  const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
14366
14594
  const stopEventHandler = eventHandler.start();
14367
14595
  const stopTaskEventHandler = taskEventHandler.start();
@@ -14381,9 +14609,19 @@ function createRunCommand() {
14381
14609
  try {
14382
14610
  await waitUntilExit();
14383
14611
  } finally {
14384
- stopEventHandler();
14385
- stopTaskEventHandler();
14386
- queryExecutor.dispose();
14612
+ shutdownHandle.dispose();
14613
+ await shutdownRunSteps({
14614
+ lspManagerDispose: null,
14615
+ pluginAdapterDispose: null,
14616
+ stopEventHandler,
14617
+ stopTaskEventHandler,
14618
+ queryExecutor,
14619
+ env,
14620
+ envService,
14621
+ shouldDisposeEnvService: true,
14622
+ output: undefined,
14623
+ logger: undefined
14624
+ });
14387
14625
  process.exit(0);
14388
14626
  }
14389
14627
  }
@@ -14396,7 +14634,7 @@ import React8 from "react";
14396
14634
  import { render as render2 } from "ink";
14397
14635
 
14398
14636
  // src/ChatTui.tsx
14399
- import { useEffect as useEffect6, useState as useState7 } from "react";
14637
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
14400
14638
  import { Box as Box7, useApp as useApp3 } from "ink";
14401
14639
  init_markdown();
14402
14640
  import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
@@ -14410,8 +14648,9 @@ var ChatScreen = ({
14410
14648
  markdownEnabled: markdownEnabled2
14411
14649
  }) => {
14412
14650
  const [history, setHistory] = useState7([
14413
- { role: "user", content: message }
14651
+ { role: "user", content: message, id: "h-init-1" }
14414
14652
  ]);
14653
+ const historyIdRef = useRef3(1);
14415
14654
  const stream = useStreamingAccumulator();
14416
14655
  const { exit } = useApp3();
14417
14656
  const width = process.stdout.columns ?? 80;
@@ -14431,16 +14670,21 @@ var ChatScreen = ({
14431
14670
  stream.finish();
14432
14671
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
14433
14672
  const finalText = truncateForTerminal(rendered, maxLines);
14434
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14673
+ const assistantId = `h${++historyIdRef.current}`;
14674
+ setHistory((h) => [
14675
+ ...h,
14676
+ { role: "assistant", content: finalText, id: assistantId }
14677
+ ]);
14435
14678
  setTimeout(() => exit(), 100);
14436
14679
  } catch (err) {
14437
14680
  if (cancelled)
14438
14681
  return;
14439
14682
  const msg = err instanceof Error ? err.message : String(err);
14440
14683
  stream.fail(err instanceof Error ? err : new Error(msg));
14684
+ const errorId = `h${++historyIdRef.current}`;
14441
14685
  setHistory((h) => [
14442
14686
  ...h,
14443
- { role: "tool", content: `❌ Error: ${msg}` }
14687
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
14444
14688
  ]);
14445
14689
  setTimeout(() => exit(), 100);
14446
14690
  }
@@ -23494,7 +23738,7 @@ var WorkflowAddCommand = {
23494
23738
  process.exit(1);
23495
23739
  }
23496
23740
  if (!agentComponent.getAgent("workflow-extract")) {
23497
- output.error("Built-in 'workflow-extract' agent is not available. This usually means the environment was not fully initialized.");
23741
+ output.error("Built-in 'workflow-extract' agent is not available. ");
23498
23742
  process.exit(1);
23499
23743
  }
23500
23744
  const prompt = `## 任务
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-agent-cli",
3
- "version": "1.7.17",
3
+ "version": "1.7.19",
4
4
  "type": "module",
5
5
  "description": "CLI for roy-agent - Non-interactive command execution",
6
6
  "main": "./dist/index.js",