@hasna/assistants 1.1.25 → 1.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21563,9 +21563,10 @@ class NativeHookRegistry {
21563
21563
  config = {};
21564
21564
  register(hook) {
21565
21565
  const eventHooks = this.hooks.get(hook.event) || [];
21566
- eventHooks.push(hook);
21567
- eventHooks.sort((a, b) => a.priority - b.priority);
21568
- this.hooks.set(hook.event, eventHooks);
21566
+ const deduped = eventHooks.filter((existing) => existing.id !== hook.id);
21567
+ deduped.push(hook);
21568
+ deduped.sort((a, b) => a.priority - b.priority);
21569
+ this.hooks.set(hook.event, deduped);
21569
21570
  }
21570
21571
  getHooks(event) {
21571
21572
  return this.hooks.get(event) || [];
@@ -72917,16 +72918,28 @@ class BudgetTracker {
72917
72918
  case "assistant":
72918
72919
  if (idOrAssistant) {
72919
72920
  this.assistantUsages.set(idOrAssistant, newUsage);
72921
+ } else {
72922
+ this.assistantUsages.clear();
72920
72923
  }
72921
72924
  break;
72922
72925
  case "swarm":
72923
72926
  this.swarmUsage = newUsage;
72924
72927
  break;
72925
72928
  case "project":
72926
- const projectId = idOrAssistant || this.activeProjectId;
72927
- if (projectId) {
72928
- this.projectUsages.set(projectId, newUsage);
72929
- this.saveProjectState(projectId, newUsage);
72929
+ if (idOrAssistant) {
72930
+ this.projectUsages.set(idOrAssistant, newUsage);
72931
+ this.saveProjectState(idOrAssistant, newUsage);
72932
+ break;
72933
+ }
72934
+ if (this.activeProjectId) {
72935
+ this.projectUsages.set(this.activeProjectId, newUsage);
72936
+ this.saveProjectState(this.activeProjectId, newUsage);
72937
+ break;
72938
+ }
72939
+ for (const projectId of this.projectUsages.keys()) {
72940
+ const resetProjectUsage = createEmptyUsage();
72941
+ this.projectUsages.set(projectId, resetProjectUsage);
72942
+ this.saveProjectState(projectId, resetProjectUsage);
72930
72943
  }
72931
72944
  break;
72932
72945
  }
@@ -72936,6 +72949,9 @@ class BudgetTracker {
72936
72949
  this.sessionUsage = createEmptyUsage();
72937
72950
  this.assistantUsages.clear();
72938
72951
  this.swarmUsage = createEmptyUsage();
72952
+ for (const projectId of this.projectUsages.keys()) {
72953
+ this.saveProjectState(projectId, createEmptyUsage());
72954
+ }
72939
72955
  this.projectUsages.clear();
72940
72956
  this.saveState();
72941
72957
  }
@@ -73034,6 +73050,22 @@ var init_tracker = __esm(() => {
73034
73050
  });
73035
73051
 
73036
73052
  // packages/core/src/budget/tools.ts
73053
+ function normalizeScope2(value) {
73054
+ return String(value ?? "").trim().toLowerCase();
73055
+ }
73056
+ function isBudgetToolScope(value) {
73057
+ return BUDGET_TOOL_SCOPES.includes(value);
73058
+ }
73059
+ function isBudgetResetScope(value) {
73060
+ return value === "all" || isBudgetToolScope(value);
73061
+ }
73062
+ function parseLimitValue(value) {
73063
+ const numeric = Number(value);
73064
+ if (!Number.isFinite(numeric) || numeric < 0) {
73065
+ return null;
73066
+ }
73067
+ return numeric === 0 ? undefined : numeric;
73068
+ }
73037
73069
  function createBudgetToolExecutors(getBudgetTracker) {
73038
73070
  return {
73039
73071
  budget_status: async (input) => {
@@ -73041,7 +73073,11 @@ function createBudgetToolExecutors(getBudgetTracker) {
73041
73073
  if (!tracker) {
73042
73074
  return "Budget tracking is not enabled.";
73043
73075
  }
73044
- const scope = String(input.scope || "session");
73076
+ const scopeInput = normalizeScope2(input.scope || "session");
73077
+ if (!isBudgetToolScope(scopeInput)) {
73078
+ return `Invalid scope: ${scopeInput || "(empty)"}. Use "session", "swarm", or "project".`;
73079
+ }
73080
+ const scope = scopeInput;
73045
73081
  const status = tracker.checkBudget(scope);
73046
73082
  const lines = [];
73047
73083
  lines.push(`## Budget Status (${scope})`);
@@ -73096,53 +73132,51 @@ function createBudgetToolExecutors(getBudgetTracker) {
73096
73132
  if (!tracker) {
73097
73133
  return "Budget tracking is not enabled.";
73098
73134
  }
73099
- const scope = String(input.scope || "session");
73100
- const config = tracker.getConfig();
73101
- let limitsKey;
73102
- switch (scope) {
73103
- case "session":
73104
- limitsKey = "sessionLimits";
73105
- break;
73106
- case "swarm":
73107
- limitsKey = "swarmLimits";
73108
- break;
73109
- case "project":
73110
- limitsKey = "projectLimits";
73111
- break;
73112
- default:
73113
- return `Invalid scope: ${scope}. Use "session", "swarm", or "project".`;
73135
+ const scopeInput = normalizeScope2(input.scope || "session");
73136
+ if (!isBudgetToolScope(scopeInput)) {
73137
+ return `Invalid scope: ${scopeInput || "(empty)"}. Use "session", "swarm", or "project".`;
73114
73138
  }
73139
+ const scope = scopeInput;
73140
+ const config = tracker.getConfig();
73115
73141
  const updates = {};
73116
- if (input.maxInputTokens !== undefined)
73117
- updates.maxInputTokens = Number(input.maxInputTokens);
73118
- if (input.maxOutputTokens !== undefined)
73119
- updates.maxOutputTokens = Number(input.maxOutputTokens);
73120
- if (input.maxTotalTokens !== undefined)
73121
- updates.maxTotalTokens = Number(input.maxTotalTokens);
73122
- if (input.maxLlmCalls !== undefined)
73123
- updates.maxLlmCalls = Number(input.maxLlmCalls);
73124
- if (input.maxToolCalls !== undefined)
73125
- updates.maxToolCalls = Number(input.maxToolCalls);
73126
- if (input.maxDurationMs !== undefined)
73127
- updates.maxDurationMs = Number(input.maxDurationMs);
73128
- if (Object.keys(updates).length === 0) {
73142
+ const providedFields = [];
73143
+ const invalidFields = [];
73144
+ for (const field of LIMIT_FIELDS) {
73145
+ if (input[field] === undefined)
73146
+ continue;
73147
+ providedFields.push(field);
73148
+ const parsed = parseLimitValue(input[field]);
73149
+ if (parsed === null) {
73150
+ invalidFields.push(field);
73151
+ continue;
73152
+ }
73153
+ updates[field] = parsed;
73154
+ }
73155
+ if (providedFields.length === 0) {
73129
73156
  return "No limits specified. Provide at least one limit to update.";
73130
73157
  }
73158
+ if (invalidFields.length > 0) {
73159
+ return `Invalid limit values for: ${invalidFields.join(", ")}. Values must be numbers >= 0 (0 = unlimited).`;
73160
+ }
73131
73161
  tracker.updateConfig({
73132
- [limitsKey]: {
73133
- ...config[limitsKey],
73162
+ [scope]: {
73163
+ ...config[scope] || {},
73134
73164
  ...updates
73135
73165
  }
73136
73166
  });
73167
+ const applied = Object.fromEntries(providedFields.map((field) => [field, updates[field] ?? "unlimited"]));
73137
73168
  return `Budget limits updated for ${scope} scope:
73138
- ${JSON.stringify(updates, null, 2)}`;
73169
+ ${JSON.stringify(applied, null, 2)}`;
73139
73170
  },
73140
73171
  budget_reset: async (input) => {
73141
73172
  const tracker = getBudgetTracker();
73142
73173
  if (!tracker) {
73143
73174
  return "Budget tracking is not enabled.";
73144
73175
  }
73145
- const scope = String(input.scope || "session");
73176
+ const scope = normalizeScope2(input.scope || "session");
73177
+ if (!isBudgetResetScope(scope)) {
73178
+ return `Invalid scope: ${scope || "(empty)"}. Use "session", "swarm", "project", or "all".`;
73179
+ }
73146
73180
  if (scope === "all") {
73147
73181
  tracker.resetAll();
73148
73182
  return "All budget counters have been reset.";
@@ -73158,8 +73192,17 @@ function registerBudgetTools(registry, getBudgetTracker) {
73158
73192
  registry.register(tool, executors[tool.name]);
73159
73193
  }
73160
73194
  }
73161
- var budgetStatusTool, budgetGetTool, budgetSetTool, budgetResetTool, budgetTools;
73195
+ var BUDGET_TOOL_SCOPES, LIMIT_FIELDS, budgetStatusTool, budgetGetTool, budgetSetTool, budgetResetTool, budgetTools;
73162
73196
  var init_tools4 = __esm(() => {
73197
+ BUDGET_TOOL_SCOPES = ["session", "swarm", "project"];
73198
+ LIMIT_FIELDS = [
73199
+ "maxInputTokens",
73200
+ "maxOutputTokens",
73201
+ "maxTotalTokens",
73202
+ "maxLlmCalls",
73203
+ "maxToolCalls",
73204
+ "maxDurationMs"
73205
+ ];
73163
73206
  budgetStatusTool = {
73164
73207
  name: "budget_status",
73165
73208
  description: "Get current budget status showing usage vs limits for the specified scope (session, swarm, or project).",
@@ -73231,8 +73274,8 @@ var init_tools4 = __esm(() => {
73231
73274
  properties: {
73232
73275
  scope: {
73233
73276
  type: "string",
73234
- description: 'Budget scope to reset: "session", "swarm", or "all"',
73235
- enum: ["session", "swarm", "all"]
73277
+ description: 'Budget scope to reset: "session", "swarm", "project", or "all"',
73278
+ enum: ["session", "swarm", "project", "all"]
73236
73279
  }
73237
73280
  },
73238
73281
  required: ["scope"]
@@ -78300,6 +78343,7 @@ class BuiltinCommands {
78300
78343
  registerAll(loader) {
78301
78344
  loader.register(this.helpCommand(loader));
78302
78345
  loader.register(this.aboutCommand());
78346
+ loader.register(this.docsCommand());
78303
78347
  loader.register(this.clearCommand());
78304
78348
  loader.register(this.newCommand());
78305
78349
  loader.register(this.sessionCommand());
@@ -78347,6 +78391,8 @@ class BuiltinCommands {
78347
78391
  loader.register(this.tasksCommand());
78348
78392
  loader.register(this.setupCommand());
78349
78393
  loader.register(this.exitCommand());
78394
+ loader.register(this.diffCommand());
78395
+ loader.register(this.undoCommand());
78350
78396
  }
78351
78397
  aboutCommand() {
78352
78398
  return {
@@ -78369,6 +78415,62 @@ class BuiltinCommands {
78369
78415
 
78370
78416
  `;
78371
78417
  message += `assistants is a terminal-first AI environment for connecting tools, running tasks, and collaborating with agents.
78418
+ `;
78419
+ context.emit("text", message);
78420
+ context.emit("done");
78421
+ return { handled: true };
78422
+ }
78423
+ };
78424
+ }
78425
+ docsCommand() {
78426
+ return {
78427
+ name: "docs",
78428
+ description: "Open docs panel in terminal or print full usage guide",
78429
+ builtin: true,
78430
+ selfHandled: true,
78431
+ content: "",
78432
+ handler: async (_args, context) => {
78433
+ let message = `
78434
+ **assistants Documentation**
78435
+
78436
+ `;
78437
+ message += "In the terminal app, `/docs` opens an interactive documentation panel with keyboard navigation.\n\n";
78438
+ message += `**Quick Start**
78439
+ `;
78440
+ message += " 1. Run `/init` in a project.\n";
78441
+ message += " 2. Run `/onboarding` to select provider, model, and API key setup.\n";
78442
+ message += " 3. Start work with `/new` and inspect status via `/status`, `/tokens`, and `/cost`.\n\n";
78443
+ message += `**Core Workflow**
78444
+ `;
78445
+ message += ` - Sessions keep history, tool calls, context, and model state.
78446
+ `;
78447
+ message += " - `/sessions` switches sessions.\n";
78448
+ message += " - `/compact` summarizes long context.\n";
78449
+ message += " - `/resume` recovers interrupted work.\n\n";
78450
+ message += `**Configuration and Models**
78451
+ `;
78452
+ message += " - `/model` opens interactive model selection.\n";
78453
+ message += " - `/config` manages user/project/local config.\n";
78454
+ message += " - `/memory`, `/context`, `/hooks`, and `/guardrails` control behavior and safety.\n\n";
78455
+ message += `**Workspaces and Projects**
78456
+ `;
78457
+ message += " - `/workspace` switches isolated workspace state.\n";
78458
+ message += " - `/projects` and `/plans` manage project scope and plan execution.\n\n";
78459
+ message += `**Resources and Operations**
78460
+ `;
78461
+ message += " - `/wallet`, `/secrets`, and `/budgets` manage cards, secrets, and limits.\n";
78462
+ message += " - `/tasks`, `/schedules`, `/jobs`, `/orders`, `/heartbeat`, and `/logs` manage operations.\n\n";
78463
+ message += `**Collaboration**
78464
+ `;
78465
+ message += " - `/assistants`, `/identity`, `/messages`, `/channels`, `/people`, `/telephony`.\n\n";
78466
+ message += `**Voice**
78467
+ `;
78468
+ message += " - `/voice`, `/listen`, `/talk`, `/say`.\n\n";
78469
+ message += `**Storage**
78470
+ `;
78471
+ message += " - Project data: `.assistants/`\n";
78472
+ message += " - User/global data: `~/.assistants/`\n";
78473
+ message += ` - Workspace switching isolates sessions, assistants, settings, and resource state.
78372
78474
  `;
78373
78475
  context.emit("text", message);
78374
78476
  context.emit("done");
@@ -86888,6 +86990,140 @@ ${repoUrl}/issues/new
86888
86990
  }
86889
86991
  };
86890
86992
  }
86993
+ diffCommand() {
86994
+ return {
86995
+ name: "diff",
86996
+ description: "Show git diff of current changes (supports --staged, <file>)",
86997
+ builtin: true,
86998
+ selfHandled: true,
86999
+ content: "",
87000
+ handler: async (args, context) => {
87001
+ const { exec } = await import("child_process");
87002
+ const { promisify } = await import("util");
87003
+ const execAsync = promisify(exec);
87004
+ const parts = splitArgs(args);
87005
+ try {
87006
+ let diffCmd = "git diff";
87007
+ let statCmd = "git diff --stat";
87008
+ if (parts.includes("--staged") || parts.includes("--cached")) {
87009
+ diffCmd += " --staged";
87010
+ statCmd += " --staged";
87011
+ const fileArgs = parts.filter((p) => p !== "--staged" && p !== "--cached");
87012
+ if (fileArgs.length > 0) {
87013
+ const files = fileArgs.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ");
87014
+ diffCmd += ` -- ${files}`;
87015
+ statCmd += ` -- ${files}`;
87016
+ }
87017
+ } else if (parts.length > 0) {
87018
+ const files = parts.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ");
87019
+ diffCmd += ` -- ${files}`;
87020
+ statCmd += ` -- ${files}`;
87021
+ }
87022
+ const opts = { cwd: context.cwd, maxBuffer: 1048576 };
87023
+ const [statResult, diffResult] = await Promise.all([
87024
+ execAsync(statCmd, opts).catch(() => ({ stdout: "" })),
87025
+ execAsync(diffCmd, opts).catch(() => ({ stdout: "" }))
87026
+ ]);
87027
+ const statOutput = (statResult.stdout || "").trim();
87028
+ const diffOutput = (diffResult.stdout || "").trim();
87029
+ if (!statOutput && !diffOutput) {
87030
+ context.emit("text", `
87031
+ No changes detected.
87032
+ `);
87033
+ context.emit("done");
87034
+ return { handled: true };
87035
+ }
87036
+ let message = `
87037
+ **Git Diff**
87038
+
87039
+ `;
87040
+ if (statOutput) {
87041
+ message += "**Summary:**\n```\n" + statOutput + "\n```\n\n";
87042
+ }
87043
+ if (diffOutput) {
87044
+ message += "**Changes:**\n```diff\n" + diffOutput + "\n```\n";
87045
+ }
87046
+ context.emit("text", message);
87047
+ } catch {
87048
+ context.emit("text", `
87049
+ Not a git repository or git not available.
87050
+ `);
87051
+ }
87052
+ context.emit("done");
87053
+ return { handled: true };
87054
+ }
87055
+ };
87056
+ }
87057
+ undoCommand() {
87058
+ return {
87059
+ name: "undo",
87060
+ description: "Revert uncommitted changes (file, all, or show preview)",
87061
+ builtin: true,
87062
+ selfHandled: true,
87063
+ content: "",
87064
+ handler: async (args, context) => {
87065
+ const { exec } = await import("child_process");
87066
+ const { promisify } = await import("util");
87067
+ const execAsync = promisify(exec);
87068
+ const parts = splitArgs(args);
87069
+ const opts = { cwd: context.cwd };
87070
+ try {
87071
+ if (parts.length === 0) {
87072
+ const { stdout: stdout2 } = await execAsync("git diff --stat", opts);
87073
+ const statOutput = stdout2.trim();
87074
+ if (!statOutput) {
87075
+ context.emit("text", `
87076
+ No uncommitted changes to undo.
87077
+ `);
87078
+ context.emit("done");
87079
+ return { handled: true };
87080
+ }
87081
+ let message = "\n**Uncommitted Changes:**\n```\n" + statOutput + "\n```\n\n";
87082
+ message += "Use `/undo <file>` to revert a specific file\n";
87083
+ message += "Use `/undo all` to revert all changes\n";
87084
+ context.emit("text", message);
87085
+ context.emit("done");
87086
+ return { handled: true };
87087
+ }
87088
+ if (parts[0] === "all") {
87089
+ const { stdout: stdout2 } = await execAsync("git diff --stat", opts);
87090
+ const statOutput = stdout2.trim();
87091
+ if (!statOutput) {
87092
+ context.emit("text", `
87093
+ No uncommitted changes to undo.
87094
+ `);
87095
+ context.emit("done");
87096
+ return { handled: true };
87097
+ }
87098
+ await execAsync("git checkout -- .", opts);
87099
+ context.emit("text", "\n**Reverted all uncommitted changes:**\n```\n" + statOutput + "\n```\n");
87100
+ context.emit("done");
87101
+ return { handled: true };
87102
+ }
87103
+ const escaped = parts.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ");
87104
+ const { stdout } = await execAsync(`git diff --stat -- ${escaped}`, opts);
87105
+ const checkOutput = stdout.trim();
87106
+ if (!checkOutput) {
87107
+ context.emit("text", `
87108
+ No changes found for: ${parts.join(" ")}
87109
+ `);
87110
+ context.emit("done");
87111
+ return { handled: true };
87112
+ }
87113
+ await execAsync(`git checkout -- ${escaped}`, opts);
87114
+ context.emit("text", `
87115
+ **Reverted:** ${parts.join(" ")}
87116
+ `);
87117
+ } catch {
87118
+ context.emit("text", `
87119
+ Not a git repository or git not available.
87120
+ `);
87121
+ }
87122
+ context.emit("done");
87123
+ return { handled: true };
87124
+ }
87125
+ };
87126
+ }
86891
87127
  async resolveProject(context, target) {
86892
87128
  const byId = await readProject(context.cwd, target);
86893
87129
  if (byId)
@@ -86918,7 +87154,7 @@ ${repoUrl}/issues/new
86918
87154
  context.setProjectContext(projectContext);
86919
87155
  }
86920
87156
  }
86921
- var VERSION2 = "1.1.25";
87157
+ var VERSION2 = "1.1.26";
86922
87158
  var init_builtin = __esm(async () => {
86923
87159
  init_src2();
86924
87160
  init_store();
@@ -92083,14 +92319,22 @@ var init_conventions = __esm(() => {
92083
92319
  });
92084
92320
 
92085
92321
  // packages/core/src/heartbeat/auto-schedule-hook.ts
92086
- async function autoScheduleHeartbeatHandler(_input, context) {
92087
- const heartbeatCfg = context.config?.heartbeat;
92322
+ function resolveHeartbeatConfig(input, context) {
92323
+ const inputHeartbeat = input.heartbeat;
92324
+ if (inputHeartbeat && typeof inputHeartbeat === "object") {
92325
+ return inputHeartbeat;
92326
+ }
92327
+ return context.config?.heartbeat;
92328
+ }
92329
+ async function autoScheduleHeartbeatHandler(input, context) {
92330
+ const heartbeatCfg = resolveHeartbeatConfig(input, context);
92088
92331
  if (!heartbeatCfg?.autonomous)
92089
92332
  return null;
92090
92333
  const scheduleId = heartbeatScheduleId(context.sessionId);
92091
92334
  try {
92092
92335
  const existing = await getSchedule(context.cwd, scheduleId);
92093
- if (existing && existing.status === "active") {
92336
+ const hasValidNextRunAt = Number.isFinite(existing?.nextRunAt);
92337
+ if (existing && existing.status === "active" && hasValidNextRunAt) {
92094
92338
  return null;
92095
92339
  }
92096
92340
  const maxSleep = heartbeatCfg.maxSleepMs ?? DEFAULT_MAX_SLEEP_MS;
@@ -92135,11 +92379,13 @@ var init_auto_schedule_hook = __esm(async () => {
92135
92379
  async function ensureWatchdogSchedule(cwd, sessionId, intervalMs = DEFAULT_WATCHDOG_INTERVAL_MS) {
92136
92380
  const scheduleId = watchdogScheduleId(sessionId);
92137
92381
  const existing = await getSchedule(cwd, scheduleId);
92138
- if (existing && existing.status === "active") {
92382
+ const existingHasValidNextRunAt = Number.isFinite(existing?.nextRunAt);
92383
+ if (existing && existing.status === "active" && existingHasValidNextRunAt) {
92139
92384
  return;
92140
92385
  }
92141
92386
  const legacy = await getSchedule(cwd, WATCHDOG_SCHEDULE_ID);
92142
- if (legacy && legacy.status === "active" && legacy.sessionId === sessionId) {
92387
+ const legacyHasValidNextRunAt = Number.isFinite(legacy?.nextRunAt);
92388
+ if (legacy && legacy.status === "active" && legacy.sessionId === sessionId && legacyHasValidNextRunAt) {
92143
92389
  return;
92144
92390
  }
92145
92391
  const intervalSeconds = Math.max(60, Math.round(intervalMs / 1000));
@@ -92170,24 +92416,33 @@ var init_watchdog = __esm(async () => {
92170
92416
 
92171
92417
  // packages/core/src/heartbeat/install-skills.ts
92172
92418
  import { join as join30 } from "path";
92173
- async function writeSkillIfMissing(dir, skillName, content) {
92174
- const { mkdir: mkdir10, writeFile: writeFile8, access } = await import("fs/promises");
92419
+ async function writeSkillIfNeeded(dir, skillName, content) {
92420
+ const { mkdir: mkdir10, writeFile: writeFile8, readFile: readFile11 } = await import("fs/promises");
92175
92421
  const skillDir = join30(dir, `skill-${skillName}`);
92176
92422
  const skillFile = join30(skillDir, "SKILL.md");
92177
- try {
92178
- await access(skillFile);
92179
- return false;
92180
- } catch {}
92181
92423
  await mkdir10(skillDir, { recursive: true });
92182
- await writeFile8(skillFile, content, "utf-8");
92183
- return true;
92424
+ try {
92425
+ const existing = await readFile11(skillFile, "utf-8");
92426
+ if (existing === content) {
92427
+ return false;
92428
+ }
92429
+ const hasLegacyPatterns = LEGACY_SKILL_PATTERNS.some((pattern) => pattern.test(existing));
92430
+ if (!hasLegacyPatterns) {
92431
+ return false;
92432
+ }
92433
+ await writeFile8(skillFile, content, "utf-8");
92434
+ return true;
92435
+ } catch {
92436
+ await writeFile8(skillFile, content, "utf-8");
92437
+ return true;
92438
+ }
92184
92439
  }
92185
92440
  async function installHeartbeatSkills() {
92186
92441
  const sharedSkillsDir = join30(getConfigDir(), "shared", "skills");
92187
92442
  const installed = [];
92188
92443
  const results = await Promise.all([
92189
- writeSkillIfMissing(sharedSkillsDir, "main-loop", MAIN_LOOP_SKILL),
92190
- writeSkillIfMissing(sharedSkillsDir, "watchdog", WATCHDOG_SKILL)
92444
+ writeSkillIfNeeded(sharedSkillsDir, "main-loop", MAIN_LOOP_SKILL),
92445
+ writeSkillIfNeeded(sharedSkillsDir, "watchdog", WATCHDOG_SKILL)
92191
92446
  ]);
92192
92447
  if (results[0])
92193
92448
  installed.push("main-loop");
@@ -92216,8 +92471,8 @@ You are running as an autonomous heartbeat turn. This is a scheduled wakeup \u20
92216
92471
  - \`memory_save agent.state.lastActions "..."\` with a brief summary of what you did this turn.
92217
92472
  - \`memory_save agent.state.pending "..."\` with any items still pending.
92218
92473
  7. **Schedule next heartbeat** \u2014 choose when you should wake up next based on urgency:
92219
- - Delete the old heartbeat schedule: \`schedule_delete heartbeat-{SESSION_ID}\`
92220
- - Create a new schedule: \`schedule_create\` with \`actionType: "message"\`, \`message: "/main-loop"\`, and either \`at\` (one-shot) or \`cron\` + \`startImmediately: true\` (recurring).
92474
+ - Delete the old heartbeat schedule: call \`schedule\` with \`{ action: "delete", id: "heartbeat-{SESSION_ID}" }\`
92475
+ - Create a new schedule: call \`schedule\` with \`action: "create"\`, \`actionType: "message"\`, \`message: "/main-loop"\`, and either \`at\` (one-shot) or \`cron\` + \`startImmediately: true\` (recurring).
92221
92476
  - Save your reasoning: \`memory_save agent.heartbeat.intention "..."\`
92222
92477
  8. **Record timestamp** \u2014 \`memory_save agent.heartbeat.last\` with the current ISO timestamp.
92223
92478
 
@@ -92237,7 +92492,7 @@ You are running as an autonomous heartbeat turn. This is a scheduled wakeup \u20
92237
92492
  name: watchdog
92238
92493
  description: Safety-net watchdog \u2014 checks if the heartbeat is healthy and forces a wakeup if overdue.
92239
92494
  user-invocable: false
92240
- allowed-tools: memory_recall, memory_save, schedule_create, schedule_delete, schedules_list
92495
+ allowed-tools: memory_recall, memory_save, schedule
92241
92496
  ---
92242
92497
 
92243
92498
  ## Watchdog Check
@@ -92249,14 +92504,20 @@ You are the watchdog. Your only job is to verify the heartbeat is running and fo
92249
92504
  1. Read \`memory_recall agent.heartbeat.last\` to get the last heartbeat timestamp.
92250
92505
  2. Read \`memory_recall agent.heartbeat.next\` to get the expected next heartbeat time.
92251
92506
  3. If the last heartbeat is more than **double** the expected interval overdue:
92252
- - Call \`schedules_list\` to check if a heartbeat schedule exists.
92507
+ - Call \`schedule\` with \`{ action: "list" }\` to check if a heartbeat schedule exists.
92253
92508
  - If no active heartbeat schedule exists, create one that fires immediately:
92254
- \`schedule_create\` with \`actionType: "message"\`, \`message: "/main-loop"\`, and \`cron: "* * * * *"\` + \`startImmediately: true\`.
92509
+ call \`schedule\` with \`action: "create"\`, \`actionType: "message"\`, \`message: "/main-loop"\`, and \`cron: "* * * * *"\` + \`startImmediately: true\`.
92255
92510
  - Save \`memory_save agent.heartbeat.intention "Watchdog forced wakeup \u2014 heartbeat was overdue."\`
92256
92511
  4. If the heartbeat is healthy, do nothing.
92257
- `;
92512
+ `, LEGACY_SKILL_PATTERNS;
92258
92513
  var init_install_skills = __esm(async () => {
92259
92514
  await init_config();
92515
+ LEGACY_SKILL_PATTERNS = [
92516
+ /`schedule_create`/,
92517
+ /`schedule_delete`/,
92518
+ /`schedules_list`/,
92519
+ /allowed-tools:\s*.*schedule_create/i
92520
+ ];
92260
92521
  });
92261
92522
 
92262
92523
  // packages/core/src/heartbeat/index.ts
@@ -186336,15 +186597,6 @@ var init_loop = __esm(async () => {
186336
186597
  if (heartbeatCfg?.autonomous) {
186337
186598
  nativeHookRegistry.register(createAutoScheduleHeartbeatHook());
186338
186599
  installHeartbeatSkills().catch(() => {});
186339
- nativeHookRegistry.setConfig({
186340
- ...nativeHookRegistry.getConfig(),
186341
- heartbeat: {
186342
- autonomous: heartbeatCfg.autonomous,
186343
- maxSleepMs: heartbeatCfg.maxSleepMs,
186344
- watchdogEnabled: heartbeatCfg.watchdogEnabled,
186345
- watchdogIntervalMs: heartbeatCfg.watchdogIntervalMs
186346
- }
186347
- });
186348
186600
  if (heartbeatCfg.watchdogEnabled) {
186349
186601
  ensureWatchdogSchedule(this.cwd, this.sessionId, heartbeatCfg.watchdogIntervalMs).catch(() => {});
186350
186602
  }
@@ -186380,8 +186632,8 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
186380
186632
  - \`memory_save agent.state.pending "items waiting for follow-up"\`
186381
186633
  - \`memory_save agent.state.lastActions "what you just did"\`
186382
186634
  2. **Schedule your next heartbeat**:
186383
- - Delete old: \`schedule_delete heartbeat-${this.sessionId}\`
186384
- - Create new: \`schedule_create\` with \`actionType: "message"\`, \`message: "/main-loop"\`, and either \`at\` (one-shot) or \`cron\` + \`startImmediately: true\` (recurring)
186635
+ - Delete old: call \`schedule\` with \`{ action: "delete", id: "heartbeat-${this.sessionId}" }\`
186636
+ - Create new: call \`schedule\` with \`action: "create"\`, \`actionType: "message"\`, \`message: "/main-loop"\`, and either \`at\` (one-shot) or \`cron\` + \`startImmediately: true\` (recurring)
186385
186637
  3. **Save goals** when they change: \`memory_save agent.goals "..."\`
186386
186638
 
186387
186639
  ### Timing guidelines
@@ -186635,9 +186887,9 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
186635
186887
  turn++;
186636
186888
  this.cumulativeTurns++;
186637
186889
  if (this.paused) {
186638
- this.onBudgetWarning?.("Budget exceeded - agent paused. Use /budget resume to continue.");
186890
+ this.onBudgetWarning?.("Budget exceeded - agent paused. Use /budgets resume to continue.");
186639
186891
  this.emit({ type: "text", content: `
186640
- [Agent paused - budget exceeded. Use /budget resume to continue.]
186892
+ [Agent paused - budget exceeded. Use /budgets resume to continue.]
186641
186893
  ` });
186642
186894
  await new Promise((resolve5) => {
186643
186895
  this.pauseResolve = resolve5;
@@ -186714,11 +186966,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
186714
186966
  });
186715
186967
  } catch {}
186716
186968
  try {
186717
- await nativeHookRegistry.execute("Stop", {
186718
- session_id: this.sessionId,
186719
- hook_event_name: "Stop",
186720
- cwd: this.cwd
186721
- }, {
186969
+ await nativeHookRegistry.execute("Stop", this.buildNativeStopHookInput(), {
186722
186970
  sessionId: this.sessionId,
186723
186971
  cwd: this.cwd,
186724
186972
  messages: this.context.getMessages()
@@ -186840,11 +187088,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
186840
187088
  if (!scopeContext) {
186841
187089
  return null;
186842
187090
  }
186843
- const result = await nativeHookRegistry.execute("Stop", {
186844
- session_id: this.sessionId,
186845
- hook_event_name: "Stop",
186846
- cwd: this.cwd
186847
- }, {
187091
+ const result = await nativeHookRegistry.execute("Stop", this.buildNativeStopHookInput(), {
186848
187092
  sessionId: this.sessionId,
186849
187093
  cwd: this.cwd,
186850
187094
  messages: this.context.getMessages(),
@@ -186859,6 +187103,20 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
186859
187103
  systemMessage: result.systemMessage
186860
187104
  };
186861
187105
  }
187106
+ buildNativeStopHookInput() {
187107
+ const heartbeatCfg = this.config?.heartbeat;
187108
+ return {
187109
+ session_id: this.sessionId,
187110
+ hook_event_name: "Stop",
187111
+ cwd: this.cwd,
187112
+ heartbeat: heartbeatCfg ? {
187113
+ autonomous: heartbeatCfg.autonomous,
187114
+ maxSleepMs: heartbeatCfg.maxSleepMs,
187115
+ watchdogEnabled: heartbeatCfg.watchdogEnabled,
187116
+ watchdogIntervalMs: heartbeatCfg.watchdogIntervalMs
187117
+ } : undefined
187118
+ };
187119
+ }
186862
187120
  async firePostToolUseFailure(toolCall, resultContent) {
186863
187121
  await this.hookExecutor.execute(this.hookLoader.getHooks("PostToolUseFailure"), {
186864
187122
  session_id: this.sessionId,
@@ -187323,9 +187581,32 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
187323
187581
  setBudgetEnabled: (enabled) => {
187324
187582
  if (this.budgetTracker) {
187325
187583
  this.budgetTracker.setEnabled(enabled);
187326
- } else if (enabled && this.budgetConfig) {
187327
- this.budgetTracker = new BudgetTracker(this.sessionId, this.budgetConfig);
187328
- this.budgetTracker.setEnabled(true);
187584
+ this.budgetConfig = this.budgetTracker.getConfig();
187585
+ return;
187586
+ }
187587
+ if (!enabled) {
187588
+ if (this.budgetConfig) {
187589
+ this.budgetConfig = {
187590
+ ...this.budgetConfig,
187591
+ enabled: false
187592
+ };
187593
+ }
187594
+ return;
187595
+ }
187596
+ const seed = this.budgetConfig || DEFAULT_BUDGET_CONFIG;
187597
+ this.budgetConfig = {
187598
+ ...DEFAULT_BUDGET_CONFIG,
187599
+ ...seed,
187600
+ session: { ...DEFAULT_BUDGET_CONFIG.session || {}, ...seed.session || {} },
187601
+ assistant: { ...DEFAULT_BUDGET_CONFIG.assistant || {}, ...seed.assistant || {} },
187602
+ swarm: { ...DEFAULT_BUDGET_CONFIG.swarm || {}, ...seed.swarm || {} },
187603
+ project: { ...DEFAULT_BUDGET_CONFIG.project || {}, ...seed.project || {} },
187604
+ enabled: true
187605
+ };
187606
+ this.budgetTracker = new BudgetTracker(this.sessionId, this.budgetConfig);
187607
+ this.budgetTracker.setEnabled(true);
187608
+ if (this.activeProjectId) {
187609
+ this.budgetTracker.setActiveProject(this.activeProjectId);
187329
187610
  }
187330
187611
  },
187331
187612
  resetBudget: (scope) => {
@@ -187751,7 +188032,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
187751
188032
  this.onBudgetWarning?.("Budget exceeded - stopping assistant");
187752
188033
  this.stop();
187753
188034
  } else if (onExceeded === "pause") {
187754
- this.onBudgetWarning?.("Budget exceeded - pausing (requires /budget resume to continue)");
188035
+ this.onBudgetWarning?.("Budget exceeded - pausing (requires /budgets resume to continue)");
187755
188036
  this.paused = true;
187756
188037
  }
187757
188038
  }
@@ -210481,6 +210762,12 @@ function useSafeInput(handler, options = {}) {
210481
210762
  ` : "\x1B\r";
210482
210763
  }
210483
210764
  const key2 = { ...inkKey };
210765
+ if ((input2 === "m" || input2 === "j") && key2.ctrl && !key2.return) {
210766
+ input2 = input2 === "j" ? `
210767
+ ` : "\r";
210768
+ key2.return = true;
210769
+ key2.ctrl = false;
210770
+ }
210484
210771
  const isReturnInput = input2 === "\r" || input2 === `
210485
210772
  ` || input2 === `\r
210486
210773
  ` || input2 === `
@@ -210610,6 +210897,8 @@ var COMMANDS = [
210610
210897
  { name: "/secrets", description: "manage assistant secrets" },
210611
210898
  { name: "/jobs", description: "manage background jobs" },
210612
210899
  { name: "/docs", description: "interactive app documentation" },
210900
+ { name: "/diff", description: "show git diff of changes" },
210901
+ { name: "/undo", description: "revert uncommitted changes" },
210613
210902
  { name: "/rest", description: "enter rest mode" },
210614
210903
  { name: "/logs", description: "view security event logs" },
210615
210904
  { name: "/verification", description: "scope verification status" },
@@ -210644,6 +210933,7 @@ function isLargePaste(text, thresholds = DEFAULT_PASTE_THRESHOLDS) {
210644
210933
  }
210645
210934
  var Input = import_react28.default.forwardRef(function Input2({
210646
210935
  onSubmit,
210936
+ onStopProcessing,
210647
210937
  isProcessing,
210648
210938
  queueLength = 0,
210649
210939
  commands: commands9,
@@ -210657,7 +210947,8 @@ var Input = import_react28.default.forwardRef(function Input2({
210657
210947
  pasteConfig,
210658
210948
  isRecording = false,
210659
210949
  recordingStatus,
210660
- onStopRecording
210950
+ onStopRecording,
210951
+ onFileSearch
210661
210952
  }, ref) {
210662
210953
  const pasteEnabled = pasteConfig?.enabled !== false;
210663
210954
  const pasteThresholds = pasteConfig?.thresholds ?? DEFAULT_PASTE_THRESHOLDS;
@@ -210712,8 +211003,14 @@ var Input = import_react28.default.forwardRef(function Input2({
210712
211003
  if (value.startsWith("/") && !value.includes(" ")) {
210713
211004
  return "command";
210714
211005
  }
211006
+ if (onFileSearch) {
211007
+ const atMatch = value.match(/(?:^|.*\s)@([^\s]*)$/);
211008
+ if (atMatch) {
211009
+ return "file";
211010
+ }
211011
+ }
210715
211012
  return null;
210716
- }, [value, isAskingUser]);
211013
+ }, [value, isAskingUser, onFileSearch]);
210717
211014
  const filteredCommands = import_react28.useMemo(() => {
210718
211015
  if (autocompleteMode !== "command")
210719
211016
  return [];
@@ -210726,7 +211023,18 @@ var Input = import_react28.default.forwardRef(function Input2({
210726
211023
  const search = value.slice(1).toLowerCase();
210727
211024
  return skills.filter((skill) => skill.name.toLowerCase().startsWith(search));
210728
211025
  }, [value, autocompleteMode, skills]);
210729
- const autocompleteItems = autocompleteMode === "skill" ? filteredSkills : filteredCommands;
211026
+ const fileSearchQuery = import_react28.useMemo(() => {
211027
+ if (autocompleteMode !== "file")
211028
+ return "";
211029
+ const atMatch = value.match(/(?:^|.*\s)@([^\s]*)$/);
211030
+ return atMatch ? atMatch[1] : "";
211031
+ }, [value, autocompleteMode]);
211032
+ const filteredFiles = import_react28.useMemo(() => {
211033
+ if (autocompleteMode !== "file" || !onFileSearch)
211034
+ return [];
211035
+ return onFileSearch(fileSearchQuery);
211036
+ }, [autocompleteMode, fileSearchQuery, onFileSearch]);
211037
+ const autocompleteItems = autocompleteMode === "skill" ? filteredSkills : autocompleteMode === "file" ? filteredFiles.map((f6) => ({ name: f6 })) : filteredCommands;
210730
211038
  import_react28.useEffect(() => {
210731
211039
  if (autocompleteItems.length === 0) {
210732
211040
  setSelectedIndex(0);
@@ -210907,6 +211215,10 @@ var Input = import_react28.default.forwardRef(function Input2({
210907
211215
  return;
210908
211216
  }
210909
211217
  if (key.escape && !isAskingUser) {
211218
+ if (isProcessing && onStopProcessing) {
211219
+ onStopProcessing();
211220
+ return;
211221
+ }
210910
211222
  if (largePaste) {
210911
211223
  setLargePaste(null);
210912
211224
  setShowPastePreview(false);
@@ -210932,8 +211244,15 @@ var Input = import_react28.default.forwardRef(function Input2({
210932
211244
  if (key.tab) {
210933
211245
  if (autocompleteItems.length > 0) {
210934
211246
  const selected = autocompleteItems[selectedIndex] || autocompleteItems[0];
210935
- const nextValue = autocompleteMode === "skill" ? `$${selected.name} ` : `${selected.name} `;
210936
- setValueAndCursor(nextValue);
211247
+ if (autocompleteMode === "file") {
211248
+ const atMatch = value.match(/^(.*(?:^|\s))@[^\s]*$/);
211249
+ const prefix2 = atMatch ? atMatch[1] : "";
211250
+ const nextValue = prefix2 + selected.name + " ";
211251
+ setValueAndCursor(nextValue);
211252
+ } else {
211253
+ const nextValue = autocompleteMode === "skill" ? `$${selected.name} ` : `${selected.name} `;
211254
+ setValueAndCursor(nextValue);
211255
+ }
210937
211256
  return;
210938
211257
  }
210939
211258
  if (isProcessing && value.trim()) {
@@ -211013,12 +211332,6 @@ var Input = import_react28.default.forwardRef(function Input2({
211013
211332
  deleteForward();
211014
211333
  return;
211015
211334
  }
211016
- if (input === "\x1B\r" || input === `\x1B
211017
- `) {
211018
- insertText(`
211019
- `);
211020
- return;
211021
- }
211022
211335
  if (key.return) {
211023
211336
  if (isRecording && onStopRecording) {
211024
211337
  onStopRecording();
@@ -211029,7 +211342,8 @@ var Input = import_react28.default.forwardRef(function Input2({
211029
211342
  setValueAndCursor("");
211030
211343
  return;
211031
211344
  }
211032
- if (key.meta && value.trim()) {
211345
+ if (key.meta && value.trim() && input !== "\x1B\r" && input !== `\x1B
211346
+ `) {
211033
211347
  onSubmit(value, "queue");
211034
211348
  setValueAndCursor("");
211035
211349
  return;
@@ -211068,6 +211382,8 @@ var Input = import_react28.default.forwardRef(function Input2({
211068
211382
  };
211069
211383
  const visibleSkills = getVisibleItems(filteredSkills);
211070
211384
  const visibleCommands = getVisibleItems(filteredCommands);
211385
+ const fileItems = import_react28.useMemo(() => filteredFiles.map((f6) => ({ name: f6 })), [filteredFiles]);
211386
+ const visibleFiles = getVisibleItems(fileItems);
211071
211387
  const layout = buildLayout(value, cursor, textWidth);
211072
211388
  const lines = layout.displayLines;
211073
211389
  const lineCount = value.split(`
@@ -211340,6 +211656,41 @@ var Input = import_react28.default.forwardRef(function Input2({
211340
211656
  ]
211341
211657
  }, undefined, true, undefined, this)
211342
211658
  ]
211659
+ }, undefined, true, undefined, this),
211660
+ autocompleteMode === "file" && filteredFiles.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
211661
+ flexDirection: "column",
211662
+ marginTop: 1,
211663
+ marginLeft: 2,
211664
+ children: [
211665
+ visibleFiles.startIndex > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text3, {
211666
+ dimColor: true,
211667
+ children: [
211668
+ " \u2191 ",
211669
+ visibleFiles.startIndex,
211670
+ " more above"
211671
+ ]
211672
+ }, undefined, true, undefined, this),
211673
+ visibleFiles.items.map((file, i5) => {
211674
+ const actualIndex = visibleFiles.startIndex + i5;
211675
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
211676
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text3, {
211677
+ color: actualIndex === selectedIndex ? "cyan" : "#5fb3a1",
211678
+ children: [
211679
+ actualIndex === selectedIndex ? "\u25B8 " : " ",
211680
+ file.name
211681
+ ]
211682
+ }, undefined, true, undefined, this)
211683
+ }, file.name, false, undefined, this);
211684
+ }),
211685
+ visibleFiles.startIndex + maxVisible < filteredFiles.length && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text3, {
211686
+ dimColor: true,
211687
+ children: [
211688
+ " \u2193 ",
211689
+ filteredFiles.length - visibleFiles.startIndex - maxVisible,
211690
+ " more below"
211691
+ ]
211692
+ }, undefined, true, undefined, this)
211693
+ ]
211343
211694
  }, undefined, true, undefined, this)
211344
211695
  ]
211345
211696
  }, undefined, true, undefined, this);
@@ -213778,7 +214129,8 @@ function Status({
213778
214129
  backgroundProcessingCount = 0,
213779
214130
  processingStartTime,
213780
214131
  verboseTools = false,
213781
- recentTools = []
214132
+ recentTools = [],
214133
+ gitBranch
213782
214134
  }) {
213783
214135
  const [elapsed, setElapsed] = import_react30.useState(0);
213784
214136
  const [heartbeatCountdown, setHeartbeatCountdown] = import_react30.useState("");
@@ -213879,7 +214231,8 @@ function Status({
213879
214231
  dimColor: true,
213880
214232
  children: [
213881
214233
  "/help",
213882
- sessionCount && sessionCount > 1 ? " \xB7 Ctrl+]" : ""
214234
+ sessionCount && sessionCount > 1 ? " \xB7 Ctrl+]" : "",
214235
+ gitBranch ? ` \xB7 \u2387 ${gitBranch}` : ""
213883
214236
  ]
213884
214237
  }, undefined, true, undefined, this),
213885
214238
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
@@ -214851,6 +215204,7 @@ function ConnectorsPanel({
214851
215204
  const [loadedConnectors, setLoadedConnectors] = import_react36.useState(new Map);
214852
215205
  const [autoRefreshEntries, setAutoRefreshEntries] = import_react36.useState(new Map);
214853
215206
  const [autoRefreshError, setAutoRefreshError] = import_react36.useState(null);
215207
+ const pendingAuthChecksRef = import_react36.useRef(new Set);
214854
215208
  const filteredConnectors = import_react36.useMemo(() => {
214855
215209
  if (!searchQuery.trim()) {
214856
215210
  return connectors;
@@ -214874,24 +215228,6 @@ function ConnectorsPanel({
214874
215228
  }
214875
215229
  }
214876
215230
  }, [initialConnector, filteredConnectors]);
214877
- import_react36.useEffect(() => {
214878
- const loadStatuses = async () => {
214879
- const results = await Promise.all(connectors.map(async (connector) => {
214880
- try {
214881
- const status = await onCheckAuth(connector);
214882
- return { name: connector.name, status };
214883
- } catch {
214884
- return { name: connector.name, status: { authenticated: false, error: "Failed to check" } };
214885
- }
214886
- }));
214887
- const statusMap = new Map;
214888
- for (const { name: name2, status } of results) {
214889
- statusMap.set(name2, status);
214890
- }
214891
- setAuthStatuses(statusMap);
214892
- };
214893
- loadStatuses();
214894
- }, [connectors, onCheckAuth]);
214895
215231
  const loadAutoRefreshEntries = import_react36.useCallback(async () => {
214896
215232
  try {
214897
215233
  const manager = ConnectorAutoRefreshManager.getInstance();
@@ -214991,6 +215327,10 @@ function ConnectorsPanel({
214991
215327
  }, [mode, loadCommandHelp]);
214992
215328
  useSafeInput((input, key) => {
214993
215329
  if (isSearching) {
215330
+ if (input === "q" || input === "Q") {
215331
+ onClose();
215332
+ return;
215333
+ }
214994
215334
  if (key.escape) {
214995
215335
  if (searchQuery) {
214996
215336
  setSearchQuery("");
@@ -215049,7 +215389,7 @@ function ConnectorsPanel({
215049
215389
  if (key.upArrow) {
215050
215390
  if (mode === "list" && filteredConnectors.length > 0) {
215051
215391
  setConnectorIndex((prev) => prev === 0 ? filteredConnectors.length - 1 : prev - 1);
215052
- } else if (mode === "detail") {
215392
+ } else if (mode === "detail" && currentCommands.length > 0) {
215053
215393
  setCommandIndex((prev) => prev === 0 ? currentCommands.length - 1 : prev - 1);
215054
215394
  }
215055
215395
  return;
@@ -215057,7 +215397,7 @@ function ConnectorsPanel({
215057
215397
  if (key.downArrow) {
215058
215398
  if (mode === "list" && filteredConnectors.length > 0) {
215059
215399
  setConnectorIndex((prev) => prev === filteredConnectors.length - 1 ? 0 : prev + 1);
215060
- } else if (mode === "detail") {
215400
+ } else if (mode === "detail" && currentCommands.length > 0) {
215061
215401
  setCommandIndex((prev) => prev === currentCommands.length - 1 ? 0 : prev + 1);
215062
215402
  }
215063
215403
  return;
@@ -215071,10 +215411,6 @@ function ConnectorsPanel({
215071
215411
  }
215072
215412
  return;
215073
215413
  }
215074
- if (mode === "list" && input && /^[a-zA-Z]$/.test(input)) {
215075
- setIsSearching(true);
215076
- setSearchQuery(input);
215077
- }
215078
215414
  });
215079
215415
  const getStatusIcon = (status) => {
215080
215416
  if (!status)
@@ -215088,6 +215424,45 @@ function ConnectorsPanel({
215088
215424
  const connectorRange = import_react36.useMemo(() => getVisibleRange(safeConnectorIndex, filteredConnectors.length), [safeConnectorIndex, filteredConnectors.length]);
215089
215425
  const commandRange = import_react36.useMemo(() => getVisibleRange(commandIndex, currentCommands.length), [commandIndex, currentCommands.length]);
215090
215426
  const visibleConnectors = filteredConnectors.slice(connectorRange.start, connectorRange.end);
215427
+ import_react36.useEffect(() => {
215428
+ const targets = mode === "list" ? visibleConnectors : currentConnector ? [currentConnector] : [];
215429
+ if (targets.length === 0) {
215430
+ return;
215431
+ }
215432
+ let cancelled = false;
215433
+ const loadStatuses = async () => {
215434
+ for (const connector of targets) {
215435
+ const name2 = connector.name;
215436
+ if (authStatuses.has(name2) || pendingAuthChecksRef.current.has(name2)) {
215437
+ continue;
215438
+ }
215439
+ pendingAuthChecksRef.current.add(name2);
215440
+ try {
215441
+ let status;
215442
+ try {
215443
+ status = await onCheckAuth(connector);
215444
+ } catch {
215445
+ status = { authenticated: false, error: "Failed to check" };
215446
+ }
215447
+ if (cancelled)
215448
+ continue;
215449
+ setAuthStatuses((prev) => {
215450
+ if (prev.has(name2))
215451
+ return prev;
215452
+ const next = new Map(prev);
215453
+ next.set(name2, status);
215454
+ return next;
215455
+ });
215456
+ } finally {
215457
+ pendingAuthChecksRef.current.delete(name2);
215458
+ }
215459
+ }
215460
+ };
215461
+ loadStatuses();
215462
+ return () => {
215463
+ cancelled = true;
215464
+ };
215465
+ }, [authStatuses, currentConnector, mode, onCheckAuth, visibleConnectors]);
215091
215466
  if (connectors.length === 0) {
215092
215467
  return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
215093
215468
  flexDirection: "column",
@@ -226654,16 +227029,16 @@ function JobsPanel({ manager, onClose }) {
226654
227029
  if (!job || isWorking)
226655
227030
  return;
226656
227031
  if (!isActiveStatus(job.status)) {
226657
- setStatusMessage(`Job ${job.id} is ${job.status}. Only pending/running jobs can be cancelled.`);
227032
+ setStatusMessage(`Job ${job.id} is ${job.status}. Only pending/running jobs can be killed.`);
226658
227033
  return;
226659
227034
  }
226660
227035
  setIsWorking(true);
226661
227036
  try {
226662
227037
  const cancelled = await manager.cancelJob(job.id);
226663
227038
  if (cancelled) {
226664
- setStatusMessage(`Cancelled ${job.id}.`);
227039
+ setStatusMessage(`Killed ${job.id}.`);
226665
227040
  } else {
226666
- setStatusMessage(`Could not cancel ${job.id}.`);
227041
+ setStatusMessage(`Could not kill ${job.id}.`);
226667
227042
  }
226668
227043
  await refreshJobs(false);
226669
227044
  } catch (err) {
@@ -226672,6 +227047,38 @@ function JobsPanel({ manager, onClose }) {
226672
227047
  setIsWorking(false);
226673
227048
  }
226674
227049
  }, [isWorking, manager, refreshJobs]);
227050
+ const killAllActiveJobs = import_react50.useCallback(async () => {
227051
+ if (isWorking)
227052
+ return;
227053
+ const activeJobs = jobs2.filter((job) => isActiveStatus(job.status));
227054
+ if (activeJobs.length === 0) {
227055
+ setStatusMessage("No active jobs to kill.");
227056
+ return;
227057
+ }
227058
+ setIsWorking(true);
227059
+ try {
227060
+ let killed = 0;
227061
+ for (const job of activeJobs) {
227062
+ try {
227063
+ const cancelled = await manager.cancelJob(job.id);
227064
+ if (cancelled)
227065
+ killed += 1;
227066
+ } catch {}
227067
+ }
227068
+ if (killed === 0) {
227069
+ setStatusMessage("Could not kill any active jobs.");
227070
+ } else if (killed === activeJobs.length) {
227071
+ setStatusMessage(`Killed ${killed} active job${killed === 1 ? "" : "s"}.`);
227072
+ } else {
227073
+ setStatusMessage(`Killed ${killed}/${activeJobs.length} active jobs.`);
227074
+ }
227075
+ await refreshJobs(false);
227076
+ } catch (err) {
227077
+ setError(err instanceof Error ? err.message : String(err));
227078
+ } finally {
227079
+ setIsWorking(false);
227080
+ }
227081
+ }, [isWorking, jobs2, manager, refreshJobs]);
226675
227082
  const openDetail = import_react50.useCallback(() => {
226676
227083
  const selected = filteredJobs[selectedIndex];
226677
227084
  if (!selected)
@@ -226694,7 +227101,11 @@ function JobsPanel({ manager, onClose }) {
226694
227101
  refreshJobs(true);
226695
227102
  return;
226696
227103
  }
226697
- if (input === "x" || input === "c") {
227104
+ if (input === "K") {
227105
+ killAllActiveJobs();
227106
+ return;
227107
+ }
227108
+ if (input === "x" || input === "c" || input === "d") {
226698
227109
  cancelJob(detailJob);
226699
227110
  }
226700
227111
  return;
@@ -226748,7 +227159,11 @@ function JobsPanel({ manager, onClose }) {
226748
227159
  refreshJobs(true);
226749
227160
  return;
226750
227161
  }
226751
- if (input === "x" || input === "c") {
227162
+ if (input === "K") {
227163
+ killAllActiveJobs();
227164
+ return;
227165
+ }
227166
+ if (input === "x" || input === "c" || input === "d") {
226752
227167
  cancelJob(filteredJobs[selectedIndex] || null);
226753
227168
  }
226754
227169
  });
@@ -226786,7 +227201,9 @@ function JobsPanel({ manager, onClose }) {
226786
227201
  children: [
226787
227202
  "(",
226788
227203
  jobs2.length,
226789
- " total)"
227204
+ " total, ",
227205
+ jobs2.filter((job) => isActiveStatus(job.status)).length,
227206
+ " active)"
226790
227207
  ]
226791
227208
  }, undefined, true, undefined, this)
226792
227209
  ]
@@ -226907,7 +227324,7 @@ function JobsPanel({ manager, onClose }) {
226907
227324
  marginTop: 1,
226908
227325
  children: /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text3, {
226909
227326
  color: "gray",
226910
- children: "x/c cancel r refresh esc back q close"
227327
+ children: "d/x/c kill selected K kill all active r refresh esc back q close"
226911
227328
  }, undefined, false, undefined, this)
226912
227329
  }, undefined, false, undefined, this)
226913
227330
  ]
@@ -227027,7 +227444,7 @@ function JobsPanel({ manager, onClose }) {
227027
227444
  marginTop: 1,
227028
227445
  children: /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Text3, {
227029
227446
  color: "gray",
227030
- children: "j/k or arrows move enter view x/c cancel r refresh 1/2/3 filter q close"
227447
+ children: "j/k or arrows move enter view d/x/c kill selected K kill all active r refresh 1/2/3 filter q close"
227031
227448
  }, undefined, false, undefined, this)
227032
227449
  }, undefined, false, undefined, this)
227033
227450
  ]
@@ -227116,7 +227533,7 @@ var DOCS_SECTIONS = [
227116
227533
  content: [
227117
227534
  "Use /tasks for queued local tasks with priority and pause/resume controls.",
227118
227535
  "Use /schedules for recurring command execution with next-run visibility.",
227119
- "Use /jobs for background connector/tool jobs. Cancel jobs directly in the panel.",
227536
+ "Use /jobs for background connector/tool jobs. Kill jobs directly in the panel.",
227120
227537
  "Use /orders for interactive order/store workflows (tabs, table navigation, detail views).",
227121
227538
  "Use /logs for security events and /heartbeat for recurring assistant runs."
227122
227539
  ]
@@ -236360,7 +236777,7 @@ function SecretsPanel({
236360
236777
  setIsProcessing(false);
236361
236778
  }
236362
236779
  };
236363
- const normalizeScope2 = (rawScope) => {
236780
+ const normalizeScope3 = (rawScope) => {
236364
236781
  const scope = rawScope.trim().toLowerCase();
236365
236782
  if (!scope)
236366
236783
  return "assistant";
@@ -236369,7 +236786,7 @@ function SecretsPanel({
236369
236786
  return null;
236370
236787
  };
236371
236788
  const normalizeAddInput = (form) => {
236372
- const scope = normalizeScope2(String(form.scope));
236789
+ const scope = normalizeScope3(String(form.scope));
236373
236790
  if (!scope) {
236374
236791
  return null;
236375
236792
  }
@@ -236390,7 +236807,7 @@ function SecretsPanel({
236390
236807
  return;
236391
236808
  }
236392
236809
  if (currentAddField.key === "scope") {
236393
- const normalizedScope = normalizeScope2(currentValue);
236810
+ const normalizedScope = normalizeScope3(currentValue);
236394
236811
  if (!normalizedScope) {
236395
236812
  setStatusMessage('Scope must be "assistant" or "global".');
236396
236813
  return;
@@ -238870,6 +239287,18 @@ function ResumePanel({
238870
239287
  }, undefined, true, undefined, this);
238871
239288
  }
238872
239289
 
239290
+ // packages/terminal/src/components/queueUtils.ts
239291
+ function takeNextQueuedMessage(queue, sessionId) {
239292
+ const next = queue.find((msg) => msg.sessionId === sessionId);
239293
+ if (!next) {
239294
+ return { next: null, remaining: queue };
239295
+ }
239296
+ return {
239297
+ next,
239298
+ remaining: queue.filter((msg) => msg.id !== next.id)
239299
+ };
239300
+ }
239301
+
238873
239302
  // packages/terminal/src/output/sanitize.ts
238874
239303
  var CLEAR_SCREEN_SEQUENCE = "\x1B[2J\x1B[3J\x1B[H";
238875
239304
  var CLEAR_SCREEN_TOKEN = "__ASSISTANTS_CLEAR_SCREEN__";
@@ -239256,6 +239685,7 @@ function App2({ cwd: cwd2, version: version3 }) {
239256
239685
  const [heartbeatState, setHeartbeatState] = import_react72.useState();
239257
239686
  const [identityInfo, setIdentityInfo] = import_react72.useState();
239258
239687
  const [verboseTools, setVerboseTools] = import_react72.useState(false);
239688
+ const [gitBranch, setGitBranch] = import_react72.useState();
239259
239689
  const [askUserState, setAskUserState] = import_react72.useState(null);
239260
239690
  const [processingStartTime, setProcessingStartTime] = import_react72.useState();
239261
239691
  const [currentTurnTokens, setCurrentTurnTokens] = import_react72.useState(0);
@@ -239269,6 +239699,42 @@ function App2({ cwd: cwd2, version: version3 }) {
239269
239699
  const pttRecorderRef = import_react72.useRef(null);
239270
239700
  const [skills, setSkills] = import_react72.useState([]);
239271
239701
  const [commands9, setCommands] = import_react72.useState([]);
239702
+ const fileListCacheRef = import_react72.useRef({ files: [], timestamp: 0 });
239703
+ const searchFiles = import_react72.useCallback((query) => {
239704
+ const cache7 = fileListCacheRef.current;
239705
+ const now2 = Date.now();
239706
+ if (now2 - cache7.timestamp > 30000 || cache7.files.length === 0) {
239707
+ try {
239708
+ const { execSync } = __require("child_process");
239709
+ let output;
239710
+ try {
239711
+ output = execSync("git ls-files --cached --others --exclude-standard 2>/dev/null", {
239712
+ cwd: cwd2,
239713
+ encoding: "utf-8",
239714
+ maxBuffer: 1024 * 1024,
239715
+ timeout: 3000
239716
+ });
239717
+ } catch {
239718
+ output = execSync('find . -type f -not -path "*/node_modules/*" -not -path "*/.git/*" -maxdepth 5 2>/dev/null | head -1000', {
239719
+ cwd: cwd2,
239720
+ encoding: "utf-8",
239721
+ maxBuffer: 1024 * 1024,
239722
+ timeout: 3000
239723
+ });
239724
+ }
239725
+ cache7.files = output.trim().split(`
239726
+ `).filter(Boolean).slice(0, 2000);
239727
+ cache7.timestamp = now2;
239728
+ } catch {
239729
+ return [];
239730
+ }
239731
+ }
239732
+ if (!query)
239733
+ return cache7.files.slice(0, 20);
239734
+ const lower = query.toLowerCase();
239735
+ const matches = cache7.files.filter((f6) => f6.toLowerCase().includes(lower));
239736
+ return matches.slice(0, 20);
239737
+ }, [cwd2]);
239272
239738
  const lastCtrlCRef = import_react72.useRef(0);
239273
239739
  const [showExitHint, setShowExitHint] = import_react72.useState(false);
239274
239740
  const responseRef = import_react72.useRef("");
@@ -239277,6 +239743,8 @@ function App2({ cwd: cwd2, version: version3 }) {
239277
239743
  const activityLogRef = import_react72.useRef([]);
239278
239744
  const skipNextDoneRef = import_react72.useRef(false);
239279
239745
  const isProcessingRef = import_react72.useRef(isProcessing);
239746
+ const currentToolCallRef = import_react72.useRef(currentToolCall);
239747
+ const hasPendingToolsRef = import_react72.useRef(false);
239280
239748
  const inputRef = import_react72.useRef(null);
239281
239749
  const isListeningRef = import_react72.useRef(isListening);
239282
239750
  const listenLoopRef = import_react72.useRef({
@@ -239357,6 +239825,9 @@ function App2({ cwd: cwd2, version: version3 }) {
239357
239825
  import_react72.useEffect(() => {
239358
239826
  isProcessingRef.current = isProcessing;
239359
239827
  }, [isProcessing]);
239828
+ import_react72.useEffect(() => {
239829
+ currentToolCallRef.current = currentToolCall;
239830
+ }, [currentToolCall]);
239360
239831
  import_react72.useEffect(() => {
239361
239832
  isListeningRef.current = isListening;
239362
239833
  }, [isListening]);
@@ -239370,6 +239841,14 @@ function App2({ cwd: cwd2, version: version3 }) {
239370
239841
  processingStartTimeRef.current = now2;
239371
239842
  }
239372
239843
  }, [isProcessing, processingStartTime]);
239844
+ import_react72.useEffect(() => {
239845
+ const { exec: exec3 } = __require("child_process");
239846
+ exec3("git branch --show-current 2>/dev/null", { cwd: cwd2 }, (err, stdout2) => {
239847
+ if (!err && stdout2?.trim()) {
239848
+ setGitBranch(stdout2.trim());
239849
+ }
239850
+ });
239851
+ }, [cwd2]);
239373
239852
  const buildFullResponse = import_react72.useCallback(() => {
239374
239853
  const parts = activityLogRef.current.filter((entry) => entry.type === "text" && entry.content).map((entry) => entry.content);
239375
239854
  if (responseRef.current.trim()) {
@@ -240048,7 +240527,7 @@ function App2({ cwd: cwd2, version: version3 }) {
240048
240527
  const pendingIndex = pendingSendsRef.current.findIndex((entry) => entry.sessionId === active.id);
240049
240528
  if (pendingIndex !== -1) {
240050
240529
  const [started] = pendingSendsRef.current.splice(pendingIndex, 1);
240051
- if (started?.mode === "inline") {
240530
+ if (started) {
240052
240531
  setInlinePending((prev) => prev.filter((msg) => msg.id !== started.id));
240053
240532
  }
240054
240533
  }
@@ -240702,17 +241181,10 @@ function App2({ cwd: cwd2, version: version3 }) {
240702
241181
  const activeSession2 = registryRef.current.getActiveSession();
240703
241182
  if (!activeSession2 || !activeSessionId)
240704
241183
  return;
240705
- let nextMessage;
240706
- setMessageQueue((prev) => {
240707
- const idx = prev.findIndex((msg) => msg.sessionId === activeSessionId);
240708
- if (idx === -1) {
240709
- return prev;
240710
- }
240711
- nextMessage = prev[idx];
240712
- return [...prev.slice(0, idx), ...prev.slice(idx + 1)];
240713
- });
241184
+ const { next: nextMessage, remaining } = takeNextQueuedMessage(messageQueue, activeSessionId);
240714
241185
  if (!nextMessage)
240715
241186
  return;
241187
+ setMessageQueue(remaining);
240716
241188
  const userMessage = {
240717
241189
  id: nextMessage.id,
240718
241190
  role: "user",
@@ -240741,7 +241213,6 @@ function App2({ cwd: cwd2, version: version3 }) {
240741
241213
  setIsProcessing(true);
240742
241214
  isProcessingRef.current = true;
240743
241215
  registryRef.current.setProcessing(activeSession2.id, true);
240744
- pendingSendsRef.current.push({ id: nextMessage.id, sessionId: activeSessionId, mode: "queued" });
240745
241216
  try {
240746
241217
  await activeSession2.client.send(nextMessage.content);
240747
241218
  } catch (err) {
@@ -240750,8 +241221,9 @@ function App2({ cwd: cwd2, version: version3 }) {
240750
241221
  setIsProcessing(false);
240751
241222
  isProcessingRef.current = false;
240752
241223
  registryRef.current.setProcessing(activeSession2.id, false);
241224
+ setQueueFlushTrigger((prev) => prev + 1);
240753
241225
  }
240754
- }, [activeSessionId, clearPendingSend]);
241226
+ }, [activeSessionId, clearPendingSend, messageQueue]);
240755
241227
  const activeQueue = activeSessionId ? messageQueue.filter((msg) => msg.sessionId === activeSessionId) : [];
240756
241228
  const activeInline = activeSessionId ? inlinePending.filter((msg) => msg.sessionId === activeSessionId) : [];
240757
241229
  const queuedMessageIds = import_react72.useMemo(() => new Set(activeQueue.filter((msg) => msg.mode === "queued").map((msg) => msg.id)), [activeQueue]);
@@ -240811,6 +241283,9 @@ function App2({ cwd: cwd2, version: version3 }) {
240811
241283
  }
240812
241284
  return false;
240813
241285
  }, [activityLog]);
241286
+ import_react72.useEffect(() => {
241287
+ hasPendingToolsRef.current = hasPendingTools;
241288
+ }, [hasPendingTools]);
240814
241289
  const isBusy = isProcessing || hasPendingTools;
240815
241290
  const listenHints = import_react72.useMemo(() => {
240816
241291
  if (!isListening)
@@ -240890,6 +241365,26 @@ function App2({ cwd: cwd2, version: version3 }) {
240890
241365
  setError(err instanceof Error ? err.message : "Failed to create session");
240891
241366
  }
240892
241367
  }, [cwd2, createAndActivateSession]);
241368
+ const stopActiveProcessing = import_react72.useCallback((status = "stopped") => {
241369
+ const active = registryRef.current.getActiveSession();
241370
+ if (!active)
241371
+ return false;
241372
+ const sessionProcessing = active.isProcessing;
241373
+ const shouldStopNow = isProcessingRef.current || hasPendingToolsRef.current || sessionProcessing || Boolean(currentToolCallRef.current);
241374
+ if (!shouldStopNow)
241375
+ return false;
241376
+ active.client.stop();
241377
+ const finalized = finalizeResponse2(status);
241378
+ if (finalized) {
241379
+ skipNextDoneRef.current = true;
241380
+ }
241381
+ resetTurnState();
241382
+ registryRef.current.setProcessing(active.id, false);
241383
+ setIsProcessing(false);
241384
+ isProcessingRef.current = false;
241385
+ setQueueFlushTrigger((prev) => prev + 1);
241386
+ return true;
241387
+ }, [finalizeResponse2, resetTurnState]);
240893
241388
  useSafeInput((input, key) => {
240894
241389
  if (isListeningRef.current && key.ctrl && input === "l") {
240895
241390
  stopListening();
@@ -240910,18 +241405,7 @@ function App2({ cwd: cwd2, version: version3 }) {
240910
241405
  if (hasAsk) {
240911
241406
  cancelAskUser("Cancelled by user", activeSessionId);
240912
241407
  }
240913
- const sessionProcessing = activeSession?.isProcessing ?? false;
240914
- if ((isProcessing || hasPendingTools || sessionProcessing || currentToolCall) && activeSession) {
240915
- activeSession.client.stop();
240916
- const finalized = finalizeResponse2("stopped");
240917
- if (finalized) {
240918
- skipNextDoneRef.current = true;
240919
- }
240920
- resetTurnState();
240921
- registryRef.current.setProcessing(activeSession.id, false);
240922
- setIsProcessing(false);
240923
- isProcessingRef.current = false;
240924
- setQueueFlushTrigger((prev) => prev + 1);
241408
+ if (stopActiveProcessing("stopped")) {
240925
241409
  lastCtrlCRef.current = 0;
240926
241410
  setShowExitHint(false);
240927
241411
  return;
@@ -240950,18 +241434,8 @@ function App2({ cwd: cwd2, version: version3 }) {
240950
241434
  if (activeSessionId && askUserStateRef.current.has(activeSessionId)) {
240951
241435
  cancelAskUser("Cancelled by user", activeSessionId);
240952
241436
  }
240953
- const sessionProcessing = activeSession?.isProcessing ?? false;
240954
- if ((isProcessing || hasPendingTools || sessionProcessing || currentToolCall) && activeSession) {
240955
- activeSession.client.stop();
240956
- const finalized = finalizeResponse2("stopped");
240957
- if (finalized) {
240958
- skipNextDoneRef.current = true;
240959
- }
240960
- resetTurnState();
240961
- registryRef.current.setProcessing(activeSession.id, false);
240962
- setIsProcessing(false);
240963
- isProcessingRef.current = false;
240964
- setQueueFlushTrigger((prev) => prev + 1);
241437
+ if (stopActiveProcessing("stopped")) {
241438
+ return;
240965
241439
  }
240966
241440
  }
240967
241441
  if (key.ctrl && input === "a") {
@@ -240972,7 +241446,7 @@ function App2({ cwd: cwd2, version: version3 }) {
240972
241446
  openBudgetsPanel();
240973
241447
  return;
240974
241448
  }
240975
- if (key.ctrl && input === "m") {
241449
+ if (key.ctrl && input === "m" && !key.return) {
240976
241450
  const messagesManager = registry2.getActiveSession()?.client.getMessagesManager?.();
240977
241451
  if (messagesManager) {
240978
241452
  messagesManager.list({ limit: 50 }).then((msgs) => {
@@ -241455,7 +241929,7 @@ function App2({ cwd: cwd2, version: version3 }) {
241455
241929
  timestamp: now()
241456
241930
  }
241457
241931
  ]);
241458
- pendingSendsRef.current.push({ id: inlineId, sessionId: activeSessionId, mode: "inline" });
241932
+ pendingSendsRef.current.push({ id: inlineId, sessionId: activeSessionId });
241459
241933
  try {
241460
241934
  await activeSession.client.send(trimmedInput);
241461
241935
  } catch (err) {
@@ -241464,17 +241938,8 @@ function App2({ cwd: cwd2, version: version3 }) {
241464
241938
  }
241465
241939
  return;
241466
241940
  }
241467
- if (mode === "interrupt" && isProcessing) {
241468
- activeSession.client.stop();
241469
- const finalized = finalizeResponse2("interrupted");
241470
- if (finalized) {
241471
- skipNextDoneRef.current = true;
241472
- }
241473
- resetTurnState();
241474
- setIsProcessing(false);
241475
- isProcessingRef.current = false;
241476
- registry2.setProcessing(activeSession.id, false);
241477
- setQueueFlushTrigger((prev) => prev + 1);
241941
+ if (mode === "interrupt" && isBusy) {
241942
+ stopActiveProcessing("interrupted");
241478
241943
  await new Promise((r6) => setTimeout(r6, 100));
241479
241944
  }
241480
241945
  const userMessage = {
@@ -241516,6 +241981,7 @@ function App2({ cwd: cwd2, version: version3 }) {
241516
241981
  }, [
241517
241982
  activeSession,
241518
241983
  isProcessing,
241984
+ isBusy,
241519
241985
  registry2,
241520
241986
  sessions,
241521
241987
  handleNewSession,
@@ -241527,7 +241993,8 @@ function App2({ cwd: cwd2, version: version3 }) {
241527
241993
  submitAskAnswer,
241528
241994
  clearPendingSend,
241529
241995
  startListening,
241530
- stopListening
241996
+ stopListening,
241997
+ stopActiveProcessing
241531
241998
  ]);
241532
241999
  import_react72.useEffect(() => {
241533
242000
  sendListenMessageRef.current = (text) => {
@@ -243292,6 +243759,9 @@ ${msg.body || msg.preview}`);
243292
243759
  /* @__PURE__ */ jsx_dev_runtime48.jsxDEV(Input, {
243293
243760
  ref: inputRef,
243294
243761
  onSubmit: handleSubmit,
243762
+ onStopProcessing: () => {
243763
+ stopActiveProcessing("stopped");
243764
+ },
243295
243765
  isProcessing: isBusy,
243296
243766
  queueLength: activeQueue.length + inlineCount,
243297
243767
  commands: commands9,
@@ -243303,7 +243773,8 @@ ${msg.body || msg.preview}`);
243303
243773
  assistantName: identityInfo?.assistant?.name || undefined,
243304
243774
  isRecording: pttRecording,
243305
243775
  recordingStatus: pttStatus,
243306
- onStopRecording: togglePushToTalk
243776
+ onStopRecording: togglePushToTalk,
243777
+ onFileSearch: searchFiles
243307
243778
  }, undefined, false, undefined, this),
243308
243779
  /* @__PURE__ */ jsx_dev_runtime48.jsxDEV(Status, {
243309
243780
  isProcessing: isBusy,
@@ -243318,7 +243789,8 @@ ${msg.body || msg.preview}`);
243318
243789
  sessionCount,
243319
243790
  backgroundProcessingCount,
243320
243791
  processingStartTime,
243321
- verboseTools
243792
+ verboseTools,
243793
+ gitBranch
243322
243794
  }, undefined, false, undefined, this)
243323
243795
  ]
243324
243796
  }, undefined, true, undefined, this);
@@ -243766,7 +244238,7 @@ Interactive Mode:
243766
244238
  // packages/terminal/src/index.tsx
243767
244239
  var jsx_dev_runtime49 = __toESM(require_jsx_dev_runtime(), 1);
243768
244240
  setRuntime(bunRuntime);
243769
- var VERSION4 = "1.1.25";
244241
+ var VERSION4 = "1.1.26";
243770
244242
  var SYNC_START = "\x1B[?2026h";
243771
244243
  var SYNC_END = "\x1B[?2026l";
243772
244244
  function enableSynchronizedOutput() {
@@ -243906,4 +244378,4 @@ export {
243906
244378
  main
243907
244379
  };
243908
244380
 
243909
- //# debugId=57A038F79EFFC02464756E2164756E21
244381
+ //# debugId=6204ABF8948FEE3864756E2164756E21