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

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.18",
9465
9465
  type: "module",
9466
9466
  description: "CLI for roy-agent - Non-interactive command execution",
9467
9467
  main: "./dist/index.js",
@@ -12108,6 +12108,52 @@ async function shutdownInteractiveSteps(deps) {
12108
12108
  }
12109
12109
  }
12110
12110
 
12111
+ // src/commands/lifecycle.ts
12112
+ var DEFAULT_LOGGER = {
12113
+ warn: (msg) => console.error(`[shutdown] ${msg}`)
12114
+ };
12115
+ function registerShutdownHandlers(cleanup, options = {}) {
12116
+ const logger5 = options.logger ?? DEFAULT_LOGGER;
12117
+ const exitCode = options.exitCode ?? 0;
12118
+ const shutdownMessage = options.shutdownMessage ?? "Cleaning up...";
12119
+ let shuttingDown = false;
12120
+ const shutdown = async (signal) => {
12121
+ if (shuttingDown) {
12122
+ return;
12123
+ }
12124
+ shuttingDown = true;
12125
+ try {
12126
+ logger5.debug?.(`[${signal}] ${shutdownMessage}`);
12127
+ } catch {}
12128
+ if (cleanup) {
12129
+ try {
12130
+ await cleanup();
12131
+ } catch (err) {
12132
+ const message = err instanceof Error ? err.message : String(err);
12133
+ try {
12134
+ logger5.warn(`Cleanup failed: ${message}`);
12135
+ } catch {
12136
+ try {
12137
+ process.stderr.write(`[shutdown] Cleanup failed: ${message}
12138
+ `);
12139
+ } catch {}
12140
+ }
12141
+ }
12142
+ }
12143
+ try {
12144
+ process.exit(exitCode);
12145
+ } catch {}
12146
+ };
12147
+ process.on("SIGINT", shutdown);
12148
+ process.on("SIGTERM", shutdown);
12149
+ return {
12150
+ dispose: () => {
12151
+ process.removeListener("SIGINT", shutdown);
12152
+ process.removeListener("SIGTERM", shutdown);
12153
+ }
12154
+ };
12155
+ }
12156
+
12111
12157
  // src/commands/interactive.ts
12112
12158
  import { AskUserError, cascadeStopSessionTree } from "@ai-setting/roy-agent-core";
12113
12159
 
@@ -13453,6 +13499,22 @@ function createInteractiveCommand(externalEnvService) {
13453
13499
  });
13454
13500
  }
13455
13501
  }, env);
13502
+ registerShutdownHandlers(async () => {
13503
+ await shutdownInteractiveSteps({
13504
+ memoryPluginDispose,
13505
+ memoryPluginInstance,
13506
+ lspManagerDispose,
13507
+ pluginAdapterDispose,
13508
+ eventHandler,
13509
+ queryExecutor,
13510
+ env,
13511
+ envService,
13512
+ shouldDisposeEnvService,
13513
+ output,
13514
+ logger: logger5,
13515
+ console
13516
+ });
13517
+ });
13456
13518
  const eventHandler = new EventHandler({
13457
13519
  env,
13458
13520
  eventTypes: ["task.background.*", "event-source.event.*"],
@@ -13682,9 +13744,9 @@ var App = ({
13682
13744
  }, undefined, true, undefined, this),
13683
13745
  /* @__PURE__ */ jsxDEV6(Static2, {
13684
13746
  items: history,
13685
- children: (item) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13747
+ children: (item, index) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13686
13748
  item
13687
- }, `${item.role}:${item.content}`, false, undefined, this)
13749
+ }, item.id ?? `${item.role}:${index}`, false, undefined, this)
13688
13750
  }, undefined, false, undefined, this),
