@hasna/oldpal 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -28613,7 +28613,7 @@ var import_react20 = __toESM(require_react(), 1);
28613
28613
  // node_modules/.pnpm/ink@5.2.1_@types+react@18.3.27_react-devtools-core@4.28.5_react@18.3.1/node_modules/ink/build/hooks/use-focus-manager.js
28614
28614
  var import_react21 = __toESM(require_react(), 1);
28615
28615
  // packages/terminal/src/components/App.tsx
28616
- var import_react26 = __toESM(require_react(), 1);
28616
+ var import_react27 = __toESM(require_react(), 1);
28617
28617
  // packages/shared/src/utils.ts
28618
28618
  import { randomUUID } from "crypto";
28619
28619
  function generateId() {
@@ -29868,7 +29868,8 @@ ARGUMENTS: ${args.join(" ")}`;
29868
29868
  const fullMatch = match[0];
29869
29869
  const command = match[1];
29870
29870
  try {
29871
- const output = await Bun.$`cd ${skillDir} && ${command}`.quiet().text();
29871
+ const fullCommand = `cd ${skillDir} && ${command}`;
29872
+ const output = await Bun.$`sh -c ${fullCommand}`.quiet().text();
29872
29873
  result = result.replace(fullMatch, output.trim());
29873
29874
  } catch (error) {
29874
29875
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -30259,6 +30260,7 @@ class BuiltinCommands {
30259
30260
  loader.register(this.helpCommand(loader));
30260
30261
  loader.register(this.clearCommand());
30261
30262
  loader.register(this.newCommand());
30263
+ loader.register(this.sessionCommand());
30262
30264
  loader.register(this.statusCommand());
30263
30265
  loader.register(this.tokensCommand());
30264
30266
  loader.register(this.compactCommand());
@@ -30381,6 +30383,29 @@ class BuiltinCommands {
30381
30383
  }
30382
30384
  };
30383
30385
  }
30386
+ sessionCommand() {
30387
+ return {
30388
+ name: "session",
30389
+ description: "List sessions or switch to a session by number",
30390
+ builtin: true,
30391
+ selfHandled: true,
30392
+ content: "",
30393
+ handler: async (args, context) => {
30394
+ const arg = args.trim();
30395
+ if (arg === "new") {
30396
+ context.emit("done");
30397
+ return { handled: true, sessionAction: "new" };
30398
+ }
30399
+ const num = parseInt(arg, 10);
30400
+ if (!isNaN(num) && num > 0) {
30401
+ context.emit("done");
30402
+ return { handled: true, sessionAction: "switch", sessionNumber: num };
30403
+ }
30404
+ context.emit("done");
30405
+ return { handled: true, sessionAction: "list" };
30406
+ }
30407
+ };
30408
+ }
30384
30409
  tokensCommand() {
30385
30410
  return {
30386
30411
  name: "tokens",
@@ -31423,6 +31448,153 @@ class EmbeddedClient {
31423
31448
  this.messages = [];
31424
31449
  this.logger.info("Conversation cleared");
31425
31450
  }
31451
+ getCwd() {
31452
+ return this.cwd;
31453
+ }
31454
+ getStartedAt() {
31455
+ return this.startedAt;
31456
+ }
31457
+ getMessages() {
31458
+ return [...this.messages];
31459
+ }
31460
+ }
31461
+ // packages/core/src/sessions/registry.ts
31462
+ class SessionRegistry {
31463
+ sessions = new Map;
31464
+ activeSessionId = null;
31465
+ chunkBuffers = new Map;
31466
+ chunkCallbacks = [];
31467
+ errorCallbacks = [];
31468
+ async createSession(cwd2) {
31469
+ const client = new EmbeddedClient(cwd2);
31470
+ await client.initialize();
31471
+ const sessionInfo = {
31472
+ id: client.getSessionId(),
31473
+ cwd: cwd2,
31474
+ startedAt: Date.now(),
31475
+ updatedAt: Date.now(),
31476
+ isProcessing: false,
31477
+ client
31478
+ };
31479
+ client.onChunk((chunk) => {
31480
+ this.handleChunk(sessionInfo.id, chunk);
31481
+ });
31482
+ client.onError((error) => {
31483
+ if (this.activeSessionId === sessionInfo.id) {
31484
+ for (const callback of this.errorCallbacks) {
31485
+ callback(error);
31486
+ }
31487
+ }
31488
+ });
31489
+ this.sessions.set(sessionInfo.id, sessionInfo);
31490
+ this.chunkBuffers.set(sessionInfo.id, []);
31491
+ if (this.activeSessionId === null) {
31492
+ this.activeSessionId = sessionInfo.id;
31493
+ }
31494
+ return sessionInfo;
31495
+ }
31496
+ handleChunk(sessionId, chunk) {
31497
+ const session = this.sessions.get(sessionId);
31498
+ if (session) {
31499
+ session.updatedAt = Date.now();
31500
+ if (chunk.type === "done") {
31501
+ session.isProcessing = false;
31502
+ }
31503
+ }
31504
+ if (sessionId === this.activeSessionId) {
31505
+ for (const callback of this.chunkCallbacks) {
31506
+ callback(chunk);
31507
+ }
31508
+ } else {
31509
+ const buffer = this.chunkBuffers.get(sessionId);
31510
+ if (buffer) {
31511
+ buffer.push(chunk);
31512
+ }
31513
+ }
31514
+ }
31515
+ async switchSession(id) {
31516
+ if (!this.sessions.has(id)) {
31517
+ throw new Error(`Session ${id} not found`);
31518
+ }
31519
+ if (this.activeSessionId === id) {
31520
+ return;
31521
+ }
31522
+ this.activeSessionId = id;
31523
+ const buffer = this.chunkBuffers.get(id);
31524
+ if (buffer && buffer.length > 0) {
31525
+ for (const chunk of buffer) {
31526
+ for (const callback of this.chunkCallbacks) {
31527
+ callback(chunk);
31528
+ }
31529
+ }
31530
+ this.chunkBuffers.set(id, []);
31531
+ }
31532
+ }
31533
+ listSessions() {
31534
+ return Array.from(this.sessions.values()).sort((a, b) => b.updatedAt - a.updatedAt);
31535
+ }
31536
+ getActiveSession() {
31537
+ if (!this.activeSessionId)
31538
+ return null;
31539
+ return this.sessions.get(this.activeSessionId) || null;
31540
+ }
31541
+ getActiveSessionId() {
31542
+ return this.activeSessionId;
31543
+ }
31544
+ getSession(id) {
31545
+ return this.sessions.get(id) || null;
31546
+ }
31547
+ getSessionIndex(id) {
31548
+ const sessions = this.listSessions();
31549
+ return sessions.findIndex((s) => s.id === id) + 1;
31550
+ }
31551
+ getSessionCount() {
31552
+ return this.sessions.size;
31553
+ }
31554
+ closeSession(id) {
31555
+ const session = this.sessions.get(id);
31556
+ if (session) {
31557
+ session.client.disconnect();
31558
+ this.sessions.delete(id);
31559
+ this.chunkBuffers.delete(id);
31560
+ if (this.activeSessionId === id) {
31561
+ const remaining = this.listSessions();
31562
+ this.activeSessionId = remaining.length > 0 ? remaining[0].id : null;
31563
+ }
31564
+ }
31565
+ }
31566
+ closeAll() {
31567
+ for (const session of this.sessions.values()) {
31568
+ session.client.disconnect();
31569
+ }
31570
+ this.sessions.clear();
31571
+ this.chunkBuffers.clear();
31572
+ this.activeSessionId = null;
31573
+ }
31574
+ onChunk(callback) {
31575
+ this.chunkCallbacks.push(callback);
31576
+ }
31577
+ onError(callback) {
31578
+ this.errorCallbacks.push(callback);
31579
+ }
31580
+ setProcessing(id, isProcessing) {
31581
+ const session = this.sessions.get(id);
31582
+ if (session) {
31583
+ session.isProcessing = isProcessing;
31584
+ session.updatedAt = Date.now();
31585
+ }
31586
+ }
31587
+ hasProcessingSession() {
31588
+ for (const session of this.sessions.values()) {
31589
+ if (session.isProcessing) {
31590
+ return true;
31591
+ }
31592
+ }
31593
+ return false;
31594
+ }
31595
+ getBackgroundProcessingSessions() {
31596
+ return Array.from(this.sessions.values()).filter((s) => s.isProcessing && s.id !== this.activeSessionId);
31597
+ }
31426
31598
  }
31427
31599
  // packages/terminal/src/components/Input.tsx
31428
31600
  var import_react23 = __toESM(require_react(), 1);
@@ -32000,26 +32172,47 @@ function truncate(text, maxLength) {
32000
32172
 
32001
32173
  // packages/terminal/src/components/Status.tsx
32002
32174
  var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
32003
- function Status({ isProcessing, cwd: cwd2, queueLength = 0, tokenUsage }) {
32175
+ function Status({
32176
+ isProcessing,
32177
+ cwd: cwd2,
32178
+ queueLength = 0,
32179
+ tokenUsage,
32180
+ sessionIndex,
32181
+ sessionCount,
32182
+ backgroundProcessingCount = 0
32183
+ }) {
32004
32184
  let contextInfo = "";
32005
32185
  if (tokenUsage && tokenUsage.maxContextTokens > 0) {
32006
32186
  const percent = Math.round(tokenUsage.totalTokens / tokenUsage.maxContextTokens * 100);
32007
- contextInfo = `${percent}% context used`;
32187
+ contextInfo = `${percent}% context`;
32008
32188
  }
32189
+ const sessionInfo = sessionIndex && sessionCount ? `Session ${sessionIndex}/${sessionCount}` : "";
32190
+ const bgIndicator = backgroundProcessingCount > 0 ? ` (${backgroundProcessingCount} processing)` : "";
32009
32191
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
32010
32192
  marginTop: 1,
32011
32193
  justifyContent: "space-between",
32012
32194
  children: [
32013
32195
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
32014
32196
  dimColor: true,
32015
- children: "/help for commands"
32016
- }, undefined, false, undefined, this),
32197
+ children: [
32198
+ "/help for commands",
32199
+ sessionCount && sessionCount > 1 ? " | Ctrl+S sessions" : ""
32200
+ ]
32201
+ }, undefined, true, undefined, this),
32017
32202
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
32018
32203
  children: [
32019
32204
  isProcessing && /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
32020
32205
  dimColor: true,
32021
32206
  children: "esc to stop \xB7 "
32022
32207
  }, undefined, false, undefined, this),
32208
+ sessionInfo && /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
32209
+ dimColor: true,
32210
+ children: [
32211
+ sessionInfo,
32212
+ bgIndicator,
32213
+ contextInfo ? " \xB7 " : ""
32214
+ ]
32215
+ }, undefined, true, undefined, this),
32023
32216
  contextInfo && /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
32024
32217
  dimColor: true,
32025
32218
  children: contextInfo
@@ -32218,8 +32411,131 @@ function WelcomeBanner({ version, model, directory }) {
32218
32411
  }, undefined, true, undefined, this);
32219
32412
  }
32220
32413
 
32221
- // packages/terminal/src/components/App.tsx
32414
+ // packages/terminal/src/components/SessionSelector.tsx
32415
+ var import_react26 = __toESM(require_react(), 1);
32222
32416
  var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
32417
+ function formatSessionTime(timestamp) {
32418
+ const date = new Date(timestamp);
32419
+ const now2 = new Date;
32420
+ const isToday = date.toDateString() === now2.toDateString();
32421
+ if (isToday) {
32422
+ return date.toLocaleTimeString("en-US", {
32423
+ hour: "numeric",
32424
+ minute: "2-digit",
32425
+ hour12: true
32426
+ }).toLowerCase();
32427
+ }
32428
+ return date.toLocaleDateString("en-US", {
32429
+ month: "short",
32430
+ day: "numeric",
32431
+ hour: "numeric",
32432
+ minute: "2-digit",
32433
+ hour12: true
32434
+ }).toLowerCase();
32435
+ }
32436
+ function formatPath(cwd2) {
32437
+ const home = process.env.HOME || "";
32438
+ if (cwd2.startsWith(home)) {
32439
+ return "~" + cwd2.slice(home.length);
32440
+ }
32441
+ return cwd2;
32442
+ }
32443
+ function SessionSelector({
32444
+ sessions,
32445
+ activeSessionId,
32446
+ onSelect,
32447
+ onNew,
32448
+ onCancel
32449
+ }) {
32450
+ const [selectedIndex, setSelectedIndex] = import_react26.useState(0);
32451
+ use_input_default((input, key) => {
32452
+ if (key.escape) {
32453
+ onCancel();
32454
+ return;
32455
+ }
32456
+ if (key.return) {
32457
+ if (selectedIndex === sessions.length) {
32458
+ onNew();
32459
+ } else {
32460
+ onSelect(sessions[selectedIndex].id);
32461
+ }
32462
+ return;
32463
+ }
32464
+ if (key.upArrow) {
32465
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
32466
+ }
32467
+ if (key.downArrow) {
32468
+ setSelectedIndex((prev) => Math.min(sessions.length, prev + 1));
32469
+ }
32470
+ const num = parseInt(input, 10);
32471
+ if (!isNaN(num) && num >= 1 && num <= sessions.length) {
32472
+ onSelect(sessions[num - 1].id);
32473
+ }
32474
+ if (input === "n") {
32475
+ onNew();
32476
+ }
32477
+ });
32478
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32479
+ flexDirection: "column",
32480
+ paddingY: 1,
32481
+ children: [
32482
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32483
+ marginBottom: 1,
32484
+ children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
32485
+ bold: true,
32486
+ children: "Sessions"
32487
+ }, undefined, false, undefined, this)
32488
+ }, undefined, false, undefined, this),
32489
+ sessions.map((session, index) => {
32490
+ const isActive = session.id === activeSessionId;
32491
+ const isSelected = index === selectedIndex;
32492
+ const prefix = isActive ? "[*]" : " ";
32493
+ const time = formatSessionTime(session.updatedAt);
32494
+ const path = formatPath(session.cwd);
32495
+ const processing = session.isProcessing ? " (processing)" : "";
32496
+ return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32497
+ children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
32498
+ inverse: isSelected,
32499
+ color: isActive ? "green" : undefined,
32500
+ dimColor: !isSelected && !isActive,
32501
+ children: [
32502
+ prefix,
32503
+ " ",
32504
+ index + 1,
32505
+ ". ",
32506
+ time,
32507
+ " ",
32508
+ path,
32509
+ processing
32510
+ ]
32511
+ }, undefined, true, undefined, this)
32512
+ }, session.id, false, undefined, this);
32513
+ }),
32514
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32515
+ marginTop: 1,
32516
+ children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
32517
+ inverse: selectedIndex === sessions.length,
32518
+ dimColor: selectedIndex !== sessions.length,
32519
+ children: "+ New session (n)"
32520
+ }, undefined, false, undefined, this)
32521
+ }, undefined, false, undefined, this),
32522
+ /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32523
+ marginTop: 1,
32524
+ children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
32525
+ dimColor: true,
32526
+ children: [
32527
+ "Enter to select | Esc to cancel | 1-",
32528
+ sessions.length,
32529
+ " to switch | n for new"
32530
+ ]
32531
+ }, undefined, true, undefined, this)
32532
+ }, undefined, false, undefined, this)
32533
+ ]
32534
+ }, undefined, true, undefined, this);
32535
+ }
32536
+
32537
+ // packages/terminal/src/components/App.tsx
32538
+ var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
32223
32539
  function formatToolName(toolCall) {
32224
32540
  const { name, input } = toolCall;
32225
32541
  switch (name) {
@@ -32246,27 +32562,183 @@ function formatToolName(toolCall) {
32246
32562
  }
32247
32563
  function App2({ cwd: cwd2 }) {
32248
32564
  const { exit } = use_app_default();
32249
- const [client, setClient] = import_react26.useState(null);
32250
- const [messages, setMessages] = import_react26.useState([]);
32251
- const [currentResponse, setCurrentResponse] = import_react26.useState("");
32252
- const [currentToolCall, setCurrentToolCall] = import_react26.useState();
32253
- const [lastToolResult, setLastToolResult] = import_react26.useState();
32254
- const [isProcessing, setIsProcessing] = import_react26.useState(false);
32255
- const [isInitializing, setIsInitializing] = import_react26.useState(true);
32256
- const [error, setError] = import_react26.useState(null);
32257
- const [messageQueue, setMessageQueue] = import_react26.useState([]);
32258
- const [activityLog, setActivityLog] = import_react26.useState([]);
32259
- const [tokenUsage, setTokenUsage] = import_react26.useState();
32260
- const [processingStartTime, setProcessingStartTime] = import_react26.useState();
32261
- const [currentTurnTokens, setCurrentTurnTokens] = import_react26.useState(0);
32262
- const [scrollOffset, setScrollOffset] = import_react26.useState(0);
32263
- const [autoScroll, setAutoScroll] = import_react26.useState(true);
32264
- const responseRef = import_react26.useRef("");
32265
- const clientRef = import_react26.useRef(null);
32266
- const toolCallsRef = import_react26.useRef([]);
32267
- const toolResultsRef = import_react26.useRef([]);
32268
- const processQueue = import_react26.useCallback(async () => {
32269
- if (!clientRef.current || messageQueue.length === 0)
32565
+ const [registry2] = import_react27.useState(() => new SessionRegistry);
32566
+ const registryRef = import_react27.useRef(registry2);
32567
+ const [activeSessionId, setActiveSessionId] = import_react27.useState(null);
32568
+ const [isInitializing, setIsInitializing] = import_react27.useState(true);
32569
+ const [showSessionSelector, setShowSessionSelector] = import_react27.useState(false);
32570
+ const sessionUIStates = import_react27.useRef(new Map);
32571
+ const [messages, setMessages] = import_react27.useState([]);
32572
+ const [currentResponse, setCurrentResponse] = import_react27.useState("");
32573
+ const [currentToolCall, setCurrentToolCall] = import_react27.useState();
32574
+ const [isProcessing, setIsProcessing] = import_react27.useState(false);
32575
+ const [error, setError] = import_react27.useState(null);
32576
+ const [messageQueue, setMessageQueue] = import_react27.useState([]);
32577
+ const [activityLog, setActivityLog] = import_react27.useState([]);
32578
+ const [tokenUsage, setTokenUsage] = import_react27.useState();
32579
+ const [processingStartTime, setProcessingStartTime] = import_react27.useState();
32580
+ const [currentTurnTokens, setCurrentTurnTokens] = import_react27.useState(0);
32581
+ const [scrollOffset, setScrollOffset] = import_react27.useState(0);
32582
+ const [autoScroll, setAutoScroll] = import_react27.useState(true);
32583
+ const responseRef = import_react27.useRef("");
32584
+ const toolCallsRef = import_react27.useRef([]);
32585
+ const toolResultsRef = import_react27.useRef([]);
32586
+ const activityLogRef = import_react27.useRef([]);
32587
+ const saveCurrentSessionState = import_react27.useCallback(() => {
32588
+ if (activeSessionId) {
32589
+ sessionUIStates.current.set(activeSessionId, {
32590
+ messages,
32591
+ currentResponse: responseRef.current,
32592
+ activityLog: activityLogRef.current,
32593
+ tokenUsage,
32594
+ processingStartTime,
32595
+ currentTurnTokens,
32596
+ messageQueue,
32597
+ error
32598
+ });
32599
+ }
32600
+ }, [activeSessionId, messages, tokenUsage, processingStartTime, currentTurnTokens, messageQueue, error]);
32601
+ const loadSessionState = import_react27.useCallback((sessionId) => {
32602
+ const state = sessionUIStates.current.get(sessionId);
32603
+ if (state) {
32604
+ setMessages(state.messages);
32605
+ setCurrentResponse(state.currentResponse);
32606
+ responseRef.current = state.currentResponse;
32607
+ setActivityLog(state.activityLog);
32608
+ activityLogRef.current = state.activityLog;
32609
+ setTokenUsage(state.tokenUsage);
32610
+ setProcessingStartTime(state.processingStartTime);
32611
+ setCurrentTurnTokens(state.currentTurnTokens);
32612
+ setMessageQueue(state.messageQueue);
32613
+ setError(state.error);
32614
+ } else {
32615
+ setMessages([]);
32616
+ setCurrentResponse("");
32617
+ responseRef.current = "";
32618
+ setActivityLog([]);
32619
+ activityLogRef.current = [];
32620
+ setTokenUsage(undefined);
32621
+ setProcessingStartTime(undefined);
32622
+ setCurrentTurnTokens(0);
32623
+ setMessageQueue([]);
32624
+ setError(null);
32625
+ }
32626
+ setScrollOffset(0);
32627
+ setAutoScroll(true);
32628
+ }, []);
32629
+ const handleChunk = import_react27.useCallback((chunk) => {
32630
+ if (chunk.type === "text" && chunk.content) {
32631
+ responseRef.current += chunk.content;
32632
+ setCurrentResponse(responseRef.current);
32633
+ } else if (chunk.type === "tool_use" && chunk.toolCall) {
32634
+ if (responseRef.current.trim()) {
32635
+ const textEntry = {
32636
+ id: generateId(),
32637
+ type: "text",
32638
+ content: responseRef.current,
32639
+ timestamp: now()
32640
+ };
32641
+ activityLogRef.current = [...activityLogRef.current, textEntry];
32642
+ setActivityLog(activityLogRef.current);
32643
+ setCurrentResponse("");
32644
+ responseRef.current = "";
32645
+ }
32646
+ toolCallsRef.current.push(chunk.toolCall);
32647
+ const toolEntry = {
32648
+ id: generateId(),
32649
+ type: "tool_call",
32650
+ toolCall: chunk.toolCall,
32651
+ timestamp: now()
32652
+ };
32653
+ activityLogRef.current = [...activityLogRef.current, toolEntry];
32654
+ setActivityLog(activityLogRef.current);
32655
+ setCurrentToolCall(chunk.toolCall);
32656
+ } else if (chunk.type === "tool_result" && chunk.toolResult) {
32657
+ toolResultsRef.current.push(chunk.toolResult);
32658
+ const resultEntry = {
32659
+ id: generateId(),
32660
+ type: "tool_result",
32661
+ toolResult: chunk.toolResult,
32662
+ timestamp: now()
32663
+ };
32664
+ activityLogRef.current = [...activityLogRef.current, resultEntry];
32665
+ setActivityLog(activityLogRef.current);
32666
+ setCurrentToolCall(undefined);
32667
+ } else if (chunk.type === "error" && chunk.error) {
32668
+ setError(chunk.error);
32669
+ setIsProcessing(false);
32670
+ } else if (chunk.type === "exit") {
32671
+ registry2.closeAll();
32672
+ exit();
32673
+ } else if (chunk.type === "usage" && chunk.usage) {
32674
+ setTokenUsage(chunk.usage);
32675
+ setCurrentTurnTokens((prev) => prev + (chunk.usage?.outputTokens || 0));
32676
+ } else if (chunk.type === "done") {
32677
+ if (responseRef.current.trim()) {
32678
+ const textEntry = {
32679
+ id: generateId(),
32680
+ type: "text",
32681
+ content: responseRef.current,
32682
+ timestamp: now()
32683
+ };
32684
+ activityLogRef.current = [...activityLogRef.current, textEntry];
32685
+ }
32686
+ const fullContent = activityLogRef.current.filter((e) => e.type === "text").map((e) => e.content).join(`
32687
+ `) + (responseRef.current ? `
32688
+ ` + responseRef.current : "");
32689
+ if (fullContent.trim() || toolCallsRef.current.length > 0) {
32690
+ setMessages((prev) => [
32691
+ ...prev,
32692
+ {
32693
+ id: generateId(),
32694
+ role: "assistant",
32695
+ content: fullContent.trim(),
32696
+ timestamp: now(),
32697
+ toolCalls: toolCallsRef.current.length > 0 ? [...toolCallsRef.current] : undefined,
32698
+ toolResults: toolResultsRef.current.length > 0 ? [...toolResultsRef.current] : undefined
32699
+ }
32700
+ ]);
32701
+ }
32702
+ setCurrentResponse("");
32703
+ responseRef.current = "";
32704
+ toolCallsRef.current = [];
32705
+ toolResultsRef.current = [];
32706
+ setCurrentToolCall(undefined);
32707
+ setActivityLog([]);
32708
+ activityLogRef.current = [];
32709
+ setProcessingStartTime(undefined);
32710
+ setCurrentTurnTokens(0);
32711
+ setIsProcessing(false);
32712
+ const activeSession2 = registry2.getActiveSession();
32713
+ if (activeSession2) {
32714
+ setTokenUsage(activeSession2.client.getTokenUsage());
32715
+ }
32716
+ }
32717
+ }, [registry2, exit]);
32718
+ import_react27.useEffect(() => {
32719
+ const initSession = async () => {
32720
+ try {
32721
+ registry2.onChunk(handleChunk);
32722
+ registry2.onError((err) => {
32723
+ setError(err.message);
32724
+ setIsProcessing(false);
32725
+ });
32726
+ const session = await registry2.createSession(cwd2);
32727
+ setActiveSessionId(session.id);
32728
+ setIsInitializing(false);
32729
+ } catch (err) {
32730
+ setError(err instanceof Error ? err.message : String(err));
32731
+ setIsInitializing(false);
32732
+ }
32733
+ };
32734
+ initSession();
32735
+ return () => {
32736
+ registry2.closeAll();
32737
+ };
32738
+ }, [cwd2, registry2, handleChunk]);
32739
+ const processQueue = import_react27.useCallback(async () => {
32740
+ const activeSession2 = registryRef.current.getActiveSession();
32741
+ if (!activeSession2 || messageQueue.length === 0)
32270
32742
  return;
32271
32743
  const nextMessage = messageQueue[0];
32272
32744
  setMessageQueue((prev) => prev.slice(1));
@@ -32283,129 +32755,20 @@ function App2({ cwd: cwd2 }) {
32283
32755
  toolResultsRef.current = [];
32284
32756
  setError(null);
32285
32757
  setCurrentToolCall(undefined);
32286
- setLastToolResult(undefined);
32287
32758
  setActivityLog([]);
32759
+ activityLogRef.current = [];
32288
32760
  setProcessingStartTime(Date.now());
32289
32761
  setCurrentTurnTokens(0);
32290
32762
  setIsProcessing(true);
32291
- await clientRef.current.send(nextMessage);
32763
+ registryRef.current.setProcessing(activeSession2.id, true);
32764
+ await activeSession2.client.send(nextMessage);
32292
32765
  }, [messageQueue]);
32293
- import_react26.useEffect(() => {
32294
- const initClient = async () => {
32295
- try {
32296
- const newClient = new EmbeddedClient(cwd2);
32297
- clientRef.current = newClient;
32298
- newClient.onChunk((chunk) => {
32299
- if (chunk.type === "text" && chunk.content) {
32300
- responseRef.current += chunk.content;
32301
- setCurrentResponse(responseRef.current);
32302
- } else if (chunk.type === "tool_use" && chunk.toolCall) {
32303
- if (responseRef.current.trim()) {
32304
- setActivityLog((prev) => [
32305
- ...prev,
32306
- {
32307
- id: generateId(),
32308
- type: "text",
32309
- content: responseRef.current,
32310
- timestamp: now()
32311
- }
32312
- ]);
32313
- setCurrentResponse("");
32314
- responseRef.current = "";
32315
- }
32316
- toolCallsRef.current.push(chunk.toolCall);
32317
- setActivityLog((prev) => [
32318
- ...prev,
32319
- {
32320
- id: generateId(),
32321
- type: "tool_call",
32322
- toolCall: chunk.toolCall,
32323
- timestamp: now()
32324
- }
32325
- ]);
32326
- setCurrentToolCall(chunk.toolCall);
32327
- } else if (chunk.type === "tool_result" && chunk.toolResult) {
32328
- toolResultsRef.current.push(chunk.toolResult);
32329
- setActivityLog((prev) => [
32330
- ...prev,
32331
- {
32332
- id: generateId(),
32333
- type: "tool_result",
32334
- toolResult: chunk.toolResult,
32335
- timestamp: now()
32336
- }
32337
- ]);
32338
- setCurrentToolCall(undefined);
32339
- } else if (chunk.type === "error" && chunk.error) {
32340
- setError(chunk.error);
32341
- setIsProcessing(false);
32342
- } else if (chunk.type === "exit") {
32343
- exit();
32344
- } else if (chunk.type === "usage" && chunk.usage) {
32345
- setTokenUsage(chunk.usage);
32346
- setCurrentTurnTokens((prev) => prev + (chunk.usage?.outputTokens || 0));
32347
- } else if (chunk.type === "done") {
32348
- if (responseRef.current.trim()) {
32349
- setActivityLog((prev) => [
32350
- ...prev,
32351
- {
32352
- id: generateId(),
32353
- type: "text",
32354
- content: responseRef.current,
32355
- timestamp: now()
32356
- }
32357
- ]);
32358
- }
32359
- const fullContent = activityLog.filter((e) => e.type === "text").map((e) => e.content).join(`
32360
- `) + (responseRef.current ? `
32361
- ` + responseRef.current : "");
32362
- if (fullContent.trim() || toolCallsRef.current.length > 0) {
32363
- setMessages((prev) => [
32364
- ...prev,
32365
- {
32366
- id: generateId(),
32367
- role: "assistant",
32368
- content: fullContent.trim(),
32369
- timestamp: now(),
32370
- toolCalls: toolCallsRef.current.length > 0 ? [...toolCallsRef.current] : undefined,
32371
- toolResults: toolResultsRef.current.length > 0 ? [...toolResultsRef.current] : undefined
32372
- }
32373
- ]);
32374
- }
32375
- setCurrentResponse("");
32376
- responseRef.current = "";
32377
- toolCallsRef.current = [];
32378
- toolResultsRef.current = [];
32379
- setCurrentToolCall(undefined);
32380
- setActivityLog([]);
32381
- setProcessingStartTime(undefined);
32382
- setCurrentTurnTokens(0);
32383
- setIsProcessing(false);
32384
- if (newClient) {
32385
- setTokenUsage(newClient.getTokenUsage());
32386
- }
32387
- }
32388
- });
32389
- newClient.onError((err) => {
32390
- setError(err.message);
32391
- setIsProcessing(false);
32392
- });
32393
- await newClient.initialize();
32394
- setClient(newClient);
32395
- setIsInitializing(false);
32396
- } catch (err) {
32397
- setError(err instanceof Error ? err.message : String(err));
32398
- setIsInitializing(false);
32399
- }
32400
- };
32401
- initClient();
32402
- }, [cwd2]);
32403
- import_react26.useEffect(() => {
32766
+ import_react27.useEffect(() => {
32404
32767
  if (!isProcessing && messageQueue.length > 0) {
32405
32768
  processQueue();
32406
32769
  }
32407
32770
  }, [isProcessing, messageQueue.length, processQueue]);
32408
- import_react26.useEffect(() => {
32771
+ import_react27.useEffect(() => {
32409
32772
  if (autoScroll) {
32410
32773
  setScrollOffset(0);
32411
32774
  }
@@ -32413,10 +32776,48 @@ function App2({ cwd: cwd2 }) {
32413
32776
  const baseMaxVisible = 10;
32414
32777
  const toolCallsHeight = isProcessing ? Math.min(toolCallsRef.current.length, 5) : 0;
32415
32778
  const maxVisibleMessages = Math.max(3, baseMaxVisible - toolCallsHeight);
32779
+ const sessions = registry2.listSessions();
32780
+ const activeSession = registry2.getActiveSession();
32781
+ const sessionIndex = activeSessionId ? registry2.getSessionIndex(activeSessionId) : 0;
32782
+ const sessionCount = registry2.getSessionCount();
32783
+ const backgroundProcessingCount = registry2.getBackgroundProcessingSessions().length;
32784
+ const handleSessionSwitch = import_react27.useCallback(async (sessionId) => {
32785
+ if (sessionId === activeSessionId) {
32786
+ setShowSessionSelector(false);
32787
+ return;
32788
+ }
32789
+ saveCurrentSessionState();
32790
+ await registry2.switchSession(sessionId);
32791
+ setActiveSessionId(sessionId);
32792
+ loadSessionState(sessionId);
32793
+ const session = registry2.getSession(sessionId);
32794
+ if (session) {
32795
+ setIsProcessing(session.isProcessing);
32796
+ }
32797
+ setShowSessionSelector(false);
32798
+ }, [activeSessionId, registry2, saveCurrentSessionState, loadSessionState]);
32799
+ const handleNewSession = import_react27.useCallback(async () => {
32800
+ saveCurrentSessionState();
32801
+ const newSession = await registry2.createSession(cwd2);
32802
+ await registry2.switchSession(newSession.id);
32803
+ setActiveSessionId(newSession.id);
32804
+ loadSessionState(newSession.id);
32805
+ setIsProcessing(false);
32806
+ setShowSessionSelector(false);
32807
+ }, [cwd2, registry2, saveCurrentSessionState, loadSessionState]);
32416
32808
  use_input_default((input, key) => {
32809
+ if (showSessionSelector) {
32810
+ return;
32811
+ }
32812
+ if (key.ctrl && input === "s") {
32813
+ if (sessions.length > 0) {
32814
+ setShowSessionSelector(true);
32815
+ }
32816
+ return;
32817
+ }
32417
32818
  if (key.ctrl && input === "c") {
32418
- if (isProcessing && client) {
32419
- client.stop();
32819
+ if (isProcessing && activeSession) {
32820
+ activeSession.client.stop();
32420
32821
  if (responseRef.current) {
32421
32822
  setMessages((prev) => [
32422
32823
  ...prev,
@@ -32434,27 +32835,30 @@ function App2({ cwd: cwd2 }) {
32434
32835
  }
32435
32836
  setIsProcessing(false);
32436
32837
  } else {
32838
+ registry2.closeAll();
32437
32839
  exit();
32438
32840
  }
32439
32841
  }
32440
- if (key.escape && isProcessing && client) {
32441
- client.stop();
32442
- if (responseRef.current) {
32443
- setMessages((prev) => [
32444
- ...prev,
32445
- {
32446
- id: generateId(),
32447
- role: "assistant",
32448
- content: responseRef.current + `
32842
+ if (key.escape) {
32843
+ if (isProcessing && activeSession) {
32844
+ activeSession.client.stop();
32845
+ if (responseRef.current) {
32846
+ setMessages((prev) => [
32847
+ ...prev,
32848
+ {
32849
+ id: generateId(),
32850
+ role: "assistant",
32851
+ content: responseRef.current + `
32449
32852
 
32450
32853
  [stopped]`,
32451
- timestamp: now()
32452
- }
32453
- ]);
32454
- setCurrentResponse("");
32455
- responseRef.current = "";
32854
+ timestamp: now()
32855
+ }
32856
+ ]);
32857
+ setCurrentResponse("");
32858
+ responseRef.current = "";
32859
+ }
32860
+ setIsProcessing(false);
32456
32861
  }
