xibecode 0.9.8 → 1.0.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.
Files changed (46) hide show
  1. package/dist/commands/chat.d.ts.map +1 -1
  2. package/dist/commands/chat.js +20 -0
  3. package/dist/commands/chat.js.map +1 -1
  4. package/dist/commands/resume.d.ts +7 -0
  5. package/dist/commands/resume.d.ts.map +1 -0
  6. package/dist/commands/resume.js +136 -0
  7. package/dist/commands/resume.js.map +1 -0
  8. package/dist/commands/run-pr.js +1 -1
  9. package/dist/commands/run.d.ts.map +1 -1
  10. package/dist/commands/run.js +27 -15
  11. package/dist/commands/run.js.map +1 -1
  12. package/dist/core/agent.d.ts +2 -0
  13. package/dist/core/agent.d.ts.map +1 -1
  14. package/dist/core/agent.js +114 -31
  15. package/dist/core/agent.js.map +1 -1
  16. package/dist/core/history-manager.d.ts.map +1 -1
  17. package/dist/core/history-manager.js +23 -19
  18. package/dist/core/history-manager.js.map +1 -1
  19. package/dist/core/modes.d.ts.map +1 -1
  20. package/dist/core/modes.js +8 -7
  21. package/dist/core/modes.js.map +1 -1
  22. package/dist/core/plan-artifacts.d.ts.map +1 -1
  23. package/dist/core/plan-artifacts.js +12 -5
  24. package/dist/core/plan-artifacts.js.map +1 -1
  25. package/dist/core/plan-session.d.ts.map +1 -1
  26. package/dist/core/plan-session.js +14 -10
  27. package/dist/core/plan-session.js.map +1 -1
  28. package/dist/core/session-manager.d.ts.map +1 -1
  29. package/dist/core/session-manager.js +17 -14
  30. package/dist/core/session-manager.js.map +1 -1
  31. package/dist/core/tool-orchestrator.d.ts +8 -0
  32. package/dist/core/tool-orchestrator.d.ts.map +1 -1
  33. package/dist/core/tool-orchestrator.js +34 -1
  34. package/dist/core/tool-orchestrator.js.map +1 -1
  35. package/dist/core/tools.d.ts +9 -0
  36. package/dist/core/tools.d.ts.map +1 -1
  37. package/dist/core/tools.js +114 -97
  38. package/dist/core/tools.js.map +1 -1
  39. package/dist/index.js +10 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/ui/claude-style-chat.d.ts +5 -0
  42. package/dist/ui/claude-style-chat.d.ts.map +1 -1
  43. package/dist/ui/claude-style-chat.js +220 -13
  44. package/dist/ui/claude-style-chat.js.map +1 -1
  45. package/dist/webui/server.js +1 -1
  46. package/package.json +1 -1
@@ -16,7 +16,70 @@ import { formatToolArgs, formatToolOutcome, formatRunSwarmDetailLines } from '..
16
16
  import { SPINNER_VERBS } from '../constants/spinnerVerbs.js';
17
17
  import { extractAtReferences, splitAtReferences } from '../utils/at-references.js';
18
18
  import { loadImageAttachment, mimeFromExtension } from '../utils/image-attachments.js';