13689
13751
  status === "thinking" ? /* @__PURE__ */ jsxDEV6(Box4, {
13690
13752
  marginBottom: 1,
@@ -13965,6 +14027,7 @@ var RunScreen = ({
13965
14027
  const assistantTurnCountRef = useRef2(0);
13966
14028
  const isProcessingRef = useRef2(0);
13967
14029
  const executeQueryRef = useRef2(null);
14030
+ const historyIdRef = useRef2(0);
13968
14031
  const width = process.stdout.columns ?? 80;
13969
14032
  const doExit = useCallback4(() => {
13970
14033
  onExit?.();
@@ -13983,15 +14046,20 @@ var RunScreen = ({
13983
14046
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
13984
14047
  const finalText = truncateForTerminal(rendered, maxLines);
13985
14048
  stream.finish();
13986
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14049
+ const assistantId = `h${++historyIdRef.current}`;
14050
+ setHistory((h) => [
14051
+ ...h,
14052
+ { role: "assistant", content: finalText, id: assistantId }
14053
+ ]);
13987
14054
  assistantTurnCountRef.current += 1;
13988
14055
  } catch (err) {
13989
14056
  const msg = err instanceof Error ? err.message : String(err);
13990
14057
  stream.fail(err instanceof Error ? err : new Error(msg));
13991
14058
  setErrorMessage(msg);
14059
+ const errorId = `h${++historyIdRef.current}`;
13992
14060
  setHistory((h) => [
13993
14061
  ...h,
13994
- { role: "tool", content: `❌ Error: ${msg}` }
14062
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
13995
14063
  ]);
13996
14064
  } finally {
13997
14065
  isProcessingRef.current -= 1;
@@ -14004,9 +14072,10 @@ var RunScreen = ({
14004
14072
  if (!onRegisterHandlers)
14005
14073
  return;
14006
14074
  const handleNotification = async (message, _envEvent) => {
14075
+ const notifId = `h${++historyIdRef.current}`;
14007
14076
  setHistory((h) => [
14008
14077
  ...h,
14009
- { role: "tool", content: `\uD83D\uDCE2 ${message}` }
14078
+ { role: "tool", content: `\uD83D\uDCE2 ${message}`, id: notifId }
14010
14079
  ]);
14011
14080
  if (executeQueryRef.current) {
14012
14081
  await executeQueryRef.current(message);
@@ -14030,10 +14099,12 @@ var RunScreen = ({
14030
14099
  };
14031
14100
  const result = dispatchSlashCommand(trimmed, slashCtx);
14032
14101
  if (result.kind === "exit") {
14102
+ const userId2 = `h${++historyIdRef.current}`;
14103
+ const toolId = `h${++historyIdRef.current}`;
14033
14104
  setHistory((h) => [
14034
14105
  ...h,
14035
- { role: "user", content: trimmed },
14036
- { role: "tool", content: "\uD83D\uDC4B Goodbye!" }
14106
+ { role: "user", content: trimmed, id: userId2 },
14107
+ { role: "tool", content: "\uD83D\uDC4B Goodbye!", id: toolId }
14037
14108
  ]);
14038
14109
  doExit();
14039
14110
  return;
@@ -14047,35 +14118,46 @@ var RunScreen = ({
14047
14118
  setErrorMessage(null);
14048
14119
  return;
14049
14120
  }
14121
+ const userId2 = `h${++historyIdRef.current}`;
14122
+ const toolId = `h${++historyIdRef.current}`;
14050
14123
  setHistory((h) => [
14051
14124
  ...h,
14052
- { role: "user", content: trimmed },
14053
- { role: "tool", content: result.message }
14125
+ { role: "user", content: trimmed, id: userId2 },
14126
+ { role: "tool", content: result.message, id: toolId }
14054
14127
  ]);
14055
14128
  return;
14056
14129
  }
14057
14130
  if (result.kind === "unknown") {
14131
+ const userId2 = `h${++historyIdRef.current}`;
14132
+ const toolId = `h${++historyIdRef.current}`;
14058
14133
  setHistory((h) => [
14059
14134
  ...h,
14060
- { role: "user", content: trimmed },
14061
- { role: "tool", content: `⚠ ${result.message}` }
14135
+ { role: "user", content: trimmed, id: userId2 },
14136
+ { role: "tool", content: `⚠ ${result.message}`, id: toolId }
14062
14137
  ]);
14063
14138
  return;
14064
14139
  }
14065
14140
  if (result.kind === "set-max-lines") {
14066
14141
  setMaxLines(result.value);
14142
+ const userId2 = `h${++historyIdRef.current}`;
14143
+ const toolId = `h${++historyIdRef.current}`;
14067
14144
  setHistory((h) => [
14068
14145
  ...h,
14069
- { role: "user", content: trimmed },
14146
+ { role: "user", content: trimmed, id: userId2 },
14070
14147
  {
14071
14148
  role: "tool",
14072
- content: `max-lines set to ${result.value}`
14149
+ content: `max-lines set to ${result.value}`,
14150
+ id: toolId
14073
14151
  }
14074
14152
  ]);
14075
14153
  return;
14076
14154
  }
14077
14155
  setErrorMessage(null);
14078
- setHistory((h) => [...h, { role: "user", content: trimmed }]);
14156
+ const userId = `h${++historyIdRef.current}`;
14157
+ setHistory((h) => [
14158
+ ...h,
14159
+ { role: "user", content: trimmed, id: userId }
14160
+ ]);
14079
14161
  setUserHistory((uh) => [trimmed, ...uh]);
14080
14162
  userTurnCountRef.current += 1;
14081
14163
  await executeQuery(trimmed);
@@ -14128,6 +14210,89 @@ var RunTui_default = RunScreen;
14128
14210
 
14129
14211
  // src/commands/run.ts
14130
14212
  init_markdown();
14213
+
14214
+ // src/commands/run-shutdown.ts
14215
+ import { TempFileManager as TempFileManager3 } from "@ai-setting/roy-agent-core/utils/temp-file-manager";
14216
+ async function shutdownRunSteps(deps) {
14217
+ const {
14218
+ lspManagerDispose,
14219
+ pluginAdapterDispose,
14220
+ stopEventHandler,
14221
+ stopTaskEventHandler,
14222
+ queryExecutor,
14223
+ env,
14224
+ envService,
14225
+ shouldDisposeEnvService,
14226
+ output,
14227
+ logger: logger6
14228
+ } = deps;
14229
+ try {
14230
+ stopEventHandler();
14231
+ } catch (err) {
14232
+ logger6?.warn?.(`[Shutdown] EventHandler.stop failed: ${err}`);
14233
+ }
14234
+ try {
14235
+ stopTaskEventHandler();
14236
+ } catch (err) {
14237
+ logger6?.warn?.(`[Shutdown] TaskEventHandler.stop failed: ${err}`);
14238
+ }
14239
+ if (lspManagerDispose) {
14240
+ try {
14241
+ await lspManagerDispose();
14242
+ } catch (err) {
14243
+ logger6?.warn?.(`[Shutdown] LSP Manager dispose failed: ${err}`);
14244
+ }
14245
+ }
14246
+ if (pluginAdapterDispose) {
14247
+ try {
14248
+ await pluginAdapterDispose();
14249
+ } catch (err) {
14250
+ logger6?.warn?.(`[Shutdown] PluginAdapter dispose failed: ${err}`);
14251
+ }
14252
+ }
14253
+ try {
14254
+ queryExecutor.dispose();
14255
+ } catch (err) {
14256
+ logger6?.warn?.(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
14257
+ }
14258
+ try {
14259
+ const eventSourceComponent = env.getComponent("event-source");
14260
+ if (eventSourceComponent) {
14261
+ const sources = eventSourceComponent.list?.();
14262
+ if (sources && sources.length > 0) {
14263
+ for (const source of sources) {
14264
+ const status = eventSourceComponent.getStatus?.(source.id);
14265
+ logger6?.debug?.(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
14266
+ if (status !== "stopped" && status !== "created") {
14267
+ try {
14268
+ await eventSourceComponent.stopSource(source.id);
14269
+ output?.info?.(`✓ 已停止事件源: ${source.name} (${status})`);
14270
+ } catch (error) {
14271
+ output?.warn?.(`⚠️ 停止事件源 ${source.name} 失败: ${error}`);
14272
+ }
14273
+ }
14274
+ }
14275
+ }
14276
+ }
14277
+ } catch (err) {
14278
+ logger6?.warn?.(`[Shutdown] EventSource cleanup failed: ${err}`);
14279
+ }
14280
+ if (shouldDisposeEnvService) {
14281
+ try {
14282
+ await envService.dispose();
14283
+ } catch (err) {
14284
+ logger6?.warn?.(`[Shutdown] envService.dispose failed: ${err}`);
14285
+ }
14286
+ }
14287
+ try {
14288
+ await TempFileManager3.getInstance().cleanupAll();
14289
+ logger6?.debug?.("[Shutdown] TempFileManager cleaned up");
14290
+ } catch (err) {
14291
+ logger6?.warn?.(`[Shutdown] TempFileManager cleanup failed: ${err}`);
14292
+ }
14293
+ }
14294
+
14295
+ // src/commands/run.ts
14131
14296
  async function loadHandleQuery(options, hooks) {
14132
14297
  const quiet = CliQuietModeService.getInstance().isQuiet();
14133
14298
  const output = new OutputService2;
@@ -14237,7 +14402,8 @@ async function loadHandleQuery(options, hooks) {
14237
14402
  eventSourceSystemPrompt,
14238
14403
  sessionComponent,
14239
14404
  eventHandler,
14240
- taskEventHandler
14405
+ taskEventHandler,
14406
+ sessionManager
14241
14407
  };
14242
14408
  }
14243
14409
  function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
@@ -14353,16 +14519,33 @@ function createRunCommand() {
14353
14519
  };
14354
14520
  const {
14355
14521
  handleQuery,
14522
+ envService,
14523
+ env,
14356
14524
  queryExecutor,
14357
14525
  sessionId,
14358
14526
  agentContext,
14359
14527
  eventSourceSystemPrompt,
14360
14528
  eventHandler,
14361
- taskEventHandler
14529
+ taskEventHandler,
14530
+ sessionManager: _sessionManager
14362
14531
  } = await loadHandleQuery(argv, {
14363
14532
  isIdle: () => runState.isIdle(),
14364
14533
  onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
14365
14534
  });
14535
+ const shutdownHandle = registerShutdownHandlers(async () => {
14536
+ await shutdownRunSteps({
14537
+ lspManagerDispose: null,
14538
+ pluginAdapterDispose: null,
14539
+ stopEventHandler,
14540
+ stopTaskEventHandler,
14541
+ queryExecutor,
14542
+ env,
14543
+ envService,
14544
+ shouldDisposeEnvService: true,
14545
+ output: undefined,
14546
+ logger: undefined
14547
+ });
14548
+ });
14366
14549
  const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
14367
14550
  const stopEventHandler = eventHandler.start();
14368
14551
  const stopTaskEventHandler = taskEventHandler.start();
@@ -14382,9 +14565,19 @@ function createRunCommand() {
14382
14565
  try {
14383
14566
  await waitUntilExit();
14384
14567
  } finally {
14385
- stopEventHandler();
14386
- stopTaskEventHandler();
14387
- queryExecutor.dispose();
14568
+ shutdownHandle.dispose();
14569
+ await shutdownRunSteps({
14570
+ lspManagerDispose: null,
14571
+ pluginAdapterDispose: null,
14572
+ stopEventHandler,
14573
+ stopTaskEventHandler,
14574
+ queryExecutor,
14575
+ env,
14576
+ envService,
14577
+ shouldDisposeEnvService: true,
14578
+ output: undefined,
14579
+ logger: undefined
14580
+ });
14388
14581
  process.exit(0);
14389
14582
  }
14390
14583
  }
@@ -14397,7 +14590,7 @@ import React8 from "react";
14397
14590
  import { render as render2 } from "ink";
14398
14591
 
14399
14592
  // src/ChatTui.tsx
14400
- import { useEffect as useEffect6, useState as useState7 } from "react";
14593
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
14401
14594
  import { Box as Box7, useApp as useApp3 } from "ink";
14402
14595
  init_markdown();
14403
14596
  import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
@@ -14411,8 +14604,9 @@ var ChatScreen = ({
14411
14604
  markdownEnabled: markdownEnabled2
14412
14605
  }) => {
14413
14606
  const [history, setHistory] = useState7([
14414
- { role: "user", content: message }
14607
+ { role: "user", content: message, id: "h-init-1" }
14415
14608
  ]);
14609
+ const historyIdRef = useRef3(1);
14416
14610
  const stream = useStreamingAccumulator();
14417
14611
  const { exit } = useApp3();
14418
14612
  const width = process.stdout.columns ?? 80;
@@ -14432,16 +14626,21 @@ var ChatScreen = ({
14432
14626
  stream.finish();
14433
14627
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
14434
14628
  const finalText = truncateForTerminal(rendered, maxLines);
14435
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14629
+ const assistantId = `h${++historyIdRef.current}`;
14630
+ setHistory((h) => [
14631
+ ...h,
14632
+ { role: "assistant", content: finalText, id: assistantId }
14633
+ ]);
14436
14634
  setTimeout(() => exit(), 100);
14437
14635
  } catch (err) {
14438
14636
  if (cancelled)
14439
14637
  return;
14440
14638
  const msg = err instanceof Error ? err.message : String(err);
14441
14639
  stream.fail(err instanceof Error ? err : new Error(msg));
14640
+ const errorId = `h${++historyIdRef.current}`;
14442
14641
  setHistory((h) => [
14443
14642
  ...h,
14444
- { role: "tool", content: `❌ Error: ${msg}` }
14643
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
14445
14644
  ]);
14446
14645
  setTimeout(() => exit(), 100);
14447
14646
  }
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.18",
9464
9464
  type: "module",
9465
9465
  description: "CLI for roy-agent - Non-interactive command execution",
9466
9466
  main: "./dist/index.js",
@@ -12107,6 +12107,52 @@ async function shutdownInteractiveSteps(deps) {
12107
12107
  }
12108
12108
  }
12109
12109
 
12110
+ // src/commands/lifecycle.ts
12111
+ var DEFAULT_LOGGER = {
12112
+ warn: (msg) => console.error(`[shutdown] ${msg}`)
12113
+ };
12114
+ function registerShutdownHandlers(cleanup, options = {}) {
12115
+ const logger5 = options.logger ?? DEFAULT_LOGGER;
12116
+ const exitCode = options.exitCode ?? 0;
12117
+ const shutdownMessage = options.shutdownMessage ?? "Cleaning up...";
12118
+ let shuttingDown = false;
12119
+ const shutdown = async (signal) => {
12120
+ if (shuttingDown) {
12121
+ return;
12122
+ }
12123
+ shuttingDown = true;
12124
+ try {
12125
+ logger5.debug?.(`[${signal}] ${shutdownMessage}`);
12126
+ } catch {}
12127
+ if (cleanup) {
12128
+ try {
12129
+ await cleanup();
12130
+ } catch (err) {
12131
+ const message = err instanceof Error ? err.message : String(err);
12132
+ try {
12133
+ logger5.warn(`Cleanup failed: ${message}`);
12134
+ } catch {
12135
+ try {
12136
+ process.stderr.write(`[shutdown] Cleanup failed: ${message}
12137
+ `);
12138
+ } catch {}
12139
+ }
12140
+ }
12141
+ }
12142
+ try {
12143
+ process.exit(exitCode);
12144
+ } catch {}
12145
+ };
12146
+ process.on("SIGINT", shutdown);
12147
+ process.on("SIGTERM", shutdown);
12148
+ return {
12149
+ dispose: () => {
12150
+ process.removeListener("SIGINT", shutdown);
12151
+ process.removeListener("SIGTERM", shutdown);
12152
+ }
12153
+ };
12154
+ }
12155
+
12110
12156
  // src/commands/interactive.ts
12111
12157
  import { AskUserError, cascadeStopSessionTree } from "@ai-setting/roy-agent-core";
12112
12158
 
@@ -13452,6 +13498,22 @@ function createInteractiveCommand(externalEnvService) {
13452
13498
  });
13453
13499
  }
13454
13500
  }, env);
13501
+ registerShutdownHandlers(async () => {
13502
+ await shutdownInteractiveSteps({
13503
+ memoryPluginDispose,
13504
+ memoryPluginInstance,
13505
+ lspManagerDispose,
13506
+ pluginAdapterDispose,
13507
+ eventHandler,
13508
+ queryExecutor,
13509
+ env,
13510
+ envService,
13511
+ shouldDisposeEnvService,
13512
+ output,
13513
+ logger: logger5,
13514
+ console
13515
+ });
13516
+ });
13455
13517
  const eventHandler = new EventHandler({
13456
13518
  env,
13457
13519
  eventTypes: ["task.background.*", "event-source.event.*"],
@@ -13681,9 +13743,9 @@ var App = ({
13681
13743
  }, undefined, true, undefined, this),
13682
13744
  /* @__PURE__ */ jsxDEV6(Static2, {
13683
13745
  items: history,
13684
- children: (item) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13746
+ children: (item, index) => /* @__PURE__ */ jsxDEV6(HistoryEntry, {
13685
13747
  item
13686
- }, `${item.role}:${item.content}`, false, undefined, this)
13748
+ }, item.id ?? `${item.role}:${index}`, false, undefined, this)
13687
13749
  }, undefined, false, undefined, this),
13688
13750
  status === "thinking" ? /* @__PURE__ */ jsxDEV6(Box4, {
13689
13751
  marginBottom: 1,
@@ -13964,6 +14026,7 @@ var RunScreen = ({
13964
14026
  const assistantTurnCountRef = useRef2(0);
13965
14027
  const isProcessingRef = useRef2(0);
13966
14028
  const executeQueryRef = useRef2(null);
14029
+ const historyIdRef = useRef2(0);
13967
14030
  const width = process.stdout.columns ?? 80;
13968
14031
  const doExit = useCallback4(() => {
13969
14032
  onExit?.();
@@ -13982,15 +14045,20 @@ var RunScreen = ({
13982
14045
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
13983
14046
  const finalText = truncateForTerminal(rendered, maxLines);
13984
14047
  stream.finish();
13985
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14048
+ const assistantId = `h${++historyIdRef.current}`;
14049
+ setHistory((h) => [
14050
+ ...h,
14051
+ { role: "assistant", content: finalText, id: assistantId }
14052
+ ]);
13986
14053
  assistantTurnCountRef.current += 1;
13987
14054
  } catch (err) {
13988
14055
  const msg = err instanceof Error ? err.message : String(err);
13989
14056
  stream.fail(err instanceof Error ? err : new Error(msg));
13990
14057
  setErrorMessage(msg);
14058
+ const errorId = `h${++historyIdRef.current}`;
13991
14059
  setHistory((h) => [
13992
14060
  ...h,
13993
- { role: "tool", content: `❌ Error: ${msg}` }
14061
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
13994
14062
  ]);
13995
14063
  } finally {
13996
14064
  isProcessingRef.current -= 1;
@@ -14003,9 +14071,10 @@ var RunScreen = ({
14003
14071
  if (!onRegisterHandlers)
14004
14072
  return;
14005
14073
  const handleNotification = async (message, _envEvent) => {
14074
+ const notifId = `h${++historyIdRef.current}`;
14006
14075
  setHistory((h) => [
14007
14076
  ...h,
14008
- { role: "tool", content: `\uD83D\uDCE2 ${message}` }
14077
+ { role: "tool", content: `\uD83D\uDCE2 ${message}`, id: notifId }
14009
14078
  ]);
14010
14079
  if (executeQueryRef.current) {
14011
14080
  await executeQueryRef.current(message);
@@ -14029,10 +14098,12 @@ var RunScreen = ({
14029
14098
  };
14030
14099
  const result = dispatchSlashCommand(trimmed, slashCtx);
14031
14100
  if (result.kind === "exit") {
14101
+ const userId2 = `h${++historyIdRef.current}`;
14102
+ const toolId = `h${++historyIdRef.current}`;
14032
14103
  setHistory((h) => [
14033
14104
  ...h,
14034
- { role: "user", content: trimmed },
14035
- { role: "tool", content: "\uD83D\uDC4B Goodbye!" }
14105
+ { role: "user", content: trimmed, id: userId2 },
14106
+ { role: "tool", content: "\uD83D\uDC4B Goodbye!", id: toolId }
14036
14107
  ]);
14037
14108
  doExit();
14038
14109
  return;
@@ -14046,35 +14117,46 @@ var RunScreen = ({
14046
14117
  setErrorMessage(null);
14047
14118
  return;
14048
14119
  }
14120
+ const userId2 = `h${++historyIdRef.current}`;
14121
+ const toolId = `h${++historyIdRef.current}`;
14049
14122
  setHistory((h) => [
14050
14123
  ...h,
14051
- { role: "user", content: trimmed },
14052
- { role: "tool", content: result.message }
14124
+ { role: "user", content: trimmed, id: userId2 },
14125
+ { role: "tool", content: result.message, id: toolId }
14053
14126
  ]);
14054
14127
  return;
14055
14128
  }
14056
14129
  if (result.kind === "unknown") {
14130
+ const userId2 = `h${++historyIdRef.current}`;
14131
+ const toolId = `h${++historyIdRef.current}`;
14057
14132
  setHistory((h) => [
14058
14133
  ...h,
14059
- { role: "user", content: trimmed },
14060
- { role: "tool", content: `⚠ ${result.message}` }
14134
+ { role: "user", content: trimmed, id: userId2 },
14135
+ { role: "tool", content: `⚠ ${result.message}`, id: toolId }
14061
14136
  ]);
14062
14137
  return;
14063
14138
  }
14064
14139
  if (result.kind === "set-max-lines") {
14065
14140
  setMaxLines(result.value);
14141
+ const userId2 = `h${++historyIdRef.current}`;
14142
+ const toolId = `h${++historyIdRef.current}`;
14066
14143
  setHistory((h) => [
14067
14144
  ...h,
14068
- { role: "user", content: trimmed },
14145
+ { role: "user", content: trimmed, id: userId2 },
14069
14146
  {
14070
14147
  role: "tool",
14071
- content: `max-lines set to ${result.value}`
14148
+ content: `max-lines set to ${result.value}`,
14149
+ id: toolId
14072
14150
  }
14073
14151
  ]);
14074
14152
  return;
14075
14153
  }
14076
14154
  setErrorMessage(null);
14077
- setHistory((h) => [...h, { role: "user", content: trimmed }]);
14155
+ const userId = `h${++historyIdRef.current}`;
14156
+ setHistory((h) => [
14157
+ ...h,
14158
+ { role: "user", content: trimmed, id: userId }
14159
+ ]);
14078
14160
  setUserHistory((uh) => [trimmed, ...uh]);
14079
14161
  userTurnCountRef.current += 1;
14080
14162
  await executeQuery(trimmed);
@@ -14127,6 +14209,89 @@ var RunTui_default = RunScreen;
14127
14209
 
14128
14210
  // src/commands/run.ts
14129
14211
  init_markdown();
14212
+
14213
+ // src/commands/run-shutdown.ts
14214
+ import { TempFileManager as TempFileManager3 } from "@ai-setting/roy-agent-core/utils/temp-file-manager";
14215
+ async function shutdownRunSteps(deps) {
14216
+ const {
14217
+ lspManagerDispose,
14218
+ pluginAdapterDispose,
14219
+ stopEventHandler,
14220
+ stopTaskEventHandler,
14221
+ queryExecutor,
14222
+ env,
14223
+ envService,
14224
+ shouldDisposeEnvService,
14225
+ output,
14226
+ logger: logger6
14227
+ } = deps;
14228
+ try {
14229
+ stopEventHandler();
14230
+ } catch (err) {
14231
+ logger6?.warn?.(`[Shutdown] EventHandler.stop failed: ${err}`);
14232
+ }
14233
+ try {
14234
+ stopTaskEventHandler();
14235
+ } catch (err) {
14236
+ logger6?.warn?.(`[Shutdown] TaskEventHandler.stop failed: ${err}`);
14237
+ }
14238
+ if (lspManagerDispose) {
14239
+ try {
14240
+ await lspManagerDispose();
14241
+ } catch (err) {
14242
+ logger6?.warn?.(`[Shutdown] LSP Manager dispose failed: ${err}`);
14243
+ }
14244
+ }
14245
+ if (pluginAdapterDispose) {
14246
+ try {
14247
+ await pluginAdapterDispose();
14248
+ } catch (err) {
14249
+ logger6?.warn?.(`[Shutdown] PluginAdapter dispose failed: ${err}`);
14250
+ }
14251
+ }
14252
+ try {
14253
+ queryExecutor.dispose();
14254
+ } catch (err) {
14255
+ logger6?.warn?.(`[Shutdown] QueryExecutor.dispose failed: ${err}`);
14256
+ }
14257
+ try {
14258
+ const eventSourceComponent = env.getComponent("event-source");
14259
+ if (eventSourceComponent) {
14260
+ const sources = eventSourceComponent.list?.();
14261
+ if (sources && sources.length > 0) {
14262
+ for (const source of sources) {
14263
+ const status = eventSourceComponent.getStatus?.(source.id);
14264
+ logger6?.debug?.(`[Shutdown] 检查事件源: ${source.id}, 状态: ${status}`);
14265
+ if (status !== "stopped" && status !== "created") {
14266
+ try {
14267
+ await eventSourceComponent.stopSource(source.id);
14268
+ output?.info?.(`✓ 已停止事件源: ${source.name} (${status})`);
14269
+ } catch (error) {
14270
+ output?.warn?.(`⚠️ 停止事件源 ${source.name} 失败: ${error}`);
14271
+ }
14272
+ }
14273
+ }
14274
+ }
14275
+ }
14276
+ } catch (err) {
14277
+ logger6?.warn?.(`[Shutdown] EventSource cleanup failed: ${err}`);
14278
+ }
14279
+ if (shouldDisposeEnvService) {
14280
+ try {
14281
+ await envService.dispose();
14282
+ } catch (err) {
14283
+ logger6?.warn?.(`[Shutdown] envService.dispose failed: ${err}`);
14284
+ }
14285
+ }
14286
+ try {
14287
+ await TempFileManager3.getInstance().cleanupAll();
14288
+ logger6?.debug?.("[Shutdown] TempFileManager cleaned up");
14289
+ } catch (err) {
14290
+ logger6?.warn?.(`[Shutdown] TempFileManager cleanup failed: ${err}`);
14291
+ }
14292
+ }
14293
+
14294
+ // src/commands/run.ts
14130
14295
  async function loadHandleQuery(options, hooks) {
14131
14296
  const quiet = CliQuietModeService.getInstance().isQuiet();
14132
14297
  const output = new OutputService2;
@@ -14236,7 +14401,8 @@ async function loadHandleQuery(options, hooks) {
14236
14401
  eventSourceSystemPrompt,
14237
14402
  sessionComponent,
14238
14403
  eventHandler,
14239
- taskEventHandler
14404
+ taskEventHandler,
14405
+ sessionManager
14240
14406
  };
14241
14407
  }
14242
14408
  function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
@@ -14352,16 +14518,33 @@ function createRunCommand() {
14352
14518
  };
14353
14519
  const {
14354
14520
  handleQuery,
14521
+ envService,
14522
+ env,
14355
14523
  queryExecutor,
14356
14524
  sessionId,
14357
14525
  agentContext,
14358
14526
  eventSourceSystemPrompt,
14359
14527
  eventHandler,
14360
- taskEventHandler
14528
+ taskEventHandler,
14529
+ sessionManager: _sessionManager
14361
14530
  } = await loadHandleQuery(argv, {
14362
14531
  isIdle: () => runState.isIdle(),
14363
14532
  onNotification: (msg, envEvent) => runState.onNotification(msg, envEvent)
14364
14533
  });
14534
+ const shutdownHandle = registerShutdownHandlers(async () => {
14535
+ await shutdownRunSteps({
14536
+ lspManagerDispose: null,
14537
+ pluginAdapterDispose: null,
14538
+ stopEventHandler,
14539
+ stopTaskEventHandler,
14540
+ queryExecutor,
14541
+ env,
14542
+ envService,
14543
+ shouldDisposeEnvService: true,
14544
+ output: undefined,
14545
+ logger: undefined
14546
+ });
14547
+ });
14365
14548
  const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
14366
14549
  const stopEventHandler = eventHandler.start();
14367
14550
  const stopTaskEventHandler = taskEventHandler.start();
@@ -14381,9 +14564,19 @@ function createRunCommand() {
14381
14564
  try {
14382
14565
  await waitUntilExit();
14383
14566
  } finally {
14384
- stopEventHandler();
14385
- stopTaskEventHandler();
14386
- queryExecutor.dispose();
14567
+ shutdownHandle.dispose();
14568
+ await shutdownRunSteps({
14569
+ lspManagerDispose: null,
14570
+ pluginAdapterDispose: null,
14571
+ stopEventHandler,
14572
+ stopTaskEventHandler,
14573
+ queryExecutor,
14574
+ env,
14575
+ envService,
14576
+ shouldDisposeEnvService: true,
14577
+ output: undefined,
14578
+ logger: undefined
14579
+ });
14387
14580
  process.exit(0);
14388
14581
  }
14389
14582
  }
@@ -14396,7 +14589,7 @@ import React8 from "react";
14396
14589
  import { render as render2 } from "ink";
14397
14590
 
14398
14591
  // src/ChatTui.tsx
14399
- import { useEffect as useEffect6, useState as useState7 } from "react";
14592
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
14400
14593
  import { Box as Box7, useApp as useApp3 } from "ink";
14401
14594
  init_markdown();
14402
14595
  import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
@@ -14410,8 +14603,9 @@ var ChatScreen = ({
14410
14603
  markdownEnabled: markdownEnabled2
14411
14604
  }) => {
14412
14605
  const [history, setHistory] = useState7([
14413
- { role: "user", content: message }
14606
+ { role: "user", content: message, id: "h-init-1" }
14414
14607
  ]);
14608
+ const historyIdRef = useRef3(1);
14415
14609
  const stream = useStreamingAccumulator();
14416
14610
  const { exit } = useApp3();
14417
14611
  const width = process.stdout.columns ?? 80;
@@ -14431,16 +14625,21 @@ var ChatScreen = ({
14431
14625
  stream.finish();
14432
14626
  const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
14433
14627
  const finalText = truncateForTerminal(rendered, maxLines);
14434
- setHistory((h) => [...h, { role: "assistant", content: finalText }]);
14628
+ const assistantId = `h${++historyIdRef.current}`;
14629
+ setHistory((h) => [
14630
+ ...h,
14631
+ { role: "assistant", content: finalText, id: assistantId }
14632
+ ]);
14435
14633
  setTimeout(() => exit(), 100);
14436
14634
  } catch (err) {
14437
14635
  if (cancelled)
14438
14636
  return;
14439
14637
  const msg = err instanceof Error ? err.message : String(err);
14440
14638
  stream.fail(err instanceof Error ? err : new Error(msg));
14639
+ const errorId = `h${++historyIdRef.current}`;
14441
14640
  setHistory((h) => [
14442
14641
  ...h,
14443
- { role: "tool", content: `❌ Error: ${msg}` }
14642
+ { role: "tool", content: `❌ Error: ${msg}`, id: errorId }
14444
14643
  ]);
14445
14644
  setTimeout(() => exit(), 100);
14446
14645
  }
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.18",
4
4
  "type": "module",
5
5
  "description": "CLI for roy-agent - Non-interactive command execution",
6
6
  "main": "./dist/index.js",