@robota-sdk/agent-cli 3.0.0-beta.60 → 3.0.0-beta.62

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.
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
33
  ChildProcessSubagentRunner: () => ChildProcessSubagentRunner,
34
34
  GitWorktreeIsolationAdapter: () => GitWorktreeIsolationAdapter,
35
35
  createChildProcessSubagentRunnerFactory: () => createChildProcessSubagentRunnerFactory,
@@ -37,7 +37,7 @@ __export(index_exports, {
37
37
  createManagedShellProcessRunner: () => createManagedShellProcessRunner,
38
38
  startCli: () => startCli
39
39
  });
40
- module.exports = __toCommonJS(index_exports);
40
+ module.exports = __toCommonJS(src_exports);
41
41
 
42
42
  // src/cli.ts
43
43
  var import_node_fs8 = require("fs");
@@ -52,7 +52,6 @@ var import_agent_command_exit = require("@robota-sdk/agent-command-exit");
52
52
  var import_agent_command_help = require("@robota-sdk/agent-command-help");
53
53
  var import_agent_command_language = require("@robota-sdk/agent-command-language");
54
54
  var import_agent_command_memory = require("@robota-sdk/agent-command-memory");
55
- var import_agent_command_mode = require("@robota-sdk/agent-command-mode");
56
55
  var import_agent_command_model = require("@robota-sdk/agent-command-model");
57
56
  var import_agent_command_permissions = require("@robota-sdk/agent-command-permissions");
58
57
  var import_agent_command_plugin = require("@robota-sdk/agent-command-plugin");
@@ -60,8 +59,9 @@ var import_agent_command_reset = require("@robota-sdk/agent-command-reset");
60
59
  var import_agent_command_rewind = require("@robota-sdk/agent-command-rewind");
61
60
  var import_agent_command_statusline = require("@robota-sdk/agent-command-statusline");
62
61
  var import_agent_command_session = require("@robota-sdk/agent-command-session");
63
- var import_agent_sdk14 = require("@robota-sdk/agent-sdk");
64
- var import_agent_sessions = require("@robota-sdk/agent-sessions");
62
+ var import_agent_command_skills = require("@robota-sdk/agent-command-skills");
63
+ var import_agent_command_user_local2 = require("@robota-sdk/agent-command-user-local");
64
+ var import_agent_sdk15 = require("@robota-sdk/agent-sdk");
65
65
 
66
66
  // src/utils/cli-args.ts
67
67
  var import_node_util = require("util");
@@ -99,8 +99,12 @@ function parseCliArgs() {
99
99
  "fork-session": { type: "boolean", default: false },
100
100
  name: { type: "string", short: "n" },
101
101
  "output-format": { type: "string" },
102
+ format: { type: "string" },
103
+ summary: { type: "string" },
104
+ source: { type: "string" },
102
105
  "system-prompt": { type: "string" },
103
106
  "append-system-prompt": { type: "string" },
107
+ "task-file": { type: "string" },
104
108
  version: { type: "boolean", default: false },
105
109
  reset: { type: "boolean", default: false },
106
110
  bare: { type: "boolean", default: false },
@@ -117,7 +121,10 @@ function parseCliArgs() {
117
121
  "set-current": { type: "boolean", default: false },
118
122
  "settings-scope": { type: "string" },
119
123
  "check-update": { type: "boolean", default: false },
120
- "disable-update-check": { type: "boolean", default: false }
124
+ "disable-update-check": { type: "boolean", default: false },
125
+ web: { type: "boolean", default: false },
126
+ "web-port": { type: "string" },
127
+ "no-open": { type: "boolean", default: false }
121
128
  }
122
129
  });
123
130
  return {
@@ -132,8 +139,12 @@ function parseCliArgs() {
132
139
  forkSession: values["fork-session"] ?? false,
133
140
  sessionName: values["name"],
134
141
  outputFormat: values["output-format"],
142
+ format: values["format"],
143
+ summary: values["summary"],
144
+ source: values["source"],
135
145
  systemPrompt: values["system-prompt"],
136
146
  appendSystemPrompt: values["append-system-prompt"],
147
+ taskFile: values["task-file"],
137
148
  version: values["version"] ?? false,
138
149
  reset: values["reset"] ?? false,
139
150
  bare: values["bare"] ?? false,
@@ -150,9 +161,23 @@ function parseCliArgs() {
150
161
  setCurrent: values["set-current"] ?? false,
151
162
  settingsScope: values["settings-scope"],
152
163
  checkUpdate: values["check-update"] ?? false,
153
- disableUpdateCheck: values["disable-update-check"] ?? false
164
+ disableUpdateCheck: values["disable-update-check"] ?? false,
165
+ web: values["web"] ?? false,
166
+ webPort: parseWebPort(values["web-port"]),
167
+ noOpen: values["no-open"] ?? false
154
168
  };
155
169
  }
170
+ var DEFAULT_WEB_PORT = 7070;
171
+ function parseWebPort(raw) {
172
+ if (raw === void 0) return DEFAULT_WEB_PORT;
173
+ const n = parseInt(raw, 10);
174
+ if (isNaN(n) || n < 1 || n > 65535) {
175
+ process.stderr.write(`Invalid --web-port "${raw}". Must be 1\u201365535.
176
+ `);
177
+ process.exit(1);
178
+ }
179
+ return n;
180
+ }
156
181
 
157
182
  // src/utils/settings-io.ts
158
183
  var import_node_fs = require("fs");
@@ -190,6 +215,7 @@ var import_node_path2 = require("path");
190
215
 
191
216
  // src/utils/provider-default-definitions.ts
192
217
  var import_agent_provider_anthropic = require("@robota-sdk/agent-provider-anthropic");
218
+ var import_agent_provider_deepseek = require("@robota-sdk/agent-provider-deepseek");
193
219
  var import_agent_provider_gemma = require("@robota-sdk/agent-provider-gemma");
194
220
  var import_agent_provider_gemini = require("@robota-sdk/agent-provider-gemini");
195
221
  var import_agent_provider_openai = require("@robota-sdk/agent-provider-openai");
@@ -199,7 +225,8 @@ var DEFAULT_PROVIDER_DEFINITIONS = [
199
225
  (0, import_agent_provider_openai.createOpenAIProviderDefinition)(),
200
226
  (0, import_agent_provider_gemini.createGeminiProviderDefinition)(),
201
227
  (0, import_agent_provider_gemma.createGemmaProviderDefinition)(),
202
- (0, import_agent_provider_qwen.createQwenProviderDefinition)()
228
+ (0, import_agent_provider_qwen.createQwenProviderDefinition)(),
229
+ (0, import_agent_provider_deepseek.createDeepSeekProviderDefinition)()
203
230
  ];
204
231
 
205
232
  // src/utils/provider-definition.ts
@@ -335,8 +362,11 @@ function createProviderFromConfig(settings, providerDefinitions) {
335
362
  `Unknown provider: ${settings.name}. Currently supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`
336
363
  );
337
364
  }
338
- if (definition.requiresApiKey === true && !settings.apiKey) {
339
- throw new Error(`Provider ${settings.name} requires apiKey`);
365
+ const credentialRequirement = (0, import_agent_core.getProviderCredentialRequirement)(definition);
366
+ if (credentialRequirement !== void 0 && !hasRequiredProviderCredential(settings, credentialRequirement)) {
367
+ throw new Error(
368
+ `Provider ${settings.name} requires ${formatCredentialRequirement(credentialRequirement)}`
369
+ );
340
370
  }
341
371
  return definition.createProvider(settings);
342
372
  }
@@ -346,6 +376,16 @@ function createProviderFromSettings(cwd, modelOverride, options = {}) {
346
376
  const model = modelOverride ?? settings.model;
347
377
  return createProviderFromConfig({ ...settings, model }, providerDefinitions);
348
378
  }
379
+ function hasRequiredProviderCredential(settings, requirement) {
380
+ return requirement.anyOf.some((field) => hasProviderCredentialValue(settings, field));
381
+ }
382
+ function hasProviderCredentialValue(settings, field) {
383
+ const value = settings[field];
384
+ return value !== void 0 && value.length > 0;
385
+ }
386
+ function formatCredentialRequirement(requirement) {
387
+ return requirement.anyOf.join(" or ");
388
+ }
349
389
  function getProviderDefinitions(options) {
350
390
  return options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
351
391
  }
@@ -372,23 +412,40 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
372
412
  if (!profile) {
373
413
  return false;
374
414
  }
375
- if ((0, import_agent_sdk.hasUsableSecretReference)(profile.apiKey)) {
376
- return true;
377
- }
378
415
  if (!type) {
379
- return false;
416
+ return (0, import_agent_sdk.hasUsableSecretReference)(profile.apiKey);
380
417
  }
381
418
  const definition = (0, import_agent_core.findProviderDefinition)(providerDefinitions, type);
382
419
  if (definition === void 0) {
383
420
  return false;
384
421
  }
385
- return definition.requiresApiKey !== true || (0, import_agent_sdk.hasUsableSecretReference)(definition.defaults?.apiKey);
422
+ const credentialRequirement = (0, import_agent_core.getProviderCredentialRequirement)(definition);
423
+ if (credentialRequirement === void 0) {
424
+ return true;
425
+ }
426
+ return hasUsableRequiredProviderCredential(profile, definition, credentialRequirement);
427
+ }
428
+ function hasUsableRequiredProviderCredential(profile, definition, requirement) {
429
+ return requirement.anyOf.some(
430
+ (field) => (0, import_agent_sdk.hasUsableSecretReference)(resolveProviderCredentialValue(field, profile, definition))
431
+ );
432
+ }
433
+ function resolveProviderCredentialValue(field, profile, definition) {
434
+ return profile[field] ?? definition.defaults?.[field];
386
435
  }
387
436
 
388
437
  // src/utils/provider-settings.ts
389
438
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
390
439
 
391
440
  // src/utils/provider-configuration.ts
441
+ function resolveProviderSettingsWriteTargetPath(cwd, options = {}) {
442
+ const settingsPaths = options.settingsPaths ?? getProviderSettingsPaths(cwd);
443
+ const targetPath = findLastPathWithCurrentProvider(settingsPaths) ?? settingsPaths[0];
444
+ if (targetPath === void 0) {
445
+ throw new Error("No settings path available for provider update");
446
+ }
447
+ return targetPath;
448
+ }
392
449
  function readProviderDocument(settingsPath) {
393
450
  return readSettings(settingsPath);
394
451
  }
@@ -471,6 +528,15 @@ function findLastPathWithLegacyProvider(settingsPaths) {
471
528
  }
472
529
  return void 0;
473
530
  }
531
+ function findLastPathWithCurrentProvider(settingsPaths) {
532
+ for (let index = settingsPaths.length - 1; index >= 0; index -= 1) {
533
+ const settingsPath = settingsPaths[index];
534
+ if (settingsPath === void 0) continue;
535
+ const settings = readProviderDocument(settingsPath);
536
+ if (settings.currentProvider !== void 0) return settingsPath;
537
+ }
538
+ return void 0;
539
+ }
474
540
 
475
541
  // src/utils/provider-setup-flow.ts
476
542
  var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
@@ -496,7 +562,8 @@ function handleProviderConfigurationArgs(cwd, args, providerDefinitions = DEFAUL
496
562
  return !args.printMode && args.positional.length === 0;
497
563
  }