32457
- setIsProcessing(false);
32458
32862
  }
32459
32863
  if (key.pageUp || key.shift && key.upArrow) {
32460
32864
  setScrollOffset((prev) => {
@@ -32483,16 +32887,30 @@ function App2({ cwd: cwd2 }) {
32483
32887
  setAutoScroll(true);
32484
32888
  }
32485
32889
  });
32486
- const handleSubmit = import_react26.useCallback(async (input, mode = "normal") => {
32487
- if (!client || !input.trim())
32890
+ const handleSubmit = import_react27.useCallback(async (input, mode = "normal") => {
32891
+ if (!activeSession || !input.trim())
32488
32892
  return;
32489
32893
  const trimmedInput = input.trim();
32894
+ if (trimmedInput.startsWith("/session")) {
32895
+ const arg = trimmedInput.slice(8).trim();
32896
+ if (arg === "new") {
32897
+ await handleNewSession();
32898
+ return;
32899
+ }
32900
+ const num = parseInt(arg, 10);
32901
+ if (!isNaN(num) && num > 0 && num <= sessions.length) {
32902
+ await handleSessionSwitch(sessions[num - 1].id);
32903
+ return;
32904
+ }
32905
+ setShowSessionSelector(true);
32906
+ return;
32907
+ }
32490
32908
  if (mode === "queue" || isProcessing && mode === "normal") {
32491
32909
  setMessageQueue((prev) => [...prev, trimmedInput]);
32492
32910
  return;
32493
32911
  }
32494
32912
  if (mode === "interrupt" && isProcessing) {
32495
- client.stop();
32913
+ activeSession.client.stop();
32496
32914
  if (responseRef.current) {
32497
32915
  setMessages((prev) => [
32498
32916
  ...prev,
@@ -32524,39 +32942,65 @@ function App2({ cwd: cwd2 }) {
32524
32942
  toolResultsRef.current = [];
32525
32943
  setError(null);
32526
32944
  setCurrentToolCall(undefined);
32527
- setLastToolResult(undefined);
32528
32945
  setActivityLog([]);
32946
+ activityLogRef.current = [];
32529
32947
  setProcessingStartTime(Date.now());
32530
32948
  setCurrentTurnTokens(0);
32531
32949
  setIsProcessing(true);
32532
- await client.send(trimmedInput);
32533
- }, [client, isProcessing]);
32950
+ registry2.setProcessing(activeSession.id, true);
32951
+ await activeSession.client.send(trimmedInput);
32952
+ }, [activeSession, isProcessing, registry2, sessions, handleNewSession, handleSessionSwitch]);
32534
32953
  if (isInitializing) {
32535
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32954
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32536
32955
  flexDirection: "column",
32537
32956
  padding: 1,
32538
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Spinner2, {
32957
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Spinner2, {
32539
32958
  label: "Initializing..."
32540
32959
  }, undefined, false, undefined, this)
32541
32960
  }, undefined, false, undefined, this);
32542
32961
  }
32962
+ if (showSessionSelector) {
32963
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32964
+ flexDirection: "column",
32965
+ padding: 1,
32966
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(SessionSelector, {
32967
+ sessions,
32968
+ activeSessionId,
32969
+ onSelect: handleSessionSwitch,
32970
+ onNew: handleNewSession,
32971
+ onCancel: () => setShowSessionSelector(false)
32972
+ }, undefined, false, undefined, this)
32973
+ }, undefined, false, undefined, this);
32974
+ }
32543
32975
  const toolCallEntries = activityLog.filter((e) => e.type === "tool_call" && e.toolCall).map((e) => {
32544
32976
  const result = activityLog.find((r) => r.type === "tool_result" && r.toolResult?.toolCallId === e.toolCall?.id)?.toolResult;
32545
32977
  return { toolCall: e.toolCall, result };
32546
32978
  });
32547
32979
  const isThinking = isProcessing && !currentResponse && !currentToolCall && toolCallEntries.length === 0;
32548
32980
  const showWelcome = messages.length === 0 && !isProcessing;
32549
- return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32981
+ return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32550
32982
  flexDirection: "column",
32551
32983
  padding: 1,
32552
32984
  children: [
32553
- showWelcome && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(WelcomeBanner, {
32985
+ showWelcome && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(WelcomeBanner, {
32554
32986
  version: "0.4.1",
32555
32987
  model: "claude-sonnet-4",
32556
- directory: cwd2
32988
+ directory: activeSession?.cwd || cwd2
32557
32989
  }, undefined, false, undefined, this),
32558
- scrollOffset > 0 && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
32559
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
32990
+ backgroundProcessingCount > 0 && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32991
+ marginBottom: 1,
32992
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
32993
+ color: "yellow",
32994
+ children: [
32995
+ backgroundProcessingCount,
32996
+ " session",
32997
+ backgroundProcessingCount > 1 ? "s" : "",
32998
+ " processing in background (Ctrl+S to switch)"
32999
+ ]
33000
+ }, undefined, true, undefined, this)
33001
+ }, undefined, false, undefined, this),
33002
+ scrollOffset > 0 && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
33003
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
32560
33004
  dimColor: true,
32561
33005
  children: [
32562
33006
  "\u2191 ",
@@ -32565,7 +33009,7 @@ function App2({ cwd: cwd2 }) {
32565
33009
  ]
32566
33010
  }, undefined, true, undefined, this)