19
- const APP_VERSION = '0.9.8';
19
+ import { SessionManager } from '../core/session-manager.js';
20
+ function isAbortLikeError(err) {
21
+ if (!err || typeof err !== 'object')
22
+ return false;
23
+ const anyErr = err;
24
+ return (anyErr.name === 'AbortError' ||
25
+ anyErr.type === 'aborted' ||
26
+ String(anyErr.message || '').toLowerCase().includes('aborted'));
27
+ }
28
+ function summarizeToolResultContent(content) {
29
+ const raw = typeof content === 'string' ? content : JSON.stringify(content ?? '');
30
+ try {
31
+ const parsed = JSON.parse(raw);
32
+ if (parsed && typeof parsed === 'object') {
33
+ const record = parsed;
34
+ if (typeof record.path === 'string') {
35
+ const details = [
36
+ typeof record.lines === 'number' ? `${record.lines} lines` : null,
37
+ typeof record.count === 'number' ? `${record.count} entries` : null,
38
+ typeof record.total === 'number' ? `${record.total} result(s)` : null,
39
+ ].filter(Boolean).join(', ');
40
+ return details ? `${record.path} (${details})` : record.path;
41
+ }
42
+ if (typeof record.message === 'string')
43
+ return record.message;
44
+ if (typeof record.stdout === 'string')
45
+ return record.stdout.slice(0, 300);
46
+ }
47
+ }
48
+ catch {
49
+ // Fall through to plain text summary.
50
+ }
51
+ return raw.replace(/\s+/g, ' ').trim().slice(0, 300);
52
+ }
53
+ function transcriptLinesFromMessage(message) {
54
+ if (typeof message.content === 'string') {
55
+ const text = message.content.trim();
56
+ return text ? [{ type: message.role === 'assistant' ? 'assistant' : 'user', text }] : [];
57
+ }
58
+ if (!Array.isArray(message.content))
59
+ return [];
60
+ const lines = [];
61
+ for (const block of message.content) {
62
+ if (block?.type === 'text') {
63
+ const text = String(block.text || '').trim();
64
+ if (text)
65
+ lines.push({ type: message.role === 'assistant' ? 'assistant' : 'user', text });
66
+ continue;
67
+ }
68
+ if (block?.type === 'tool_use') {
69
+ const name = String(block.name || 'tool');
70
+ lines.push({ type: 'tool', text: `${name}${block.input ? ` — ${formatToolArgs(name, block.input)}` : ''}` });
71
+ continue;
72
+ }
73
+ if (block?.type === 'tool_result') {
74
+ lines.push({
75
+ type: 'tool_out',
76
+ text: `tool_result: ${summarizeToolResultContent(block.content)}`,
77
+ });
78
+ }
79
+ }
80
+ return lines;
81
+ }
82
+ const APP_VERSION = '1.0.0';
20
83
  const HERO_LOGO = [
21
84
  '██╗ ██╗██╗██████╗ ███████╗',
22
85
  '╚██╗██╔╝██║██╔══██╗██╔════╝',
@@ -167,27 +230,57 @@ function XibeCodeChatApp(props) {
167
230
  const [workSpinnerFrame, setWorkSpinnerFrame] = useState(0);
168
231
  const [workVerbIndex, setWorkVerbIndex] = useState(0);
169
232
  const nextLineIdRef = useRef(1);
170
- const [lines, setLines] = useState([
171
- {
172
- kind: 'line',
173
- id: nextLineIdRef.current++,
174
- type: 'info',
175
- text: 'XibeCode interactive session. Type /exit to quit, /clear to reset the transcript.',
176
- },
177
- { kind: 'line', id: nextLineIdRef.current++, type: 'info', text: 'Type /help for shortcuts.' },
178
- ]);
233
+ const initialMessagesRef = useRef(props.initialMessages);
234
+ const sessionIdRef = useRef(props.sessionId);
235
+ const buildInitialLines = useCallback(() => {
236
+ const base = [
237
+ {
238
+ kind: 'line',
239
+ id: nextLineIdRef.current++,
240
+ type: 'info',
241
+ text: 'XibeCode interactive session. Type /exit to quit, /clear to reset the transcript.',
242
+ },
243
+ { kind: 'line', id: nextLineIdRef.current++, type: 'info', text: 'Type /help for shortcuts.' },
244
+ ];
245
+ if (sessionIdRef.current) {
246
+ base.push({
247
+ kind: 'line',
248
+ id: nextLineIdRef.current++,
249
+ type: 'info',
250
+ text: `Resumed session: ${sessionIdRef.current}`,
251
+ });
252
+ }
253
+ if (initialMessagesRef.current && initialMessagesRef.current.length > 0) {
254
+ for (const msg of initialMessagesRef.current) {
255
+ for (const line of transcriptLinesFromMessage(msg)) {
256
+ base.push({ kind: 'line', id: nextLineIdRef.current++, ...line });
257
+ }
258
+ }
259
+ }
260
+ return base;
261
+ }, []);
262
+ const [lines, setLines] = useState(buildInitialLines);
179
263
  const pushLine = useCallback((line) => {
180
264
  const withId = { ...line, kind: 'line', id: nextLineIdRef.current++ };
181
265
  props.onUiLine?.(line);
182
266
  // Keep a much larger in-memory transcript so context doesn't vanish quickly.
183
267
  setLines((prev) => [...prev.slice(-5000), withId]);
184
268
  }, [props]);
269
+ useEffect(() => {
270
+ props.registerUiSink?.(pushLine);
271
+ }, [props, pushLine]);
272
+ useEffect(() => {
273
+ props.registerModeSink?.((nextMode) => {
274
+ setActiveMode(nextMode);
275
+ });
276
+ }, [props]);
185
277
  const abortControllerRef = useRef(null);
186
278
  const abortReasonRef = useRef('none');
187
279
  const queuedPromptRef = useRef(null);
188
280
  const lastVisibleOutputAtRef = useRef(Date.now());
189
281
  const currentPromptRef = useRef(null);
190
282
  const restartAttemptsRef = useRef(0);
283
+ const sessionMessagesRef = useRef(props.initialMessages || []);
191
284
  const lastBgLineByTask = useRef(new Map());
192
285
  useEffect(() => {
193
286
  if (!isRunning)
@@ -486,6 +579,7 @@ function XibeCodeChatApp(props) {
486
579
  }
487
580
  }
488
581
  if (resolvedInput === '/exit') {
582
+ props.onMessagesUpdate?.(props.getCurrentMessages?.() ?? sessionMessagesRef.current);
489
583
  exit();
490
584
  return;
491
585
  }
@@ -618,6 +712,7 @@ function XibeCodeChatApp(props) {
618
712
  lastVisibleOutputAtRef.current = Date.now();
619
713
  abortControllerRef.current = new AbortController();
620
714
  pushLine({ type: 'user', text: prompt });
715
+ sessionMessagesRef.current.push({ role: 'user', content: prompt });
621
716
  setIsRunning(true);
622
717
  try {
623
718
  const startedAt = Date.now();
@@ -1104,9 +1199,9 @@ function XibeCodeChatApp(props) {
1104
1199
  if (item.kind === 'hero') {
1105
1200
  return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [HERO_LOGO.map((line, idx) => (_jsx(React.Fragment, { children: _jsx(Text, { bold: true, color: idx < 6 ? 'claude' : 'suggestion', children: line }) }, `logo-${idx}`))), _jsx(Text, { color: "suggestion", children: "\u2726 Any model, Every tool, Zero limits. \u2726" }), _jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Provider " }), _jsx(Text, { color: "claude", bold: true, children: providerName })] }), _jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Model " }), _jsx(Text, { children: activeModel })] }), _jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Endpoint " }), _jsx(Text, { children: props.baseUrl || 'provider default' })] }), _jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Format " }), _jsxs(Text, { children: [wireFormat, ' ', _jsxs(Text, { dimColor: true, children: ["(", isAnthropicWireFormat(wireFormat, props.provider, props.customProviderFormat)
1106
1201
  ? 'Anthropic Messages'
1107
- : 'OpenAI chat', ")"] })] })] })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "suggestion", children: "\u25C8 cloud" }), _jsx(Text, { color: "inactive", children: " Ready \u2014 type " }), _jsx(Text, { color: "claude", children: "/help" }), _jsx(Text, { color: "inactive", children: " to begin" })] }), _jsxs(Text, { color: "inactive", children: ["xibecode ", _jsxs(Text, { color: "claude", children: ["v", APP_VERSION] })] }), _jsx(Text, { color: "subtle", children: '─'.repeat(98) }), _jsx(Text, { color: "inactive", children: "Agent transcript" })] }));
1202
+ : 'OpenAI chat', ")"] })] })] })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "suggestion", children: "\u25C8 cloud" }), _jsx(Text, { color: "inactive", children: " Ready \u2014 type " }), _jsx(Text, { color: "claude", children: "/help" }), _jsx(Text, { color: "inactive", children: " to begin" })] }), _jsxs(Text, { color: "inactive", children: ["xibecode ", _jsxs(Text, { color: "claude", children: ["v", APP_VERSION] })] }), _jsx(Text, { color: "subtle", children: '─'.repeat(98) }), _jsx(Text, { color: "inactive", children: "Agent transcript" })] }, item.id));
1108
1203
  }