498
564
  if (args.provider && args.setCurrent) {
499
- applyProviderSwitch(settingsPath, args.provider, {
565
+ const switchSettingsPath = args.settingsScope === void 0 ? resolveProviderSettingsWriteTargetPath(cwd) : settingsPath;
566
+ applyProviderSwitch(switchSettingsPath, args.provider, {
500
567
  knownProviders: readMergedProviderSettings(cwd).providers
501
568
  });
502
569
  process.stdout.write(`Current provider set to ${args.provider}
@@ -530,7 +597,9 @@ async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefi
530
597
  const providerChoice = await promptInput2((0, import_agent_sdk3.formatProviderSetupSelectionPrompt)(providerDefinitions));
531
598
  const type = (0, import_agent_sdk3.resolveProviderSetupSelection)(providerChoice, providerDefinitions);
532
599
  const settingsPath = getSettingsPathForScope(cwd, args.settingsScope);
533
- const input = await (0, import_agent_sdk3.runProviderSetupPromptFlow)(type, promptInput2, providerDefinitions);
600
+ const input = await (0, import_agent_sdk3.runProviderSetupPromptFlow)(type, promptInput2, providerDefinitions, {
601
+ existingProfileNames: Object.keys(readMergedProviderSettings(cwd).providers ?? {})
602
+ });
534
603
  applyProviderConfiguration(settingsPath, input, {
535
604
  providerDefinitions
536
605
  });
@@ -613,66 +682,60 @@ function formatConfigureProviderExample(definition) {
613
682
  var import_agent_transport_headless = require("@robota-sdk/agent-transport-headless");
614
683
 
615
684
  // src/ui/render.tsx
616
- var import_ink22 = require("ink");
685
+ var import_ink24 = require("ink");
617
686
 
618
687
  // src/ui/App.tsx
619
- var import_react19 = require("react");
620
- var import_ink21 = require("ink");
688
+ var import_react20 = require("react");
689
+ var import_ink23 = require("ink");
690
+ var import_agent_sdk9 = require("@robota-sdk/agent-sdk");
621
691
  var import_agent_core11 = require("@robota-sdk/agent-core");
622
692
 
623
693
  // src/ui/hooks/useInteractiveSession.ts
624
694
  var import_react2 = require("react");
695
+ var import_open = __toESM(require("open"), 1);
625
696
  var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
626
- var import_agent_core3 = require("@robota-sdk/agent-core");
627
697
 
628
- // src/ui/background-task-view-model.ts
629
- var BACKGROUND_PREVIEW_LENGTH = 120;
630
- var BACKGROUND_PREVIEW_WHITESPACE = /\s+/g;
631
- var BACKGROUND_PREVIEW_SEPARATOR = " ";
632
- var SUCCESS_EXIT_CODE = 0;
633
- function toBackgroundTaskViewModel(state, partialText) {
634
- return {
635
- id: state.id,
636
- kind: state.kind,
637
- label: state.label,
638
- status: state.status,
639
- statusLabel: getBackgroundTaskStatusLabel(state),
640
- mode: state.mode,
641
- currentAction: state.currentAction,
642
- unread: state.unread,
643
- preview: trimBackgroundPreview(state.promptPreview ?? state.commandPreview) ?? "",
644
- resultPreview: trimBackgroundPreview(state.result?.output ?? partialText),
645
- errorPreview: trimBackgroundPreview(state.error?.message),
646
- startedAt: state.startedAt,
647
- lastActivityAt: state.lastActivityAt,
648
- timeoutReason: state.timeoutReason,
649
- exitCode: state.result?.exitCode,
650
- signalCode: state.result?.signalCode,
651
- worktreePath: state.worktreePath,
652
- branchName: state.branchName,
653
- worktreeStatus: state.worktreeStatus,
654
- worktreeNextAction: state.worktreeNextAction
655
- };
656
- }
657
- function getBackgroundTaskStatusLabel(state) {
658
- if (state.status === "failed" && state.timeoutReason) {
659
- if (state.timeoutReason === "idle" || state.timeoutReason === "max_runtime") {
660
- return "timed out";
661
- }
662
- return state.timeoutReason.replace(/_/g, " ");
663
- }
664
- return state.status;
665
- }
666
- function shouldHideAtNextUserTurn(state) {
667
- return state.status === "completed" && !state.error && (state.result?.exitCode === void 0 || state.result.exitCode === SUCCESS_EXIT_CODE) && !state.result?.signalCode && !state.worktreePath && !state.branchName;
668
- }
669
- function trimBackgroundPreview(value) {
670
- if (!value) return void 0;
671
- const preview = value.trim().replace(BACKGROUND_PREVIEW_WHITESPACE, BACKGROUND_PREVIEW_SEPARATOR);
672
- if (!preview) return void 0;
673
- return preview.length > BACKGROUND_PREVIEW_LENGTH ? `${preview.slice(0, BACKGROUND_PREVIEW_LENGTH)}...` : preview;
698
+ // src/web-sidecar/web-sidecar-server.ts
699
+ var import_node_http = require("http");
700
+ var import_ws = require("ws");
701
+ var import_agent_transport_ws = require("@robota-sdk/agent-transport-ws");
702
+ function startWebSidecarServer(session, port) {
703
+ return new Promise((resolve3, reject) => {
704
+ const httpServer = (0, import_node_http.createServer)((_req, res) => {
705
+ res.writeHead(200, { "Content-Type": "text/plain" });
706
+ res.end("Robota web monitor sidecar");
707
+ });
708
+ const wss = new import_ws.WebSocketServer({ server: httpServer });
709
+ wss.on("connection", (ws) => {
710
+ const send = (message) => {
711
+ if (ws.readyState === import_ws.WebSocket.OPEN) {
712
+ ws.send(JSON.stringify(message));
713
+ }
714
+ };
715
+ const { onMessage, cleanup } = (0, import_agent_transport_ws.createWsHandler)({ session, send });
716
+ ws.on("message", (data) => onMessage(String(data)));
717
+ ws.on("close", cleanup);
718
+ ws.on("error", cleanup);
719
+ const messages = session.getMessages();
720
+ send({ type: "messages", messages });
721
+ });
722
+ httpServer.on("error", reject);
723
+ httpServer.listen(port, "127.0.0.1", () => {
724
+ resolve3({
725
+ port,
726
+ stop: () => new Promise((res) => {
727
+ wss.close(() => {
728
+ httpServer.close(() => res());
729
+ });
730
+ })
731
+ });
732
+ });
733
+ });
674
734
  }
675
735
 
736
+ // src/ui/hooks/useInteractiveSession.ts
737
+ var import_agent_core3 = require("@robota-sdk/agent-core");
738
+
676
739
  // src/ui/tui-state-manager.ts
677
740
  var MAX_RENDERED_MESSAGES = 100;
678
741
  var STREAMING_DEBOUNCE_MS = 300;
@@ -704,13 +767,12 @@ var TuiStateManager = class {
704
767
  isAborting = false;
705
768
  pendingPrompt = null;
706
769
  contextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };
707
- backgroundTasks = [];
770
+ executionWorkspaceSnapshot = null;
771
+ selectedExecutionEntryId;
708
772
  /** Called after any state change. React hook sets this to trigger re-render. */
709
773
  onChange = null;
710
774
  // ── Internal ──────────────────────────────────────────────────
711
775
  streamBuf = "";
712
- backgroundTextBuffers = /* @__PURE__ */ new Map();
713
- backgroundTasksHiddenOnNextTurn = /* @__PURE__ */ new Set();
714
776
  debouncedStreamNotify = createDebouncedNotify(() => this.notify(), STREAMING_DEBOUNCE_MS);
715
777
  notify() {
716
778
  this.onChange?.();
@@ -779,30 +841,6 @@ var TuiStateManager = class {
779
841
  maxTokens: state.maxTokens
780
842
  });
781
843
  };
782
- onBackgroundTaskEvent = (event) => {
783
- if ("task" in event) {
784
- this.upsertBackgroundTask(event.task);
785
- return;
786
- }
787
- if (event.type === "background_task_closed") {
788
- this.backgroundTextBuffers.delete(event.taskId);
789
- this.backgroundTasksHiddenOnNextTurn.delete(event.taskId);
790
- this.backgroundTasks = this.backgroundTasks.filter((task) => task.id !== event.taskId);
791
- this.notify();
792
- return;
793
- }
794
- if (event.type === "background_task_text_delta") {
795
- this.appendBackgroundTaskText(event.taskId, event.delta);
796
- return;
797
- }
798
- if (event.type === "background_task_tool_start") {
799
- this.updateBackgroundTaskAction(event.taskId, event.firstArg ?? event.toolName);
800
- return;
801
- }
802
- if (event.type === "background_task_tool_end") {
803
- this.updateBackgroundTaskAction(event.taskId, event.success ? void 0 : event.error);
804
- }
805
- };
806
844
  // ── State updates from external sources ───────────────────────
807
845
  /** Sync history from InteractiveSession */
808
846
  syncHistory(entries) {
@@ -839,56 +877,30 @@ var TuiStateManager = class {
839
877
  this.contextState = state;
840
878
  this.notify();
841
879
  }
842
- onUserTurnAccepted() {
843
- if (this.backgroundTasksHiddenOnNextTurn.size === 0) return;
844
- const visible = this.backgroundTasks.filter(
845
- (task) => !this.backgroundTasksHiddenOnNextTurn.has(task.id)
846
- );
847
- this.backgroundTasksHiddenOnNextTurn.clear();
848
- if (visible.length === this.backgroundTasks.length) return;
849
- this.backgroundTasks = visible;
850
- this.notify();
851
- }
852
- upsertBackgroundTask(state) {
853
- const partialText = state.result ? void 0 : this.backgroundTextBuffers.get(state.id);
854
- const viewModel = toBackgroundTaskViewModel(state, partialText);
855
- const index = this.backgroundTasks.findIndex((task) => task.id === state.id);
856
- if (index === -1) {
857
- this.backgroundTasks = [...this.backgroundTasks, viewModel];
858
- } else {
859
- const updated = [...this.backgroundTasks];
860
- updated[index] = viewModel;
861
- this.backgroundTasks = updated;
862
- }
863
- if (state.status === "completed" || state.status === "failed" || state.status === "cancelled") {
864
- this.backgroundTextBuffers.delete(state.id);
865
- }
866
- if (shouldHideAtNextUserTurn(state)) {
867
- this.backgroundTasksHiddenOnNextTurn.add(state.id);
868
- } else {
869
- this.backgroundTasksHiddenOnNextTurn.delete(state.id);
870
- }
871
- this.notify();
872
- }
873
- appendBackgroundTaskText(taskId, delta) {
874
- const nextText = `${this.backgroundTextBuffers.get(taskId) ?? ""}${delta}`;
875
- this.backgroundTextBuffers.set(taskId, nextText);
876
- this.backgroundTasks = this.backgroundTasks.map(
877
- (task) => task.id === taskId ? { ...task, resultPreview: trimBackgroundPreview(nextText) } : task
878
- );
880
+ syncExecutionWorkspaceSnapshot(snapshot) {
881
+ const currentSelection = this.selectedExecutionEntryId;
882
+ const hasCurrentSelection = currentSelection !== void 0 && snapshot.entries.some((entry) => entry.id === currentSelection);
883
+ const selectedExecutionEntryId = hasCurrentSelection ? currentSelection : snapshot.selectedEntryId ?? snapshot.entries[0]?.id;
884
+ this.executionWorkspaceSnapshot = {
885
+ ...snapshot,
886
+ ...selectedExecutionEntryId ? { selectedEntryId: selectedExecutionEntryId } : {}
887
+ };
888
+ this.selectedExecutionEntryId = selectedExecutionEntryId;
879
889
  this.notify();
880
890
  }
881
- updateBackgroundTaskAction(taskId, currentAction) {
882
- this.backgroundTasks = this.backgroundTasks.map(
883
- (task) => task.id === taskId ? { ...task, currentAction } : task
884
- );
891
+ selectExecutionWorkspaceEntry(entryId) {
892
+ if (!this.executionWorkspaceSnapshot?.entries.some((entry) => entry.id === entryId)) return;
893
+ this.selectedExecutionEntryId = entryId;
894
+ this.executionWorkspaceSnapshot = {
895
+ ...this.executionWorkspaceSnapshot,
896
+ selectedEntryId: entryId
897
+ };
885
898
  this.notify();
886
899
  }
887
900
  };
888
901
 
889
902
  // src/ui/hooks/useSlashRouting.ts
890
903
  var import_react = require("react");
891
- var import_node_crypto = require("crypto");
892
904
  var import_agent_core2 = require("@robota-sdk/agent-core");
893
905
 
894
906
  // src/plugins/plugin-command-source-loader.ts
@@ -917,10 +929,9 @@ function reloadPluginCommandSource(registry) {
917
929
  }
918
930
 
919
931
  // src/ui/hooks/useSlashRouting.ts
920
- function useSlashRouting(interactiveSession, registry, manager) {
932
+ function useSlashRouting(interactiveSession, registry, manager, commandEffectQueue) {
921
933
  return (0, import_react.useCallback)(
922
934
  async (input) => {
923
- manager.onUserTurnAccepted();
924
935
  if (!input.startsWith("/")) {
925
936
  await interactiveSession.submit(input);
926
937
  manager.setPendingPrompt(interactiveSession.getPendingPrompt());
@@ -931,10 +942,11 @@ function useSlashRouting(interactiveSession, registry, manager) {
931
942
  const args = parts.slice(1).join(" ");
932
943
  const result = await interactiveSession.executeCommand(cmd, args);
933
944
  if (result) {
934
- applySystemCommandResult(result, interactiveSession, registry, manager);
935
- return;
936
- }
937
- if (await routeSkillCommand(input, cmd, registry, interactiveSession, manager)) {
945
+ if (result.effects?.some((effect) => effect.type === "session-execution-started")) {
946
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
947
+ return;
948
+ }
949
+ applySystemCommandResult(result, interactiveSession, registry, manager, commandEffectQueue);
938
950
  return;
939
951
  }
940
952
  manager.addEntry(
@@ -943,18 +955,17 @@ function useSlashRouting(interactiveSession, registry, manager) {
943
955
  )
944
956
  );
945
957
  },
946
- [interactiveSession, registry, manager]
958
+ [interactiveSession, registry, manager, commandEffectQueue]
947
959
  );
948
960
  }
949
- function applySystemCommandResult(result, interactiveSession, registry, manager) {
961
+ function applySystemCommandResult(result, interactiveSession, registry, manager, commandEffectQueue) {
950
962
  const pendingEffects = applyImmediateCommandEffects(result.effects, registry, manager);
951
963
  manager.addEntry((0, import_agent_core2.messageToHistoryEntry)((0, import_agent_core2.createSystemMessage)(result.message)));
952
- const effects = getEffects(interactiveSession);
953
964
  if (result.interaction !== void 0) {
954
- effects._pendingCommandInteraction = result.interaction;
965
+ commandEffectQueue.enqueueInteraction(result.interaction);
955
966
  }
956
967
  if (pendingEffects.length > 0) {
957
- effects._pendingCommandEffects = pendingEffects;
968
+ commandEffectQueue.enqueueEffects(pendingEffects);
958
969
  }
959
970
  const ctx = interactiveSession.getContextState();
960
971
  manager.setContextState({
@@ -979,37 +990,42 @@ function applyImmediateCommandEffects(effects, registry, manager) {
979
990
  }
980
991
  return pendingEffects;
981
992
  }
982
- async function routeSkillCommand(input, cmd, registry, interactiveSession, manager) {
983
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
984
- if (!skillCmd) {
985
- return false;
993
+
994
+ // src/ui/hooks/command-effect-queue.ts
995
+ var CommandEffectQueue = class {
996
+ queue = [];
997
+ enqueueInteraction(interaction) {
998
+ this.queue.push({ type: "interaction", interaction });
986
999
  }
987
- manager.addEntry({
988
- id: (0, import_node_crypto.randomUUID)(),
989
- timestamp: /* @__PURE__ */ new Date(),
990
- category: "event",
991
- type: "skill-invocation",
992
- data: {
993
- skillName: cmd,
994
- source: skillCmd.source,
995
- message: `Invoking ${skillCmd.source}: ${cmd}`
996
- }
997
- });
998
- const args = input.slice(1 + cmd.length).trimStart();
999
- const qualifiedName = registry.resolveQualifiedName(cmd);
1000
- const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
1001
- await interactiveSession.executeSkillCommand(skillCmd, args, input, hookInput);
1002
- manager.setPendingPrompt(interactiveSession.getPendingPrompt());
1003
- return true;
1004
- }
1005
- function getEffects(interactiveSession) {
1006
- return interactiveSession;
1007
- }
1000
+ enqueueEffects(effects) {
1001
+ if (effects.length === 0) return;
1002
+ this.queue.push({ type: "effects", effects: [...effects] });
1003
+ }
1004
+ drain() {
1005
+ return this.queue.shift();
1006
+ }
1007
+ clear() {
1008
+ this.queue.length = 0;
1009
+ }
1010
+ };
1008
1011
 
1009
1012
  // src/ui/hooks/useInteractiveSession.ts
1010
1013
  function applyCompactEventToManager(interactiveSession, manager) {
1011
1014
  manager.syncHistory(interactiveSession.getFullHistory());
1012
1015
  }
1016
+ function applySkillActivationEventToManager(interactiveSession, manager) {
1017
+ manager.syncHistory(interactiveSession.getFullHistory());
1018
+ }
1019
+ function syncExecutionWorkspaceFromSession(interactiveSession, manager) {
1020
+ try {
1021
+ manager.syncExecutionWorkspaceSnapshot(
1022
+ interactiveSession.getExecutionWorkspaceSnapshot({
1023
+ selectedEntryId: manager.selectedExecutionEntryId
1024
+ })
1025
+ );
1026
+ } catch {
1027
+ }
1028
+ }
1013
1029
  function initializeSession(props, permissionHandler) {
1014
1030
  const interactiveSession = new import_agent_sdk5.InteractiveSession({
1015
1031
  cwd: props.cwd,
@@ -1027,14 +1043,13 @@ function initializeSession(props, permissionHandler) {
1027
1043
  commandHostAdapters: props.commandHostAdapters
1028
1044
  });
1029
1045
  const registry = new import_agent_sdk5.CommandRegistry();
1030
- registry.addModule((0, import_agent_sdk5.createBuiltinCommandModule)());
1031
1046
  for (const module2 of props.commandModules ?? []) {
1032
1047
  registry.addModule(module2);
1033
1048
  }
1034
- registry.addSource(new import_agent_sdk5.SkillCommandSource(props.cwd));
1035
1049
  reloadPluginCommandSource(registry);
1036
1050
  const manager = new TuiStateManager();
1037
- return { interactiveSession, registry, manager };
1051
+ const commandEffectQueue = new CommandEffectQueue();
1052
+ return { interactiveSession, registry, manager, commandEffectQueue };
1038
1053
  }
1039
1054
  function useInteractiveSession(props) {
1040
1055
  const [, forceRender] = (0, import_react2.useState)(0);
@@ -1063,8 +1078,8 @@ function useInteractiveSession(props) {
1063
1078
  });
1064
1079
  }, []);
1065
1080
  const permissionHandler = (0, import_react2.useCallback)(
1066
- (toolName, toolArgs) => new Promise((resolve2) => {
1067
- permissionQueueRef.current.push({ toolName, toolArgs, resolve: resolve2 });
1081
+ (toolName, toolArgs) => new Promise((resolve3) => {
1082
+ permissionQueueRef.current.push({ toolName, toolArgs, resolve: resolve3 });
1068
1083
  processNextPermission();
1069
1084
  }),
1070
1085
  [processNextPermission]
@@ -1073,7 +1088,7 @@ function useInteractiveSession(props) {
1073
1088
  if (stateRef.current === null) {
1074
1089
  stateRef.current = initializeSession(props, permissionHandler);
1075
1090
  }
1076
- const { interactiveSession, registry, manager } = stateRef.current;
1091
+ const { interactiveSession, registry, manager, commandEffectQueue } = stateRef.current;
1077
1092
  manager.onChange = () => forceRender((n) => n + 1);
1078
1093
  if (manager.history.length === 0) {
1079
1094
  const restored = interactiveSession.getFullHistory();
@@ -1081,8 +1096,32 @@ function useInteractiveSession(props) {
1081
1096
  manager.syncHistory(restored);
1082
1097
  }
1083
1098
  }
1099
+ (0, import_react2.useEffect)(() => {
1100
+ if (!props.webPort) return;
1101
+ const port = props.webPort;
1102
+ let stopped = false;
1103
+ let stopFn = null;
1104
+ startWebSidecarServer(interactiveSession, port).then((server) => {
1105
+ stopFn = server.stop;
1106
+ if (stopped) {
1107
+ server.stop().catch(() => void 0);
1108
+ return;
1109
+ }
1110
+ const shouldOpen = !props.noOpen && !process.env["ROBOTA_NO_OPEN"];
1111
+ const monitorUrl = process.env["ROBOTA_MONITOR_URL"] ?? "http://localhost:7071/monitor";
1112
+ if (shouldOpen) {
1113
+ (0, import_open.default)(monitorUrl).catch(() => void 0);
1114
+ }
1115
+ }).catch(() => void 0);
1116
+ return () => {
1117
+ stopped = true;
1118
+ if (stopFn) stopFn().catch(() => void 0);
1119
+ };
1120
+ }, [interactiveSession, props.webPort, props.noOpen]);
1084
1121
  (0, import_react2.useEffect)(() => {
1085
1122
  const onCompact = () => applyCompactEventToManager(interactiveSession, manager);
1123
+ const onSkillActivation = () => applySkillActivationEventToManager(interactiveSession, manager);
1124
+ const onExecutionWorkspaceEvent = (event) => manager.syncExecutionWorkspaceSnapshot(event.snapshot);
1086
1125
  interactiveSession.on("text_delta", manager.onTextDelta);
1087
1126
  interactiveSession.on("tool_start", manager.onToolStart);
1088
1127
  interactiveSession.on("tool_end", manager.onToolEnd);
@@ -1092,7 +1131,8 @@ function useInteractiveSession(props) {
1092
1131
  interactiveSession.on("error", manager.onError);
1093
1132
  interactiveSession.on("context_update", manager.onContextUpdate);
1094
1133
  interactiveSession.on("compact", onCompact);
1095
- interactiveSession.on("background_task_event", manager.onBackgroundTaskEvent);
1134
+ interactiveSession.on("skill_activation", onSkillActivation);
1135
+ interactiveSession.on("execution_workspace_event", onExecutionWorkspaceEvent);
1096
1136
  const initCheck = setInterval(() => {
1097
1137
  try {
1098
1138
  const ctx = interactiveSession.getContextState();
@@ -1105,6 +1145,7 @@ function useInteractiveSession(props) {
1105
1145
  if (restored.length > 0) {
1106
1146
  manager.syncHistory(restored);
1107
1147
  }
1148
+ syncExecutionWorkspaceFromSession(interactiveSession, manager);
1108
1149
  clearInterval(initCheck);
1109
1150
  } catch {
1110
1151
  }
@@ -1120,16 +1161,18 @@ function useInteractiveSession(props) {
1120
1161
  interactiveSession.off("error", manager.onError);
1121
1162
  interactiveSession.off("context_update", manager.onContextUpdate);
1122
1163
  interactiveSession.off("compact", onCompact);
1123
- interactiveSession.off("background_task_event", manager.onBackgroundTaskEvent);
1164
+ interactiveSession.off("skill_activation", onSkillActivation);
1165
+ interactiveSession.off("execution_workspace_event", onExecutionWorkspaceEvent);
1124
1166
  };
1125
1167
  }, [interactiveSession, manager]);
1126
1168
  (0, import_react2.useEffect)(() => {
1127
1169
  manager.syncHistory(interactiveSession.getFullHistory());
1170
+ syncExecutionWorkspaceFromSession(interactiveSession, manager);
1128
1171
  if (!manager.isThinking) {
1129
1172
  manager.setPendingPrompt(interactiveSession.getPendingPrompt());
1130
1173
  }
1131
1174
  }, [manager.isThinking, interactiveSession, manager]);
1132
- const handleSubmit = useSlashRouting(interactiveSession, registry, manager);
1175
+ const handleSubmit = useSlashRouting(interactiveSession, registry, manager, commandEffectQueue);
1133
1176
  const handleAbort = (0, import_react2.useCallback)(() => {
1134
1177
  manager.setAborting(true);
1135
1178
  interactiveSession.abort();
@@ -1147,9 +1190,18 @@ function useInteractiveSession(props) {
1147
1190
  },
1148
1191
  [interactiveSession, manager, isShuttingDown]
1149
1192
  );
1193
+ const selectExecutionWorkspaceEntry = (0, import_react2.useCallback)(
1194
+ (entryId) => manager.selectExecutionWorkspaceEntry(entryId),
1195
+ [manager]
1196
+ );
1197
+ const readExecutionWorkspaceDetail = (0, import_react2.useCallback)(
1198
+ (entryId) => interactiveSession.readExecutionWorkspaceDetail(entryId),
1199
+ [interactiveSession]
1200
+ );
1150
1201
  return {
1151
1202
  interactiveSession,
1152
1203
  registry,
1204
+ commandEffectQueue,
1153
1205
  history: manager.history,
1154
1206
  addEntry: (entry) => manager.addEntry(entry),
1155
1207
  streamingText: manager.streamingText,
@@ -1158,13 +1210,16 @@ function useInteractiveSession(props) {
1158
1210
  isAborting: manager.isAborting,
1159
1211
  isShuttingDown,
1160
1212
  pendingPrompt: manager.pendingPrompt,
1161
- backgroundTasks: manager.backgroundTasks,
1213
+ executionWorkspaceSnapshot: manager.executionWorkspaceSnapshot,
1214
+ selectedExecutionEntryId: manager.selectedExecutionEntryId,
1162
1215
  permissionRequest,
1163
1216
  contextState: manager.contextState,
1164
1217
  handleSubmit,
1165
1218
  handleAbort,
1166
1219
  handleCancelQueue,
1167
- handleShutdown
1220
+ handleShutdown,
1221
+ selectExecutionWorkspaceEntry,
1222
+ readExecutionWorkspaceDetail
1168
1223
  };
1169
1224
  }
1170
1225
 
@@ -1433,10 +1488,12 @@ function useSideEffects({
1433
1488
  cwd,
1434
1489
  providerOverride,
1435
1490
  interactiveSession,
1491
+ commandEffectQueue,
1436
1492
  addEntry,
1437
1493
  baseHandleSubmit,
1438
1494
  setSessionName,
1439
- setStatusLineSettings
1495
+ setStatusLineSettings,
1496
+ showSessionPickerOnStart
1440
1497
  }) {
1441
1498
  const { exit } = (0, import_ink.useApp)();
1442
1499
  const [pendingModelId, setPendingModelId] = (0, import_react4.useState)(null);
@@ -1444,7 +1501,7 @@ function useSideEffects({
1444
1501
  const [pendingInteractionPrompt, setPendingInteractionPrompt] = (0, import_react4.useState)(null);
1445
1502
  const commandInteractionRef = (0, import_react4.useRef)(null);
1446
1503
  const [showPluginTUI, setShowPluginTUI] = (0, import_react4.useState)(false);
1447
- const [showSessionPicker, setShowSessionPicker] = (0, import_react4.useState)(false);
1504
+ const [showSessionPicker, setShowSessionPicker] = (0, import_react4.useState)(showSessionPickerOnStart ?? false);
1448
1505
  const requestShutdown = (0, import_react4.useCallback)(
1449
1506
  (reason, message) => {
1450
1507
  addEntry((0, import_agent_core6.messageToHistoryEntry)((0, import_agent_core6.createSystemMessage)("Shutting down...")));
@@ -1485,33 +1542,31 @@ function useSideEffects({
1485
1542
  commandInteractionRef.current = null;
1486
1543
  setPendingInteractionPrompt(null);
1487
1544
  if (result.effects !== void 0 && result.effects.length > 0) {
1488
- applyEffects(result.effects, interactiveSession);
1545
+ applyEffects(result.effects, getHostSideEffects(interactiveSession));
1489
1546
  }
1490
1547
  },
1491
1548
  [addEntry, applyEffects, interactiveSession]
1492
1549
  );
1493
1550
  const applyQueuedCommandState = (0, import_react4.useCallback)(
1494
1551
  (sideEffects) => {
1495
- if (sideEffects._pendingCommandInteraction !== void 0) {
1496
- const interaction = sideEffects._pendingCommandInteraction;
1497
- delete sideEffects._pendingCommandInteraction;
1552
+ const queued = commandEffectQueue.drain();
1553
+ if (queued === void 0) {
1554
+ return false;
1555
+ }
1556
+ if (queued.type === "interaction") {
1557
+ const { interaction } = queued;
1498
1558
  commandInteractionRef.current = interaction;
1499
1559
  setPendingInteractionPrompt(interaction.prompt);
1500
1560
  return true;
1501
1561
  }
1502
- if (sideEffects._pendingCommandEffects !== void 0) {
1503
- const effects = sideEffects._pendingCommandEffects;
1504
- delete sideEffects._pendingCommandEffects;
1505
- return applyEffects(effects, sideEffects);
1506
- }
1507
- return false;
1562
+ return applyEffects(queued.effects, sideEffects);
1508
1563
  },
1509
- [applyEffects]
1564
+ [applyEffects, commandEffectQueue]
1510
1565
  );
1511
1566
  const handleSubmit = (0, import_react4.useCallback)(
1512
1567
  async (input) => {
1513
1568
  await baseHandleSubmit(input);
1514
- const sideEffects = interactiveSession;
1569
+ const sideEffects = getHostSideEffects(interactiveSession);
1515
1570
  if (applyQueuedCommandState(sideEffects)) return;
1516
1571
  if (sideEffects._pendingModelId) {
1517
1572
  const modelId = sideEffects._pendingModelId;
@@ -1622,6 +1677,9 @@ function useSideEffects({
1622
1677
  handleInteractionCancel
1623
1678
  };
1624
1679
  }
1680
+ function getHostSideEffects(interactiveSession) {
1681
+ return interactiveSession;
1682
+ }
1625
1683
 
1626
1684
  // src/ui/hooks/useStatusLineSettings.ts
1627
1685
  var import_react5 = require("react");
@@ -1639,10 +1697,12 @@ var import_agent_core8 = require("@robota-sdk/agent-core");
1639
1697
  // src/ui/render-markdown.ts
1640
1698
  var import_marked = require("marked");
1641
1699
  var import_marked_terminal = __toESM(require("marked-terminal"), 1);
1642
- var ANSI_RED = "\x1B[31m";
1643
- var ANSI_GREEN = "\x1B[32m";
1700
+ var ANSI_LIGHT_RED = "\x1B[38;5;210m";
1701
+ var ANSI_LIGHT_GREEN = "\x1B[38;5;120m";
1644
1702
  var ANSI_CYAN = "\x1B[36m";
1645
1703
  var ANSI_DIM = "\x1B[2m";
1704
+ var ANSI_DARK_RED_BACKGROUND = "\x1B[48;5;52m";
1705
+ var ANSI_DARK_GREEN_BACKGROUND = "\x1B[48;5;22m";
1646
1706
  var ANSI_RESET = "\x1B[0m";
1647
1707
  var CODE_BLOCK_INDENT = " ";
1648
1708
  var ZERO_COLOR = "0";
@@ -1662,36 +1722,59 @@ function shouldUseColor(option) {
1662
1722
  function isDiffLanguage(language) {
1663
1723
  return language?.trim().toLowerCase() === "diff";
1664
1724
  }
1665
- function colorizeDiffLine(line, color) {
1725
+ function styleAddedOrRemovedDiffRow(line, rowWidth, color) {
1726
+ const row = `${CODE_BLOCK_INDENT}${line}`.padEnd(rowWidth);
1666
1727
  if (!color) {
1667
- return line;
1728
+ return row.trimEnd();
1668
1729
  }
1669
1730
  if (line.startsWith("+")) {
1670
- return `${ANSI_GREEN}${line}${ANSI_RESET}`;
1731
+ return `${ANSI_DARK_GREEN_BACKGROUND}${ANSI_LIGHT_GREEN}${row}${ANSI_RESET}`;
1671
1732
  }
1672
1733
  if (line.startsWith("-")) {
1673
- return `${ANSI_RED}${line}${ANSI_RESET}`;
1734
+ return `${ANSI_DARK_RED_BACKGROUND}${ANSI_LIGHT_RED}${row}${ANSI_RESET}`;
1735
+ }
1736
+ return row.trimEnd();
1737
+ }
1738
+ function colorizeDiffLine(line, color, rowWidth) {
1739
+ if (line.startsWith("+") || line.startsWith("-")) {
1740
+ return styleAddedOrRemovedDiffRow(line, rowWidth, color);
1741
+ }
1742
+ const row = `${CODE_BLOCK_INDENT}${line}`;
1743
+ if (!color) {
1744
+ return row;
1674
1745
  }
1675
1746
  if (line.startsWith("@@")) {
1676
- return `${ANSI_CYAN}${line}${ANSI_RESET}`;
1747
+ return `${ANSI_CYAN}${row}${ANSI_RESET}`;
1677
1748
  }
1678
1749
  if (line.startsWith("diff ") || line.startsWith("index ")) {
1679
- return `${ANSI_DIM}${line}${ANSI_RESET}`;
1750
+ return `${ANSI_DIM}${row}${ANSI_RESET}`;
1751
+ }
1752
+ return row;
1753
+ }
1754
+ function resolveDiffRowWidth(lines, requestedWidth) {
1755
+ const minimumWidth = lines.reduce(
1756
+ (maxWidth, line) => Math.max(maxWidth, CODE_BLOCK_INDENT.length + line.length),
1757
+ 0
1758
+ );
1759
+ if (requestedWidth === void 0) {
1760
+ return minimumWidth;
1680
1761
  }
1681
- return line;
1762
+ return Math.max(minimumWidth, requestedWidth);
1682
1763
  }
1683
- function renderDiffCodeBlock(code, color) {
1684
- const body = code.split("\n").map((line) => `${CODE_BLOCK_INDENT}${colorizeDiffLine(line, color)}`).join("\n");
1764
+ function renderDiffCodeBlock(code, color, codeBlockWidth) {
1765
+ const lines = code.split("\n");
1766
+ const rowWidth = resolveDiffRowWidth(lines, codeBlockWidth);
1767
+ const body = lines.map((line) => colorizeDiffLine(line, color, rowWidth)).join("\n");
1685
1768
  return `${body}
1686
1769
 
1687
1770
  `;
1688
1771
  }
1689
- function createTerminalRenderer(color) {
1772
+ function createTerminalRenderer(color, codeBlockWidth) {
1690
1773
  const renderer = new TerminalRendererConstructor(void 0, { ignoreIllegals: true });
1691
1774
  const renderCode = renderer.code.bind(renderer);
1692
1775
  renderer.code = (code, language, escaped) => {
1693
1776
  if (isDiffLanguage(language)) {
1694
- return renderDiffCodeBlock(code, color);
1777
+ return renderDiffCodeBlock(code, color, codeBlockWidth);
1695
1778
  }
1696
1779
  return renderCode(code, language, escaped);
1697
1780
  };
@@ -1699,7 +1782,7 @@ function createTerminalRenderer(color) {
1699
1782
  }
1700
1783
  function renderMarkdown(md, options = {}) {
1701
1784
  const result = import_marked.marked.parse(md, {
1702
- renderer: createTerminalRenderer(shouldUseColor(options.color))
1785
+ renderer: createTerminalRenderer(shouldUseColor(options.color), options.codeBlockWidth)
1703
1786
  });
1704
1787
  return typeof result === "string" ? result.trimEnd() : md;
1705
1788
  }
@@ -1818,7 +1901,7 @@ function formatUsageTokenCount(tokens) {
1818
1901
 
1819
1902
  // src/ui/command-output-summary.ts
1820
1903
  var MAX_PREVIEW_LINES = 4;
1821
- var SUCCESS_EXIT_CODE2 = 0;
1904
+ var SUCCESS_EXIT_CODE = 0;
1822
1905
  var COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set(["Bash", "BackgroundProcess"]);
1823
1906
  function formatCommandOutputSummary(tool) {
1824
1907
  if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return void 0;
@@ -1829,7 +1912,7 @@ function formatCommandOutputSummary(tool) {
1829
1912
  const lines = trimTrailingBlankLines(splitOutputLines(output));
1830
1913
  const previewLines = lines.slice(0, MAX_PREVIEW_LINES);
1831
1914
  const omittedLineCount = Math.max(0, lines.length - previewLines.length);
1832
- const isFailed = tool.result === "error" || successValue === false || exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2;
1915
+ const isFailed = tool.result === "error" || successValue === false || exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE;
1833
1916
  return {
1834
1917
  status: isFailed ? "error" : "success",
1835
1918
  statusLabel: formatStatusLabel(isFailed, exitCode),
@@ -1859,7 +1942,7 @@ function buildOutputText(raw, parsed) {
1859
1942
  return lines.join("\n");
1860
1943
  }
1861
1944
  function formatStatusLabel(isFailed, exitCode) {
1862
- if (exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2) return `exit ${exitCode}`;
1945
+ if (exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE) return `exit ${exitCode}`;
1863
1946
  return isFailed ? "error" : "ok";
1864
1947
  }
1865
1948
  function splitOutputLines(output) {
@@ -2211,11 +2294,7 @@ function StatusActivityText({
2211
2294
  activeBackgroundTaskCount,
2212
2295
  hasPendingPrompt
2213
2296
  });
2214
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2215
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "cyan", bold: true, children: "Activity:" }),
2216
- " ",
2217
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: activity.color, bold: activity.kind !== "idle", children: activity.text })
2218
- ] });
2297
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: activity.color, bold: activity.kind !== "idle", children: activity.text });
2219
2298
  }
2220
2299
  function ContextText({
2221
2300
  percentage,
@@ -2239,77 +2318,79 @@ function ModeText({ permissionMode }) {
2239
2318
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { children: permissionMode })
2240
2319
  ] });
2241
2320
  }
2242
- function StatusLeft({
2243
- permissionMode,
2321
+ function shouldShowPermissionMode(permissionMode) {
2322
+ return permissionMode !== "default";
2323
+ }
2324
+ function ProviderText({
2244
2325
  modelName,
2245
- isThinking,
2246
- activeToolCount,
2247
- activeBackgroundTaskCount,
2248
- hasPendingPrompt,
2249
- contextPercentage,
2250
- contextUsedTokens,
2251
- contextMaxTokens,
2252
- sessionName,
2253
- gitBranch,
2254
- showGitBranch
2326
+ providerProfileName,
2327
+ providerType
2255
2328
  }) {
2256
- const shouldShowGitBranch = showGitBranch && gitBranch !== void 0 && gitBranch.length > 0;
2329
+ if (providerProfileName !== void 0 && providerType !== void 0) {
2330
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
2331
+ providerProfileName,
2332
+ " (",
2333
+ providerType,
2334
+ ") ",
2335
+ modelName
2336
+ ] });
2337
+ }
2338
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { dimColor: true, children: modelName });
2339
+ }
2340
+ function StatusLeft(props) {
2341
+ const shouldShowGitBranch = props.showGitBranch && props.gitBranch !== void 0 && props.gitBranch.length > 0;
2342
+ const showPermissionMode = shouldShowPermissionMode(props.permissionMode);
2257
2343
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { children: [
2258
2344
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2259
2345
  StatusActivityText,
2260
2346
  {
2261
- isThinking,
2262
- activeToolCount,
2263
- activeBackgroundTaskCount,
2264
- hasPendingPrompt
2347
+ isThinking: props.isThinking,
2348
+ activeToolCount: props.activeToolCount,
2349
+ activeBackgroundTaskCount: props.activeBackgroundTaskCount,
2350
+ hasPendingPrompt: props.hasPendingPrompt
2265
2351
  }
2266
2352
  ),
2267
- " | ",
2268
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModeText, { permissionMode }),
2269
- sessionName && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2353
+ showPermissionMode && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2354
+ " | ",
2355
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModeText, { permissionMode: props.permissionMode })
2356
+ ] }),
2357
+ props.sessionName && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2270
2358
  " | ",
2271
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "magenta", children: sessionName })
2359
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "magenta", children: props.sessionName })
2272
2360
  ] }),
2273
2361
  shouldShowGitBranch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2274
2362
  " | ",
2275
2363
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
2276
2364
  "git: ",
2277
- gitBranch
2365
+ props.gitBranch
2278
2366
  ] })
2279
2367
  ] }),
2280
2368
  " | ",
2281
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { dimColor: true, children: modelName }),
2369
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2370
+ ProviderText,
2371
+ {
2372
+ modelName: props.modelName,
2373
+ providerProfileName: props.providerProfileName,
2374
+ providerType: props.providerType
2375
+ }
2376
+ ),
2282
2377
  " | ",
2283
2378
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2284
2379
  ContextText,
2285
2380
  {
2286
- percentage: contextPercentage,
2287
- usedTokens: contextUsedTokens,
2288
- maxTokens: contextMaxTokens
2381
+ percentage: props.contextPercentage,
2382
+ usedTokens: props.contextUsedTokens,
2383
+ maxTokens: props.contextMaxTokens
2289
2384
  }
2290
2385
  )
2291
2386
  ] });
2292
2387
  }
2293
- function StatusRight({
2294
- isThinking,
2295
- messageCount
2296
- }) {
2297
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { children: [
2298
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2299
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "yellow", children: "thinking..." }),
2300
- " "
2301
- ] }),
2302
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
2303
- "msgs: ",
2304
- messageCount
2305
- ] })
2306
- ] });
2307
- }
2308
2388
  function StatusBar({
2309
2389
  permissionMode,
2310
2390
  modelName,
2391
+ providerProfileName,
2392
+ providerType,
2311
2393
  sessionId: _sessionId,
2312
- messageCount,
2313
2394
  isThinking,
2314
2395
  activeToolCount = 0,
2315
2396
  activeBackgroundTaskCount = 0,
@@ -2321,7 +2402,7 @@ function StatusBar({
2321
2402
  gitBranch,
2322
2403
  showGitBranch = true
2323
2404
  }) {
2324
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2405
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2325
2406
  import_ink6.Box,
2326
2407
  {
2327
2408
  borderStyle: "single",
@@ -2329,26 +2410,25 @@ function StatusBar({
2329
2410
  paddingLeft: 1,
2330
2411
  paddingRight: 1,
2331
2412
  justifyContent: "space-between",
2332
- children: [
2333
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2334
- StatusLeft,
2335
- {
2336
- permissionMode,
2337
- modelName,
2338
- isThinking,
2339
- activeToolCount,
2340
- activeBackgroundTaskCount,
2341
- hasPendingPrompt,
2342
- contextPercentage,
2343
- contextUsedTokens,
2344
- contextMaxTokens,
2345
- sessionName,
2346
- gitBranch,
2347
- showGitBranch
2348
- }
2349
- ),
2350
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatusRight, { isThinking, messageCount })
2351
- ]
2413
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2414
+ StatusLeft,
2415
+ {
2416
+ permissionMode,
2417
+ modelName,
2418
+ providerProfileName,
2419
+ providerType,
2420
+ isThinking,
2421
+ activeToolCount,
2422
+ activeBackgroundTaskCount,
2423
+ hasPendingPrompt,
2424
+ contextPercentage,
2425
+ contextUsedTokens,
2426
+ contextMaxTokens,
2427
+ sessionName,
2428
+ gitBranch,
2429
+ showGitBranch
2430
+ }
2431
+ )
2352
2432
  }
2353
2433
  );
2354
2434
  }
@@ -2359,8 +2439,9 @@ function SessionStatusBar({
2359
2439
  cwd,
2360
2440
  permissionMode,
2361
2441
  modelId,
2442
+ providerProfileName,
2443
+ providerType,
2362
2444
  sessionId,
2363
- messageCount,
2364
2445
  isThinking,
2365
2446
  activeToolCount,
2366
2447
  activeBackgroundTaskCount,
@@ -2376,8 +2457,9 @@ function SessionStatusBar({
2376
2457
  {
2377
2458
  permissionMode,
2378
2459
  modelName: modelId ? (0, import_agent_core10.getModelName)(modelId) : "",
2460
+ providerProfileName,
2461
+ providerType,
2379
2462
  sessionId,
2380
- messageCount,
2381
2463
  isThinking,
2382
2464
  activeToolCount,
2383
2465
  activeBackgroundTaskCount,
@@ -3405,6 +3487,7 @@ function submitTextPromptValue(state, options) {
3405
3487
  var import_jsx_runtime13 = require("react/jsx-runtime");
3406
3488
  function TextPrompt({
3407
3489
  title,
3490
+ description,
3408
3491
  placeholder,
3409
3492
  onSubmit,
3410
3493
  onCancel,
@@ -3429,12 +3512,11 @@ function TextPrompt({
3429
3512
  );
3430
3513
  (0, import_ink13.useInput)((input, key) => {
3431
3514
  const action = getTextPromptInputAction(input, key);
3432
- if (action !== void 0) {
3433
- applyAction(action);
3434
- }
3515
+ if (action !== void 0) applyAction(action);
3435
3516
  });
3436
3517
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3437
3518
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "yellow", bold: true, children: title }),
3519
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptDescription, { description }),
3438
3520
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { marginTop: 1, children: [
3439
3521
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "cyan", children: "> " }),
3440
3522
  state.value ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: placeholder }) : null,
@@ -3444,6 +3526,12 @@ function TextPrompt({
3444
3526
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
3445
3527
  ] });
3446
3528
  }
3529
+ function PromptDescription({ description }) {
3530
+ if (description === void 0 || description.length === 0) {
3531
+ return null;
3532
+ }
3533
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: description });
3534
+ }
3447
3535
 
3448
3536
  // src/ui/InteractivePrompt.tsx
3449
3537
  var import_jsx_runtime14 = require("react/jsx-runtime");
@@ -3457,6 +3545,7 @@ function InteractivePrompt({
3457
3545
  TextPrompt,
3458
3546
  {
3459
3547
  title: prompt.title,
3548
+ description: prompt.description,
3460
3549
  placeholder: prompt.placeholder,
3461
3550
  allowEmpty: prompt.allowEmpty,
3462
3551
  masked: prompt.masked,
@@ -3469,6 +3558,7 @@ function InteractivePrompt({
3469
3558
  }
3470
3559
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
3471
3560
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { bold: true, children: prompt.title }),
3561
+ prompt.description !== void 0 && prompt.description.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { dimColor: true, children: prompt.description }),
3472
3562
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3473
3563
  ListPicker,
3474
3564
  {
@@ -4059,26 +4149,20 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
4059
4149
  var import_ink18 = require("ink");
4060
4150
  var import_jsx_runtime19 = require("react/jsx-runtime");
4061
4151
  var SESSION_ID_DISPLAY_LENGTH = 8;
4152
+ var SESSION_PREVIEW_DISPLAY_LENGTH = 60;
4062
4153
  function SessionPicker({
4063
- sessionStore,
4064
- cwd,
4154
+ sessions,
4065
4155
  onSelect,
4066
4156
  onCancel
4067
4157
  }) {
4068
- const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
4069
4158
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4070
4159
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
4071
4160
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4072
4161
  ListPicker,
4073
4162
  {
4074
- items: sessions,
4163
+ items: [...sessions],
4075
4164
  renderItem: (session, isSelected) => {
4076
- const lastMsg = session.messages.slice().reverse().find((m) => {
4077
- const msg = m;
4078
- return msg.role === "assistant" && msg.content;
4079
- });
4080
- const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
4081
- const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
4165
+ const preview = session.preview ? session.preview.slice(0, SESSION_PREVIEW_DISPLAY_LENGTH) + (session.preview.length > SESSION_PREVIEW_DISPLAY_LENGTH ? "..." : "") : "";
4082
4166
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Text, { children: [
4083
4167
  isSelected ? "> " : " ",
4084
4168
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
@@ -4092,7 +4176,7 @@ function SessionPicker({
4092
4176
  " ",
4093
4177
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Text, { dimColor: true, children: [
4094
4178
  "msgs: ",
4095
- session.messages.length
4179
+ session.messageCount
4096
4180
  ] }),
4097
4181
  preview ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4098
4182
  "\n ",
@@ -4110,115 +4194,281 @@ function SessionPicker({
4110
4194
  // src/ui/BackgroundTaskPanel.tsx
4111
4195
  var import_ink19 = require("ink");
4112
4196
 
4113
- // src/ui/background-task-row-format.ts
4114
- var MS_PER_SECOND = 1e3;
4115
- var SECONDS_PER_MINUTE = 60;
4116
- var MINUTES_PER_HOUR = 60;
4117
- var SUCCESS_EXIT_CODE3 = 0;
4118
- function formatBackgroundTaskRow(task, options = {}) {
4119
- const row = {
4120
- connector: options.isLast === false ? "\u251C" : "\u2514",
4121
- marker: getStatusMarker(task),
4122
- color: getStatusColor(task),
4123
- label: getTaskLabel(task),
4124
- segments: getTaskSegments(task, options.now ?? Date.now()),
4125
- preview: getTaskPreview(task)
4126
- };
4127
- return {
4128
- ...row,
4129
- accessibleText: formatAccessibleText(row)
4130
- };
4197
+ // src/ui/execution-workspace-view-model.ts
4198
+ var ACTIVE_STATUSES = [
4199
+ "active",
4200
+ "queued",
4201
+ "running",
4202
+ "waiting_permission"
4203
+ ];
4204
+ var DETAIL_RECORD_TEXT_LIMIT = 160;
4205
+ var PREVIEW_WHITESPACE = /\s+/g;
4206
+ var PREVIEW_SEPARATOR = " ";
4207
+ function getDefaultBackgroundWorkspaceEntries(snapshot) {
4208
+ return (snapshot?.entries ?? []).filter(
4209
+ (entry) => entry.kind === "background_task" && entry.visibility === "default"
4210
+ );
4131
4211
  }
4132
- function getStatusColor(task) {
4133
- if (isFailedTask(task)) return "red";
4134
- if (task.status === "completed") return "green";
4135
- if (task.status === "cancelled") return "yellow";
4136
- return "cyan";
4212
+ function countActiveBackgroundWorkspaceEntries(snapshot) {
4213
+ return getDefaultBackgroundWorkspaceEntries(snapshot).filter(
4214
+ (entry) => ACTIVE_STATUSES.includes(entry.status)
4215
+ ).length;
4137
4216
  }
4138
- function getStatusMarker(task) {
4139
- if (task.status === "queued" || task.status === "running") return "\u25A1";
4140
- return "\u25A0";
4217
+ function formatExecutionWorkspaceEntryRow(entry, options = {}) {
4218
+ const isSelected = entry.id === options.selectedEntryId;
4219
+ const row = {
4220
+ id: entry.id,
4221
+ radio: isSelected ? "\u25CF" : "\u25CB",
4222
+ title: formatEntryTitle(entry),
4223
+ subtitle: formatEntrySubtitle(entry),
4224
+ statusLabel: formatStatusLabel2(entry.status),
4225
+ preview: trimPreview(entry.preview ?? entry.currentAction),
4226
+ color: getEntryColor(entry),
4227
+ isSelected
4228
+ };
4229
+ return { ...row, accessibleText: formatAccessibleText(row) };
4230
+ }
4231
+ function formatExecutionDetailRecord(record) {
4232
+ const text = record.text.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);
4233
+ if (!text) return record.kind;
4234
+ return text.length > DETAIL_RECORD_TEXT_LIMIT ? `${text.slice(0, DETAIL_RECORD_TEXT_LIMIT)}...` : text;
4235
+ }
4236
+ function formatEntryTitle(entry) {
4237
+ if (entry.kind === "main_thread") return entry.title;
4238
+ if (entry.kind === "background_group") return `${entry.title} group`;
4239
+ if (entry.taskKind === "agent") return `${entry.title} agent`;
4240
+ if (entry.taskKind === "process") return entry.title || "Process";
4241
+ return entry.title;
4242
+ }
4243
+ function formatEntrySubtitle(entry) {
4244
+ if (entry.kind === "main_thread") return entry.subtitle;
4245
+ const parts = [
4246
+ entry.taskKind,
4247
+ entry.subtitle,
4248
+ entry.attention === "none" ? void 0 : entry.attention
4249
+ ];
4250
+ return parts.filter((part) => typeof part === "string" && part.length > 0).join(" \xB7 ") || void 0;
4141
4251
  }
4142
- function getTaskLabel(task) {
4143
- if (task.kind === "agent") return `${task.label} agent`;
4144
- if (task.kind === "process") return task.label || "Process";
4145
- return task.label;
4252
+ function formatStatusLabel2(status) {
4253
+ return status.replace(/_/g, " ");
4146
4254
  }
4147
- function getTaskSegments(task, now) {
4148
- const segments = [];
4149
- if (task.status === "running") {
4150
- const idle = formatAge(task.lastActivityAt, now);
4151
- if (idle) segments.push(`idle ${idle}`);
4152
- }
4153
- if (task.status === "failed") {
4154
- segments.push(task.statusLabel === "timed out" ? "timed out" : "failed");
4155
- }
4156
- if (task.status === "cancelled") {
4157
- segments.push("cancelled");
4158
- }
4159
- if (task.timeoutReason) {
4160
- segments.push(task.timeoutReason);
4161
- }
4162
- if (task.status === "completed" && task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3) {
4163
- segments.push(`exit ${task.exitCode}`);
4164
- }
4165
- if (task.signalCode) {
4166
- segments.push(`signal ${task.signalCode}`);
4167
- }
4168
- if (task.worktreePath || task.branchName) {
4169
- segments.push("worktree");
4170
- }
4171
- return segments;
4255
+ function getEntryColor(entry) {
4256
+ if (entry.attention === "failed" || entry.status === "failed") return "red";
4257
+ if (entry.attention === "permission" || entry.status === "waiting_permission") return "yellow";
4258
+ if (entry.status === "completed") return "green";
4259
+ if (entry.status === "cancelled") return "yellow";
4260
+ if (ACTIVE_STATUSES.includes(entry.status)) return "cyan";
4261
+ return "white";
4172
4262
  }
4173
- function getTaskPreview(task) {
4174
- if (task.worktreeNextAction) return task.worktreeNextAction;
4175
- if (task.worktreePath) return task.worktreePath;
4176
- const preview = task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
4263
+ function trimPreview(value) {
4264
+ const preview = value?.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);
4177
4265
  return preview || void 0;
4178
4266
  }
4179
- function formatAge(iso, now) {
4180
- if (!iso) return void 0;
4181
- const timestamp = Date.parse(iso);
4182
- if (Number.isNaN(timestamp)) return void 0;
4183
- const seconds = Math.max(0, Math.floor((now - timestamp) / MS_PER_SECOND));
4184
- if (seconds < SECONDS_PER_MINUTE) return `${seconds}s`;
4185
- const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
4186
- if (minutes < MINUTES_PER_HOUR) return `${minutes}m`;
4187
- return `${Math.floor(minutes / MINUTES_PER_HOUR)}h`;
4267
+ function formatAccessibleText(row) {
4268
+ const parts = [row.radio, row.title, row.statusLabel, row.subtitle, row.preview];
4269
+ return parts.filter((part) => typeof part === "string" && part.length > 0).join(" \xB7 ");
4188
4270
  }
4189
- function isFailedTask(task) {
4190
- return task.status === "failed" || task.status === "completed" && (task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3 || !!task.signalCode);
4271
+
4272
+ // src/ui/background-task-row-format.ts
4273
+ function formatBackgroundTaskRow(entry, options = {}) {
4274
+ const row = formatExecutionWorkspaceEntryRow(entry);
4275
+ const marker = isActiveEntry(entry) ? "\u25A1" : "\u25A0";
4276
+ const segments = [row.statusLabel, row.subtitle].filter(
4277
+ (segment) => typeof segment === "string" && segment.length > 0
4278
+ );
4279
+ return {
4280
+ connector: options.isLast === false ? "\u251C" : "\u2514",
4281
+ marker,
4282
+ color: row.color,
4283
+ label: row.title,
4284
+ segments,
4285
+ preview: row.preview,
4286
+ accessibleText: [
4287
+ `${options.isLast === false ? "\u251C" : "\u2514"} ${marker} ${row.title}`,
4288
+ ...segments,
4289
+ row.preview
4290
+ ].filter((part) => typeof part === "string" && part.length > 0).join(" \xB7 ")
4291
+ };
4191
4292
  }
4192
- function formatAccessibleText(row) {
4193
- const parts = [`${row.connector} ${row.marker} ${row.label}`, ...row.segments];
4194
- if (row.preview) parts.push(row.preview);
4195
- return parts.join(" \xB7 ");
4293
+ function isActiveEntry(entry) {
4294
+ return entry.status === "active" || entry.status === "queued" || entry.status === "running" || entry.status === "waiting_permission";
4196
4295
  }
4197
4296
 
4198
4297
  // src/ui/BackgroundTaskPanel.tsx
4199
4298
  var import_jsx_runtime20 = require("react/jsx-runtime");
4200
- function BackgroundTaskPanel({ tasks }) {
4201
- if (tasks.length === 0) return null;
4299
+ function BackgroundTaskPanel({ entries }) {
4300
+ if (entries.length === 0) return null;
4202
4301
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Box, { flexDirection: "column", marginBottom: 1, children: [
4203
4302
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { color: "cyan", bold: true, children: "Background work" }),
4204
- tasks.map((task, index) => {
4205
- const row = formatBackgroundTaskRow(task, { isLast: index === tasks.length - 1 });
4303
+ entries.map((entry, index) => {
4304
+ const row = formatBackgroundTaskRow(entry, { isLast: index === entries.length - 1 });
4206
4305
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Text, { children: [
4207
4306
  `${row.connector} `,
4208
4307
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { color: row.color, children: row.marker }),
4209
4308
  ` ${row.label}`,
4210
4309
  row.segments.map((segment, segmentIndex) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { dimColor: true, children: ` \xB7 ${segment}` }, `${segment}-${segmentIndex}`)),
4211
4310
  row.preview ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
4212
- ] }, task.id);
4311
+ ] }, entry.id);
4213
4312
  })
4214
4313
  ] });
4215
4314
  }
4216
4315
 
4217
- // src/ui/UpdateNotice.tsx
4316
+ // src/ui/ExecutionWorkspaceSwitcher.tsx
4317
+ var import_react19 = require("react");
4218
4318
  var import_ink20 = require("ink");
4219
4319
  var import_jsx_runtime21 = require("react/jsx-runtime");
4320
+ var MAX_VISIBLE_WORKSPACE_ENTRIES = 8;
4321
+ function ExecutionWorkspaceSwitcher({
4322
+ snapshot,
4323
+ selectedEntryId,
4324
+ onSelect,
4325
+ onClose
4326
+ }) {
4327
+ const entries = [...snapshot?.entries ?? []];
4328
+ const { normalized, visibleEntries, applyAction } = useWorkspaceSwitcherSelection({
4329
+ entries,
4330
+ selectedEntryId,
4331
+ onSelect,
4332
+ onClose
4333
+ });
4334
+ (0, import_ink20.useInput)((_input, key) => {
4335
+ const action = getVerticalSelectionInputAction(key);
4336
+ if (action !== void 0) applyAction(action);
4337
+ });
4338
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4339
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: "cyan", bold: true, children: "Execution workspace" }),
4340
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Box, { flexDirection: "column", marginTop: 1, children: visibleEntries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: "No workspace entries" }) : visibleEntries.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4341
+ ExecutionWorkspaceSwitcherRow,
4342
+ {
4343
+ entry,
4344
+ isFocused: normalized.scrollOffset + index === normalized.selectedIndex,
4345
+ selectedEntryId
4346
+ },
4347
+ entry.id
4348
+ )) }),
4349
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: "Ctrl+B Close \u2191\u2193 Navigate Enter Switch Esc Close" })
4350
+ ] });
4351
+ }
4352
+ function useWorkspaceSwitcherSelection({
4353
+ entries,
4354
+ selectedEntryId,
4355
+ onSelect,
4356
+ onClose
4357
+ }) {
4358
+ const [state, setState] = (0, import_react19.useState)(() => createSelectionFlowState());
4359
+ const stateRef = (0, import_react19.useRef)(state);
4360
+ (0, import_react19.useEffect)(() => {
4361
+ const selectedIndex = Math.max(
4362
+ 0,
4363
+ entries.findIndex((entry) => entry.id === selectedEntryId)
4364
+ );
4365
+ const nextState = createNormalizedSelection({ selectedIndex, itemCount: entries.length });
4366
+ stateRef.current = nextState;
4367
+ setState(nextState);
4368
+ }, [entries.length, selectedEntryId]);
4369
+ const normalized = createNormalizedSelection({
4370
+ selectedIndex: state.selectedIndex,
4371
+ scrollOffset: state.scrollOffset,
4372
+ itemCount: entries.length
4373
+ });
4374
+ if (normalized !== state) stateRef.current = normalized;
4375
+ return {
4376
+ normalized,
4377
+ visibleEntries: entries.slice(
4378
+ normalized.scrollOffset,
4379
+ normalized.scrollOffset + MAX_VISIBLE_WORKSPACE_ENTRIES
4380
+ ),
4381
+ applyAction: createApplyAction({ entries, stateRef, setState, onSelect, onClose })
4382
+ };
4383
+ }
4384
+ function createApplyAction({
4385
+ entries,
4386
+ stateRef,
4387
+ setState,
4388
+ onSelect,
4389
+ onClose
4390
+ }) {
4391
+ return (action) => {
4392
+ const result = applySelectionInput(stateRef.current, action, {
4393
+ itemCount: entries.length,
4394
+ maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES
4395
+ });
4396
+ const nextState = result.effect.type === "select" || result.effect.type === "cancel" ? { ...result.state, resolved: false } : result.state;
4397
+ stateRef.current = nextState;
4398
+ setState(nextState);
4399
+ if (result.effect.type === "cancel") {
4400
+ onClose();
4401
+ } else if (result.effect.type === "select") {
4402
+ const entry = entries[result.effect.index];
4403
+ if (entry) onSelect(entry.id);
4404
+ }
4405
+ };
4406
+ }
4407
+ function createNormalizedSelection(input) {
4408
+ return normalizeSelectionState(
4409
+ {
4410
+ selectedIndex: input.selectedIndex,
4411
+ scrollOffset: input.scrollOffset ?? 0,
4412
+ resolved: false
4413
+ },
4414
+ { itemCount: input.itemCount, maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES }
4415
+ );
4416
+ }
4417
+ function ExecutionWorkspaceSwitcherRow({
4418
+ entry,
4419
+ isFocused,
4420
+ selectedEntryId
4421
+ }) {
4422
+ const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId });
4423
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Text, { children: [
4424
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: isFocused ? "cyan" : void 0, bold: isFocused, children: isFocused ? "> " : " " }),
4425
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: row.color, children: row.radio }),
4426
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: isFocused ? "cyan" : void 0, bold: isFocused, children: ` ${row.title}` }),
4427
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: ` \xB7 ${row.statusLabel}` }),
4428
+ row.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: ` \xB7 ${row.subtitle}` }) : null,
4429
+ row.preview ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
4430
+ ] });
4431
+ }
4432
+
4433
+ // src/ui/ExecutionWorkspaceDetailPane.tsx
4434
+ var import_ink21 = require("ink");
4435
+ var import_jsx_runtime22 = require("react/jsx-runtime");
4436
+ var MAX_VISIBLE_DETAIL_RECORDS = 12;
4437
+ function ExecutionWorkspaceDetailPane({
4438
+ entry,
4439
+ page,
4440
+ loading,
4441
+ error
4442
+ }) {
4443
+ const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId: entry.id });
4444
+ const records = page?.records.slice(-MAX_VISIBLE_DETAIL_RECORDS) ?? [];
4445
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", marginBottom: 1, children: [
4446
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "cyan", bold: true, children: `Viewing ${row.title}` }),
4447
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Text, { dimColor: true, children: [
4448
+ row.statusLabel,
4449
+ row.subtitle ? ` \xB7 ${row.subtitle}` : "",
4450
+ row.preview ? ` \xB7 ${row.preview}` : ""
4451
+ ] }),
4452
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: "Loading workspace detail..." }) : null,
4453
+ error ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "red", children: error }) : null,
4454
+ !loading && !error && records.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: "No detail yet" }) : null,
4455
+ !loading && !error && records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: getDetailRecordColor(record.kind), children: formatExecutionDetailRecord(record) }, record.id)),
4456
+ page?.nextCursor ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: "... more detail available" }) : null
4457
+ ] });
4458
+ }
4459
+ function getDetailRecordColor(kind) {
4460
+ if (kind === "error") return "red";
4461
+ if (kind === "result") return "green";
4462
+ if (kind === "process_output") return "white";
4463
+ if (kind === "group_summary") return "cyan";
4464
+ return void 0;
4465
+ }
4466
+
4467
+ // src/ui/UpdateNotice.tsx
4468
+ var import_ink22 = require("ink");
4469
+ var import_jsx_runtime23 = require("react/jsx-runtime");
4220
4470
  function UpdateNotice({ message }) {
4221
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Box, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: "yellow", children: message }) });
4471
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Box, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { color: "yellow", children: message }) });
4222
4472
  }
4223
4473
 
4224
4474
  // src/utils/update-check.ts
@@ -4313,10 +4563,10 @@ function comparePrereleaseIdentifier(left, right) {
4313
4563
  var CLI_UPDATE_PACKAGE_NAME = "@robota-sdk/agent-cli";
4314
4564
  var CLI_UPDATE_REGISTRY_URL = "https://registry.npmjs.org";
4315
4565
  var HOURS_PER_DAY = 24;
4316
- var MINUTES_PER_HOUR2 = 60;
4317
- var SECONDS_PER_MINUTE2 = 60;
4318
- var MS_PER_SECOND2 = 1e3;
4319
- var CLI_UPDATE_CACHE_TTL_MS = HOURS_PER_DAY * MINUTES_PER_HOUR2 * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
4566
+ var MINUTES_PER_HOUR = 60;
4567
+ var SECONDS_PER_MINUTE = 60;
4568
+ var MS_PER_SECOND = 1e3;
4569
+ var CLI_UPDATE_CACHE_TTL_MS = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND;
4320
4570
  var CLI_UPDATE_TIMEOUT_MS = 1500;
4321
4571
  var DEFAULT_INSTALL_COMMAND = "npm install -g '@robota-sdk/agent-cli@latest'";
4322
4572
  function getUserUpdateCheckCachePath(home = process.env.HOME ?? process.env.USERPROFILE ?? "/") {
@@ -4485,15 +4735,22 @@ function isJsonObject(value) {
4485
4735
  }
4486
4736
 
4487
4737
  // src/ui/App.tsx
4488
- var import_jsx_runtime22 = require("react/jsx-runtime");
4738
+ var import_jsx_runtime24 = require("react/jsx-runtime");
4489
4739
  function App(props) {
4490
- const [activeSessionId, setActiveSessionId] = (0, import_react19.useState)(props.resumeSessionId);
4491
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4740
+ const [activeSessionId, setActiveSessionId] = (0, import_react20.useState)(props.resumeSessionId);
4741
+ const [showInitialSessionPicker, setShowInitialSessionPicker] = (0, import_react20.useState)(
4742
+ props.showSessionPickerOnStart ?? false
4743
+ );
4744
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4492
4745
  AppInner,
4493
4746
  {
4494
4747
  ...props,
4748
+ showSessionPickerOnStart: showInitialSessionPicker,
4495
4749
  resumeSessionId: activeSessionId,
4496
- onSessionSwitch: (sessionId) => setActiveSessionId(sessionId)
4750
+ onSessionSwitch: (sessionId) => {
4751
+ setShowInitialSessionPicker(false);
4752
+ setActiveSessionId(sessionId);
4753
+ }
4497
4754
  },
4498
4755
  activeSessionId ?? "__new__"
4499
4756
  );
@@ -4503,6 +4760,7 @@ function AppInner(props) {
4503
4760
  const {
4504
4761
  interactiveSession,
4505
4762
  registry,
4763
+ commandEffectQueue,
4506
4764
  history,
4507
4765
  addEntry,
4508
4766
  streamingText,
@@ -4511,7 +4769,10 @@ function AppInner(props) {
4511
4769
  isAborting,
4512
4770
  isShuttingDown,
4513
4771
  pendingPrompt,
4514
- backgroundTasks,
4772
+ executionWorkspaceSnapshot,
4773
+ selectedExecutionEntryId,
4774
+ selectExecutionWorkspaceEntry,
4775
+ readExecutionWorkspaceDetail,
4515
4776
  permissionRequest,
4516
4777
  contextState,
4517
4778
  handleSubmit: baseHandleSubmit,
@@ -4530,17 +4791,31 @@ function AppInner(props) {
4530
4791
  backgroundTaskRunners: props.backgroundTaskRunners,
4531
4792
  subagentRunnerFactory: props.subagentRunnerFactory,
4532
4793
  commandModules: props.commandModules,
4533
- commandHostAdapters: props.commandHostAdapters
4794
+ commandHostAdapters: props.commandHostAdapters,
4795
+ webPort: props.webPort,
4796
+ noOpen: props.noOpen
4534
4797
  });
4535
4798
  const fallbackPluginCallbacks = usePluginCallbacks(cwd);
4536
4799
  const pluginCallbacks = props.commandHostAdapters?.plugin ?? fallbackPluginCallbacks;
4537
- const { exit } = (0, import_ink21.useApp)();
4538
- const [sessionName, setSessionName] = (0, import_react19.useState)(props.sessionName);
4539
- const [updateNotice, setUpdateNotice] = (0, import_react19.useState)();
4800
+ const { exit } = (0, import_ink23.useApp)();
4801
+ const [sessionName, setSessionName] = (0, import_react20.useState)(props.sessionName);
4802
+ const [updateNotice, setUpdateNotice] = (0, import_react20.useState)();
4803
+ const [showExecutionWorkspaceSwitcher, setShowExecutionWorkspaceSwitcher] = (0, import_react20.useState)(false);
4804
+ const [executionDetailPage, setExecutionDetailPage] = (0, import_react20.useState)(null);
4805
+ const [executionDetailError, setExecutionDetailError] = (0, import_react20.useState)();
4806
+ const [isExecutionDetailLoading, setIsExecutionDetailLoading] = (0, import_react20.useState)(false);
4540
4807
  const [statusLineSettings, setStatusLineSettings] = useStatusLineSettings();
4541
- const activeBackgroundTaskCount = backgroundTasks.filter(
4542
- (task) => task.status === "queued" || task.status === "running"
4543
- ).length;
4808
+ const backgroundWorkspaceEntries = (0, import_react20.useMemo)(
4809
+ () => getDefaultBackgroundWorkspaceEntries(executionWorkspaceSnapshot),
4810
+ [executionWorkspaceSnapshot]
4811
+ );
4812
+ const activeBackgroundTaskCount = countActiveBackgroundWorkspaceEntries(
4813
+ executionWorkspaceSnapshot
4814
+ );
4815
+ const selectedExecutionEntry = (0, import_react20.useMemo)(
4816
+ () => executionWorkspaceSnapshot?.entries.find((entry) => entry.id === selectedExecutionEntryId),
4817
+ [executionWorkspaceSnapshot, selectedExecutionEntryId]
4818
+ );
4544
4819
  const {
4545
4820
  handleSubmit,
4546
4821
  pendingModelId,
@@ -4556,16 +4831,18 @@ function AppInner(props) {
4556
4831
  cwd,
4557
4832
  providerOverride: props.providerOverride,
4558
4833
  interactiveSession,
4834
+ commandEffectQueue,
4559
4835
  addEntry,
4560
4836
  baseHandleSubmit,
4561
4837
  setSessionName,
4562
- setStatusLineSettings
4838
+ setStatusLineSettings,
4839
+ showSessionPickerOnStart: props.showSessionPickerOnStart
4563
4840
  });
4564
- (0, import_react19.useEffect)(() => {
4841
+ (0, import_react20.useEffect)(() => {
4565
4842
  const name = interactiveSession?.getName?.();
4566
4843
  if (name && !sessionName) setSessionName(name);
4567
4844
  }, [interactiveSession, sessionName]);
4568
- (0, import_react19.useEffect)(() => {
4845
+ (0, import_react20.useEffect)(() => {
4569
4846
  let isMounted = true;
4570
4847
  props.startupUpdateNoticePromise?.then((notice) => {
4571
4848
  if (isMounted && notice !== void 0) {
@@ -4577,20 +4854,27 @@ function AppInner(props) {
4577
4854
  isMounted = false;
4578
4855
  };
4579
4856
  }, [props.startupUpdateNoticePromise]);
4580
- (0, import_react19.useEffect)(() => {
4857
+ (0, import_react20.useEffect)(() => {
4581
4858
  const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
4582
4859
  process.stdout.write(`\x1B]0;${title}\x07`);
4583
4860
  }, [sessionName]);
4584
- (0, import_ink21.useInput)((_input, key) => {
4861
+ (0, import_ink23.useInput)((_input, key) => {
4585
4862
  if (!key.escape || !isThinking) return;
4586
- if (permissionRequest || showPluginTUI || showSessionPicker) return;
4863
+ if (permissionRequest || showPluginTUI || showSessionPicker || showExecutionWorkspaceSwitcher) {
4864
+ return;
4865
+ }
4587
4866
  handleAbort();
4588
4867
  });
4589
- (0, import_ink21.useInput)((input, key) => {
4868
+ (0, import_ink23.useInput)((input, key) => {
4869
+ if (!key.ctrl || input !== "b") return;
4870
+ if (permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown) return;
4871
+ setShowExecutionWorkspaceSwitcher((shown) => !shown);
4872
+ });
4873
+ (0, import_ink23.useInput)((input, key) => {
4590
4874
  if (!key.ctrl || input !== "c" || isShuttingDown) return;
4591
4875
  void handleShutdown("prompt_input_exit").finally(() => exit());
4592
4876
  });
4593
- (0, import_react19.useEffect)(() => {
4877
+ (0, import_react20.useEffect)(() => {
4594
4878
  const onSigterm = () => {
4595
4879
  if (isShuttingDown) return;
4596
4880
  void handleShutdown("other").finally(() => exit());
@@ -4602,6 +4886,29 @@ function AppInner(props) {
4602
4886
  process.off("SIGTERM", onSigterm);
4603
4887
  };
4604
4888
  }, [handleShutdown, exit, isShuttingDown]);
4889
+ (0, import_react20.useEffect)(() => {
4890
+ if (!selectedExecutionEntry || selectedExecutionEntry.kind === "main_thread") {
4891
+ setExecutionDetailPage(null);
4892
+ setExecutionDetailError(void 0);
4893
+ setIsExecutionDetailLoading(false);
4894
+ return;
4895
+ }
4896
+ let isCurrent = true;
4897
+ setIsExecutionDetailLoading(true);
4898
+ setExecutionDetailError(void 0);
4899
+ readExecutionWorkspaceDetail(selectedExecutionEntry.id).then((page) => {
4900
+ if (!isCurrent) return;
4901
+ setExecutionDetailPage(page);
4902
+ setIsExecutionDetailLoading(false);
4903
+ }).catch((error) => {
4904
+ if (!isCurrent) return;
4905
+ setExecutionDetailError(error.message);
4906
+ setIsExecutionDetailLoading(false);
4907
+ });
4908
+ return () => {
4909
+ isCurrent = false;
4910
+ };
4911
+ }, [executionWorkspaceSnapshot, readExecutionWorkspaceDetail, selectedExecutionEntry]);
4605
4912
  let permissionMode = props.permissionMode ?? "default";
4606
4913
  let sessionId = "";
4607
4914
  try {
@@ -4610,25 +4917,33 @@ function AppInner(props) {
4610
4917
  sessionId = session.getSessionId();
4611
4918
  } catch {
4612
4919
  }
4613
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", children: [
4614
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4615
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "cyan", bold: true, children: `
4920
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { flexDirection: "column", children: [
4921
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4922
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { color: "cyan", bold: true, children: `
4616
4923
  ____ ___ ____ ___ _____ _
4617
4924
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
4618
4925
  | |_) | | | | _ \\| | | || | / _ \\
4619
4926
  | _ <| |_| | |_) | |_| || |/ ___ \\
4620
4927
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
4621
4928
  ` }),
4622
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Text, { dimColor: true, children: [
4929
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Text, { dimColor: true, children: [
4623
4930
  " v",
4624
4931
  props.version ?? "0.0.0"
4625
4932
  ] })
4626
4933
  ] }),
4627
- updateNotice && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(UpdateNotice, { message: formatCliUpdateNotice(updateNotice) }),
4628
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
4629
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageList, { history }),
4630
- isShuttingDown && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "yellow", children: "Shutting down..." }) }),
4631
- (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4934
+ updateNotice && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(UpdateNotice, { message: formatCliUpdateNotice(updateNotice) }),
4935
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_ink23.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
4936
+ selectedExecutionEntry && selectedExecutionEntry.kind !== "main_thread" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4937
+ ExecutionWorkspaceDetailPane,
4938
+ {
4939
+ entry: selectedExecutionEntry,
4940
+ page: executionDetailPage,
4941
+ loading: isExecutionDetailLoading,
4942
+ error: executionDetailError
4943
+ }
4944
+ ) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(MessageList, { history }),
4945
+ isShuttingDown && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { color: "yellow", children: "Shutting down..." }) }),
4946
+ (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4632
4947
  StreamingIndicator,
4633
4948
  {
4634
4949
  text: streamingText,
@@ -4636,17 +4951,26 @@ function AppInner(props) {
4636
4951
  isThinking
4637
4952
  }
4638
4953
  ) }),