32567
33011
  }, undefined, false, undefined, this),
32568
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Messages4, {
33012
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Messages4, {
32569
33013
  messages,
32570
33014
  currentResponse: isProcessing ? currentResponse : undefined,
32571
33015
  currentToolCall: undefined,
@@ -32574,9 +33018,9 @@ function App2({ cwd: cwd2 }) {
32574
33018
  scrollOffset,
32575
33019
  maxVisible: maxVisibleMessages
32576
33020
  }, undefined, false, undefined, this),
32577
- isProcessing && toolCallEntries.length > 0 && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
33021
+ isProcessing && toolCallEntries.length > 0 && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32578
33022
  marginY: 1,
32579
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
33023
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
32580
33024
  dimColor: true,
32581
33025
  children: [
32582
33026
  "\u2699 ",
@@ -32589,9 +33033,9 @@ function App2({ cwd: cwd2 }) {
32589
33033
  ]
32590
33034
  }, undefined, true, undefined, this)
32591
33035
  }, undefined, false, undefined, this),
32592
- messageQueue.length > 0 && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
33036
+ messageQueue.length > 0 && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32593
33037
  marginY: 1,
32594
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
33038
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
32595
33039
  dimColor: true,
32596
33040
  children: [
32597
33041
  messageQueue.length,
@@ -32601,9 +33045,9 @@ function App2({ cwd: cwd2 }) {
32601
33045
  ]
32602
33046
  }, undefined, true, undefined, this)