1109
- return item.type === 'assistant' ? (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: prefixColorKey('assistant'), children: [prefixForType('assistant'), ":"] }), _jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(AssistantMarkdown, { content: item.text }) })] })) : (_jsxs(Text, { children: [_jsxs(Text, { bold: true, color: prefixColorKey(item.type), children: [prefixForType(item.type), ":", ' '] }), _jsx(Text, { color: lineColorKey(item.type), children: item.text })] }));
1204
+ return (_jsx(React.Fragment, { children: item.type === 'assistant' ? (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: prefixColorKey('assistant'), children: [prefixForType('assistant'), ":"] }), _jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(AssistantMarkdown, { content: item.text }) })] })) : (_jsxs(Text, { children: [_jsxs(Text, { bold: true, color: prefixColorKey(item.type), children: [prefixForType(item.type), ":", ' '] }), _jsx(Text, { color: lineColorKey(item.type), children: item.text })] })) }, item.id));
1110
1205
  } }), !hasChatContent && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { color: "inactive", children: "(send a message to start)" }) })), _jsx(Text, { color: "subtle", children: divider }), isRunning && (_jsx(Box, { marginTop: 1, paddingX: 1, borderStyle: "round", borderColor: "claudeBlue_FOR_SYSTEM_SPINNER", flexDirection: "column", children: _jsxs(Text, { wrap: "wrap", children: [_jsxs(Text, { bold: true, color: "claudeBlue_FOR_SYSTEM_SPINNER", children: [WORK_SPINNER_FRAMES[workSpinnerFrame], ' '] }), _jsx(Text, { bold: true, color: "briefLabelClaude", children: workVerbPhrase })] }) })), _jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", paddingX: 1, children: [_jsx(Text, { color: "claude", children: '> ' }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: isRunning ? 'Waiting for response…' : 'Message XibeCode…' })] }), isSlashMode && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "suggestion", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "suggestion", bold: true, children: "Commands" }), filteredCommands.length === 0 ? (_jsxs(Text, { color: "inactive", children: ["No commands match \"", input, "\""] })) : (filteredCommands.map((command, index) => (_jsx(React.Fragment, { children: _jsxs(Text, { children: [_jsx(Text, { color: index === selectedCommandIndex ? 'claude' : 'inactive', children: index === selectedCommandIndex ? '▸ ' : ' ' }), _jsx(Text, { bold: true, color: index === selectedCommandIndex ? 'claude' : 'text', children: command.name }), _jsxs(Text, { color: "inactive", children: [" \u2014 ", command.description] })] }) }, command.name)))), _jsx(Text, { color: "subtle", children: "Use \u2191/\u2193 to navigate, Tab to autocomplete." })] })), modelPickerOpen && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "claude", children: "Select model" }), isModelListLoading ? (_jsx(Text, { color: "inactive", children: "Loading models from provider..." })) : filteredModels.length === 0 ? (_jsx(Text, { color: "inactive", children: "No models matched current filter." })) : (visibleModelOptions.map((modelName, index) => {
1111
1206
  const absoluteIndex = modelPickerStart + index;
1112
1207
  const isSelected = absoluteIndex === selectedModelIndex;
@@ -1178,6 +1273,9 @@ export async function launchClaudeStyleChat(options) {
1178
1273
  config.set('model', nextModel);
1179
1274
  if (activeCreds.apiKey) {
1180
1275
  activeAgent = createAgentForModel(nextModel, { apiKey: activeCreds.apiKey, baseUrl: activeCreds.baseUrl });
1276
+ if (currentSession?.messages?.length) {
1277
+ activeAgent.setMessages(currentSession.messages);
1278
+ }
1181
1279
  activeAgent.setModeFromUser(activeMode, 'Preserve user-selected mode after model switch');
1182
1280
  }
1183
1281
  else {
@@ -1201,6 +1299,9 @@ export async function launchClaudeStyleChat(options) {
1201
1299
  activeAgent?.removeAllListeners('event');
1202
1300
  if (activeCreds.apiKey) {
1203
1301
  activeAgent = createAgentForModel(activeModel, { apiKey: activeCreds.apiKey, baseUrl: activeCreds.baseUrl });
1302
+ if (currentSession?.messages?.length) {
1303
+ activeAgent.setMessages(currentSession.messages);
1304
+ }
1204
1305
  activeAgent.setModeFromUser(activeMode, 'Preserve user-selected mode after format switch');
1205
1306
  }
1206
1307
  else {
@@ -1251,6 +1352,9 @@ export async function launchClaudeStyleChat(options) {
1251
1352
  activeCreds.baseUrl !== currentBaseUrl) {
1252
1353
  activeCreds = { apiKey: currentApiKey, baseUrl: currentBaseUrl };
1253
1354
  activeAgent = createAgentForModel(activeModel, { apiKey: currentApiKey, baseUrl: currentBaseUrl });
1355
+ if (currentSession?.messages?.length) {
1356
+ activeAgent.setMessages(currentSession.messages);
1357
+ }
1254
1358
  activeAgent.setModeFromUser(activeMode, 'Refresh agent after credential change');
1255
1359
  toolExecutor.setMode(activeMode);
1256
1360
  }
@@ -1312,6 +1416,9 @@ export async function launchClaudeStyleChat(options) {
1312
1416
  onLine({ type: 'assistant', text: event.data?.text || '' });
1313
1417
  break;
1314
1418
  case 'error':
1419
+ if (isAbortLikeError(event.data?.message) || isAbortLikeError(event.data?.error)) {
1420
+ break;
1421
+ }
1315
1422
  onLine({
1316
1423
  type: 'error',
1317
1424
  text: event.data?.message ||
@@ -1319,6 +1426,16 @@ export async function launchClaudeStyleChat(options) {
1319
1426
  'Unknown error',
1320
1427
  });
1321
1428
  break;
1429
+ case 'mode_changed': {
1430
+ const next = String(event.data?.to ?? '');
1431
+ if (next) {
1432
+ activeMode = next;
1433
+ toolExecutor.setMode(next);
1434
+ modeSink?.(next);
1435
+ onLine({ type: 'info', text: `Mode changed to ${next}` });
1436
+ }
1437
+ break;
1438
+ }
1322
1439
  default:
1323
1440
  break;
1324
1441
  }
@@ -1327,8 +1444,10 @@ export async function launchClaudeStyleChat(options) {
1327
1444
  images: opts?.images,
1328
1445
  signal: opts?.signal,
1329
1446
  });
1447
+ await onMessagesUpdate(activeAgent.getMessages());
1330
1448
  activeMode = activeAgent.getMode();
1331
1449
  toolExecutor.setMode(activeMode);
1450
+ modeSink?.(activeMode);
1332
1451
  return activeAgent.getStats();
1333
1452
  };
1334
1453
  const listBackgroundTasks = async () => {
@@ -1360,7 +1479,95 @@ export async function launchClaudeStyleChat(options) {
1360
1479
  const rendered = formatUiLineForLog(line) + '\n';
1361
1480
  logChain = logChain.then(() => fs.appendFile(chatLogPath, rendered, 'utf8')).catch(() => { });
1362
1481
  };
1482
+ // Keep the chat UI alive even if something throws unexpectedly.
1483
+ // We surface errors as chat lines instead of crashing the whole process.
1484
+ let uiSink = null;
1485
+ const registerUiSink = (sink) => {
1486
+ uiSink = sink;
1487
+ };
1488
+ // Keep the UI's displayed mode in sync with the tool permission mode.
1489
+ let modeSink = null;
1490
+ const registerModeSink = (sink) => {
1491
+ modeSink = sink;
1492
+ };
1493
+ const formatUnknownError = (err) => {
1494
+ if (err instanceof Error)
1495
+ return err.stack || err.message;
1496
+ try {
1497
+ return typeof err === 'string' ? err : JSON.stringify(err);
1498
+ }
1499
+ catch {
1500
+ return String(err);
1501
+ }
1502
+ };
1503
+ const onUnhandledRejection = (reason) => {
1504
+ if (isAbortLikeError(reason))
1505
+ return;
1506
+ uiSink?.({ type: 'error', text: `Unhandled rejection:\n${formatUnknownError(reason)}` });
1507
+ };
1508
+ const onUncaughtException = (err) => {
1509
+ if (isAbortLikeError(err))
1510
+ return;
1511
+ uiSink?.({ type: 'error', text: `Uncaught exception:\n${formatUnknownError(err)}` });
1512
+ };
1513
+ process.on('unhandledRejection', onUnhandledRejection);
1514
+ process.on('uncaughtException', onUncaughtException);
1515
+ const sessionManager = new SessionManager();
1516
+ let currentSessionId = options.sessionId;
1517
+ let currentSession = null;
1518
+ if (!currentSessionId) {
1519
+ currentSession = await sessionManager.createSession({
1520
+ title: 'Untitled Session',
1521
+ model,
1522
+ cwd: process.cwd(),
1523
+ });
1524
+ currentSessionId = currentSession.id;
1525
+ if (options.initialMessages && options.initialMessages.length > 0) {
1526
+ currentSession.messages = options.initialMessages;
1527
+ await sessionManager.saveSession(currentSession);
1528
+ }
1529
+ }
1530
+ else {
1531
+ currentSession = await sessionManager.loadSession(currentSessionId);
1532
+ }
1533
+ if (currentSession?.messages?.length) {
1534
+ activeAgent?.setMessages(currentSession.messages);
1535
+ }
1536
+ else if (options.initialMessages && options.initialMessages.length > 0) {
1537
+ activeAgent?.setMessages(options.initialMessages);
1538
+ }
1539
+ const onSessionCreated = (sessionId) => {
1540
+ currentSessionId = sessionId;
1541
+ };
1542
+ const onMessagesUpdate = async (messages) => {
1543
+ if (currentSessionId && currentSession) {
1544
+ currentSession.messages = messages;
1545
+ if (messages.length > 0) {
1546
+ const firstUserMsg = messages.find((m) => m.role === 'user');
1547
+ if (firstUserMsg && typeof firstUserMsg.content === 'string') {
1548
+ const title = firstUserMsg.content.trim().slice(0, 60);
1549
+ if (title) {
1550
+ currentSession.title = title + (firstUserMsg.content.length > 60 ? '…' : '');
1551
+ }
1552
+ }
1553
+ }
1554
+ await sessionManager.saveSession(currentSession);
1555
+ }
1556
+ };
1363
1557
  const root = createRoot({ exitOnCtrlC: true });
1364
- await renderAndRun(root, _jsx(XibeCodeChatApp, { model: model, initialMode: activeMode, provider: provider, baseUrl: baseUrl, needsFirstRunSetup: needsFirstRunSetup, defaultModel: model, modeOptions: modeOptions, initialRequestFormat: wireFormat, customProviderFormat: customProviderFormat, profile: options.profile, runPrompt: runPrompt, listBackgroundTasks: listBackgroundTasks, checkBackgroundTask: checkBackgroundTask, onUiLine: appendLogLine, loadModels: loadModels, onModelChange: onModelChange, onModeChange: onModeChange, onWireFormatChange: onWireFormatChange }));
1558
+ try {
1559
+ await renderAndRun(root, _jsx(XibeCodeChatApp, { model: model, initialMode: activeMode, provider: provider, baseUrl: baseUrl, needsFirstRunSetup: needsFirstRunSetup, defaultModel: model, modeOptions: modeOptions, initialRequestFormat: wireFormat, customProviderFormat: customProviderFormat, profile: options.profile, sessionId: currentSessionId, initialMessages: options.initialMessages, runPrompt: runPrompt, listBackgroundTasks: listBackgroundTasks, checkBackgroundTask: checkBackgroundTask, onUiLine: appendLogLine, registerUiSink: registerUiSink, registerModeSink: registerModeSink, loadModels: loadModels, onModelChange: onModelChange, onModeChange: onModeChange, onWireFormatChange: onWireFormatChange, onSessionCreated: onSessionCreated, onMessagesUpdate: onMessagesUpdate, getCurrentMessages: () => activeAgent?.getMessages() ?? currentSession?.messages ?? [] }));
1560
+ }
1561
+ finally {
1562
+ process.off('unhandledRejection', onUnhandledRejection);
1563
+ process.off('uncaughtException', onUncaughtException);
1564
+ if (currentSessionId) {
1565
+ console.log('\n');
1566
+ console.log('─'.repeat(60));
1567
+ console.log(`Session: ${currentSessionId}`);
1568
+ console.log(`To resume: xibecode resume ${currentSessionId}`);
1569
+ console.log('─'.repeat(60));
1570
+ }
1571
+ }
1365
1572
  }
1366
1573
  //# sourceMappingURL=claude-style-chat.js.map