4639
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(BackgroundTaskPanel, { tasks: backgroundTasks })
4954
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(BackgroundTaskPanel, { entries: backgroundWorkspaceEntries })
4640
4955
  ] }),
4641
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PermissionPrompt, { request: permissionRequest }),
4642
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4956
+ showExecutionWorkspaceSwitcher && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4957
+ ExecutionWorkspaceSwitcher,
4958
+ {
4959
+ snapshot: executionWorkspaceSnapshot,
4960
+ selectedEntryId: selectedExecutionEntryId,
4961
+ onSelect: selectExecutionWorkspaceEntry,
4962
+ onClose: () => setShowExecutionWorkspaceSwitcher(false)
4963
+ }
4964
+ ),
4965
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PermissionPrompt, { request: permissionRequest }),
4966
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4643
4967
  ConfirmPrompt,
4644
4968
  {
4645
4969
  message: formatModelChangeConfirmationMessage(pendingModelId),
4646
4970
  onSelect: handleModelConfirm
4647
4971
  }
4648
4972
  ),
4649
- pendingInteractionPrompt && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4973
+ pendingInteractionPrompt && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4650
4974
  InteractivePrompt,
4651
4975
  {
4652
4976
  prompt: pendingInteractionPrompt,
@@ -4654,7 +4978,7 @@ function AppInner(props) {
4654
4978
  onCancel: handleInteractionCancel
4655
4979
  }
4656
4980
  ),
