@couralabs/claudin 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/claudin.js +577 -167
  2. package/package.json +1 -1
package/claudin.js CHANGED
@@ -5928,6 +5928,12 @@ var backgroundColorSchema = object({
5928
5928
  a: channelSchema
5929
5929
  });
5930
5930
  var TRANSPARENT_BACKGROUND = { r: 0, g: 0, b: 0, a: 0 };
5931
+ // packages/events/src/models/customTools.ts
5932
+ var CUSTOM_SERVER_NAME = "claudin";
5933
+ var BATCH_EDIT_BASE_NAME = "BatchEdit";
5934
+ var BATCH_EDIT_TOOL_NAME = `mcp__${CUSTOM_SERVER_NAME}__${BATCH_EDIT_BASE_NAME}`;
5935
+ var CUSTOM_PREFIX = `mcp__${CUSTOM_SERVER_NAME}__`;
5936
+ var stripCustomPrefix = (toolName) => toolName.startsWith(CUSTOM_PREFIX) ? toolName.slice(CUSTOM_PREFIX.length) : toolName;
5931
5937
  // packages/events/src/models/fileDiff.ts
5932
5938
  var fileDiffSchema = object({
5933
5939
  path: string2(),
@@ -6064,6 +6070,7 @@ function appendEntry(state, entry) {
6064
6070
  };
6065
6071
  }
6066
6072
  var appendNotice = (state, text) => appendEntry(state, { kind: "notice", text });
6073
+ var appendMarkdownNotice = (state, text) => appendEntry(state, { kind: "notice", text, markdown: true });
6067
6074
 
6068
6075
  // packages/events/src/reducers/reduceHistory.ts
6069
6076
  var NO_MATCH_NOTICE = "No earlier prompt matches";
@@ -6111,6 +6118,7 @@ var agentSessionId = string2();
6111
6118
  var content = sdkValue();
6112
6119
  var agentOptions = sdkValue().optional();
6113
6120
  var effortLevelSchema = _enum(["low", "medium", "high", "xhigh", "max"]);
6121
+ var DEFAULT_EFFORT = "high";
6114
6122
  var permissionModeSchema = _enum(["auto", "acceptEdits", "bypassPermissions", "dontAsk"]);
6115
6123
  var DEFAULT_PERMISSION_MODE = "bypassPermissions";
6116
6124
  var PERMISSION_MODE_LABELS = {
@@ -6145,8 +6153,6 @@ var agentRequestSchemas = [
6145
6153
  options: agentOptions
6146
6154
  }),
6147
6155
  object({ type: literal("agent.prompt.requested"), agentSessionId, content }),
6148
- object({ type: literal("agent.prompt.queue.requested"), agentSessionId, content }),
6149
- object({ type: literal("agent.prompt.steer.requested"), agentSessionId, content }),
6150
6156
  object({ type: literal("agent.prompt.cancel.requested"), agentSessionId }),
6151
6157
  object({ type: literal("agent.task.stop.requested"), agentSessionId, taskId: string2() }),
6152
6158
  object({
@@ -6218,6 +6224,7 @@ var createSessionState = (sessionId) => ({
6218
6224
  models: [],
6219
6225
  sessionList: [],
6220
6226
  queuedTurns: 0,
6227
+ queuedPrompts: [],
6221
6228
  tasks: [],
6222
6229
  transcript: [],
6223
6230
  entryCounter: 0,
@@ -6317,10 +6324,11 @@ function describeSystemMessage(message) {
6317
6324
  var OPEN_STATUSES = new Set(["running", "awaiting_approval", "awaiting_answer"]);
6318
6325
  var isToolsEntry = (entry) => entry.kind === "tools";
6319
6326
  var listToolCalls = (state) => state.transcript.filter(isToolsEntry).flatMap((entry) => entry.calls);
6320
- var EDIT_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
6321
- var readEditedPath = ({ input }) => input.file_path ?? input.notebook_path;
6327
+ var EDIT_TOOLS = new Set(["Write", "Edit", "MultiEdit", "mcp__claudin__BatchEdit"]);
6328
+ var listBatchEditPaths = (input) => Array.isArray(input.edits) ? input.edits.map((edit) => edit?.file_path) : [];
6329
+ var readEditedPaths = (call) => call.name === BATCH_EDIT_TOOL_NAME ? listBatchEditPaths(call.input) : EDIT_TOOLS.has(call.name) ? [call.input.file_path ?? call.input.notebook_path] : [];
6322
6330
  var listEditedFiles = (state) => [
6323
- ...new Set(listToolCalls(state).filter((call) => EDIT_TOOLS.has(call.name) && call.status === "completed").map(readEditedPath).filter((path) => typeof path === "string"))
6331
+ ...new Set(listToolCalls(state).filter((call) => call.status === "completed").flatMap(readEditedPaths).filter((path) => typeof path === "string"))
6324
6332
  ];
6325
6333
  function countTurnTools(state) {
6326
6334
  const lastUser = state.transcript.findLastIndex((entry) => entry.kind === "user");
@@ -6519,6 +6527,43 @@ function reduceStreamEvent(state, event) {
6519
6527
  }
6520
6528
  }
6521
6529
 
6530
+ // packages/events/src/reducers/reduceTaskModel.ts
6531
+ var readMessageModel = (message) => {
6532
+ if (message.type === "assistant")
6533
+ return message.message.model;
6534
+ if (message.type !== "stream_event")
6535
+ return;
6536
+ const event = message.event;
6537
+ return event.type === "message_start" ? event.message?.model : undefined;
6538
+ };
6539
+ var readRequestedModel = (state, toolUseId) => {
6540
+ const model = listToolCalls(state).find((call) => call.id === toolUseId)?.input.model;
6541
+ return typeof model === "string" ? model : undefined;
6542
+ };
6543
+ function reduceTaskModel(state, message) {
6544
+ if (message.type !== "assistant" && message.type !== "stream_event")
6545
+ return state;
6546
+ const model = readMessageModel(message);
6547
+ const parentId = message.parent_tool_use_id;
6548
+ const stale = (task) => task.toolUseId === parentId && task.model !== model;
6549
+ if (!model || !parentId || !state.tasks.some(stale))
6550
+ return state;
6551
+ const tasks = state.tasks.map((task) => stale(task) ? { ...task, model } : task);
6552
+ return { ...state, tasks };
6553
+ }
6554
+
6555
+ // packages/events/src/reducers/reduceTaskToolUse.ts
6556
+ function reduceTaskToolUse(state, message) {
6557
+ if (message.type !== "assistant" || !message.parent_tool_use_id)
6558
+ return state;
6559
+ const call = message.message.content.findLast((block) => block.type === "tool_use");
6560
+ if (call?.type !== "tool_use")
6561
+ return state;
6562
+ const lastToolCall = { name: call.name, input: call.input ?? {} };
6563
+ const tasks = state.tasks.map((task) => task.toolUseId === message.parent_tool_use_id ? { ...task, lastToolCall } : task);
6564
+ return { ...state, tasks };
6565
+ }
6566
+
6522
6567
  // packages/events/src/reducers/reduceTaskProgress.ts
6523
6568
  function reduceTaskProgress(state, message) {
6524
6569
  const { tool_use_id: toolUseId, summary } = message;
@@ -6539,6 +6584,8 @@ var startTask = (state, message) => {
6539
6584
  kind: message.subagent_type ?? message.task_type ?? "task",
6540
6585
  description: message.description,
6541
6586
  startedAt: state.now,
6587
+ toolUseId: message.tool_use_id,
6588
+ model: readRequestedModel(state, message.tool_use_id),
6542
6589
  tokens: 0,
6543
6590
  paused: false
6544
6591
  };
@@ -6609,9 +6656,12 @@ function reduceAgentMessage(state, message) {
6609
6656
  };
6610
6657
  case "stream_event":
6611
6658
  if (message.parent_tool_use_id)
6612
- return state;
6659
+ return reduceTaskModel(state, message);
6613
6660
  return reduceStreamEvent(state, message.event);
6614
6661
  case "assistant":
6662
+ if (message.parent_tool_use_id) {
6663
+ return reduceTaskToolUse(reduceTaskModel(state, message), message);
6664
+ }
6615
6665
  return reduceAssistant(state, message);
6616
6666
  case "user":
6617
6667
  return isPromptEcho(message) ? reduceCheckpoint(state, message) : reduceToolResults(state, message);
@@ -6628,7 +6678,8 @@ function reduceTaskMessage(state, message) {
6628
6678
  return reduceTaskProgress(state, message);
6629
6679
  }
6630
6680
  if (message.type === "system" && message.subtype === "task_notification") {
6631
- return reduceNoticeMessage(state, message);
6681
+ const text = describeSystemMessage(message);
6682
+ return text ? appendMarkdownNotice(state, text) : state;
6632
6683
  }
6633
6684
  return state;
6634
6685
  }
@@ -6812,6 +6863,23 @@ function changeQuestionCustom(state, text) {
6812
6863
  return question ? withDraft(state, { ...question, custom: text }) : state;
6813
6864
  }
6814
6865
 
6866
+ // packages/events/src/reducers/reduceTheme.ts
6867
+ var applyLoadedTheme = (state, theme = state.theme) => ({
6868
+ ...state,
6869
+ theme
6870
+ });
6871
+ var applySavedTheme = (state, theme) => appendNotice({ ...state, theme }, `Theme set to ${theme}`);
6872
+
6873
+ // packages/events/src/reducers/reducePromptQueue.ts
6874
+ var queuePrompt = (state, content) => ({
6875
+ ...state,
6876
+ queuedPrompts: [...state.queuedPrompts, content]
6877
+ });
6878
+ var dequeuePrompt = (state) => ({
6879
+ ...state,
6880
+ queuedPrompts: state.queuedPrompts.slice(1)
6881
+ });
6882
+
6815
6883
  // packages/events/src/reducers/reduceToast.ts
6816
6884
  var withToast = (state, toast) => ({
6817
6885
  ...state,
@@ -6839,6 +6907,8 @@ var pickerSize = (state, kind) => {
6839
6907
  return state.models.length;
6840
6908
  if (kind === "theme")
6841
6909
  return THEME_NAMES.length;
6910
+ if (kind === "effort")
6911
+ return effortLevelSchema.options.length;
6842
6912
  if (kind === "rewind")
6843
6913
  return listCheckpoints(state).length;
6844
6914
  return state.sessionList.length;
@@ -6857,6 +6927,11 @@ var openThemePicker = (state) => {
6857
6927
  const selected = Math.max(THEME_NAMES.indexOf(state.theme), 0);
6858
6928
  return { ...state, ui: { ...state.ui, picker: { kind: "theme", selected } } };
6859
6929
  };
6930
+ var openEffortPicker = (state) => {
6931
+ const current = state.effort || DEFAULT_EFFORT;
6932
+ const selected = Math.max(effortLevelSchema.options.findIndex((level) => level === current), 0);
6933
+ return { ...state, ui: { ...state.ui, picker: { kind: "effort", selected } } };
6934
+ };
6860
6935
  function openRewindPicker(state) {
6861
6936
  const size = listCheckpoints(state).length;
6862
6937
  if (size === 0)
@@ -6881,8 +6956,12 @@ function reduceUi(state, event) {
6881
6956
  return { ...state, ui: { ...state.ui, picker: null } };
6882
6957
  case "ui.theme.picker.opened":
6883
6958
  return openThemePicker(state);
6884
- case "ui.theme.selected":
6885
- return appendNotice({ ...state, theme: event.theme }, `Theme set to ${event.theme}`);
6959
+ case "ui.effort.picker.opened":
6960
+ return openEffortPicker(state);
6961
+ case "ui.prompt.queued":
6962
+ return queuePrompt(state, event.content);
6963
+ case "ui.prompt.dequeued":
6964
+ return dequeuePrompt(state);
6886
6965
  case "ui.rewind.picker.opened":
6887
6966
  return openRewindPicker(state);
6888
6967
  case "ui.history.moved":
@@ -6961,9 +7040,6 @@ var reduceTimedEvent = (state, event, at) => reduceSessionState({ ...state, now:
6961
7040
  function reduceSessionState(state, event) {
6962
7041
  switch (event.type) {
6963
7042
  case "agent.prompt.requested":
6964
- return reducePrompt(state, event.content, false);
6965
- case "agent.prompt.queue.requested":
6966
- case "agent.prompt.steer.requested":
6967
7043
  return reducePrompt(state, event.content, state.status !== "idle");
6968
7044
  case "agent.message.received":
6969
7045
  return reduceAgentMessage(state, event.message);
@@ -7040,9 +7116,11 @@ function reduceSessionState(state, event) {
7040
7116
  case "workspace.info.received":
7041
7117
  return { ...state, cwd: event.cwd, gitBranch: event.gitBranch, gitStats: event.gitStats };
7042
7118
  case "options.loaded":
7043
- return applyLoadedBackground(state, event.background);
7119
+ return applyLoadedTheme(applyLoadedBackground(state, event.background), event.theme);
7044
7120
  case "options.background.saved":
7045
7121
  return applySavedBackground(state, event.color);
7122
+ case "options.theme.saved":
7123
+ return applySavedTheme(state, event.theme);
7046
7124
  case "workspace.files.received":
7047
7125
  return updateFileMatches(state, event.query, event.matches);
7048
7126
  default:
@@ -7055,13 +7133,22 @@ var optionsRequestSchemas = [
7055
7133
  object({
7056
7134
  type: literal("options.background.save.requested"),
7057
7135
  color: backgroundColorSchema
7058
- })
7136
+ }),
7137
+ object({ type: literal("options.theme.save.requested"), theme: themeNameSchema })
7059
7138
  ];
7060
7139
  var optionsResponseSchemas = [
7061
- object({ type: literal("options.loaded"), background: backgroundColorSchema.optional() }),
7062
- object({ type: literal("options.background.saved"), color: backgroundColorSchema })
7140
+ object({
7141
+ type: literal("options.loaded"),
7142
+ background: backgroundColorSchema.optional(),
7143
+ theme: themeNameSchema.optional()
7144
+ }),
7145
+ object({ type: literal("options.background.saved"), color: backgroundColorSchema }),
7146
+ object({ type: literal("options.theme.saved"), theme: themeNameSchema })
7063
7147
  ];
7064
- var optionsFileSchema = looseObject({ background: backgroundColorSchema.optional() });
7148
+ var optionsFileSchema = looseObject({
7149
+ background: backgroundColorSchema.optional(),
7150
+ theme: themeNameSchema.optional()
7151
+ });
7065
7152
  // packages/events/src/requests/sessionRequestEvents.ts
7066
7153
  var sessionRequestSchemas = [
7067
7154
  object({ type: literal("session.create.requested") }),
@@ -7122,7 +7209,6 @@ var agentResponseSchemas = [
7122
7209
  }),
7123
7210
  object({ type: literal("agent.session.created"), agentSessionId: agentSessionId2 }),
7124
7211
  object({ type: literal("agent.message.received"), message: unknown() }),
7125
- object({ type: literal("agent.prompt.accepted"), agentSessionId: agentSessionId2 }),
7126
7212
  object({ type: literal("agent.prompt.cancelled"), agentSessionId: agentSessionId2 }),
7127
7213
  object({
7128
7214
  type: literal("agent.tool.approval.required"),
@@ -7532,11 +7618,10 @@ var sdkRuntime = {
7532
7618
  };
7533
7619
  // packages/agent/src/runtime/buildUserMessage.ts
7534
7620
  var isSlashCommand = (content) => readPromptText(content).trimStart().startsWith("/");
7535
- var buildUserMessage = (content, priority) => ({
7621
+ var buildUserMessage = (content) => ({
7536
7622
  type: "user",
7537
7623
  message: { role: "user", content },
7538
7624
  parent_tool_use_id: null,
7539
- priority,
7540
7625
  client_composed: isSlashCommand(content) ? undefined : true
7541
7626
  });
7542
7627
  // node_modules/.bun/p-defer@4.0.1/node_modules/p-defer/index.js
@@ -8761,6 +8846,110 @@ class PermissionGate {
8761
8846
  }
8762
8847
  }
8763
8848
 
8849
+ // packages/agent/src/tools/customToolsServer.ts
8850
+ import { createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
8851
+
8852
+ // packages/agent/src/tools/batchEdit/batchEditTool.ts
8853
+ import { tool } from "@anthropic-ai/claude-agent-sdk";
8854
+
8855
+ // packages/agent/src/tools/batchEdit/batchEditSchema.ts
8856
+ var textEditSchema = object({
8857
+ file_path: string2().describe("Absolute path of the file to modify"),
8858
+ old_string: string2().describe("Exact text to replace; empty only to create a new file with new_string"),
8859
+ new_string: string2().describe("Replacement text; must differ from old_string"),
8860
+ replace_all: boolean2().optional().describe("Replace every occurrence of old_string")
8861
+ });
8862
+ var batchEditShape = {
8863
+ edits: array(textEditSchema).min(1).describe("Edits applied in order; edits to one file see the result of earlier ones")
8864
+ };
8865
+
8866
+ // packages/agent/src/tools/batchEdit/stageEdits.ts
8867
+ import { isAbsolute, resolve } from "path";
8868
+
8869
+ // packages/agent/src/tools/batchEdit/applyTextEdit.ts
8870
+ var failEdit = (reason) => ({ ok: false, reason });
8871
+ function applyTextEdit(content, edit) {
8872
+ const { old_string: oldText, new_string: newText, replace_all: replaceAll } = edit;
8873
+ if (oldText === newText)
8874
+ return failEdit("old_string and new_string are identical");
8875
+ if (oldText === "") {
8876
+ return content === "" ? { ok: true, content: newText, replacements: 1 } : failEdit("old_string is empty but the file already has content");
8877
+ }
8878
+ const parts = content.split(oldText);
8879
+ const matches = parts.length - 1;
8880
+ if (matches === 0)
8881
+ return failEdit("old_string was not found in the file");
8882
+ if (matches > 1 && !replaceAll) {
8883
+ return failEdit(`old_string matches ${matches} times; add context or set replace_all`);
8884
+ }
8885
+ return { ok: true, content: parts.join(newText), replacements: matches };
8886
+ }
8887
+
8888
+ // packages/agent/src/tools/batchEdit/stageEdits.ts
8889
+ var failStage = (index, edit, reason) => ({
8890
+ ok: false,
8891
+ message: `Edit ${index + 1} (${edit.file_path}): ${reason}`
8892
+ });
8893
+ async function stageEdits(edits, readFile) {
8894
+ const files = new Map;
8895
+ for (const [index, edit] of edits.entries()) {
8896
+ if (!isAbsolute(edit.file_path))
8897
+ return failStage(index, edit, "file_path must be absolute");
8898
+ const path = resolve(edit.file_path);
8899
+ const current = files.get(path)?.content ?? await readFile(path);
8900
+ if (current === undefined && edit.old_string !== "") {
8901
+ return failStage(index, edit, "the file does not exist");
8902
+ }
8903
+ const outcome = applyTextEdit(current ?? "", edit);
8904
+ if (!outcome.ok)
8905
+ return failStage(index, edit, outcome.reason);
8906
+ const replacements = (files.get(path)?.replacements ?? 0) + outcome.replacements;
8907
+ files.set(path, { content: outcome.content, replacements });
8908
+ }
8909
+ return { ok: true, files };
8910
+ }
8911
+
8912
+ // packages/agent/src/tools/batchEdit/runBatchEdit.ts
8913
+ var readFileText = async (path) => {
8914
+ const file = Bun.file(path);
8915
+ return await file.exists() ? file.text() : undefined;
8916
+ };
8917
+ var describeFiles = (files) => [...files].map(([path, { replacements }]) => `- ${path}: ${replacements} replacement(s)`).join(`
8918
+ `);
8919
+ async function runBatchEdit(edits, readFile = readFileText) {
8920
+ const staged = await stageEdits(edits, readFile);
8921
+ if (!staged.ok) {
8922
+ return { isError: true, text: `${staged.message}
8923
+ No files were changed.` };
8924
+ }
8925
+ try {
8926
+ await Promise.all([...staged.files].map(([path, { content }]) => Bun.write(path, content)));
8927
+ } catch (error) {
8928
+ return { isError: true, text: `Writing failed, files may be partly changed: ${error}` };
8929
+ }
8930
+ return {
8931
+ isError: false,
8932
+ text: `Applied ${edits.length} edit(s) to ${staged.files.size} file(s):
8933
+ ${describeFiles(staged.files)}`
8934
+ };
8935
+ }
8936
+
8937
+ // packages/agent/src/tools/batchEdit/batchEditTool.ts
8938
+ var DESCRIPTION = `Apply many exact-string edits across one or more files in a single call. Each edit works like Edit (file_path, old_string, new_string, replace_all). Edits run in order, and edits to the same file build on each other. The batch is all-or-nothing: if any edit fails validation, no file is changed.`;
8939
+ var batchEditTool = tool(BATCH_EDIT_BASE_NAME, DESCRIPTION, batchEditShape, async ({ edits }) => {
8940
+ const { isError, text } = await runBatchEdit(edits);
8941
+ return { isError, content: [{ type: "text", text }] };
8942
+ });
8943
+
8944
+ // packages/agent/src/tools/customToolsServer.ts
8945
+ var CUSTOM_MCP_SERVERS = {
8946
+ [CUSTOM_SERVER_NAME]: createSdkMcpServer({
8947
+ name: CUSTOM_SERVER_NAME,
8948
+ tools: [batchEditTool],
8949
+ alwaysLoad: true
8950
+ })
8951
+ };
8952
+
8764
8953
  // packages/agent/src/profile/adhdSystemPrompt.ts
8765
8954
  var ADHD_SYSTEM_PROMPT = `For the rest of this session, shape every response for a reader with ADHD. The output is not just brief; it is shaped so an ADHD brain can act on it.
8766
8955
 
@@ -8875,8 +9064,7 @@ var AGENT_TOOLS = [
8875
9064
  "TodoWrite",
8876
9065
  "AskUserQuestion",
8877
9066
  "Write",
8878
- "Edit",
8879
- "NotebookEdit"
9067
+ "Edit"
8880
9068
  ];
8881
9069
  var AGENT_DISALLOWED_TOOLS = [
8882
9070
  "Agent(Explore)",
@@ -8903,11 +9091,12 @@ var TOOL_USAGE = {
8903
9091
  AskUserQuestion: "ask the user",
8904
9092
  Write: "create new files with content",
8905
9093
  Edit: "update files",
8906
- NotebookEdit: "update notebook cells"
9094
+ mcp__claudin__BatchEdit: "update files mutiples files or multiple replaces in same file"
8907
9095
  };
8908
9096
  var TOOL_GUIDANCE = `Always use the dedicated tool, never a workaround:
8909
9097
  ${AGENT_TOOLS.map((tool) => `- Use *${tool}* to ${TOOL_USAGE[tool]}`).join(`
8910
- `)}`;
9098
+ `)}
9099
+ - Use *${BATCH_EDIT_TOOL_NAME}* instead of repeated *Edit* calls to make several edits, across one or many files, in one call`;
8911
9100
 
8912
9101
  // packages/agent/src/profile/agentSystemPrompt.ts
8913
9102
  var AGENT_SYSTEM_PROMPT = [ADHD_SYSTEM_PROMPT, TOOL_GUIDANCE, SUBAGENT_GUIDANCE].join(`
@@ -8917,6 +9106,8 @@ var AGENT_SYSTEM_PROMPT = [ADHD_SYSTEM_PROMPT, TOOL_GUIDANCE, SUBAGENT_GUIDANCE]
8917
9106
  // packages/agent/src/profile/agentProfile.ts
8918
9107
  var AGENT_PROFILE_OPTIONS = {
8919
9108
  tools: AGENT_TOOLS,
9109
+ mcpServers: CUSTOM_MCP_SERVERS,
9110
+ toolAliases: { [BATCH_EDIT_BASE_NAME]: BATCH_EDIT_TOOL_NAME },
8920
9111
  disallowedTools: AGENT_DISALLOWED_TOOLS,
8921
9112
  systemPrompt: AGENT_SYSTEM_PROMPT,
8922
9113
  agents: AGENT_SUBAGENTS,
@@ -8951,11 +9142,25 @@ var AGENT_PROFILE_OPTIONS = {
8951
9142
  includeHookEvents: true
8952
9143
  };
8953
9144
 
8954
- // packages/agent/src/session/readBackgroundTaskCount.ts
8955
- function readBackgroundTaskCount(message, current) {
8956
- if (message.type !== "system" || message.subtype !== "background_tasks_changed")
9145
+ // packages/agent/src/session/readLiveTaskIds.ts
9146
+ var FINISHED_STATUSES2 = ["completed", "failed", "killed"];
9147
+ var withTask = (current, id) => new Set([...current, id]);
9148
+ var withoutTask = (current, id) => new Set([...current].filter((taskId) => taskId !== id));
9149
+ function readLiveTaskIds(message, current) {
9150
+ if (message.type !== "system")
8957
9151
  return current;
8958
- return message.tasks.length;
9152
+ switch (message.subtype) {
9153
+ case "background_tasks_changed":
9154
+ return new Set(message.tasks.filter((task) => !task.ambient).map((task) => task.task_id));
9155
+ case "task_started":
9156
+ return message.ambient ? current : withTask(current, message.task_id);
9157
+ case "task_updated":
9158
+ return FINISHED_STATUSES2.includes(message.patch.status ?? "") ? withoutTask(current, message.task_id) : current;
9159
+ case "task_notification":
9160
+ return withoutTask(current, message.task_id);
9161
+ default:
9162
+ return current;
9163
+ }
8959
9164
  }
8960
9165
 
8961
9166
  // packages/agent/src/session/agentSession.ts
@@ -8972,7 +9177,7 @@ class AgentSession {
8972
9177
  elicitations;
8973
9178
  query;
8974
9179
  pendingTurns = 0;
8975
- backgroundTaskCount = 0;
9180
+ liveTaskIds = new Set;
8976
9181
  pumping = false;
8977
9182
  constructor(agentSessionId, baseOptions, runtime) {
8978
9183
  this.baseOptions = baseOptions;
@@ -8988,13 +9193,13 @@ class AgentSession {
8988
9193
  }
8989
9194
  });
8990
9195
  }
8991
- prompt(content, priority) {
8992
- this.send(content, priority);
9196
+ prompt(content) {
9197
+ this.send(content);
8993
9198
  return this.streamTurn();
8994
9199
  }
8995
- send(content, priority) {
9200
+ send(content) {
8996
9201
  this.pendingTurns += 1;
8997
- this.input.push(buildUserMessage(content, priority));
9202
+ this.input.push(buildUserMessage(content));
8998
9203
  }
8999
9204
  async* streamTurn() {
9000
9205
  if (this.pumping)
@@ -9094,11 +9299,11 @@ class AgentSession {
9094
9299
  if (done)
9095
9300
  break;
9096
9301
  sink.push({ type: "agent.message.received", message: value });
9097
- this.backgroundTaskCount = readBackgroundTaskCount(value, this.backgroundTaskCount);
9302
+ this.liveTaskIds = readLiveTaskIds(value, this.liveTaskIds);
9098
9303
  if (value.type !== "result")
9099
9304
  continue;
9100
9305
  this.pendingTurns = Math.max(this.pendingTurns - 1, 0);
9101
- if (this.pendingTurns === 0 && this.backgroundTaskCount === 0)
9306
+ if (this.pendingTurns === 0 && this.liveTaskIds.size === 0)
9102
9307
  break;
9103
9308
  }
9104
9309
  sink.end();
@@ -9165,19 +9370,21 @@ async function readOptions2(path) {
9165
9370
  const parsed = optionsFileSchema.safeParse(stored);
9166
9371
  return parsed.success ? parsed.data : {};
9167
9372
  }
9168
- async function saveBackground(path, background) {
9169
- await Bun.write(path, JSON.stringify({ ...await readOptions2(path), background }, null, 2));
9170
- }
9373
+ var saveOption = async (path, option) => {
9374
+ await Bun.write(path, JSON.stringify({ ...await readOptions2(path), ...option }, null, 2));
9375
+ };
9376
+ var saveBackground = (path, background) => saveOption(path, { background });
9377
+ var saveTheme = (path, theme) => saveOption(path, { theme });
9171
9378
 
9172
9379
  // packages/daemon/src/workspace/resolvePath.ts
9173
9380
  import { stat } from "fs/promises";
9174
9381
  import { homedir as homedir2 } from "os";
9175
- import { join as join3, resolve } from "path";
9382
+ import { join as join3, resolve as resolve2 } from "path";
9176
9383
  var isDirectory = async (path) => (await stat(path).catch(() => {
9177
9384
  return;
9178
9385
  }))?.isDirectory() ?? false;
9179
9386
  var expandHome = (path) => path === "~" || path.startsWith("~/") ? join3(homedir2(), path.slice(1)) : path;
9180
- var resolveUserPath = (base, path) => resolve(base, expandHome(path.trim()));
9387
+ var resolveUserPath = (base, path) => resolve2(base, expandHome(path.trim()));
9181
9388
 
9182
9389
  // packages/daemon/src/server/daemonContext.ts
9183
9390
  var SESSIONS_DIRECTORY = join4(homedir3(), ".claudin", "sessions");
@@ -9245,11 +9452,7 @@ async function createDaemonContext(store = new FileSessionStore(SESSIONS_DIRECTO
9245
9452
  // packages/daemon/src/client/replayClient.ts
9246
9453
  var MAX_GAP_MS = 1500;
9247
9454
  var READ_ONLY_REASON = "Replay is read-only";
9248
- var PROMPT_TYPES = new Set([
9249
- "agent.prompt.requested",
9250
- "agent.prompt.queue.requested",
9251
- "agent.prompt.steer.requested"
9252
- ]);
9455
+ var PROMPT_TYPES = new Set(["agent.prompt.requested"]);
9253
9456
 
9254
9457
  class ReplayClient {
9255
9458
  stored;
@@ -9362,16 +9565,6 @@ var agentHandlers = {
9362
9565
  async* "agent.prompt.requested"({ agents }, request) {
9363
9566
  yield* agents.requireSession(request.agentSessionId).prompt(request.content);
9364
9567
  },
9365
- async* "agent.prompt.queue.requested"({ agents }, request) {
9366
- const turn = agents.requireSession(request.agentSessionId).prompt(request.content, "later");
9367
- yield { type: "agent.prompt.accepted", agentSessionId: request.agentSessionId };
9368
- yield* turn;
9369
- },
9370
- async* "agent.prompt.steer.requested"({ agents }, request) {
9371
- const turn = agents.requireSession(request.agentSessionId).prompt(request.content, "now");
9372
- yield { type: "agent.prompt.accepted", agentSessionId: request.agentSessionId };
9373
- yield* turn;
9374
- },
9375
9568
  async* "agent.prompt.cancel.requested"({ agents }, request) {
9376
9569
  await agents.requireSession(request.agentSessionId).cancel();
9377
9570
  yield { type: "agent.prompt.cancelled", agentSessionId: request.agentSessionId };
@@ -9455,12 +9648,16 @@ var agentHandlers = {
9455
9648
  // packages/daemon/src/handlers/optionsHandlers.ts
9456
9649
  var optionsHandlers = {
9457
9650
  async* "options.load.requested"({ optionsPath }) {
9458
- const { background } = await readOptions2(optionsPath);
9459
- yield { type: "options.loaded", background };
9651
+ const { background, theme } = await readOptions2(optionsPath);
9652
+ yield { type: "options.loaded", background, theme };
9460
9653
  },
9461
9654
  async* "options.background.save.requested"({ optionsPath }, request) {
9462
9655
  await saveBackground(optionsPath, request.color);
9463
9656
  yield { type: "options.background.saved", color: request.color };
9657
+ },
9658
+ async* "options.theme.save.requested"({ optionsPath }, request) {
9659
+ await saveTheme(optionsPath, request.theme);
9660
+ yield { type: "options.theme.saved", theme: request.theme };
9464
9661
  }
9465
9662
  };
9466
9663
 
@@ -9496,14 +9693,14 @@ var sessionHandlers = {
9496
9693
  };
9497
9694
 
9498
9695
  // packages/daemon/src/workspace/readFileDiffs.ts
9499
- import { relative, resolve as resolve2 } from "path";
9696
+ import { relative, resolve as resolve3 } from "path";
9500
9697
  var isTracked = async (cwd, path) => (await Bun.$`git ls-files --error-unmatch -- ${path}`.cwd(cwd).quiet().nothrow()).exitCode === 0;
9501
9698
  async function readPatch(cwd, path) {
9502
9699
  const command = await isTracked(cwd, path) ? Bun.$`git diff --no-color HEAD -- ${path}` : Bun.$`git diff --no-color --no-index -- /dev/null ${path}`;
9503
9700
  return (await command.cwd(cwd).quiet().nothrow()).text();
9504
9701
  }
9505
9702
  async function readFileDiff(cwd, path) {
9506
- const absolute = resolve2(cwd, path);
9703
+ const absolute = resolve3(cwd, path);
9507
9704
  const patch = await readPatch(cwd, absolute);
9508
9705
  return {
9509
9706
  path: relative(cwd, absolute) || path,
@@ -11211,7 +11408,7 @@ async function startDaemon(port = DEFAULT_PORT, context) {
11211
11408
  }
11212
11409
  // apps/tui/src/startTui.ts
11213
11410
  import {
11214
- BoxRenderable as BoxRenderable18,
11411
+ BoxRenderable as BoxRenderable19,
11215
11412
  createCliRenderer,
11216
11413
  createHostClipboard
11217
11414
  } from "@opentui/core";
@@ -11253,7 +11450,7 @@ function buildPalette(colors, background = TRANSPARENT_BACKGROUND) {
11253
11450
  },
11254
11451
  fg: {
11255
11452
  primary: hex("text"),
11256
- secondary: rgbToHex2(tint(color("text"), color("textMuted"), SECONDARY_TEXT_WEIGHT)),
11453
+ secondary: hex("secondary") ?? rgbToHex2(tint(color("text"), color("textMuted"), SECONDARY_TEXT_WEIGHT)),
11257
11454
  muted: hex("textMuted")
11258
11455
  },
11259
11456
  border: { subtle: hex("borderSubtle"), focus: hex("borderActive"), separator: hex("border") },
@@ -11264,7 +11461,38 @@ function buildPalette(colors, background = TRANSPARENT_BACKGROUND) {
11264
11461
  warning: hex("warning"),
11265
11462
  info: hex("info")
11266
11463
  },
11267
- diff: { addedBg: hex("diffAddedBg"), removedBg: hex("diffRemovedBg") }
11464
+ diff: {
11465
+ addedBg: hex("diffAddedBg"),
11466
+ removedBg: hex("diffRemovedBg"),
11467
+ contextBg: hex("diffContextBg"),
11468
+ addedText: hex("diffAdded"),
11469
+ removedText: hex("diffRemoved"),
11470
+ contextText: hex("diffContext"),
11471
+ lineNumber: hex("diffLineNumber"),
11472
+ addedLineNumberBg: hex("diffAddedLineNumberBg"),
11473
+ removedLineNumberBg: hex("diffRemovedLineNumberBg")
11474
+ },
11475
+ syntax: {
11476
+ comment: hex("syntaxComment"),
11477
+ keyword: hex("syntaxKeyword"),
11478
+ function: hex("syntaxFunction"),
11479
+ variable: hex("syntaxVariable"),
11480
+ string: hex("syntaxString"),
11481
+ number: hex("syntaxNumber"),
11482
+ type: hex("syntaxType"),
11483
+ operator: hex("syntaxOperator"),
11484
+ punctuation: hex("syntaxPunctuation")
11485
+ },
11486
+ markdown: {
11487
+ heading: hex("markdownHeading"),
11488
+ strong: hex("markdownStrong"),
11489
+ emphasis: hex("markdownEmph"),
11490
+ listItem: hex("markdownListItem"),
11491
+ blockQuote: hex("markdownBlockQuote"),
11492
+ code: hex("markdownCode"),
11493
+ link: hex("markdownLink"),
11494
+ linkText: hex("markdownLinkText")
11495
+ }
11268
11496
  };
11269
11497
  }
11270
11498
  // apps/tui/src/theme/assets/aura.json
@@ -18733,10 +18961,16 @@ var createDiffView = (renderer, syntaxStyle, patch, filetype) => new DiffRendera
18733
18961
  syntaxStyle,
18734
18962
  view: "unified",
18735
18963
  showLineNumbers: true,
18964
+ fg: Theme.fg.primary,
18736
18965
  addedBg: Theme.diff.addedBg,
18737
18966
  removedBg: Theme.diff.removedBg,
18738
- addedSignColor: Theme.status.success,
18739
- removedSignColor: Theme.status.danger,
18967
+ contextBg: Theme.diff.contextBg,
18968
+ lineNumberFg: Theme.diff.lineNumber,
18969
+ lineNumberBg: Theme.diff.contextBg,
18970
+ addedLineNumberBg: Theme.diff.addedLineNumberBg,
18971
+ removedLineNumberBg: Theme.diff.removedLineNumberBg,
18972
+ addedSignColor: Theme.diff.addedText,
18973
+ removedSignColor: Theme.diff.removedText,
18740
18974
  flexShrink: 0
18741
18975
  });
18742
18976
 
@@ -19025,11 +19259,11 @@ var buildImageBlock = (mediaType, bytes) => ({
19025
19259
  // apps/tui/src/prompt/pastedPaths.ts
19026
19260
  import { statSync } from "fs";
19027
19261
  import { homedir as homedir6 } from "os";
19028
- import { isAbsolute } from "path";
19262
+ import { isAbsolute as isAbsolute2 } from "path";
19029
19263
  var unwrapPath = (raw) => raw.trim().replace(/^(['"])(.*)\1$/, "$2").replace(/^file:\/\//, "").replace(/^~(?=\/)/, homedir6()).replace(/\\(.)/g, "$1");
19030
19264
  var isExistingFile = (path) => {
19031
19265
  try {
19032
- return isAbsolute(path) && statSync(path).isFile();
19266
+ return isAbsolute2(path) && statSync(path).isFile();
19033
19267
  } catch {
19034
19268
  return false;
19035
19269
  }
@@ -19075,7 +19309,7 @@ function planPaste(event, attachments) {
19075
19309
 
19076
19310
  // apps/tui/src/components/promptInput.ts
19077
19311
  var MAX_INPUT_LINES = 8;
19078
- var BUSY_TITLE = " Ctrl+Q (queue) Ctrl+W (steer) ";
19312
+ var BUSY_PLACEHOLDER = "Enter (steer) Ctrl+Q (queue)";
19079
19313
  var ATTACHMENT_STYLE = "attachment";
19080
19314
  var PLACEHOLDER = "Type your prompt, /slash command, or @file reference...";
19081
19315
  var KEY_BINDINGS = [
@@ -19097,12 +19331,11 @@ class PromptInput {
19097
19331
  constructor(renderer, handlers) {
19098
19332
  this.renderable = new BoxRenderable5(renderer, {
19099
19333
  border: ["top", "bottom"],
19334
+ borderColor: Theme.fg.muted,
19100
19335
  borderStyle: "rounded",
19101
- bottomTitleAlignment: "right",
19102
19336
  flexDirection: "row",
19103
19337
  height: 3,
19104
- flexShrink: 0,
19105
- paddingLeft: 1
19338
+ flexShrink: 0
19106
19339
  });
19107
19340
  this.attachmentStyle = SyntaxStyle.create(renderer.nativeScene);
19108
19341
  this.textarea = new TextareaRenderable(renderer, {
@@ -19163,16 +19396,15 @@ class PromptInput {
19163
19396
  this.textarea.blur();
19164
19397
  }
19165
19398
  setBusy(busy) {
19166
- const title = busy ? BUSY_TITLE : undefined;
19167
- if (this.renderable.bottomTitle !== title)
19168
- this.renderable.bottomTitle = title;
19399
+ const placeholder = busy ? BUSY_PLACEHOLDER : PLACEHOLDER;
19400
+ if (this.textarea.placeholder !== placeholder)
19401
+ this.textarea.placeholder = placeholder;
19169
19402
  }
19170
19403
  setBorderColor(color) {
19171
19404
  this.renderable.borderColor = color;
19172
19405
  }
19173
19406
  applyColors() {
19174
- this.renderable.titleColor = Theme.fg.primary;
19175
- this.chevron.fg = Theme.accent.brand;
19407
+ this.chevron.fg = Theme.fg.muted;
19176
19408
  this.textarea.placeholderColor = Theme.fg.muted;
19177
19409
  this.textarea.textColor = Theme.fg.primary;
19178
19410
  this.textarea.focusedTextColor = Theme.fg.primary;
@@ -19274,6 +19506,25 @@ class AssistantMessageView {
19274
19506
  }
19275
19507
  }
19276
19508
 
19509
+ // apps/tui/src/components/markdownNoticeView.ts
19510
+ import { MarkdownRenderable as MarkdownRenderable2 } from "@opentui/core";
19511
+
19512
+ class MarkdownNoticeView {
19513
+ renderable;
19514
+ constructor(renderer, mutedSyntaxStyle) {
19515
+ this.renderable = new MarkdownRenderable2(renderer, {
19516
+ content: "",
19517
+ syntaxStyle: mutedSyntaxStyle,
19518
+ marginTop: 1,
19519
+ flexShrink: 0
19520
+ });
19521
+ }
19522
+ update(entry) {
19523
+ if (this.renderable.content !== entry.text)
19524
+ this.renderable.content = entry.text;
19525
+ }
19526
+ }
19527
+
19277
19528
  // apps/tui/src/components/noticeView.ts
19278
19529
  import { fg as fg7, StyledText as StyledText6, TextRenderable as TextRenderable7 } from "@opentui/core";
19279
19530
  class NoticeView {
@@ -19928,7 +20179,9 @@ var clipLines = (text, maxLines) => {
19928
20179
  // apps/tui/src/format/toolInputText.ts
19929
20180
  var VALUE_LENGTH = 200;
19930
20181
  var formatInputValue = (value) => truncateText(typeof value === "string" ? value : JSON.stringify(value), VALUE_LENGTH);
19931
- var formatToolInput = ({ input }) => Object.entries(input).map(([key, value]) => `${key}: ${formatInputValue(value)}`).join(`
20182
+ var formatBatchEdit = ({ input }) => listBatchEditPaths(input).map((path, index) => `${index + 1}. ${formatInputValue(path)}`).join(`
20183
+ `);
20184
+ var formatToolInput = (call) => call.name === BATCH_EDIT_TOOL_NAME ? formatBatchEdit(call) : Object.entries(call.input).map(([key, value]) => `${key}: ${formatInputValue(value)}`).join(`
19932
20185
  `);
19933
20186
 
19934
20187
  // apps/tui/src/format/toolSummary.ts
@@ -19936,9 +20189,15 @@ var SUMMARY_LENGTH = 80;
19936
20189
  var SUMMARY_KEYS = ["file_path", "path", "command", "pattern", "query", "url", "description"];
19937
20190
  var SEARCH_TOOLS = new Set(["Glob", "Grep"]);
19938
20191
  var summarizeSearch = ({ pattern, path }) => typeof path === "string" ? `${pattern} in ${path}` : String(pattern);
20192
+ var summarizeBatchEdit = (input) => {
20193
+ const paths = listBatchEditPaths(input);
20194
+ return `${paths.length} edits in ${new Set(paths).size} files`;
20195
+ };
19939
20196
  function summarizeToolCall({ name, input, status, progress }) {
19940
20197
  if (status === "running" && progress)
19941
20198
  return truncateText(progress, SUMMARY_LENGTH);
20199
+ if (name === BATCH_EDIT_TOOL_NAME)
20200
+ return summarizeBatchEdit(input);
19942
20201
  if (SEARCH_TOOLS.has(name) && typeof input.pattern === "string") {
19943
20202
  return truncateText(summarizeSearch(input), SUMMARY_LENGTH);
19944
20203
  }
@@ -19947,6 +20206,8 @@ function summarizeToolCall({ name, input, status, progress }) {
19947
20206
  return truncateText(value, SUMMARY_LENGTH);
19948
20207
  }
19949
20208
  var describeApproval = ({ name, input }) => {
20209
+ if (name === BATCH_EDIT_TOOL_NAME)
20210
+ return `Apply ${summarizeBatchEdit(input)} with BatchEdit?`;
19950
20211
  if (name === "Bash")
19951
20212
  return `Run command \`${truncateText(String(input.command ?? ""), 60)}\`?`;
19952
20213
  const path = input.file_path ?? input.notebook_path;
@@ -20192,7 +20453,7 @@ class ToolCallView {
20192
20453
  const accent = this.hovered ? Theme.accent.brand : undefined;
20193
20454
  this.title.content = new StyledText10([
20194
20455
  fg11(accent ?? toolColor(call.status))(glyph),
20195
- bold6(fg11(accent ?? Theme.fg.primary)(call.name)),
20456
+ bold6(fg11(accent ?? Theme.fg.primary)(stripCustomPrefix(call.name))),
20196
20457
  fg11(Theme.fg.secondary)(` ${summarizeToolCall(call)}`),
20197
20458
  fg11(Theme.fg.muted)(stats)
20198
20459
  ]);
@@ -20352,9 +20613,9 @@ class ScrollbackView {
20352
20613
  else if (below > 0)
20353
20614
  this.renderable.scrollBy(Math.min(below, above));
20354
20615
  }
20355
- reset(syntaxStyle) {
20616
+ reset(syntaxStyle, mutedSyntaxStyle) {
20356
20617
  this.dropViews(() => true);
20357
- this.options = { syntaxStyle, hooks: { ...this.options.hooks, syntaxStyle } };
20618
+ this.options = { syntaxStyle, mutedSyntaxStyle, hooks: { ...this.options.hooks, syntaxStyle } };
20358
20619
  }
20359
20620
  dropRemovedViews(state) {
20360
20621
  const live = new Set(state.transcript.map((entry) => entry.id));
@@ -20376,14 +20637,14 @@ class ScrollbackView {
20376
20637
  return view;
20377
20638
  }
20378
20639
  createView(entry) {
20379
- const { syntaxStyle, hooks } = this.options;
20640
+ const { syntaxStyle, mutedSyntaxStyle, hooks } = this.options;
20380
20641
  switch (entry.kind) {
20381
20642
  case "user":
20382
20643
  return new UserMessageView(this.renderer);
20383
20644
  case "assistant":
20384
20645
  return new AssistantMessageView(this.renderer, syntaxStyle);
20385
20646
  case "notice":
20386
- return new NoticeView(this.renderer);
20647
+ return entry.markdown ? new MarkdownNoticeView(this.renderer, mutedSyntaxStyle) : new NoticeView(this.renderer);
20387
20648
  case "thought":
20388
20649
  return new ThoughtView(this.renderer);
20389
20650
  case "tools":
@@ -20462,15 +20723,14 @@ class SessionInfoBar {
20462
20723
  limits;
20463
20724
  constructor(renderer) {
20464
20725
  this.renderable = new BoxRenderable13(renderer, {
20465
- marginBottom: 1,
20466
20726
  flexShrink: 0,
20467
20727
  flexDirection: "row",
20468
20728
  flexWrap: "wrap",
20469
20729
  rowGap: 1,
20470
20730
  columnGap: 0,
20471
20731
  justifyContent: "space-between",
20472
- paddingLeft: 1,
20473
- paddingRight: 1
20732
+ paddingLeft: 2,
20733
+ paddingRight: 2
20474
20734
  });
20475
20735
  this.info = new TextRenderable13(renderer, { content: "", wrapMode: "none", flexShrink: 0 });
20476
20736
  this.limits = new TextRenderable13(renderer, {
@@ -20526,12 +20786,14 @@ class StatusLine {
20526
20786
  constructor(renderer, onStop) {
20527
20787
  this.renderable = new BoxRenderable14(renderer, {
20528
20788
  height: 2,
20529
- paddingTop: 1,
20530
20789
  flexShrink: 0,
20531
20790
  flexDirection: "row",
20532
20791
  justifyContent: "space-between",
20792
+ border: ["top"],
20793
+ borderColor: Theme.border.subtle,
20533
20794
  paddingLeft: 2,
20534
- paddingRight: 2
20795
+ paddingRight: 2,
20796
+ visible: false
20535
20797
  });
20536
20798
  this.label = new TextRenderable14(renderer, { content: "", wrapMode: "none" });
20537
20799
  this.stats = new TextRenderable14(renderer, {
@@ -20547,9 +20809,11 @@ class StatusLine {
20547
20809
  update(state) {
20548
20810
  if (state.status === "idle") {
20549
20811
  this.label.content = state.lastRun ? new StyledText13(buildLastRunChunks(state.lastRun, readLastPrompt(state))) : "";
20812
+ this.renderable.visible = state.lastRun !== undefined;
20550
20813
  this.stats.content = "";
20551
20814
  return;
20552
20815
  }
20816
+ this.renderable.visible = true;
20553
20817
  const label = agentLabel(state.status);
20554
20818
  const busy = label.icon === undefined;
20555
20819
  const frame = SPINNER_FRAMES[Math.floor(state.now / SPINNER_STEP_MS) % SPINNER_FRAMES.length];
@@ -20566,26 +20830,96 @@ class StatusLine {
20566
20830
  }
20567
20831
  }
20568
20832
 
20833
+ // apps/tui/src/components/promptQueueView.ts
20834
+ import {
20835
+ BoxRenderable as BoxRenderable15,
20836
+ bold as bold9,
20837
+ dim as dim7,
20838
+ fg as fg16,
20839
+ StyledText as StyledText14,
20840
+ TextRenderable as TextRenderable15
20841
+ } from "@opentui/core";
20842
+
20843
+ // apps/tui/src/format/promptQueueRows.ts
20844
+ var MAX_QUEUE_ROWS = 5;
20845
+ var QUEUE_TEXT_LENGTH = 80;
20846
+ var buildQueueRows = (queued) => queued.slice(0, MAX_QUEUE_ROWS).map((content, index) => `${index + 1}. ${truncateText(readPromptText(content), QUEUE_TEXT_LENGTH)}`);
20847
+ var buildQueueHeader = (queued) => `Queued${SEPARATOR}${queued.length} waiting`;
20848
+ var countHiddenQueued = (queued) => Math.max(queued.length - MAX_QUEUE_ROWS, 0);
20849
+
20850
+ // apps/tui/src/components/promptQueueView.ts
20851
+ class PromptQueueView {
20852
+ renderable;
20853
+ header;
20854
+ rows;
20855
+ overflow;
20856
+ constructor(renderer) {
20857
+ this.renderable = new BoxRenderable15(renderer, {
20858
+ flexShrink: 0,
20859
+ paddingLeft: 2,
20860
+ paddingRight: 2,
20861
+ visible: false
20862
+ });
20863
+ this.header = new TextRenderable15(renderer, { content: "", wrapMode: "none" });
20864
+ this.overflow = new TextRenderable15(renderer, { content: "", wrapMode: "none" });
20865
+ this.rows = Array.from({ length: MAX_QUEUE_ROWS }, () => new TextRenderable15(renderer, { content: "", wrapMode: "none" }));
20866
+ this.renderable.add(this.header);
20867
+ for (const row of this.rows)
20868
+ this.renderable.add(row);
20869
+ this.renderable.add(this.overflow);
20870
+ }
20871
+ update({ queuedPrompts }) {
20872
+ this.renderable.visible = queuedPrompts.length > 0;
20873
+ if (queuedPrompts.length === 0)
20874
+ return;
20875
+ this.header.content = new StyledText14([
20876
+ bold9(fg16(Theme.fg.secondary)(buildQueueHeader(queuedPrompts)))
20877
+ ]);
20878
+ const lines = buildQueueRows(queuedPrompts);
20879
+ this.rows.forEach((row, index) => {
20880
+ row.content = new StyledText14([fg16(Theme.fg.primary)(lines[index] ?? "")]);
20881
+ });
20882
+ const hidden = countHiddenQueued(queuedPrompts);
20883
+ this.overflow.content = hidden ? new StyledText14([dim7(fg16(Theme.fg.muted)(` +${hidden} more`))]) : "";
20884
+ }
20885
+ }
20886
+
20569
20887
  // apps/tui/src/components/taskPanel.ts
20570
20888
  import {
20571
- BoxRenderable as BoxRenderable16,
20572
- bold as bold10,
20573
- dim as dim8,
20574
- fg as fg17,
20575
- StyledText as StyledText15,
20576
- TextRenderable as TextRenderable16
20889
+ BoxRenderable as BoxRenderable17,
20890
+ bold as bold11,
20891
+ dim as dim9,
20892
+ fg as fg18,
20893
+ StyledText as StyledText16,
20894
+ TextRenderable as TextRenderable17
20577
20895
  } from "@opentui/core";
20578
20896
 
20579
20897
  // apps/tui/src/format/taskRows.ts
20580
20898
  var MAX_TASK_ROWS = 5;
20581
- var readDetail = ({ summary, lastToolName, paused }) => {
20582
- if (paused)
20899
+ var KIND_LABELS = { "general-purpose": "Agent", local_bash: "Bash" };
20900
+ var describeLastTool = ({ lastToolCall, lastToolName }) => {
20901
+ if (!lastToolCall)
20902
+ return lastToolName;
20903
+ const { name, input } = lastToolCall;
20904
+ const summary = summarizeToolCall({
20905
+ id: "",
20906
+ name,
20907
+ input,
20908
+ status: "running",
20909
+ expanded: false,
20910
+ startedAt: 0
20911
+ });
20912
+ return `${name} ${summary}`;
20913
+ };
20914
+ var readDetail = (task) => {
20915
+ if (task.paused)
20583
20916
  return "Paused";
20584
- return summary ?? lastToolName ?? "Starting...";
20917
+ return task.summary ?? describeLastTool(task) ?? "Starting...";
20585
20918
  };
20586
20919
  var buildTaskRow = (task, now) => ({
20587
20920
  taskId: task.id,
20588
- kind: task.kind,
20921
+ kind: KIND_LABELS[task.kind] ?? task.kind,
20922
+ model: task.model ?? "",
20589
20923
  title: task.description,
20590
20924
  stats: [formatDuration(Math.max(now - task.startedAt, 0)), `\u2193${formatTokenCount(task.tokens)}`].join(SEPARATOR).trim(),
20591
20925
  detail: readDetail(task)
@@ -20596,12 +20930,12 @@ var countHiddenTasks = (tasks) => Math.max(tasks.length - MAX_TASK_ROWS, 0);
20596
20930
 
20597
20931
  // apps/tui/src/components/taskRowView.ts
20598
20932
  import {
20599
- BoxRenderable as BoxRenderable15,
20600
- bold as bold9,
20601
- dim as dim7,
20602
- fg as fg16,
20603
- StyledText as StyledText14,
20604
- TextRenderable as TextRenderable15
20933
+ BoxRenderable as BoxRenderable16,
20934
+ bold as bold10,
20935
+ dim as dim8,
20936
+ fg as fg17,
20937
+ StyledText as StyledText15,
20938
+ TextRenderable as TextRenderable16
20605
20939
  } from "@opentui/core";
20606
20940
  var SPINNER_STEP_MS2 = 100;
20607
20941
 
@@ -20612,20 +20946,20 @@ class TaskRowView {
20612
20946
  detail;
20613
20947
  taskId = "";
20614
20948
  constructor(renderer, onStop) {
20615
- this.renderable = new BoxRenderable15(renderer, { flexShrink: 0, visible: false });
20616
- const line = new BoxRenderable15(renderer, {
20949
+ this.renderable = new BoxRenderable16(renderer, { flexShrink: 0, visible: false });
20950
+ const line = new BoxRenderable16(renderer, {
20617
20951
  flexDirection: "row",
20618
20952
  justifyContent: "space-between"
20619
20953
  });
20620
- this.title = new TextRenderable15(renderer, { content: "", wrapMode: "none" });
20621
- this.stop = new TextRenderable15(renderer, {
20954
+ this.title = new TextRenderable16(renderer, { content: "", wrapMode: "none" });
20955
+ this.stop = new TextRenderable16(renderer, {
20622
20956
  content: "",
20623
20957
  wrapMode: "none",
20624
20958
  flexShrink: 0,
20625
20959
  selectable: false,
20626
20960
  onMouseDown: () => onStop(this.taskId)
20627
20961
  });
20628
- this.detail = new TextRenderable15(renderer, { content: "", wrapMode: "none" });
20962
+ this.detail = new TextRenderable16(renderer, { content: "", wrapMode: "none" });
20629
20963
  line.add(this.title);
20630
20964
  line.add(this.stop);
20631
20965
  this.renderable.add(line);
@@ -20637,16 +20971,17 @@ class TaskRowView {
20637
20971
  return;
20638
20972
  this.taskId = row.taskId;
20639
20973
  const frame = SPINNER_FRAMES[Math.floor(now / SPINNER_STEP_MS2) % SPINNER_FRAMES.length];
20640
- this.title.content = new StyledText14([
20641
- fg16(Theme.status.success)(`${frame} `),
20642
- bold9(fg16(Theme.fg.primary)(row.kind)),
20643
- fg16(Theme.fg.secondary)(` ${row.title}`)
20974
+ this.title.content = new StyledText15([
20975
+ fg17(Theme.status.success)(`${frame} `),
20976
+ bold10(fg17(Theme.fg.primary)(row.kind)),
20977
+ fg17(Theme.fg.muted)(row.model ? ` (${row.model})` : ""),
20978
+ fg17(Theme.fg.secondary)(` ${row.title}`)
20644
20979
  ]);
20645
- this.stop.content = new StyledText14([
20646
- fg16(Theme.fg.muted)(`${row.stats} `),
20647
- bold9(fg16(Theme.fg.secondary)("[stop]"))
20980
+ this.stop.content = new StyledText15([
20981
+ fg17(Theme.fg.muted)(`${row.stats} `),
20982
+ bold10(fg17(Theme.fg.secondary)("[stop]"))
20648
20983
  ]);
20649
- this.detail.content = new StyledText14([dim7(fg16(Theme.fg.muted)(` \u2514 ${row.detail}`))]);
20984
+ this.detail.content = new StyledText15([dim8(fg17(Theme.fg.muted)(` \u2514 ${row.detail}`))]);
20650
20985
  }
20651
20986
  }
20652
20987
 
@@ -20657,14 +20992,16 @@ class TaskPanel {
20657
20992
  rows;
20658
20993
  overflow;
20659
20994
  constructor(renderer, onStop) {
20660
- this.renderable = new BoxRenderable16(renderer, {
20995
+ this.renderable = new BoxRenderable17(renderer, {
20661
20996
  flexShrink: 0,
20662
20997
  paddingLeft: 2,
20663
20998
  paddingRight: 2,
20999
+ border: ["top"],
21000
+ borderColor: Theme.border.subtle,
20664
21001
  visible: false
20665
21002
  });
20666
- this.header = new TextRenderable16(renderer, { content: "", wrapMode: "none" });
20667
- this.overflow = new TextRenderable16(renderer, { content: "", wrapMode: "none" });
21003
+ this.header = new TextRenderable17(renderer, { content: "", wrapMode: "none" });
21004
+ this.overflow = new TextRenderable17(renderer, { content: "", wrapMode: "none" });
20668
21005
  this.rows = Array.from({ length: MAX_TASK_ROWS }, () => new TaskRowView(renderer, onStop));
20669
21006
  this.renderable.add(this.header);
20670
21007
  for (const row of this.rows)
@@ -20676,15 +21013,15 @@ class TaskPanel {
20676
21013
  if (tasks.length === 0)
20677
21014
  return;
20678
21015
  const rows = buildTaskRows(tasks, now);
20679
- this.header.content = new StyledText15([bold10(fg17(Theme.fg.secondary)(buildTaskHeader(tasks)))]);
21016
+ this.header.content = new StyledText16([bold11(fg18(Theme.fg.primary)(buildTaskHeader(tasks)))]);
20680
21017
  this.rows.forEach((view, index) => view.update(rows[index], now));
20681
21018
  const hidden = countHiddenTasks(tasks);
20682
- this.overflow.content = hidden ? new StyledText15([dim8(fg17(Theme.fg.muted)(` +${hidden} more`))]) : "";
21019
+ this.overflow.content = hidden ? new StyledText16([dim9(fg18(Theme.fg.muted)(` +${hidden} more`))]) : "";
20683
21020
  }
20684
21021
  }
20685
21022
 
20686
21023
  // apps/tui/src/components/toastView.ts
20687
- import { BoxRenderable as BoxRenderable17, fg as fg18, StyledText as StyledText16, TextRenderable as TextRenderable17 } from "@opentui/core";
21024
+ import { BoxRenderable as BoxRenderable18, fg as fg19, StyledText as StyledText17, TextRenderable as TextRenderable18 } from "@opentui/core";
20688
21025
  var TOAST_ICON = "\u25CF";
20689
21026
 
20690
21027
  class ToastView {
@@ -20692,7 +21029,7 @@ class ToastView {
20692
21029
  text;
20693
21030
  toastId;
20694
21031
  constructor(renderer) {
20695
- this.renderable = new BoxRenderable17(renderer, {
21032
+ this.renderable = new BoxRenderable18(renderer, {
20696
21033
  position: "absolute",
20697
21034
  top: 1,
20698
21035
  right: 2,
@@ -20703,7 +21040,7 @@ class ToastView {
20703
21040
  borderStyle: "rounded",
20704
21041
  visible: false
20705
21042
  });
20706
- this.text = new TextRenderable17(renderer, { content: "", wrapMode: "none", selectable: false });
21043
+ this.text = new TextRenderable18(renderer, { content: "", wrapMode: "none", selectable: false });
20707
21044
  this.renderable.add(this.text);
20708
21045
  }
20709
21046
  update({ ui }) {
@@ -20714,9 +21051,9 @@ class ToastView {
20714
21051
  this.toastId = toast.id;
20715
21052
  this.renderable.borderColor = Theme.border.focus;
20716
21053
  this.renderable.backgroundColor = Theme.bg.surface;
20717
- this.text.content = new StyledText16([
20718
- fg18(Theme.status.success)(`${TOAST_ICON} `),
20719
- fg18(Theme.fg.primary)(toast.text)
21054
+ this.text.content = new StyledText17([
21055
+ fg19(Theme.status.success)(`${TOAST_ICON} `),
21056
+ fg19(Theme.fg.primary)(toast.text)
20720
21057
  ]);
20721
21058
  }
20722
21059
  }
@@ -20745,6 +21082,9 @@ function requestAgent(store, request, onMissing = "notify") {
20745
21082
  return true;
20746
21083
  }
20747
21084
 
21085
+ // apps/tui/src/input/themeActions.ts
21086
+ var saveTheme2 = (store, theme) => store.request({ type: "options.theme.save.requested", theme });
21087
+
20748
21088
  // apps/tui/src/input/workflowCommands.ts
20749
21089
  var postNotice = (store, text) => store.emit({ type: "ui.notice.posted", text });
20750
21090
  var WORKFLOW_COMMANDS = {
@@ -20844,7 +21184,7 @@ var SLASH_COMMANDS = {
20844
21184
  return store.emit({ type: "ui.theme.picker.opened" });
20845
21185
  const parsed = themeNameSchema.safeParse(argument);
20846
21186
  if (parsed.success)
20847
- return store.emit({ type: "ui.theme.selected", theme: parsed.data });
21187
+ return saveTheme2(store, parsed.data);
20848
21188
  store.emit({ type: "ui.notice.posted", text: `Unknown theme: ${argument}` });
20849
21189
  }
20850
21190
  },
@@ -20873,8 +21213,10 @@ var SLASH_COMMANDS = {
20873
21213
  }
20874
21214
  },
20875
21215
  "/effort": {
20876
- description: "Show or set reasoning effort",
21216
+ description: "Pick a reasoning effort, or set one with /effort <level>",
20877
21217
  run(store, level) {
21218
+ if (!level)
21219
+ return store.emit({ type: "ui.effort.picker.opened" });
20878
21220
  const parsed = effortLevelSchema.safeParse(level);
20879
21221
  if (parsed.success) {
20880
21222
  return void requestAgent(store, {
@@ -20946,6 +21288,14 @@ function buildPickerView(state) {
20946
21288
  }));
20947
21289
  return { title: "Select a theme", rows, selected: picker.selected };
20948
21290
  }
21291
+ if (picker.kind === "effort") {
21292
+ const current = state.effort || DEFAULT_EFFORT;
21293
+ const rows = effortLevelSchema.options.map((level) => ({
21294
+ name: `${level === current ? "\u25CF " : " "}${level}`,
21295
+ description: ""
21296
+ }));
21297
+ return { title: "Select an effort level", rows, selected: picker.selected };
21298
+ }
20949
21299
  if (picker.kind === "rewind") {
20950
21300
  const rows = listCheckpoints(state).map((entry) => ({
20951
21301
  name: truncateText(entry.text.replace(/\s+/g, " "), CHECKPOINT_TEXT_LENGTH),
@@ -20963,7 +21313,6 @@ function buildPickerView(state) {
20963
21313
  var buildListView = (state) => buildPickerView(state) ?? buildAutocompleteView(state);
20964
21314
 
20965
21315
  // apps/tui/src/input/agentSessionActions.ts
20966
- var DEFAULT_EFFORT = "high";
20967
21316
  var DEFAULT_MODEL = "claude-haiku-5-5";
20968
21317
  var buildSessionOptions = (store) => ({
20969
21318
  model: store.state.model || DEFAULT_MODEL,
@@ -21142,23 +21491,22 @@ function runSlashCommand(store, line) {
21142
21491
  }
21143
21492
 
21144
21493
  // apps/tui/src/input/promptActions.ts
21145
- var DELIVERY_REQUESTS = {
21146
- send: "agent.prompt.requested",
21147
- queue: "agent.prompt.queue.requested",
21148
- steer: "agent.prompt.steer.requested"
21149
- };
21150
- var BUSY_NOTICE = "Agent is busy: Ctrl+Q queues, Ctrl+W steers, Esc Esc interrupts the turn";
21151
21494
  var buildContent = (text, images) => images.length > 0 ? [{ type: "text", text }, ...images] : text;
21152
21495
  function deliverPrompt(store, content, delivery, images) {
21153
- const busy = isAgentBusy(store);
21154
- if (busy && delivery === "send") {
21155
- store.emit({ type: "ui.notice.posted", text: BUSY_NOTICE });
21156
- return false;
21496
+ const body = buildContent(content, images);
21497
+ if (delivery === "queue" && isAgentBusy(store)) {
21498
+ store.emit({ type: "ui.prompt.queued", content: body });
21499
+ return true;
21157
21500
  }
21158
- return requestAgent(store, {
21159
- type: DELIVERY_REQUESTS[busy ? delivery : "send"],
21160
- content: buildContent(content, images)
21161
- });
21501
+ return requestAgent(store, { type: "agent.prompt.requested", content: body });
21502
+ }
21503
+ function sendNextQueuedPrompt(store) {
21504
+ const [next] = store.state.queuedPrompts;
21505
+ if (next === undefined || store.state.status !== "idle")
21506
+ return;
21507
+ const sent = requestAgent(store, { type: "agent.prompt.requested", content: next }, "ignore");
21508
+ if (sent)
21509
+ store.emit({ type: "ui.prompt.dequeued" });
21162
21510
  }
21163
21511
  function submitPrompt(store, text, delivery = "send", images = []) {
21164
21512
  const content = text.trim();
@@ -21242,7 +21590,12 @@ var PICKER_CONFIRMS = {
21242
21590
  theme(store, _state, selected) {
21243
21591
  const theme = THEME_NAMES[selected];
21244
21592
  if (theme)
21245
- store.emit({ type: "ui.theme.selected", theme });
21593
+ saveTheme2(store, theme);
21594
+ },
21595
+ effort(store, _state, selected) {
21596
+ const effort = effortLevelSchema.options[selected];
21597
+ if (effort)
21598
+ requestAgent(store, { type: "agent.effort.set.requested", effort });
21246
21599
  },
21247
21600
  rewind(store, state, selected) {
21248
21601
  const userMessageId = listCheckpoints(state)[selected]?.checkpointId;
@@ -21290,7 +21643,7 @@ var dismissQuestion = (store) => sendAnswers(store, {});
21290
21643
 
21291
21644
  // apps/tui/src/input/routeKeys.ts
21292
21645
  var createKeyPresses = () => ({ escapeAt: 0, ctrlCAt: 0 });
21293
- var DELIVERY_KEYS = { q: "queue", w: "steer" };
21646
+ var DELIVERY_KEYS = { q: "queue" };
21294
21647
  var DOUBLE_ESCAPE_MS = 500;
21295
21648
  var EXIT_CONFIRM_MS = 3000;
21296
21649
  var EXIT_NOTICE = "Press Ctrl + C again to exit";
@@ -21635,21 +21988,70 @@ class AppStore {
21635
21988
 
21636
21989
  // apps/tui/src/theme/syntaxStyle.ts
21637
21990
  import { SyntaxStyle as SyntaxStyle2 } from "@opentui/core";
21638
- var createSyntaxStyle = (renderer) => SyntaxStyle2.fromStyles({
21639
- default: { fg: Theme.fg.primary },
21640
- "markup.heading": { fg: Theme.accent.brand, bold: true },
21641
- "markup.strong": { fg: Theme.fg.primary, bold: true },
21642
- "markup.italic": { fg: Theme.fg.secondary, italic: true },
21643
- "markup.raw": { fg: Theme.status.warning },
21644
- "markup.link": { fg: Theme.status.info, underline: true },
21645
- "markup.list": { fg: Theme.accent.highlight },
21646
- keyword: { fg: Theme.accent.brand },
21647
- string: { fg: Theme.status.success },
21648
- comment: { fg: Theme.fg.muted, italic: true },
21649
- function: { fg: Theme.accent.highlight },
21650
- type: { fg: Theme.status.warning },
21651
- number: { fg: Theme.status.info }
21652
- }, renderer.nativeScene);
21991
+
21992
+ // apps/tui/src/theme/syntaxRules.ts
21993
+ var rule = (scope, foreground, style = {}) => ({
21994
+ scope,
21995
+ style: { foreground, ...style }
21996
+ });
21997
+ var buildMutedSyntaxRules = (palette) => buildSyntaxRules(palette).map(({ scope, style }) => ({
21998
+ scope,
21999
+ style: { ...style, foreground: palette.fg.muted }
22000
+ }));
22001
+ var buildSyntaxRules = ({ fg, status, syntax, markdown, diff, bg }) => [
22002
+ rule(["default"], fg.primary),
22003
+ rule(["comment", "comment.documentation"], syntax.comment, { italic: true }),
22004
+ rule(["string", "symbol", "character.special", "character"], syntax.string),
22005
+ rule(["number", "boolean", "constant", "float"], syntax.number),
22006
+ rule(["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"], syntax.keyword, {
22007
+ italic: true
22008
+ }),
22009
+ rule(["keyword.type"], syntax.type, { bold: true, italic: true }),
22010
+ rule(["keyword.function", "function.method"], syntax.function),
22011
+ rule(["keyword"], syntax.keyword, { italic: true }),
22012
+ rule(["keyword.import", "string.escape", "string.regexp", "tag.attribute", "keyword.export"], syntax.keyword),
22013
+ rule(["operator", "keyword.operator", "punctuation.delimiter", "keyword.conditional.ternary"], syntax.operator),
22014
+ rule(["variable", "variable.parameter", "function.method.call", "function.call", "property", "parameter", "field"], syntax.variable),
22015
+ rule(["variable.member", "function", "constructor"], syntax.function),
22016
+ rule(["type", "module", "class", "namespace"], syntax.type),
22017
+ rule(["type.definition"], syntax.type, { bold: true }),
22018
+ rule(["punctuation", "punctuation.bracket"], syntax.punctuation),
22019
+ rule(["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin", "variable.super"], status.danger),
22020
+ rule(["keyword.directive", "keyword.modifier", "keyword.exception"], syntax.keyword, { italic: true }),
22021
+ rule(["punctuation.special", "tag.delimiter"], syntax.operator),
22022
+ rule(["markup.heading", "markup.heading.2", "markup.heading.3", "markup.heading.4", "markup.heading.5", "markup.heading.6"], markdown.heading, { bold: true }),
22023
+ rule(["markup.heading.1"], markdown.heading, { bold: true, underline: true }),
22024
+ rule(["markup.bold", "markup.strong"], markdown.strong, { bold: true }),
22025
+ rule(["markup.italic"], markdown.emphasis, { italic: true }),
22026
+ rule(["markup.list"], markdown.listItem),
22027
+ rule(["markup.quote"], markdown.blockQuote, { italic: true }),
22028
+ rule(["markup.raw", "markup.raw.block"], markdown.code),
22029
+ rule(["markup.raw.inline"], markdown.code, { background: bg.surface }),
22030
+ rule(["markup.link", "markup.link.url", "string.special", "string.special.url"], markdown.link, {
22031
+ underline: true
22032
+ }),
22033
+ rule(["markup.link.label"], markdown.linkText, { underline: true }),
22034
+ rule(["label"], markdown.linkText),
22035
+ rule(["spell", "nospell"], fg.primary),
22036
+ rule(["markup.underline"], fg.primary, { underline: true }),
22037
+ rule(["comment.error"], status.danger, { italic: true, bold: true }),
22038
+ rule(["comment.warning"], status.warning, { italic: true, bold: true }),
22039
+ rule(["comment.todo", "comment.note"], status.info, { italic: true, bold: true }),
22040
+ rule(["attribute", "annotation"], status.warning),
22041
+ rule(["tag"], status.danger),
22042
+ rule(["markup.strikethrough", "markup.list.unchecked", "debug"], fg.muted),
22043
+ rule(["markup.list.checked"], status.success),
22044
+ rule(["diff.plus"], diff.addedText, { background: diff.addedBg }),
22045
+ rule(["diff.minus"], diff.removedText, { background: diff.removedBg }),
22046
+ rule(["diff.delta"], diff.contextText, { background: diff.contextBg }),
22047
+ rule(["error"], status.danger, { bold: true }),
22048
+ rule(["warning"], status.warning, { bold: true }),
22049
+ rule(["info"], status.info)
22050
+ ];
22051
+
22052
+ // apps/tui/src/theme/syntaxStyle.ts
22053
+ var createSyntaxStyle = (renderer) => SyntaxStyle2.fromTheme(buildSyntaxRules(Theme), renderer.nativeScene);
22054
+ var createMutedSyntaxStyle = (renderer) => SyntaxStyle2.fromTheme(buildMutedSyntaxRules(Theme), renderer.nativeScene);
21653
22055
 
21654
22056
  // apps/tui/src/startTui.ts
21655
22057
  var CLOCK_INTERVAL_MS = 100;
@@ -21662,6 +22064,7 @@ async function startTui(client, options = {}) {
21662
22064
  const store = new AppStore(client);
21663
22065
  const clipboard = options.clipboard ?? createHostClipboard();
21664
22066
  let syntaxStyle = createSyntaxStyle(activeRenderer);
22067
+ let mutedSyntaxStyle = createMutedSyntaxStyle(activeRenderer);
21665
22068
  let appliedTheme = readShownTheme(store.state);
21666
22069
  let appliedBackground = readShownBackground(store.state);
21667
22070
  const header = new HeaderBar(activeRenderer);
@@ -21671,8 +22074,10 @@ async function startTui(client, options = {}) {
21671
22074
  const toastView = new ToastView(activeRenderer);
21672
22075
  const statusLine = new StatusLine(activeRenderer, () => cancelTurn(store));
21673
22076
  const taskPanel = new TaskPanel(activeRenderer, (taskId) => stopTask(store, taskId));
22077
+ const promptQueue = new PromptQueueView(activeRenderer);
21674
22078
  const scrollback = new ScrollbackView(activeRenderer, {
21675
22079
  syntaxStyle,
22080
+ mutedSyntaxStyle,
21676
22081
  hooks: {
21677
22082
  syntaxStyle,
21678
22083
  onDecision: (decision, id) => answerApproval(store, decision, id),
@@ -21705,7 +22110,7 @@ async function startTui(client, options = {}) {
21705
22110
  saveDraft(text);
21706
22111
  }
21707
22112
  });
21708
- const root = new BoxRenderable18(activeRenderer, {
22113
+ const root = new BoxRenderable19(activeRenderer, {
21709
22114
  width: "100%",
21710
22115
  height: "100%",
21711
22116
  flexDirection: "column",
@@ -21716,11 +22121,12 @@ async function startTui(client, options = {}) {
21716
22121
  scrollback,
21717
22122
  diffViewer,
21718
22123
  statusLine,
21719
- taskPanel,
22124
+ promptQueue,
21720
22125
  popover,
21721
22126
  backgroundPanel,
21722
22127
  input,
21723
- infoBar
22128
+ infoBar,
22129
+ taskPanel
21724
22130
  ];
21725
22131
  for (const part of parts)
21726
22132
  root.add(part.renderable);
@@ -21747,11 +22153,13 @@ async function startTui(client, options = {}) {
21747
22153
  appliedBackground = shownBackground;
21748
22154
  applyTheme(shownTheme, shownBackground);
21749
22155
  if (themeChanged) {
21750
- const previous = syntaxStyle;
22156
+ const previous = [syntaxStyle, mutedSyntaxStyle];
21751
22157
  syntaxStyle = createSyntaxStyle(activeRenderer);
21752
- scrollback.reset(syntaxStyle);
22158
+ mutedSyntaxStyle = createMutedSyntaxStyle(activeRenderer);
22159
+ scrollback.reset(syntaxStyle, mutedSyntaxStyle);
21753
22160
  diffViewer.reset(syntaxStyle);
21754
- previous.destroy();
22161
+ for (const style of previous)
22162
+ style.destroy();
21755
22163
  }
21756
22164
  root.backgroundColor = Theme.bg.default;
21757
22165
  input.applyColors();
@@ -21762,6 +22170,7 @@ async function startTui(client, options = {}) {
21762
22170
  header.update(state);
21763
22171
  statusLine.update(state);
21764
22172
  taskPanel.update(state);
22173
+ promptQueue.update(state);
21765
22174
  toastView.update(state);
21766
22175
  infoBar.update(state);
21767
22176
  scrollback.update(state);
@@ -21773,7 +22182,7 @@ async function startTui(client, options = {}) {
21773
22182
  input.setFocused(inputFocused);
21774
22183
  scrollback.setFocused(!inputFocused);
21775
22184
  input.setBusy(isStatusBusy(state.status));
21776
- input.setBorderColor(inputFocused ? Theme.status.success : Theme.border.separator);
22185
+ input.setBorderColor(inputFocused ? Theme.fg.muted : Theme.border.separator);
21777
22186
  const awaitingId = (findAwaitingCalls(state)[0] ?? findQuestionCalls(state)[0])?.id;
21778
22187
  if (awaitingId && awaitingId !== revealedCallId) {
21779
22188
  scrollback.revealEntry(findGroupId(state, awaitingId) ?? "");
@@ -21781,6 +22190,7 @@ async function startTui(client, options = {}) {
21781
22190
  revealedCallId = awaitingId;
21782
22191
  };
21783
22192
  store.subscribe(render);
22193
+ store.subscribe(() => sendNextQueuedPrompt(store));
21784
22194
  const notifier = new TurnNotifier(activeRenderer);
21785
22195
  store.subscribeOwnEvents((event) => {
21786
22196
  reactToEvent(store, event);