32603
33047
  }, undefined, false, undefined, this),
32604
- error && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
33048
+ error && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
32605
33049
  marginY: 1,
32606
- children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
33050
+ children: /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
32607
33051
  color: "red",
32608
33052
  children: [
32609
33053
  "Error: ",
@@ -32611,29 +33055,32 @@ function App2({ cwd: cwd2 }) {
32611
33055
  ]
32612
33056
  }, undefined, true, undefined, this)
32613
33057
  }, undefined, false, undefined, this),
32614
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(ProcessingIndicator, {
33058
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(ProcessingIndicator, {
32615
33059
  isProcessing,
32616
33060
  startTime: processingStartTime,
32617
33061
  tokenCount: currentTurnTokens,
32618
33062
  isThinking
32619
33063
  }, undefined, false, undefined, this),
32620
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Input, {
33064
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Input, {
32621
33065
  onSubmit: handleSubmit,
32622
33066
  isProcessing,
32623
33067
  queueLength: messageQueue.length
32624
33068
  }, undefined, false, undefined, this),
32625
- /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Status, {
33069
+ /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Status, {
32626
33070
  isProcessing,
32627
- cwd: cwd2,
33071
+ cwd: activeSession?.cwd || cwd2,
32628
33072
  queueLength: messageQueue.length,
32629
- tokenUsage
33073
+ tokenUsage,
33074
+ sessionIndex,
33075
+ sessionCount,
33076
+ backgroundProcessingCount
32630
33077
  }, undefined, false, undefined, this)
32631
33078
  ]
32632
33079
  }, undefined, true, undefined, this);
32633
33080
  }
32634
33081
 
32635
33082
  // packages/terminal/src/index.tsx
32636
- var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
33083
+ var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
32637
33084
  var args = process.argv.slice(2);
32638
33085
  var options = {
32639
33086
  cwd: process.cwd(),
@@ -32665,11 +33112,11 @@ In interactive mode:
32665
33112
  `);
32666
33113
  process.exit(0);
32667
33114
  }
32668
- var { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(App2, {
33115
+ var { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime10.jsxDEV(App2, {
32669
33116
  cwd: options.cwd
32670
33117
  }, undefined, false, undefined, this));
32671
33118
  waitUntilExit().then(() => {
32672
33119
  process.exit(0);
32673
33120
  });
32674
33121
 
32675
- //# debugId=165DC707B2BFFF8564756E2164756E21
33122
+ //# debugId=E37514F436BDCDD964756E2164756E21