4657
- showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4981
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4658
4982
  PluginTUI,
4659
4983
  {
4660
4984
  callbacks: pluginCallbacks,
@@ -4662,11 +4986,10 @@ function AppInner(props) {
4662
4986
  addMessage: (msg) => addEntry((0, import_agent_core11.messageToHistoryEntry)((0, import_agent_core11.createSystemMessage)(msg.content)))
4663
4987
  }
4664
4988
  ),
4665
- showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4989
+ showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4666
4990
  SessionPicker,
4667
4991
  {
4668
- sessionStore: props.sessionStore,
4669
- cwd: props.cwd,
4992
+ sessions: (0, import_agent_sdk9.listResumableSessionSummaries)(props.sessionStore, props.cwd),
4670
4993
  onSelect: (id) => {
4671
4994
  setShowSessionPicker(false);
4672
4995
  props.onSessionSwitch(id);
@@ -4677,14 +5000,15 @@ function AppInner(props) {
4677
5000
  }
4678
5001
  }
4679
5002
  ),
4680
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5003
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4681
5004
  SessionStatusBar,
4682
5005
  {
4683
5006
  cwd,
4684
5007
  permissionMode,
4685
5008
  modelId: props.modelId,
5009
+ providerProfileName: props.providerProfileName,
5010
+ providerType: props.providerType,
4686
5011
  sessionId,
4687
- messageCount: history.length,
4688
5012
  isThinking,
4689
5013
  activeToolCount: activeTools.length,
4690
5014
  activeBackgroundTaskCount,
@@ -4694,12 +5018,12 @@ function AppInner(props) {
4694
5018
  settings: statusLineSettings
4695
5019
  }
4696
5020
  ),
4697
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5021
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4698
5022
  InputArea,
4699
5023
  {
4700
5024
  onSubmit: handleSubmit,
4701
5025
  onCancelQueue: handleCancelQueue,
4702
- isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || pendingInteractionPrompt !== null || isThinking && !!pendingPrompt,
5026
+ isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || showExecutionWorkspaceSwitcher || isShuttingDown || pendingInteractionPrompt !== null || isThinking && !!pendingPrompt,
4703
5027
  isAborting,
4704
5028
  pendingPrompt,
4705
5029
  registry,
@@ -4707,12 +5031,12 @@ function AppInner(props) {
4707
5031
  history
4708
5032
  }
4709
5033
  ),
4710
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { children: " " })
5034
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { children: " " })
4711
5035
  ] });
4712
5036
  }
4713
5037
 
4714
5038
  // src/ui/render.tsx
4715
- var import_jsx_runtime23 = require("react/jsx-runtime");
5039
+ var import_jsx_runtime25 = require("react/jsx-runtime");
4716
5040
  function renderApp(options) {
4717
5041
  process.on("unhandledRejection", (reason) => {
4718
5042
  process.stderr.write(`
@@ -4723,7 +5047,7 @@ function renderApp(options) {
4723
5047
  `);
4724
5048
  }
4725
5049
  });
4726
- const instance = (0, import_ink22.render)(/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(App, { ...options }), {
5050
+ const instance = (0, import_ink24.render)(/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(App, { ...options }), {
4727
5051
  exitOnCtrlC: false
4728
5052
  });
4729
5053
  instance.waitUntilExit().then(() => {
@@ -4740,43 +5064,14 @@ function renderApp(options) {
4740
5064
 
4741
5065
  // src/background/managed-shell-process-runner.ts
4742
5066
  var import_node_child_process = require("child_process");
4743
- var import_agent_sdk9 = require("@robota-sdk/agent-sdk");
5067
+ var import_agent_sdk10 = require("@robota-sdk/agent-sdk");
4744
5068
  var DEFAULT_OUTPUT_LIMIT_BYTES = 3e4;
4745
5069
  var DEFAULT_KILL_GRACE_MS = 2e3;
4746
- var LOG_PAGE_SIZE = 200;
4747
- function createCapture(limitBytes) {
4748
- const chunks = [];
4749
- let capturedBytes = 0;
4750
- let truncated = false;
4751
- return {
4752
- appendOutput(text) {
4753
- if (truncated) return;
4754
- const remaining = limitBytes - capturedBytes;
4755
- const buffer = Buffer.from(text, "utf8");
4756
- if (buffer.byteLength <= remaining) {
4757
- chunks.push(text);
4758
- capturedBytes += buffer.byteLength;
4759
- return;
4760
- }
4761
- chunks.push(buffer.subarray(0, Math.max(remaining, 0)).toString("utf8"));
4762
- chunks.push("\n[output truncated]\n");
4763
- truncated = true;
4764
- },
4765
- getOutput() {
4766
- return chunks.join("");
4767
- }
4768
- };
4769
- }
4770
- function appendLog(lines, source, text) {
4771
- for (const line of text.split(/\r?\n/)) {
4772
- if (line.length > 0) lines.push(`[${source}] ${line}`);
4773
- }
4774
- }
4775
5070
  function resolveShell(request) {
4776
5071
  return { command: request.shell ?? "sh", args: ["-c", request.command] };
4777
5072
  }
4778
5073
  function sendInput(child, input) {
4779
- return new Promise((resolve2, reject) => {
5074
+ return new Promise((resolve3, reject) => {
4780
5075
  const onError = (error) => {
4781
5076
  child.stdin.off("error", onError);
4782
5077
  reject(error);
@@ -4784,7 +5079,7 @@ function sendInput(child, input) {
4784
5079
  child.stdin.once("error", onError);
4785
5080
  child.stdin.end(input, () => {
4786
5081
  child.stdin.off("error", onError);
4787
- resolve2();
5082
+ resolve3();
4788
5083
  });
4789
5084
  });
4790
5085
  }
@@ -4794,7 +5089,7 @@ function createManagedShellProcessRunner(options = {}) {
4794
5089
  kind: "process",
4795
5090
  start(task) {
4796
5091
  if (task.request.kind !== "process") {
4797
- throw new import_agent_sdk9.BackgroundTaskError("runner", `Invalid process task kind: ${task.request.kind}`);
5092
+ throw new import_agent_sdk10.BackgroundTaskError("runner", `Invalid process task kind: ${task.request.kind}`);
4798
5093
  }
4799
5094
  return startProcessTask(task.taskId, task.request, killGraceMs);
4800
5095
  }
@@ -4811,7 +5106,9 @@ function startProcessTask(taskId, request, killGraceMs) {
4811
5106
  stdio: ["pipe", "pipe", "pipe"]
4812
5107
  }),
4813
5108
  logs: [],
4814
- capture: createCapture(request.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES),
5109
+ capture: (0, import_agent_sdk10.createLimitedOutputCapture)({
5110
+ limitBytes: request.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES
5111
+ }),
4815
5112
  killGraceMs
4816
5113
  };
4817
5114
  const result = createProcessResult(runtime);
@@ -4819,11 +5116,15 @@ function startProcessTask(taskId, request, killGraceMs) {
4819
5116
  }
4820
5117
  function createProcessResult(runtime) {
4821
5118
  let settled = false;
4822
- return new Promise((resolve2, reject) => {
5119
+ return new Promise((resolve3, reject) => {
4823
5120
  const timeoutTimer = runtime.request.timeoutMs ? setTimeout(() => {
4824
- appendLog(runtime.logs, "system", `timed out after ${runtime.request.timeoutMs}ms`);
5121
+ (0, import_agent_sdk10.appendPrefixedLogLines)(
5122
+ runtime.logs,
5123
+ "system",
5124
+ `timed out after ${runtime.request.timeoutMs}ms`
5125
+ );
4825
5126
  runtime.child.kill("SIGTERM");
4826
- rejectOnceLocal(new import_agent_sdk9.BackgroundTaskError("timeout", "Background process timed out"));
5127
+ rejectOnceLocal(new import_agent_sdk10.BackgroundTaskError("timeout", "Background process timed out"));
4827
5128
  }, runtime.request.timeoutMs) : void 0;
4828
5129
  function clearTimers() {
4829
5130
  if (timeoutTimer) clearTimeout(timeoutTimer);
@@ -4833,7 +5134,7 @@ function createProcessResult(runtime) {
4833
5134
  if (settled) return;
4834
5135
  settled = true;
4835
5136
  clearTimers();
4836
- resolve2({
5137
+ resolve3({
4837
5138
  taskId: runtime.taskId,
4838
5139
  kind: "process",
4839
5140
  output: runtime.capture.getOutput(),
@@ -4849,8 +5150,8 @@ function createProcessResult(runtime) {
4849
5150
  }
4850
5151
  attachOutputListeners(runtime);
4851
5152
  runtime.child.on("error", (error) => {
4852
- appendLog(runtime.logs, "system", error.message);
4853
- rejectOnceLocal(new import_agent_sdk9.BackgroundTaskError("process", error.message));
5153
+ (0, import_agent_sdk10.appendPrefixedLogLines)(runtime.logs, "system", error.message);
5154
+ rejectOnceLocal(new import_agent_sdk10.BackgroundTaskError("process", error.message));
4854
5155
  });
4855
5156
  runtime.child.on("close", (code, signal) => {
4856
5157
  resolveOnce(code ?? void 0, signal ?? void 0);
@@ -4880,40 +5181,37 @@ function attachOutputListeners(runtime) {
4880
5181
  runtime.child.stdout.on("data", (chunk) => {
4881
5182
  const text = chunk.toString("utf8");
4882
5183
  runtime.capture.appendOutput(text);
4883
- appendLog(runtime.logs, "stdout", text);
5184
+ (0, import_agent_sdk10.appendPrefixedLogLines)(runtime.logs, "stdout", text);
4884
5185
  });
4885
5186
  runtime.child.stderr.on("data", (chunk) => {
4886
5187
  const text = chunk.toString("utf8");
4887
5188
  runtime.capture.appendOutput(text);
4888
- appendLog(runtime.logs, "stderr", text);
5189
+ (0, import_agent_sdk10.appendPrefixedLogLines)(runtime.logs, "stderr", text);
4889
5190
  });
4890
5191
  }
4891
5192
  function cancelProcess(runtime, reason) {
4892
- appendLog(runtime.logs, "system", reason ? `cancel requested: ${reason}` : "cancel requested");
5193
+ (0, import_agent_sdk10.appendPrefixedLogLines)(
5194
+ runtime.logs,
5195
+ "system",
5196
+ reason ? `cancel requested: ${reason}` : "cancel requested"
5197
+ );
4893
5198
  if (!runtime.child.killed) runtime.child.kill("SIGTERM");
4894
5199
  runtime.killTimer = setTimeout(() => {
4895
5200
  if (!runtime.child.killed) runtime.child.kill("SIGKILL");
4896
5201
  }, runtime.killGraceMs);
4897
5202
  }
4898
5203
  function readProcessLog(runtime, cursor) {
4899
- const offset = cursor?.offset ?? 0;
4900
- const nextOffset = Math.min(offset + LOG_PAGE_SIZE, runtime.logs.length);
4901
- return {
4902
- taskId: runtime.taskId,
4903
- cursor,
4904
- nextCursor: nextOffset < runtime.logs.length ? { offset: nextOffset } : void 0,
4905
- lines: runtime.logs.slice(offset, nextOffset)
4906
- };
5204
+ return (0, import_agent_sdk10.createBackgroundTaskLogPage)(runtime.taskId, runtime.logs, cursor);
4907
5205
  }
4908
5206
 
4909
5207
  // src/subagents/child-process-subagent-runner.ts
4910
5208
  var import_node_child_process3 = require("child_process");
4911
5209
  var import_node_fs7 = require("fs");
4912
5210
  var import_node_path9 = require("path");
4913
- var import_agent_sdk13 = require("@robota-sdk/agent-sdk");
5211
+ var import_agent_sdk14 = require("@robota-sdk/agent-sdk");
4914
5212
 
4915
5213
  // src/subagents/child-process-subagent-runner-result.ts
4916
- var import_agent_sdk11 = require("@robota-sdk/agent-sdk");
5214
+ var import_agent_sdk12 = require("@robota-sdk/agent-sdk");
4917
5215
 
4918
5216
  // src/subagents/child-process-subagent-ipc.ts
4919
5217
  function isRecord2(value) {
@@ -4945,7 +5243,7 @@ function isSubagentWorkerChildMessage(value) {
4945
5243
  }
4946
5244
 
4947
5245
  // src/subagents/child-process-subagent-transport.ts
4948
- var import_agent_sdk10 = require("@robota-sdk/agent-sdk");
5246
+ var import_agent_sdk11 = require("@robota-sdk/agent-sdk");
4949
5247
  function handleWorkerMessage(message, startWorker, resolveOnce, rejectOnce, emit) {
4950
5248
  switch (message.type) {
4951
5249
  case "ready":
@@ -4955,10 +5253,10 @@ function handleWorkerMessage(message, startWorker, resolveOnce, rejectOnce, emit
4955
5253
  resolveOnce(message.output);
4956
5254
  break;
4957
5255
  case "error":
4958
- rejectOnce(new import_agent_sdk10.BackgroundTaskError("runner", message.message));
5256
+ rejectOnce(new import_agent_sdk11.BackgroundTaskError("runner", message.message));
4959
5257
  break;
4960
5258
  case "cancelled":
4961
- rejectOnce(new import_agent_sdk10.BackgroundTaskError("runner", message.reason ?? "Subagent worker cancelled"));
5259
+ rejectOnce(new import_agent_sdk11.BackgroundTaskError("runner", message.reason ?? "Subagent worker cancelled"));
4962
5260
  break;
4963
5261
  case "text_delta":
4964
5262
  emit?.({ type: "background_task_text_delta", delta: message.delta });
@@ -4978,7 +5276,7 @@ function handleWorkerMessage(message, startWorker, resolveOnce, rejectOnce, emit
4978
5276
  });
4979
5277
  break;
4980
5278
  default:
4981
- rejectOnce(new import_agent_sdk10.BackgroundTaskError("runner", "Unhandled subagent worker message"));
5279
+ rejectOnce(new import_agent_sdk11.BackgroundTaskError("runner", "Unhandled subagent worker message"));
4982
5280
  }
4983
5281
  }
4984
5282
  function extractFirstArg(toolArgs) {
@@ -4988,9 +5286,9 @@ function extractFirstArg(toolArgs) {
4988
5286
  return typeof firstValue === "object" ? JSON.stringify(firstValue) : String(firstValue);
4989
5287
  }
4990
5288
  function sendWorkerMessage(child, message) {
4991
- return new Promise((resolve2, reject) => {
5289
+ return new Promise((resolve3, reject) => {
4992
5290
  if (!child.connected) {
4993
- reject(new import_agent_sdk10.BackgroundTaskError("crash", "Subagent worker IPC channel is closed"));
5291
+ reject(new import_agent_sdk11.BackgroundTaskError("crash", "Subagent worker IPC channel is closed"));
4994
5292
  return;
4995
5293
  }
4996
5294
  child.send(message, (error) => {
@@ -4998,7 +5296,7 @@ function sendWorkerMessage(child, message) {
4998
5296
  reject(error);
4999
5297
  return;
5000
5298
  }
5001
- resolve2();
5299
+ resolve3();
5002
5300
  });
5003
5301
  });
5004
5302
  }
@@ -5015,14 +5313,14 @@ async function cancelChildProcess(runtime, reason) {
5015
5313
 
5016
5314
  // src/subagents/child-process-subagent-runner-result.ts
5017
5315
  function createChildProcessSubagentResult(options) {
5018
- return new Promise((resolve2, reject) => {
5019
- new ChildProcessSubagentResultController(options, resolve2, reject).start();
5316
+ return new Promise((resolve3, reject) => {
5317
+ new ChildProcessSubagentResultController(options, resolve3, reject).start();
5020
5318
  });
5021
5319
  }
5022
5320
  var ChildProcessSubagentResultController = class {
5023
- constructor(options, resolve2, reject) {
5321
+ constructor(options, resolve3, reject) {
5024
5322
  this.options = options;
5025
- this.resolve = resolve2;
5323
+ this.resolve = resolve3;
5026
5324
  this.reject = reject;
5027
5325
  this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));
5028
5326
  }
@@ -5051,7 +5349,7 @@ var ChildProcessSubagentResultController = class {
5051
5349
  onMessage = (message) => {
5052
5350
  if (!isSubagentWorkerChildMessage(message)) {
5053
5351
  this.rejectOnce(
5054
- new import_agent_sdk11.BackgroundTaskError("runner", "Received malformed subagent worker message")
5352
+ new import_agent_sdk12.BackgroundTaskError("runner", "Received malformed subagent worker message")
5055
5353
  );
5056
5354
  return;
5057
5355
  }
@@ -5059,11 +5357,11 @@ var ChildProcessSubagentResultController = class {
5059
5357
  handleWorkerMessage(message, this.startWorker, this.resolveOnce, this.rejectOnce, job.emit);
5060
5358
  };
5061
5359
  onError = (error) => {
5062
- this.rejectOnce(new import_agent_sdk11.BackgroundTaskError("crash", error.message));
5360
+ this.rejectOnce(new import_agent_sdk12.BackgroundTaskError("crash", error.message));
5063
5361
  };
5064
5362
  onExit = (code, signal) => {
5065
5363
  if (this.settled) return;
5066
- this.rejectOnce(new import_agent_sdk11.BackgroundTaskError("crash", formatEarlyExitMessage(code, signal)));
5364
+ this.rejectOnce(new import_agent_sdk12.BackgroundTaskError("crash", formatEarlyExitMessage(code, signal)));
5067
5365
  };
5068
5366
  resolveOnce = (output) => {
5069
5367
  if (this.settled) return;
@@ -5103,7 +5401,7 @@ function createCancellationResult(jobId) {
5103
5401
  reject(reason) {
5104
5402
  if (settled) return;
5105
5403
  settled = true;
5106
- rejectFn(new import_agent_sdk11.BackgroundTaskError("runner", reason ?? `Subagent job cancelled: ${jobId}`));
5404
+ rejectFn(new import_agent_sdk12.BackgroundTaskError("runner", reason ?? `Subagent job cancelled: ${jobId}`));
5107
5405
  }
5108
5406
  };
5109
5407
  }
@@ -5111,7 +5409,7 @@ function createTimeoutTimer(runtime, rejectOnce) {
5111
5409
  if (!runtime.job.request.timeoutMs) return void 0;
5112
5410
  return setTimeout(() => {
5113
5411
  void cancelChildProcess(runtime, "Subagent worker timed out");
5114
- rejectOnce(new import_agent_sdk11.BackgroundTaskError("timeout", "Subagent worker timed out"));
5412
+ rejectOnce(new import_agent_sdk12.BackgroundTaskError("timeout", "Subagent worker timed out"));
5115
5413
  }, runtime.job.request.timeoutMs);
5116
5414
  }
5117
5415
  function toSubagentResult(job, output, resolveTranscriptPath) {
@@ -5129,10 +5427,10 @@ function formatEarlyExitMessage(code, signal) {
5129
5427
 
5130
5428
  // src/subagents/git-worktree-isolation-adapter.ts
5131
5429
  var import_node_child_process2 = require("child_process");
5132
- var import_node_crypto2 = require("crypto");
5430
+ var import_node_crypto = require("crypto");
5133
5431
  var import_node_fs6 = require("fs");
5134
5432
  var import_node_path8 = require("path");
5135
- var import_agent_sdk12 = require("@robota-sdk/agent-sdk");
5433
+ var import_agent_sdk13 = require("@robota-sdk/agent-sdk");
5136
5434
  var WORKTREE_DIR = ".robota/worktrees";
5137
5435
  var BRANCH_PREFIX = "robota";
5138
5436
  var GIT_ENCODING = "utf8";
@@ -5155,7 +5453,7 @@ var GitWorktreeIsolationAdapter = class {
5155
5453
  }
5156
5454
  prepare(request) {
5157
5455
  if (this.maxCreateAttempts < 1) {
5158
- throw new import_agent_sdk12.BackgroundTaskError("runner", "Git worktree creation attempts must be at least 1");
5456
+ throw new import_agent_sdk13.BackgroundTaskError("runner", "Git worktree creation attempts must be at least 1");
5159
5457
  }
5160
5458
  const repoRoot = resolveRepoRoot(request.cwd);
5161
5459
  const baseRevision = runGit(repoRoot, ["rev-parse", "HEAD"]).trim();
@@ -5176,7 +5474,7 @@ var GitWorktreeIsolationAdapter = class {
5176
5474
  if (!isCollisionError(lastError)) throw lastError;
5177
5475
  }
5178
5476
  }
5179
- throw new import_agent_sdk12.BackgroundTaskError(
5477
+ throw new import_agent_sdk13.BackgroundTaskError(
5180
5478
  "runner",
5181
5479
  `Unable to create Git worktree after ${this.maxCreateAttempts} attempts due to branch or path collisions. Last error: ${lastError?.message ?? "unknown error"}`
5182
5480
  );
@@ -5202,7 +5500,7 @@ function runGit(cwd, args) {
5202
5500
  });
5203
5501
  } catch (error) {
5204
5502
  const message = error instanceof Error ? error.message : String(error);
5205
- throw new import_agent_sdk12.BackgroundTaskError("runner", `git ${args.join(" ")} failed: ${message}`);
5503
+ throw new import_agent_sdk13.BackgroundTaskError("runner", `git ${args.join(" ")} failed: ${message}`);
5206
5504
  }
5207
5505
  }
5208
5506
  function createGitEnvironment() {
@@ -5219,14 +5517,14 @@ function resolveRepoRoot(cwd) {
5219
5517
  return runGit(cwd, ["rev-parse", "--show-toplevel"]).trim();
5220
5518
  } catch (error) {
5221
5519
  const message = error instanceof Error ? error.message : String(error);
5222
- throw new import_agent_sdk12.BackgroundTaskError(
5520
+ throw new import_agent_sdk13.BackgroundTaskError(
5223
5521
  "runner",
5224
5522
  `Worktree isolation requires a Git repository. Run from a Git worktree or request isolation "none". Details: ${message}`
5225
5523
  );
5226
5524
  }
5227
5525
  }
5228
5526
  function createShortId() {
5229
- return (0, import_node_crypto2.randomUUID)().slice(0, SHORT_ID_LENGTH);
5527
+ return (0, import_node_crypto.randomUUID)().slice(0, SHORT_ID_LENGTH);
5230
5528
  }
5231
5529
  function normalizeShortId(value) {
5232
5530
  const sanitized = sanitizePathSegment(value).slice(0, SHORT_ID_LENGTH);
@@ -5245,12 +5543,11 @@ function isCollisionError(error) {
5245
5543
 
5246
5544
  // src/subagents/child-process-subagent-runner.ts
5247
5545
  var DEFAULT_KILL_GRACE_MS2 = 2e3;
5248
- var LOG_PAGE_SIZE2 = 200;
5249
5546
  function createChildProcessSubagentRunnerFactory(options = {}) {
5250
5547
  return (deps) => {
5251
5548
  const runner = new ChildProcessSubagentRunner(deps, options);
5252
5549
  if (options.worktreeIsolation === false) return runner;
5253
- return (0, import_agent_sdk13.createWorktreeSubagentRunner)({
5550
+ return (0, import_agent_sdk14.createWorktreeSubagentRunner)({
5254
5551
  runner,
5255
5552
  worktreeAdapter: options.worktreeAdapter ?? createGitWorktreeIsolationAdapter(),
5256
5553
  hooks: deps.config.hooks,
@@ -5332,9 +5629,9 @@ var ChildProcessSubagentRunner = class {
5332
5629
  }
5333
5630
  };
5334
5631
  function resolveAgentDefinition(agentType, customRegistry) {
5335
- const definition = customRegistry?.(agentType) ?? (0, import_agent_sdk13.getBuiltInAgent)(agentType);
5632
+ const definition = customRegistry?.(agentType) ?? (0, import_agent_sdk14.getBuiltInAgent)(agentType);
5336
5633
  if (!definition) {
5337
- throw new import_agent_sdk13.BackgroundTaskError("validation", `Unknown agent type: ${agentType}`);
5634
+ throw new import_agent_sdk14.BackgroundTaskError("validation", `Unknown agent type: ${agentType}`);
5338
5635
  }
5339
5636
  return definition;
5340
5637
  }
@@ -5383,7 +5680,6 @@ function resolveDefaultExecArgv(workerPath) {
5383
5680
  return [...process.execArgv, "--import", "tsx"];
5384
5681
  }
5385
5682
  function readTranscriptLog(jobId, transcriptPath, cursor) {
5386
- const offset = cursor?.offset ?? 0;
5387
5683
  if (!(0, import_node_fs7.existsSync)(transcriptPath)) {
5388
5684
  return {
5389
5685
  taskId: jobId,
@@ -5392,13 +5688,30 @@ function readTranscriptLog(jobId, transcriptPath, cursor) {
5392
5688
  };
5393
5689
  }
5394
5690
  const lines = (0, import_node_fs7.readFileSync)(transcriptPath, "utf8").split(/\r?\n/).filter(Boolean);
5395
- const nextOffset = Math.min(offset + LOG_PAGE_SIZE2, lines.length);
5396
- return {
5397
- taskId: jobId,
5398
- cursor,
5399
- nextCursor: nextOffset < lines.length ? { offset: nextOffset } : void 0,
5400
- lines: lines.slice(offset, nextOffset)
5401
- };
5691
+ return (0, import_agent_sdk14.createBackgroundTaskLogPage)(jobId, lines, cursor);
5692
+ }
5693
+
5694
+ // src/user-local-direct-command.ts
5695
+ var import_agent_command_user_local = require("@robota-sdk/agent-command-user-local");
5696
+ async function runUserLocalDirectCommandIfRequested(args, cwd) {
5697
+ if (args.positional[0] !== "user-local") {
5698
+ return false;
5699
+ }
5700
+ const result = await (0, import_agent_command_user_local.executeUserLocalDirectCommand)({
5701
+ cwd,
5702
+ argv: args.positional.slice(1),
5703
+ format: args.format,
5704
+ summary: args.summary,
5705
+ source: args.source
5706
+ });
5707
+ const output = result.message.endsWith("\n") ? result.message : `${result.message}
5708
+ `;
5709
+ if (!result.success) {
5710
+ process.stderr.write(output);
5711
+ process.exit(1);
5712
+ }
5713
+ process.stdout.write(output);
5714
+ return true;
5402
5715
  }
5403
5716
 
5404
5717
  // src/cli.ts
@@ -5424,11 +5737,19 @@ function readVersion() {
5424
5737
  }
5425
5738
  }
5426
5739
  function promptInput(label, masked = false) {
5427
- return new Promise((resolve2) => {
5740
+ return new Promise((resolve3, reject) => {
5428
5741
  process.stdout.write(label);
5429
5742
  let input = "";
5430
5743
  const stdin = process.stdin;
5431
5744
  const wasRaw = stdin.isRaw;
5745
+ if (!stdin.isTTY) {
5746
+ reject(
5747
+ new Error(
5748
+ "Cannot prompt for input: stdin is not a TTY.\nSet your API key via environment variable instead:\n ANTHROPIC_API_KEY=<key> robota\n OPENAI_API_KEY=<key> robota"
5749
+ )
5750
+ );
5751
+ return;
5752
+ }
5432
5753
  stdin.setRawMode(true);
5433
5754
  stdin.resume();
5434
5755
  stdin.setEncoding("utf8");
@@ -5439,7 +5760,7 @@ function promptInput(label, masked = false) {
5439
5760
  stdin.setRawMode(wasRaw ?? false);
5440
5761
  stdin.pause();
5441
5762
  process.stdout.write("\n");
5442
- resolve2(input.trim());
5763
+ resolve3(input.trim());
5443
5764
  return;
5444
5765
  } else if (ch === "\x7F" || ch === "\b") {
5445
5766
  if (input.length > 0) {
@@ -5458,6 +5779,15 @@ function promptInput(label, masked = false) {
5458
5779
  stdin.on("data", onData);
5459
5780
  });
5460
5781
  }
5782
+ function readTaskFilePrompt(cwd, taskFile) {
5783
+ const taskPath = (0, import_node_path10.resolve)(cwd, taskFile);
5784
+ const content = (0, import_node_fs8.readFileSync)(taskPath, "utf8").trim();
5785
+ if (content.length === 0) {
5786
+ throw new Error(`Task file is empty: ${taskFile}`);
5787
+ }
5788
+ return `Task file (${taskFile}):
5789
+ ${content}`;
5790
+ }
5461
5791
  function resetConfig() {
5462
5792
  const userPath = getUserSettingsPath();
5463
5793
  if (deleteSettings(userPath)) {
@@ -5467,6 +5797,43 @@ function resetConfig() {
5467
5797
  process.stdout.write("No user settings found.\n");
5468
5798
  }
5469
5799
  }
5800
+ function createDefaultCliCommandModules({
5801
+ cwd,
5802
+ providerDefinitions
5803
+ }) {
5804
+ return [
5805
+ (0, import_agent_command_skills.createSkillsCommandModule)({ cwd }),
5806
+ (0, import_agent_command_help.createHelpCommandModule)(),
5807
+ (0, import_agent_command_agent.createAgentCommandModule)(),
5808
+ (0, import_agent_command_model.createModelCommandModule)({
5809
+ providerDefinitions,
5810
+ settings: {
5811
+ readMergedSettings: () => readMergedProviderSettings(cwd)
5812
+ }
5813
+ }),
5814
+ (0, import_agent_command_permissions.createPermissionsCommandModule)(),
5815
+ (0, import_agent_command_language.createLanguageCommandModule)(),
5816
+ (0, import_agent_command_background.createBackgroundCommandModule)(),
5817
+ (0, import_agent_command_memory.createMemoryCommandModule)(),
5818
+ (0, import_agent_command_user_local2.createUserLocalCommandModule)(),
5819
+ (0, import_agent_command_compact.createCompactCommandModule)(),
5820
+ (0, import_agent_command_context.createContextCommandModule)(),
5821
+ (0, import_agent_command_exit.createExitCommandModule)(),
5822
+ (0, import_agent_command_session.createSessionCommandModule)(),
5823
+ (0, import_agent_command_reset.createResetCommandModule)(),
5824
+ (0, import_agent_command_rewind.createRewindCommandModule)(),
5825
+ (0, import_agent_command_statusline.createStatusLineCommandModule)(),
5826
+ (0, import_agent_command_plugin.createPluginCommandModule)(),
5827
+ (0, import_agent_command_provider.createProviderCommandModule)({
5828
+ providerDefinitions,
5829
+ settings: {
5830
+ readMergedSettings: () => readMergedProviderSettings(cwd),
5831
+ readTargetSettings: () => readSettings(resolveProviderSettingsWriteTargetPath(cwd)),
5832
+ writeTargetSettings: (settings) => writeSettings(resolveProviderSettingsWriteTargetPath(cwd), settings)
5833
+ }
5834
+ })
5835
+ ];
5836
+ }
5470
5837
  async function startCli(options = {}) {
5471
5838
  const args = parseCliArgs();
5472
5839
  const version = readVersion();
@@ -5492,6 +5859,9 @@ async function startCli(options = {}) {
5492
5859
  return;
5493
5860
  }
5494
5861
  const cwd = process.cwd();
5862
+ if (await runUserLocalDirectCommandIfRequested(args, cwd)) {
5863
+ return;
5864
+ }
5495
5865
  const commandHostAdapters = {
5496
5866
  settings: {
5497
5867
  read: () => readSettings(getUserSettingsPath()),
@@ -5501,30 +5871,7 @@ async function startCli(options = {}) {
5501
5871
  };
5502
5872
  const providerDefinitions = options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
5503
5873
  const commandModules = [
5504
- (0, import_agent_command_help.createHelpCommandModule)(),
5505
- (0, import_agent_command_agent.createAgentCommandModule)(),
5506
- (0, import_agent_command_model.createModelCommandModule)(),
5507
- (0, import_agent_command_mode.createModeCommandModule)(),
5508
- (0, import_agent_command_permissions.createPermissionsCommandModule)(),
5509
- (0, import_agent_command_language.createLanguageCommandModule)(),
5510
- (0, import_agent_command_background.createBackgroundCommandModule)(),
5511
- (0, import_agent_command_memory.createMemoryCommandModule)(),
5512
- (0, import_agent_command_compact.createCompactCommandModule)(),
5513
- (0, import_agent_command_context.createContextCommandModule)(),
5514
- (0, import_agent_command_exit.createExitCommandModule)(),
5515
- (0, import_agent_command_session.createSessionCommandModule)(),
5516
- (0, import_agent_command_reset.createResetCommandModule)(),
5517
- (0, import_agent_command_rewind.createRewindCommandModule)(),
5518
- (0, import_agent_command_statusline.createStatusLineCommandModule)(),
5519
- (0, import_agent_command_plugin.createPluginCommandModule)(),
5520
- (0, import_agent_command_provider.createProviderCommandModule)({
5521
- providerDefinitions,
5522
- settings: {
5523
- readMergedSettings: () => readMergedProviderSettings(cwd),
5524
- readTargetSettings: () => readSettings(getUserSettingsPath()),
5525
- writeTargetSettings: (settings) => writeSettings(getUserSettingsPath(), settings)
5526
- }
5527
- }),
5874
+ ...createDefaultCliCommandModules({ cwd, providerDefinitions }),
5528
5875
  ...options.commandModules ?? []
5529
5876
  ];
5530
5877
  const startupUpdateNoticePromise = shouldRunStartupCliUpdateCheck(args) ? getStartupCliUpdateNotice({ currentVersion: version }) : void 0;
@@ -5543,31 +5890,28 @@ async function startCli(options = {}) {
5543
5890
  process.exit(1);
5544
5891
  }
5545
5892
  const providerOptions = args.provider ? { providerOverride: args.provider, providerDefinitions } : { providerDefinitions };
5893
+ const activeProviderSettings = readMergedProviderSettings(cwd);
5894
+ const providerProfileName = args.provider ?? activeProviderSettings.currentProvider;
5546
5895
  const providerSettings = readProviderSettings(cwd, providerOptions);
5547
5896
  const modelId = args.model ?? providerSettings.model;
5548
5897
  const provider = createProviderFromSettings(cwd, args.model, providerOptions);
5549
5898
  const backgroundTaskRunners = [createManagedShellProcessRunner()];
5550
- const paths = (0, import_agent_sdk14.projectPaths)(cwd);
5899
+ const paths = (0, import_agent_sdk15.projectPaths)(cwd);
5551
5900
  const subagentRunnerFactory = createChildProcessSubagentRunnerFactory({
5552
5901
  providerConfig: { ...providerSettings, model: modelId },
5553
5902
  logsDir: paths.logs
5554
5903
  });
5555
- const sessionStore = new import_agent_sessions.SessionStore(paths.sessions);
5904
+ const sessionStore = (0, import_agent_sdk15.createProjectSessionStore)(cwd);
5556
5905
  let resumeSessionId;
5906
+ let showSessionPickerOnStart = false;
5557
5907
  if (args.continueMode) {
5558
- const sessions = sessionStore.list().filter((s) => s.cwd === cwd);
5559
- if (sessions.length > 0) {
5560
- resumeSessionId = sessions[0].id;
5561
- }
5908
+ resumeSessionId = (0, import_agent_sdk15.resolveLatestSessionId)(sessionStore, cwd);
5562
5909
  } else if (args.resumeId !== void 0) {
5563
5910
  if (args.resumeId === "") {
5564
- resumeSessionId = "__picker__";
5911
+ showSessionPickerOnStart = true;
5565
5912
  } else {
5566
- const sessions = sessionStore.list();
5567
- const match = sessions.find((s) => s.id === args.resumeId || s.name === args.resumeId);
5568
- if (match) {
5569
- resumeSessionId = match.id;
5570
- } else {
5913
+ resumeSessionId = (0, import_agent_sdk15.resolveSessionIdByIdOrName)(sessionStore, args.resumeId);
5914
+ if (resumeSessionId === void 0) {
5571
5915
  process.stderr.write(`Session not found: ${args.resumeId}
5572
5916
  `);
5573
5917
  process.exit(1);
@@ -5589,13 +5933,25 @@ async function startCli(options = {}) {
5589
5933
  }
5590
5934
  const appendParts = [];
5591
5935
  if (args.appendSystemPrompt) appendParts.push(args.appendSystemPrompt);
5936
+ if (args.taskFile) {
5937
+ try {
5938
+ appendParts.push(readTaskFilePrompt(cwd, args.taskFile));
5939
+ } catch (error) {
5940
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}
5941
+ `);
5942
+ process.exit(1);
5943
+ }
5944
+ }
5592
5945
  if (args.jsonSchema)
5593
5946
  appendParts.push(
5594
5947
  `Respond with valid JSON only, matching this JSON schema:
5595
5948
  ${args.jsonSchema}`
5596
5949
  );
5597
5950
  const appendSystemPrompt = appendParts.length > 0 ? appendParts.join("\n\n") : void 0;
5598
- const session = new import_agent_sdk14.InteractiveSession({
5951
+ if (args.systemPrompt) {
5952
+ process.stderr.write("Warning: --system-prompt is not yet functional and will be ignored.\n");
5953
+ }
5954
+ const session = new import_agent_sdk15.InteractiveSession({
5599
5955
  cwd,
5600
5956
  provider,
5601
5957
  permissionMode: args.permissionMode ?? "bypassPermissions",
@@ -5623,6 +5979,8 @@ ${args.jsonSchema}`
5623
5979
  cwd,
5624
5980
  provider,
5625
5981
  providerOverride: args.provider,
5982
+ providerProfileName,
5983
+ providerType: providerSettings.name,
5626
5984
  modelId,
5627
5985
  language: args.language,
5628
5986
  permissionMode: args.permissionMode,
@@ -5630,13 +5988,16 @@ ${args.jsonSchema}`
5630
5988
  version,
5631
5989
  sessionStore,
5632
5990
  resumeSessionId,
5991
+ showSessionPickerOnStart,
5633
5992
  forkSession: args.forkSession,
5634
5993
  sessionName: args.sessionName,
5635
5994
  backgroundTaskRunners,
5636
5995
  subagentRunnerFactory,
5637
5996
  commandModules,
5638
5997
  commandHostAdapters,
5639
- startupUpdateNoticePromise
5998
+ startupUpdateNoticePromise,
5999
+ webPort: args.web ? args.webPort : void 0,
6000
+ noOpen: args.noOpen
5640
6001
  });
5641
6002
  }
5642
6003
  // Annotate the CommonJS export names for ESM import in node: