@hasna/assistants 0.6.21 → 0.6.23

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
@@ -27290,16 +27290,16 @@ var exports_anthropic = {};
27290
27290
  __export(exports_anthropic, {
27291
27291
  AnthropicClient: () => AnthropicClient
27292
27292
  });
27293
- import { readFileSync as readFileSync3, existsSync as existsSync8 } from "fs";
27293
+ import { readFileSync as readFileSync4, existsSync as existsSync8 } from "fs";
27294
27294
  import { homedir as homedir8 } from "os";
27295
- import { join as join12 } from "path";
27295
+ import { join as join13 } from "path";
27296
27296
  function loadApiKeyFromSecrets() {
27297
27297
  const envHome = process.env.HOME || process.env.USERPROFILE;
27298
27298
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir8();
27299
- const secretsPath = join12(homeDir, ".secrets");
27299
+ const secretsPath = join13(homeDir, ".secrets");
27300
27300
  if (existsSync8(secretsPath)) {
27301
27301
  try {
27302
- const content = readFileSync3(secretsPath, "utf-8");
27302
+ const content = readFileSync4(secretsPath, "utf-8");
27303
27303
  const match = content.match(/export\s+ANTHROPIC_API_KEY\s*=\s*["']?([^"'\n]+)["']?/);
27304
27304
  if (match) {
27305
27305
  return match[1];
@@ -35505,9 +35505,12 @@ var import_react19 = __toESM(require_react(), 1);
35505
35505
  var import_react20 = __toESM(require_react(), 1);
35506
35506
  // node_modules/.bun/ink@5.2.1+f4eacebf2041cd4f/node_modules/ink/build/hooks/use-focus-manager.js
35507
35507
  var import_react21 = __toESM(require_react(), 1);
35508
+ // packages/terminal/src/components/App.tsx
35509
+ var import_react29 = __toESM(require_react(), 1);
35510
+
35508
35511
  // packages/core/src/agent/loop.ts
35509
35512
  init_src();
35510
- import { join as join19 } from "path";
35513
+ import { join as join20 } from "path";
35511
35514
 
35512
35515
  // packages/core/src/agent/context.ts
35513
35516
  init_src();
@@ -35515,6 +35518,7 @@ init_src();
35515
35518
  class AgentContext {
35516
35519
  messages = [];
35517
35520
  maxMessages;
35521
+ scopeContext = null;
35518
35522
  constructor(maxMessages = 100) {
35519
35523
  this.maxMessages = maxMessages;
35520
35524
  }
@@ -35610,6 +35614,15 @@ class AgentContext {
35610
35614
  import(messages) {
35611
35615
  this.messages = messages;
35612
35616
  }
35617
+ setScopeContext(scope) {
35618
+ this.scopeContext = scope;
35619
+ }
35620
+ getScopeContext() {
35621
+ return this.scopeContext;
35622
+ }
35623
+ clearScopeContext() {
35624
+ this.scopeContext = null;
35625
+ }
35613
35626
  }
35614
35627
 
35615
35628
  // packages/core/src/context/manager.ts
@@ -36140,7 +36153,7 @@ import { dirname, join as join2 } from "path";
36140
36153
  // packages/core/src/config.ts
36141
36154
  import { join } from "path";
36142
36155
  import { homedir } from "os";
36143
- var DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant running in the terminal.
36156
+ var DEFAULT_SYSTEM_PROMPT = `You are Hasna Assistant, a helpful AI assistant running in the terminal.
36144
36157
 
36145
36158
  ## Runtime Environment
36146
36159
  - Use **Bun** as the default runtime for JavaScript/TypeScript scripts
@@ -36292,10 +36305,6 @@ function getConfigDir() {
36292
36305
  if (assistantsOverride && assistantsOverride.trim()) {
36293
36306
  return assistantsOverride;
36294
36307
  }
36295
- const legacyOverride = process.env.OLDPAL_DIR;
36296
- if (legacyOverride && legacyOverride.trim()) {
36297
- return legacyOverride;
36298
- }
36299
36308
  const envHome = process.env.HOME || process.env.USERPROFILE;
36300
36309
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir();
36301
36310
  return join(homeDir, ".assistants");
@@ -36313,12 +36322,10 @@ async function loadConfig(cwd2 = process.cwd()) {
36313
36322
  const userConfig = await loadJsonFile(userConfigPath) || await loadJsonFile(legacyUserConfigPath);
36314
36323
  config = mergeConfig(config, userConfig || undefined);
36315
36324
  const projectConfigPath = join(getProjectConfigDir(cwd2), "config.json");
36316
- const legacyProjectConfigPath = join(cwd2, ".oldpal", "settings.json");
36317
- const projectConfig = await loadJsonFile(projectConfigPath) || await loadJsonFile(legacyProjectConfigPath);
36325
+ const projectConfig = await loadJsonFile(projectConfigPath);
36318
36326
  config = mergeConfig(config, projectConfig || undefined);
36319
36327
  const localConfigPath = join(getProjectConfigDir(cwd2), "config.local.json");
36320
- const legacyLocalConfigPath = join(cwd2, ".oldpal", "settings.local.json");
36321
- const localConfig = await loadJsonFile(localConfigPath) || await loadJsonFile(legacyLocalConfigPath);
36328
+ const localConfig = await loadJsonFile(localConfigPath);
36322
36329
  config = mergeConfig(config, localConfig || undefined);
36323
36330
  return config;
36324
36331
  }
@@ -36377,13 +36384,11 @@ async function ensureConfigDir(sessionId) {
36377
36384
  async function loadSystemPrompt(cwd2 = process.cwd()) {
36378
36385
  const prompts = [];
36379
36386
  const globalPromptPath = getConfigPath("ASSISTANTS.md");
36380
- const legacyGlobalPromptPath = getConfigPath("OLDPAL.md");
36381
- const globalPrompt = await loadTextFile(globalPromptPath) ?? await loadTextFile(legacyGlobalPromptPath);
36387
+ const globalPrompt = await loadTextFile(globalPromptPath);
36382
36388
  if (globalPrompt)
36383
36389
  prompts.push(globalPrompt);
36384
36390
  const projectPromptPath = join(getProjectConfigDir(cwd2), "ASSISTANTS.md");
36385
- const legacyProjectPromptPath = join(cwd2, ".oldpal", "OLDPAL.md");
36386
- const projectPrompt = await loadTextFile(projectPromptPath) ?? await loadTextFile(legacyProjectPromptPath);
36391
+ const projectPrompt = await loadTextFile(projectPromptPath);
36387
36392
  if (projectPrompt)
36388
36393
  prompts.push(projectPrompt);
36389
36394
  if (prompts.length === 0) {
@@ -37379,7 +37384,6 @@ ${stderr || stdout}`.trim(), {
37379
37384
 
37380
37385
  // packages/core/src/tools/filesystem.ts
37381
37386
  import { join as join4, resolve as resolve3, dirname as dirname2, sep } from "path";
37382
- import { existsSync as existsSync3 } from "fs";
37383
37387
  init_errors();
37384
37388
  var {Glob } = globalThis.Bun;
37385
37389
 
@@ -37469,10 +37473,6 @@ async function isPathSafe(targetPath, operation, options = {}) {
37469
37473
  var currentSessionId = "default";
37470
37474
  function getScriptsFolder(cwd2, sessionId) {
37471
37475
  const resolvedSessionId = sessionId || currentSessionId;
37472
- const legacyDir = join4(cwd2, ".oldpal");
37473
- if (existsSync3(legacyDir)) {
37474
- return join4(legacyDir, "scripts", resolvedSessionId);
37475
- }
37476
37476
  return join4(getProjectConfigDir(cwd2), "scripts", resolvedSessionId);
37477
37477
  }
37478
37478
  function isInScriptsFolder(path2, cwd2, sessionId) {
@@ -37608,7 +37608,7 @@ class FilesystemTools {
37608
37608
  };
37609
37609
  static writeTool = {
37610
37610
  name: "write",
37611
- description: "Write content to a file. RESTRICTED: Can only write to the project scripts folder (.oldpal/scripts/{session} or .assistants/scripts/{session}). Provide a filename and it will be saved under the scripts folder.",
37611
+ description: "Write content to a file. RESTRICTED: Can only write to the project scripts folder (.assistants/scripts/{session}). Provide a filename and it will be saved under the scripts folder.",
37612
37612
  parameters: {
37613
37613
  type: "object",
37614
37614
  properties: {
@@ -37894,16 +37894,22 @@ class WebFetchTool {
37894
37894
  });
37895
37895
  }
37896
37896
  const controller = new AbortController;
37897
- const timeoutId = setTimeout(abortController, timeout, controller);
37898
- response = await fetch(currentUrl, {
37899
- signal: controller.signal,
37900
- redirect: "manual",
37901
- headers: {
37902
- "User-Agent": "assistants/1.0 (AI Assistant)",
37903
- Accept: extractType === "json" ? "application/json" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
37897
+ let timeoutId = null;
37898
+ try {
37899
+ timeoutId = setTimeout(abortController, timeout, controller);
37900
+ response = await fetch(currentUrl, {
37901
+ signal: controller.signal,
37902
+ redirect: "manual",
37903
+ headers: {
37904
+ "User-Agent": "assistants/1.0 (AI Assistant)",
37905
+ Accept: extractType === "json" ? "application/json" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
37906
+ }
37907
+ });
37908
+ } finally {
37909
+ if (timeoutId) {
37910
+ clearTimeout(timeoutId);
37904
37911
  }
37905
- });
37906
- clearTimeout(timeoutId);
37912
+ }
37907
37913
  if ([301, 302, 303, 307, 308].includes(response.status)) {
37908
37914
  const location = response.headers.get("location");
37909
37915
  if (!location) {
@@ -38179,18 +38185,24 @@ class CurlTool {
38179
38185
  });
38180
38186
  }
38181
38187
  const controller = new AbortController;
38182
- const timeoutId = setTimeout(abortController, timeout, controller);
38183
- response = await fetch(currentUrl, {
38184
- method,
38185
- signal: controller.signal,
38186
- redirect: "manual",
38187
- headers: {
38188
- "User-Agent": "assistants/1.0 (AI Assistant)",
38189
- ...headers
38190
- },
38191
- body: body && ["POST", "PUT"].includes(method) ? body : undefined
38192
- });
38193
- clearTimeout(timeoutId);
38188
+ let timeoutId = null;
38189
+ try {
38190
+ timeoutId = setTimeout(abortController, timeout, controller);
38191
+ response = await fetch(currentUrl, {
38192
+ method,
38193
+ signal: controller.signal,
38194
+ redirect: "manual",
38195
+ headers: {
38196
+ "User-Agent": "assistants/1.0 (AI Assistant)",
38197
+ ...headers
38198
+ },
38199
+ body: body && ["POST", "PUT"].includes(method) ? body : undefined
38200
+ });
38201
+ } finally {
38202
+ if (timeoutId) {
38203
+ clearTimeout(timeoutId);
38204
+ }
38205
+ }
38194
38206
  if ([301, 302, 303, 307, 308].includes(response.status)) {
38195
38207
  if (!["GET", "HEAD"].includes(method)) {
38196
38208
  throw new ToolExecutionError("Redirects are only supported for GET/HEAD requests", {
@@ -38395,7 +38407,7 @@ function isPrivateIPv4(octets) {
38395
38407
  // packages/core/src/tools/feedback.ts
38396
38408
  init_src();
38397
38409
  import { join as join5 } from "path";
38398
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
38410
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
38399
38411
  function normalizeTags(value) {
38400
38412
  if (Array.isArray(value)) {
38401
38413
  const tags = value.map((t) => String(t).trim()).filter(Boolean);
@@ -38409,9 +38421,9 @@ function normalizeTags(value) {
38409
38421
  }
38410
38422
  function resolveFeedbackDir(cwd2) {
38411
38423
  const baseCwd = cwd2 && cwd2.trim().length > 0 ? cwd2 : process.cwd();
38412
- const legacyDir = join5(baseCwd, ".oldpal");
38413
- if (existsSync4(legacyDir)) {
38414
- return join5(legacyDir, "feedback");
38424
+ const projectConfigDir = join5(baseCwd, ".assistants");
38425
+ if (existsSync3(projectConfigDir)) {
38426
+ return join5(projectConfigDir, "feedback");
38415
38427
  }
38416
38428
  return join5(getConfigDir(), "feedback");
38417
38429
  }
@@ -38734,7 +38746,7 @@ async function updateSchedule(cwd2, id, updater) {
38734
38746
  return null;
38735
38747
  }
38736
38748
  }
38737
- async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_MS) {
38749
+ async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_MS, allowRetry = true) {
38738
38750
  await ensureDirs(cwd2);
38739
38751
  const path2 = lockPath(cwd2, id);
38740
38752
  const now2 = Date.now();
@@ -38751,9 +38763,18 @@ async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_M
38751
38763
  const ttl = lock?.ttlMs ?? ttlMs;
38752
38764
  if (now2 - updatedAt > ttl) {
38753
38765
  await unlink(path2);
38754
- return acquireScheduleLock(cwd2, id, ownerId, ttlMs);
38766
+ return acquireScheduleLock(cwd2, id, ownerId, ttlMs, false);
38755
38767
  }
38756
- } catch {}
38768
+ } catch {
38769
+ if (allowRetry) {
38770
+ try {
38771
+ await unlink(path2);
38772
+ return acquireScheduleLock(cwd2, id, ownerId, ttlMs, false);
38773
+ } catch {
38774
+ return false;
38775
+ }
38776
+ }
38777
+ }
38757
38778
  }
38758
38779
  return false;
38759
38780
  }
@@ -38980,7 +39001,7 @@ class SchedulerTool {
38980
39001
 
38981
39002
  // packages/core/src/tools/image.ts
38982
39003
  init_src();
38983
- import { existsSync as existsSync5, writeFileSync as writeFileSync3, unlinkSync } from "fs";
39004
+ import { existsSync as existsSync4, writeFileSync as writeFileSync3, unlinkSync } from "fs";
38984
39005
  import { tmpdir } from "os";
38985
39006
  import { join as join7 } from "path";
38986
39007
  import { homedir as homedir4 } from "os";
@@ -39067,7 +39088,7 @@ class ImageDisplayTool {
39067
39088
  return `Error: Failed to fetch image: ${error instanceof Error ? error.message : String(error)}`;
39068
39089
  }
39069
39090
  }
39070
- if (!existsSync5(localPath)) {
39091
+ if (!existsSync4(localPath)) {
39071
39092
  return `Error: Image file not found: ${localPath}`;
39072
39093
  }
39073
39094
  try {
@@ -39092,7 +39113,7 @@ class ImageDisplayTool {
39092
39113
  } catch (error) {
39093
39114
  return `Error: ${error instanceof Error ? error.message : String(error)}`;
39094
39115
  } finally {
39095
- if (tempFile && existsSync5(tempFile)) {
39116
+ if (tempFile && existsSync4(tempFile)) {
39096
39117
  try {
39097
39118
  unlinkSync(tempFile);
39098
39119
  } catch {}
@@ -39156,10 +39177,6 @@ class SkillLoader {
39156
39177
  await this.loadFromDirectory(userSkillsDir);
39157
39178
  const projectSkillsDir = join8(projectDir, ".assistants", "skills");
39158
39179
  await this.loadFromDirectory(projectSkillsDir);
39159
- const legacyUserSkillsDir = join8(userHome, ".oldpal", "skills");
39160
- const legacyProjectSkillsDir = join8(projectDir, ".oldpal", "skills");
39161
- await this.loadFromDirectory(legacyUserSkillsDir);
39162
- await this.loadFromDirectory(legacyProjectSkillsDir);
39163
39180
  const nestedGlob = new Glob2("**/.assistants/skills/*/SKILL.md");
39164
39181
  for await (const file of nestedGlob.scan({ cwd: projectDir, dot: true })) {
39165
39182
  await this.loadSkillFile(join8(projectDir, file));
@@ -39369,7 +39386,6 @@ class HookLoader {
39369
39386
  this.hooks = {};
39370
39387
  }
39371
39388
  }
39372
-
39373
39389
  // packages/core/src/hooks/executor.ts
39374
39390
  init_src();
39375
39391
  function killSpawnedProcess(proc) {
@@ -39595,211 +39611,720 @@ Respond with JSON only: {"allow": boolean, "reason": string}`;
39595
39611
  return result;
39596
39612
  }
39597
39613
  }
39598
- // packages/core/src/commands/loader.ts
39599
- import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
39600
- import { join as join9, basename as basename2, extname } from "path";
39601
- import { homedir as homedir6 } from "os";
39602
-
39603
- class CommandLoader {
39604
- commands = new Map;
39605
- cwd;
39606
- constructor(cwd2) {
39607
- this.cwd = cwd2 || process.cwd();
39614
+ // packages/core/src/hooks/native.ts
39615
+ class NativeHookRegistry {
39616
+ hooks = new Map;
39617
+ config = {};
39618
+ register(hook) {
39619
+ const eventHooks = this.hooks.get(hook.event) || [];
39620
+ eventHooks.push(hook);
39621
+ eventHooks.sort((a, b) => a.priority - b.priority);
39622
+ this.hooks.set(hook.event, eventHooks);
39608
39623
  }
39609
- async loadAll() {
39610
- this.commands.clear();
39611
- const envHome = process.env.HOME || process.env.USERPROFILE;
39612
- const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir6();
39613
- const globalDir = join9(homeDir, ".assistants", "commands");
39614
- await this.loadFromDirectory(globalDir, "global");
39615
- const projectDir = join9(this.cwd, ".assistants", "commands");
39616
- await this.loadFromDirectory(projectDir, "project");
39617
- const legacyGlobalDir = join9(homeDir, ".oldpal", "commands");
39618
- const legacyProjectDir = join9(this.cwd, ".oldpal", "commands");
39619
- await this.loadFromDirectory(legacyGlobalDir, "global");
39620
- await this.loadFromDirectory(legacyProjectDir, "project");
39624
+ getHooks(event) {
39625
+ return this.hooks.get(event) || [];
39621
39626
  }
39622
- async loadFromDirectory(dir, source, prefix = "") {
39623
- if (!existsSync6(dir))
39624
- return;
39625
- const entries = readdirSync2(dir);
39626
- for (const entry of entries) {
39627
- const fullPath = join9(dir, entry);
39628
- const stat = statSync2(fullPath);
39629
- if (stat.isDirectory()) {
39630
- const newPrefix = prefix ? `${prefix}:${entry}` : entry;
39631
- await this.loadFromDirectory(fullPath, source, newPrefix);
39632
- } else if (stat.isFile() && extname(entry) === ".md") {
39633
- const command = await this.loadCommandFile(fullPath, prefix);
39634
- if (command) {
39635
- this.commands.set(command.name, command);
39636
- }
39637
- }
39638
- }
39627
+ hasHooks(event) {
39628
+ const hooks = this.hooks.get(event);
39629
+ return hooks !== undefined && hooks.length > 0;
39639
39630
  }
39640
- async loadCommandFile(filePath, prefix) {
39641
- try {
39642
- const content = await Bun.file(filePath).text();
39643
- const { frontmatter, body } = this.parseFrontmatter(content);
39644
- const fileName = basename2(filePath, ".md");
39645
- const name = frontmatter.name || (prefix ? `${prefix}:${fileName}` : fileName);
39646
- const allowedToolsRaw = frontmatter["allowed-tools"];
39647
- const allowedTools = Array.isArray(allowedToolsRaw) ? allowedToolsRaw.map((t) => String(t).trim()).filter(Boolean) : typeof allowedToolsRaw === "string" ? allowedToolsRaw.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
39648
- return {
39649
- name,
39650
- description: frontmatter.description || `Run the ${name} command`,
39651
- tags: frontmatter.tags,
39652
- allowedTools,
39653
- content: body,
39654
- filePath,
39655
- builtin: false
39656
- };
39657
- } catch (error) {
39658
- console.error(`Failed to load command from ${filePath}:`, error);
39659
- return null;
39660
- }
39631
+ setConfig(config) {
39632
+ this.config = config;
39661
39633
  }
39662
- parseFrontmatter(content) {
39663
- const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
39664
- const match = content.match(frontmatterRegex);
39665
- if (!match) {
39666
- return { frontmatter: {}, body: content };
39634
+ getConfig() {
39635
+ return this.config;
39636
+ }
39637
+ async execute(event, input, context) {
39638
+ const hooks = this.getHooks(event);
39639
+ if (hooks.length === 0) {
39640
+ return null;
39667
39641
  }
39668
- const [, yamlContent, body] = match;
39669
- const frontmatter = {};
39670
- const lines = yamlContent.split(`
39671
- `);
39672
- let currentListKey = null;
39673
- for (const rawLine of lines) {
39674
- const line = rawLine.trimEnd();
39675
- if (!line.trim() || line.trim().startsWith("#"))
39676
- continue;
39677
- const listMatch = line.match(/^\s*-\s+(.+)$/);
39678
- if (listMatch && currentListKey) {
39679
- const list = frontmatter[currentListKey] ?? [];
39680
- list.push(this.parseYamlValue(listMatch[1]));
39681
- frontmatter[currentListKey] = list;
39642
+ const fullContext = {
39643
+ ...context,
39644
+ config: this.config
39645
+ };
39646
+ for (const hook of hooks) {
39647
+ if (hook.enabled === false) {
39682
39648
  continue;
39683
39649
  }
39684
- const keyMatch = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
39685
- if (!keyMatch)
39650
+ if (this.isHookDisabled(hook.id)) {
39686
39651
  continue;
39687
- const key = keyMatch[1];
39688
- const valueRaw = keyMatch[2] ?? "";
39689
- if (valueRaw.trim() === "") {
39690
- currentListKey = key;
39691
- if (!frontmatter[key]) {
39692
- frontmatter[key] = [];
39652
+ }
39653
+ try {
39654
+ const result = await hook.handler(input, fullContext);
39655
+ if (result && result.continue === false) {
39656
+ return result;
39657
+ }
39658
+ if (result?.permissionDecision) {
39659
+ return result;
39693
39660
  }
39661
+ } catch (error) {
39662
+ console.error(`Native hook ${hook.id} error:`, error);
39694
39663
  continue;
39695
39664
  }
39696
- currentListKey = null;
39697
- frontmatter[key] = this.parseYamlValue(valueRaw);
39698
39665
  }
39699
- return { frontmatter, body: body.trim() };
39666
+ return null;
39700
39667
  }
39701
- parseYamlValue(value) {
39702
- const trimmed = value.trim();
39703
- if (trimmed === "true")
39704
- return true;
39705
- if (trimmed === "false")
39706
- return false;
39707
- if (!Number.isNaN(Number(trimmed)) && trimmed !== "")
39708
- return Number(trimmed);
39709
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
39710
- const inner = trimmed.slice(1, -1).trim();
39711
- if (!inner)
39712
- return [];
39713
- return inner.split(",").map((item) => this.parseYamlValue(item));
39714
- }
39715
- if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
39716
- return trimmed.slice(1, -1);
39668
+ isHookDisabled(hookId) {
39669
+ if (hookId === "scope-verification") {
39670
+ return this.config.scopeVerification?.enabled === false;
39717
39671
  }
39718
- return trimmed;
39672
+ return false;
39719
39673
  }
39720
- register(command) {
39721
- this.commands.set(command.name, command);
39674
+ listAll() {
39675
+ const result = [];
39676
+ for (const [event, hooks] of this.hooks.entries()) {
39677
+ result.push({ event, hooks });
39678
+ }
39679
+ return result;
39722
39680
  }
39723
- getCommand(name) {
39724
- return this.commands.get(name);
39681
+ clear() {
39682
+ this.hooks.clear();
39725
39683
  }
39726
- getCommands() {
39727
- return Array.from(this.commands.values());
39684
+ }
39685
+ var nativeHookRegistry = new NativeHookRegistry;
39686
+ // packages/core/src/hooks/scope-context.ts
39687
+ init_src();
39688
+ var DEFAULT_EXCLUDE_PATTERNS = [
39689
+ /^(hi|hello|hey|good\s+(morning|afternoon|evening))[\s!.,]*$/i,
39690
+ /^(thanks|thank\s+you|ty|thx)[\s!.,]*$/i,
39691
+ /^(bye|goodbye|see\s+you|later)[\s!.,]*$/i,
39692
+ /^what\s+(is|are)\s+/i,
39693
+ /^how\s+(do|does|can|could)\s+/i,
39694
+ /^(can|could)\s+you\s+(tell|explain|describe)\s+/i,
39695
+ /^(who|when|where|why)\s+(is|are|was|were|did)\s+/i,
39696
+ /^\?+$/
39697
+ ];
39698
+
39699
+ class ScopeContextManager {
39700
+ scopeContext = null;
39701
+ config;
39702
+ constructor(config = {}) {
39703
+ this.config = {
39704
+ enabled: config.enabled ?? true,
39705
+ maxRetries: config.maxRetries ?? 2,
39706
+ excludePatterns: config.excludePatterns ?? []
39707
+ };
39728
39708
  }
39729
- hasCommand(name) {
39730
- return this.commands.has(name);
39709
+ setConfig(config) {
39710
+ this.config = {
39711
+ enabled: config.enabled ?? this.config.enabled,
39712
+ maxRetries: config.maxRetries ?? this.config.maxRetries,
39713
+ excludePatterns: config.excludePatterns ?? this.config.excludePatterns
39714
+ };
39731
39715
  }
39732
- findMatching(partial) {
39733
- const lower = partial.toLowerCase();
39734
- return this.getCommands().filter((cmd) => cmd.name.toLowerCase().startsWith(lower) || cmd.description.toLowerCase().includes(lower));
39716
+ isEnabled() {
39717
+ return this.config.enabled !== false;
39735
39718
  }
39736
- }
39737
- // packages/core/src/commands/executor.ts
39738
- class CommandExecutor {
39739
- loader;
39740
- constructor(loader) {
39741
- this.loader = loader;
39719
+ shouldExclude(message) {
39720
+ const trimmed = message.trim();
39721
+ for (const pattern of DEFAULT_EXCLUDE_PATTERNS) {
39722
+ if (pattern.test(trimmed)) {
39723
+ return true;
39724
+ }
39725
+ }
39726
+ const userPatterns = this.config.excludePatterns || [];
39727
+ for (const patternStr of userPatterns) {
39728
+ try {
39729
+ const pattern = new RegExp(patternStr, "i");
39730
+ if (pattern.test(trimmed)) {
39731
+ return true;
39732
+ }
39733
+ } catch {
39734
+ continue;
39735
+ }
39736
+ }
39737
+ if (trimmed.startsWith("!")) {
39738
+ return true;
39739
+ }
39740
+ return false;
39742
39741
  }
39743
- parseCommand(input) {
39744
- const trimmed = input.trim();
39745
- if (!trimmed.startsWith("/")) {
39742
+ async createContext(message, llmClient) {
39743
+ if (!this.isEnabled()) {
39746
39744
  return null;
39747
39745
  }
39748
- const match = trimmed.match(/^\/(\S+)(?:\s+(.*))?$/);
39749
- if (!match) {
39746
+ if (this.shouldExclude(message)) {
39750
39747
  return null;
39751
39748
  }
39752
- return {
39753
- name: match[1],
39754
- args: match[2] || ""
39749
+ const goals = await this.extractGoals(message, llmClient);
39750
+ if (goals.length === 0) {
39751
+ return null;
39752
+ }
39753
+ this.scopeContext = {
39754
+ originalMessage: message,
39755
+ extractedGoals: goals,
39756
+ timestamp: Date.now(),
39757
+ verificationAttempts: 0,
39758
+ maxAttempts: this.config.maxRetries ?? 2
39755
39759
  };
39760
+ return this.scopeContext;
39756
39761
  }
39757
- isCommand(input) {
39758
- return this.parseCommand(input) !== null;
39759
- }
39760
- async execute(input, context) {
39761
- const parsed = this.parseCommand(input);
39762
- if (!parsed) {
39763
- return { handled: false };
39762
+ async extractGoals(message, llmClient) {
39763
+ if (!llmClient) {
39764
+ return this.extractGoalsHeuristic(message);
39764
39765
  }
39765
- const command = this.loader.getCommand(parsed.name);
39766
- if (!command) {
39767
- context.emit("text", `Unknown command: /${parsed.name}
39766
+ try {
39767
+ const prompt = `Analyze the following user request and extract the specific goals or tasks they want accomplished.
39768
+ Return a JSON array of strings, where each string is a concise, actionable goal.
39769
+ If the message is just a greeting, question, or doesn't contain actionable goals, return an empty array [].
39768
39770
 
39769
- Use /help to see available commands.
39770
- `);
39771
- context.emit("done");
39772
- return { handled: true };
39773
- }
39774
- if (command.selfHandled && command.handler) {
39775
- return command.handler(parsed.args, context);
39771
+ User request: "${message}"
39772
+
39773
+ Respond with ONLY a JSON array, no other text. Example: ["goal 1", "goal 2"]`;
39774
+ const messages = [
39775
+ { id: generateId(), role: "user", content: prompt, timestamp: Date.now() }
39776
+ ];
39777
+ let response = "";
39778
+ for await (const chunk of llmClient.chat(messages)) {
39779
+ if (chunk.type === "text" && chunk.content) {
39780
+ response += chunk.content;
39781
+ }
39782
+ }
39783
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
39784
+ if (jsonMatch) {
39785
+ const goals = JSON.parse(jsonMatch[0]);
39786
+ if (Array.isArray(goals) && goals.every((g) => typeof g === "string")) {
39787
+ return goals.filter((g) => g.trim().length > 0);
39788
+ }
39789
+ }
39790
+ return this.extractGoalsHeuristic(message);
39791
+ } catch (error) {
39792
+ console.error("Goal extraction error:", error);
39793
+ return this.extractGoalsHeuristic(message);
39776
39794
  }
39777
- const prompt = await this.preparePrompt(command, parsed.args, context);
39778
- return {
39779
- handled: false,
39780
- prompt
39781
- };
39782
- }
39783
- async preparePrompt(command, args, context) {
39784
- let content = command.content;
39785
- content = content.replace(/\$ARGUMENTS/g, args || "(no arguments provided)");
39786
- content = await this.processShellCommands(content, context.cwd);
39787
- return content;
39788
39795
  }
39789
- async processShellCommands(content, cwd2) {
39790
- const lines = content.split(`
39791
- `);
39792
- const processedLines = [];
39793
- for (const line of lines) {
39794
- const trimmed = line.trim();
39795
- if (trimmed.startsWith("!")) {
39796
- const command = trimmed.slice(1).trim();
39797
- const output = await this.executeShell(command, cwd2);
39798
- processedLines.push(`\`\`\`
39799
- ${output}
39800
- \`\`\``);
39801
- } else {
39802
- processedLines.push(line);
39796
+ extractGoalsHeuristic(message) {
39797
+ const goals = [];
39798
+ const trimmed = message.trim();
39799
+ if (trimmed.length < 10) {
39800
+ return goals;
39801
+ }
39802
+ const taskIndicators = [
39803
+ /^(create|make|build|write|implement|add|fix|update|change|modify|remove|delete|refactor)/i,
39804
+ /^(find|search|look for|locate|get|fetch)/i,
39805
+ /^(run|execute|test|deploy|install|configure|setup)/i,
39806
+ /^(explain|describe|document|analyze|review)/i
39807
+ ];
39808
+ for (const pattern of taskIndicators) {
39809
+ if (pattern.test(trimmed)) {
39810
+ goals.push(trimmed);
39811
+ return goals;
39812
+ }
39813
+ }
39814
+ const lines = trimmed.split(/\n/);
39815
+ for (const line of lines) {
39816
+ const listMatch = line.match(/^[\d\-\*\\u2022]\s*[.)\]:]?\s*(.+)/);
39817
+ if (listMatch && listMatch[1].trim().length > 5) {
39818
+ goals.push(listMatch[1].trim());
39819
+ }
39820
+ }
39821
+ if (goals.length === 0 && trimmed.length >= 20) {
39822
+ goals.push(trimmed);
39823
+ }
39824
+ return goals;
39825
+ }
39826
+ getContext() {
39827
+ return this.scopeContext;
39828
+ }
39829
+ setContext(context) {
39830
+ this.scopeContext = context;
39831
+ }
39832
+ incrementAttempts() {
39833
+ if (this.scopeContext) {
39834
+ this.scopeContext.verificationAttempts++;
39835
+ }
39836
+ }
39837
+ hasReachedMaxAttempts() {
39838
+ if (!this.scopeContext) {
39839
+ return true;
39840
+ }
39841
+ return this.scopeContext.verificationAttempts >= this.scopeContext.maxAttempts;
39842
+ }
39843
+ clear() {
39844
+ this.scopeContext = null;
39845
+ }
39846
+ }
39847
+ // packages/core/src/hooks/scope-verification.ts
39848
+ init_src();
39849
+
39850
+ // packages/core/src/sessions/verification.ts
39851
+ init_src();
39852
+ import { join as join9 } from "path";
39853
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2 } from "fs";
39854
+
39855
+ class VerificationSessionStore {
39856
+ basePath;
39857
+ maxSessions;
39858
+ constructor(basePath, maxSessions = 100) {
39859
+ this.basePath = join9(basePath, "verifications");
39860
+ this.maxSessions = maxSessions;
39861
+ this.ensureDirectory();
39862
+ }
39863
+ ensureDirectory() {
39864
+ if (!existsSync5(this.basePath)) {
39865
+ mkdirSync3(this.basePath, { recursive: true });
39866
+ }
39867
+ }
39868
+ create(parentSessionId, goals, verificationResult) {
39869
+ const session = {
39870
+ id: generateId(),
39871
+ parentSessionId,
39872
+ type: "scope-verification",
39873
+ result: verificationResult.goalsMet ? "pass" : "fail",
39874
+ goals,
39875
+ reason: verificationResult.reason,
39876
+ suggestions: verificationResult.suggestions,
39877
+ verificationResult,
39878
+ createdAt: new Date().toISOString()
39879
+ };
39880
+ this.save(session);
39881
+ this.pruneOldSessions();
39882
+ return session;
39883
+ }
39884
+ save(session) {
39885
+ const filePath = join9(this.basePath, `${session.id}.json`);
39886
+ writeFileSync4(filePath, JSON.stringify(session, null, 2));
39887
+ }
39888
+ get(id) {
39889
+ const filePath = join9(this.basePath, `${id}.json`);
39890
+ if (!existsSync5(filePath)) {
39891
+ return null;
39892
+ }
39893
+ try {
39894
+ const content = readFileSync3(filePath, "utf-8");
39895
+ return JSON.parse(content);
39896
+ } catch {
39897
+ return null;
39898
+ }
39899
+ }
39900
+ getByParentSession(parentSessionId) {
39901
+ const sessions = [];
39902
+ const files = this.listFiles();
39903
+ for (const file of files) {
39904
+ try {
39905
+ const content = readFileSync3(join9(this.basePath, file), "utf-8");
39906
+ const session = JSON.parse(content);
39907
+ if (session.parentSessionId === parentSessionId) {
39908
+ sessions.push(session);
39909
+ }
39910
+ } catch {
39911
+ continue;
39912
+ }
39913
+ }
39914
+ return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
39915
+ }
39916
+ listRecent(limit = 10) {
39917
+ const sessions = [];
39918
+ const files = this.listFiles();
39919
+ for (const file of files) {
39920
+ try {
39921
+ const content = readFileSync3(join9(this.basePath, file), "utf-8");
39922
+ const session = JSON.parse(content);
39923
+ sessions.push(session);
39924
+ } catch {
39925
+ continue;
39926
+ }
39927
+ }
39928
+ return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, limit);
39929
+ }
39930
+ updateResult(id, result) {
39931
+ const session = this.get(id);
39932
+ if (!session)
39933
+ return;
39934
+ session.result = result;
39935
+ this.save(session);
39936
+ }
39937
+ listFiles() {
39938
+ if (!existsSync5(this.basePath)) {
39939
+ return [];
39940
+ }
39941
+ return readdirSync2(this.basePath).filter((f) => f.endsWith(".json"));
39942
+ }
39943
+ pruneOldSessions() {
39944
+ const files = this.listFiles();
39945
+ if (files.length <= this.maxSessions) {
39946
+ return;
39947
+ }
39948
+ const sessions = [];
39949
+ for (const file of files) {
39950
+ try {
39951
+ const content = readFileSync3(join9(this.basePath, file), "utf-8");
39952
+ const session = JSON.parse(content);
39953
+ sessions.push({
39954
+ file,
39955
+ timestamp: new Date(session.createdAt).getTime()
39956
+ });
39957
+ } catch {
39958
+ continue;
39959
+ }
39960
+ }
39961
+ sessions.sort((a, b) => a.timestamp - b.timestamp);
39962
+ const toRemove = sessions.slice(0, sessions.length - this.maxSessions);
39963
+ for (const item of toRemove) {
39964
+ try {
39965
+ const { unlinkSync: unlinkSync2 } = __require("fs");
39966
+ unlinkSync2(join9(this.basePath, item.file));
39967
+ } catch {
39968
+ continue;
39969
+ }
39970
+ }
39971
+ }
39972
+ clear() {
39973
+ const files = this.listFiles();
39974
+ for (const file of files) {
39975
+ try {
39976
+ const { unlinkSync: unlinkSync2 } = __require("fs");
39977
+ unlinkSync2(join9(this.basePath, file));
39978
+ } catch {
39979
+ continue;
39980
+ }
39981
+ }
39982
+ }
39983
+ }
39984
+
39985
+ // packages/core/src/hooks/scope-verification.ts
39986
+ var VERIFICATION_PROMPT = `You are a goal verification assistant. Your task is to determine if the user's original goals were accomplished based on the conversation history.
39987
+
39988
+ Original request: {originalMessage}
39989
+
39990
+ Extracted goals:
39991
+ {goals}
39992
+
39993
+ Analyze the conversation history that follows. For each goal, determine if it was addressed and provide evidence.
39994
+
39995
+ Respond with ONLY valid JSON in this exact format:
39996
+ {
39997
+ "goalsMet": boolean,
39998
+ "goalsAnalysis": [
39999
+ {"goal": "string", "met": boolean, "evidence": "string"}
40000
+ ],
40001
+ "reason": "string explaining overall assessment",
40002
+ "suggestions": ["string"] // Only include if goalsMet is false, listing what still needs to be done
40003
+ }`;
40004
+ function summarizeConversation(messages) {
40005
+ const relevant = messages.filter((m) => m.role !== "system");
40006
+ const summary = [];
40007
+ for (const msg of relevant.slice(-20)) {
40008
+ if (msg.role === "user") {
40009
+ if (msg.toolResults && msg.toolResults.length > 0) {
40010
+ for (const result of msg.toolResults) {
40011
+ const content = result.content.slice(0, 500);
40012
+ summary.push(`[Tool Result - ${result.toolName || "unknown"}]: ${content}`);
40013
+ }
40014
+ } else {
40015
+ summary.push(`User: ${msg.content.slice(0, 300)}`);
40016
+ }
40017
+ } else if (msg.role === "assistant") {
40018
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
40019
+ for (const call of msg.toolCalls) {
40020
+ const input = JSON.stringify(call.input).slice(0, 200);
40021
+ summary.push(`[Tool Call - ${call.name}]: ${input}`);
40022
+ }
40023
+ }
40024
+ if (msg.content.trim()) {
40025
+ summary.push(`Assistant: ${msg.content.slice(0, 300)}`);
40026
+ }
40027
+ }
40028
+ }
40029
+ return summary.join(`
40030
+
40031
+ `);
40032
+ }
40033
+ async function runVerification(scopeContext, messages, llmClient) {
40034
+ const goalsText = scopeContext.extractedGoals.map((g, i) => `${i + 1}. ${g}`).join(`
40035
+ `);
40036
+ const prompt = VERIFICATION_PROMPT.replace("{originalMessage}", scopeContext.originalMessage).replace("{goals}", goalsText);
40037
+ const conversationSummary = summarizeConversation(messages);
40038
+ const verificationMessages = [
40039
+ {
40040
+ id: generateId(),
40041
+ role: "user",
40042
+ content: `${prompt}
40043
+
40044
+ ---
40045
+
40046
+ Conversation history:
40047
+ ${conversationSummary}`,
40048
+ timestamp: Date.now()
40049
+ }
40050
+ ];
40051
+ let response = "";
40052
+ for await (const chunk of llmClient.chat(verificationMessages)) {
40053
+ if (chunk.type === "text" && chunk.content) {
40054
+ response += chunk.content;
40055
+ }
40056
+ }
40057
+ try {
40058
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
40059
+ if (jsonMatch) {
40060
+ const result = JSON.parse(jsonMatch[0]);
40061
+ if (typeof result.goalsMet === "boolean" && typeof result.reason === "string" && Array.isArray(result.goalsAnalysis)) {
40062
+ return result;
40063
+ }
40064
+ }
40065
+ } catch {}
40066
+ return {
40067
+ goalsMet: true,
40068
+ goalsAnalysis: scopeContext.extractedGoals.map((goal) => ({
40069
+ goal,
40070
+ met: true,
40071
+ evidence: "Unable to verify - defaulting to complete"
40072
+ })),
40073
+ reason: "Verification parsing failed - assuming goals were met"
40074
+ };
40075
+ }
40076
+ async function scopeVerificationHandler(input, context) {
40077
+ const { scopeContext, messages, config, sessionId, cwd: cwd2 } = context;
40078
+ const llmClient = context.llmClient;
40079
+ if (!scopeContext || !llmClient) {
40080
+ return null;
40081
+ }
40082
+ if (scopeContext.verificationAttempts >= scopeContext.maxAttempts) {
40083
+ return null;
40084
+ }
40085
+ if (config?.scopeVerification?.enabled === false) {
40086
+ return null;
40087
+ }
40088
+ if (scopeContext.extractedGoals.length === 0) {
40089
+ return null;
40090
+ }
40091
+ try {
40092
+ const result = await runVerification(scopeContext, messages, llmClient);
40093
+ const store = new VerificationSessionStore(getConfigDir());
40094
+ const session = store.create(sessionId, scopeContext.extractedGoals, result);
40095
+ if (result.goalsMet) {
40096
+ return null;
40097
+ }
40098
+ const suggestions = result.suggestions?.join(`
40099
+ - `) || "Please complete the remaining tasks.";
40100
+ return {
40101
+ continue: false,
40102
+ stopReason: "Goals not fully achieved",
40103
+ systemMessage: `[Scope Verification - Session ${session.id}]
40104
+ The following goals were not completed:
40105
+ ${result.goalsAnalysis.filter((g) => !g.met).map((g) => `- ${g.goal}: ${g.evidence}`).join(`
40106
+ `)}
40107
+
40108
+ ${result.reason}
40109
+
40110
+ Please continue and:
40111
+ - ${suggestions}`,
40112
+ additionalContext: `Verification session: ${session.id}`
40113
+ };
40114
+ } catch (error) {
40115
+ console.error("Scope verification error:", error);
40116
+ return null;
40117
+ }
40118
+ }
40119
+ function createScopeVerificationHook() {
40120
+ return {
40121
+ id: "scope-verification",
40122
+ event: "Stop",
40123
+ priority: 100,
40124
+ handler: scopeVerificationHandler
40125
+ };
40126
+ }
40127
+ // packages/core/src/commands/loader.ts
40128
+ import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
40129
+ import { join as join10, basename as basename2, extname } from "path";
40130
+ import { homedir as homedir6 } from "os";
40131
+
40132
+ class CommandLoader {
40133
+ commands = new Map;
40134
+ cwd;
40135
+ constructor(cwd2) {
40136
+ this.cwd = cwd2 || process.cwd();
40137
+ }
40138
+ async loadAll() {
40139
+ this.commands.clear();
40140
+ const envHome = process.env.HOME || process.env.USERPROFILE;
40141
+ const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir6();
40142
+ const globalDir = join10(homeDir, ".assistants", "commands");
40143
+ await this.loadFromDirectory(globalDir, "global");
40144
+ const projectDir = join10(this.cwd, ".assistants", "commands");
40145
+ await this.loadFromDirectory(projectDir, "project");
40146
+ }
40147
+ async loadFromDirectory(dir, source, prefix = "") {
40148
+ if (!existsSync6(dir))
40149
+ return;
40150
+ const entries = readdirSync3(dir);
40151
+ for (const entry of entries) {
40152
+ const fullPath = join10(dir, entry);
40153
+ const stat = statSync2(fullPath);
40154
+ if (stat.isDirectory()) {
40155
+ const newPrefix = prefix ? `${prefix}:${entry}` : entry;
40156
+ await this.loadFromDirectory(fullPath, source, newPrefix);
40157
+ } else if (stat.isFile() && extname(entry) === ".md") {
40158
+ const command = await this.loadCommandFile(fullPath, prefix);
40159
+ if (command) {
40160
+ this.commands.set(command.name, command);
40161
+ }
40162
+ }
40163
+ }
40164
+ }
40165
+ async loadCommandFile(filePath, prefix) {
40166
+ try {
40167
+ const content = await Bun.file(filePath).text();
40168
+ const { frontmatter, body } = this.parseFrontmatter(content);
40169
+ const fileName = basename2(filePath, ".md");
40170
+ const name = frontmatter.name || (prefix ? `${prefix}:${fileName}` : fileName);
40171
+ const allowedToolsRaw = frontmatter["allowed-tools"];
40172
+ const allowedTools = Array.isArray(allowedToolsRaw) ? allowedToolsRaw.map((t) => String(t).trim()).filter(Boolean) : typeof allowedToolsRaw === "string" ? allowedToolsRaw.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
40173
+ return {
40174
+ name,
40175
+ description: frontmatter.description || `Run the ${name} command`,
40176
+ tags: frontmatter.tags,
40177
+ allowedTools,
40178
+ content: body,
40179
+ filePath,
40180
+ builtin: false
40181
+ };
40182
+ } catch (error) {
40183
+ console.error(`Failed to load command from ${filePath}:`, error);
40184
+ return null;
40185
+ }
40186
+ }
40187
+ parseFrontmatter(content) {
40188
+ const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
40189
+ const match = content.match(frontmatterRegex);
40190
+ if (!match) {
40191
+ return { frontmatter: {}, body: content };
40192
+ }
40193
+ const [, yamlContent, body] = match;
40194
+ const frontmatter = {};
40195
+ const lines = yamlContent.split(`
40196
+ `);
40197
+ let currentListKey = null;
40198
+ for (const rawLine of lines) {
40199
+ const line = rawLine.trimEnd();
40200
+ if (!line.trim() || line.trim().startsWith("#"))
40201
+ continue;
40202
+ const listMatch = line.match(/^\s*-\s+(.+)$/);
40203
+ if (listMatch && currentListKey) {
40204
+ const list = frontmatter[currentListKey] ?? [];
40205
+ list.push(this.parseYamlValue(listMatch[1]));
40206
+ frontmatter[currentListKey] = list;
40207
+ continue;
40208
+ }
40209
+ const keyMatch = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
40210
+ if (!keyMatch)
40211
+ continue;
40212
+ const key = keyMatch[1];
40213
+ const valueRaw = keyMatch[2] ?? "";
40214
+ if (valueRaw.trim() === "") {
40215
+ currentListKey = key;
40216
+ if (!frontmatter[key]) {
40217
+ frontmatter[key] = [];
40218
+ }
40219
+ continue;
40220
+ }
40221
+ currentListKey = null;
40222
+ frontmatter[key] = this.parseYamlValue(valueRaw);
40223
+ }
40224
+ return { frontmatter, body: body.trim() };
40225
+ }
40226
+ parseYamlValue(value) {
40227
+ const trimmed = value.trim();
40228
+ if (trimmed === "true")
40229
+ return true;
40230
+ if (trimmed === "false")
40231
+ return false;
40232
+ if (!Number.isNaN(Number(trimmed)) && trimmed !== "")
40233
+ return Number(trimmed);
40234
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
40235
+ const inner = trimmed.slice(1, -1).trim();
40236
+ if (!inner)
40237
+ return [];
40238
+ return inner.split(",").map((item) => this.parseYamlValue(item));
40239
+ }
40240
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
40241
+ return trimmed.slice(1, -1);
40242
+ }
40243
+ return trimmed;
40244
+ }
40245
+ register(command) {
40246
+ this.commands.set(command.name, command);
40247
+ }
40248
+ getCommand(name) {
40249
+ return this.commands.get(name);
40250
+ }
40251
+ getCommands() {
40252
+ return Array.from(this.commands.values());
40253
+ }
40254
+ hasCommand(name) {
40255
+ return this.commands.has(name);
40256
+ }
40257
+ findMatching(partial) {
40258
+ const lower = partial.toLowerCase();
40259
+ return this.getCommands().filter((cmd) => cmd.name.toLowerCase().startsWith(lower) || cmd.description.toLowerCase().includes(lower));
40260
+ }
40261
+ }
40262
+ // packages/core/src/commands/executor.ts
40263
+ class CommandExecutor {
40264
+ loader;
40265
+ constructor(loader) {
40266
+ this.loader = loader;
40267
+ }
40268
+ parseCommand(input) {
40269
+ const trimmed = input.trim();
40270
+ if (!trimmed.startsWith("/")) {
40271
+ return null;
40272
+ }
40273
+ const match = trimmed.match(/^\/(\S+)(?:\s+(.*))?$/);
40274
+ if (!match) {
40275
+ return null;
40276
+ }
40277
+ return {
40278
+ name: match[1],
40279
+ args: match[2] || ""
40280
+ };
40281
+ }
40282
+ isCommand(input) {
40283
+ return this.parseCommand(input) !== null;
40284
+ }
40285
+ async execute(input, context) {
40286
+ const parsed = this.parseCommand(input);
40287
+ if (!parsed) {
40288
+ return { handled: false };
40289
+ }
40290
+ const command = this.loader.getCommand(parsed.name);
40291
+ if (!command) {
40292
+ context.emit("text", `Unknown command: /${parsed.name}
40293
+
40294
+ Use /help to see available commands.
40295
+ `);
40296
+ context.emit("done");
40297
+ return { handled: true };
40298
+ }
40299
+ if (command.selfHandled && command.handler) {
40300
+ return command.handler(parsed.args, context);
40301
+ }
40302
+ const prompt = await this.preparePrompt(command, parsed.args, context);
40303
+ return {
40304
+ handled: false,
40305
+ prompt
40306
+ };
40307
+ }
40308
+ async preparePrompt(command, args, context) {
40309
+ let content = command.content;
40310
+ content = content.replace(/\$ARGUMENTS/g, args || "(no arguments provided)");
40311
+ content = await this.processShellCommands(content, context.cwd);
40312
+ return content;
40313
+ }
40314
+ async processShellCommands(content, cwd2) {
40315
+ const lines = content.split(`
40316
+ `);
40317
+ const processedLines = [];
40318
+ for (const line of lines) {
40319
+ const trimmed = line.trim();
40320
+ if (trimmed.startsWith("!")) {
40321
+ const command = trimmed.slice(1).trim();
40322
+ const output = await this.executeShell(command, cwd2);
40323
+ processedLines.push(`\`\`\`
40324
+ ${output}
40325
+ \`\`\``);
40326
+ } else {
40327
+ processedLines.push(line);
39803
40328
  }
39804
40329
  }
39805
40330
  return processedLines.join(`
@@ -39849,20 +40374,20 @@ ${stderr}`;
39849
40374
  }
39850
40375
  }
39851
40376
  // packages/core/src/commands/builtin.ts
39852
- import { join as join11 } from "path";
40377
+ import { join as join12 } from "path";
39853
40378
  import { homedir as homedir7, platform as platform2, release, arch } from "os";
39854
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
40379
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
39855
40380
  init_src();
39856
40381
 
39857
40382
  // packages/core/src/projects/store.ts
39858
40383
  init_src();
39859
- import { join as join10 } from "path";
40384
+ import { join as join11 } from "path";
39860
40385
  import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
39861
40386
  function projectsDir(cwd2) {
39862
- return join10(cwd2, ".oldpal", "projects");
40387
+ return join11(cwd2, ".assistants", "projects");
39863
40388
  }
39864
40389
  function projectPath(cwd2, id) {
39865
- return join10(projectsDir(cwd2), `${id}.json`);
40390
+ return join11(projectsDir(cwd2), `${id}.json`);
39866
40391
  }
39867
40392
  async function ensureProjectsDir(cwd2) {
39868
40393
  await mkdir3(projectsDir(cwd2), { recursive: true });
@@ -39879,7 +40404,7 @@ async function listProjects(cwd2) {
39879
40404
  if (!file.endsWith(".json"))
39880
40405
  continue;
39881
40406
  try {
39882
- const raw = await readFile2(join10(dir, file), "utf-8");
40407
+ const raw = await readFile2(join11(dir, file), "utf-8");
39883
40408
  const parsed = JSON.parse(raw);
39884
40409
  if (parsed?.id && parsed?.name) {
39885
40410
  projects.push(parsed);
@@ -40057,7 +40582,7 @@ async function buildProjectContext(project, options) {
40057
40582
  }
40058
40583
 
40059
40584
  // packages/core/src/commands/builtin.ts
40060
- var VERSION = process.env.ASSISTANTS_VERSION || process.env.npm_package_version || process.env.OLDPAL_VERSION || "unknown";
40585
+ var VERSION = process.env.ASSISTANTS_VERSION || process.env.npm_package_version || "unknown";
40061
40586
  function resolveAuthTimeout(resolve5) {
40062
40587
  resolve5({ exitCode: 1, stdout: { toString: () => "{}" } });
40063
40588
  }
@@ -40133,6 +40658,7 @@ class BuiltinCommands {
40133
40658
  loader.register(this.resumeScheduleCommand());
40134
40659
  loader.register(this.connectorsCommand());
40135
40660
  loader.register(this.securityLogCommand());
40661
+ loader.register(this.verificationCommand());
40136
40662
  loader.register(this.exitCommand());
40137
40663
  }
40138
40664
  voiceCommand() {
@@ -40642,6 +41168,130 @@ Identities:
40642
41168
  }
40643
41169
  };
40644
41170
  }
41171
+ verificationCommand() {
41172
+ return {
41173
+ name: "verification",
41174
+ description: "Manage scope verification (list/view/enable/disable)",
41175
+ builtin: true,
41176
+ selfHandled: true,
41177
+ content: "",
41178
+ handler: async (args, context) => {
41179
+ const arg = args.trim().toLowerCase();
41180
+ const store = new VerificationSessionStore(getConfigDir());
41181
+ if (arg === "disable" || arg === "off") {
41182
+ nativeHookRegistry.setConfig({
41183
+ ...nativeHookRegistry.getConfig(),
41184
+ scopeVerification: {
41185
+ ...nativeHookRegistry.getConfig().scopeVerification,
41186
+ enabled: false
41187
+ }
41188
+ });
41189
+ context.emit("text", `Scope verification disabled.
41190
+ `);
41191
+ context.emit("done");
41192
+ return { handled: true };
41193
+ }
41194
+ if (arg === "enable" || arg === "on") {
41195
+ nativeHookRegistry.setConfig({
41196
+ ...nativeHookRegistry.getConfig(),
41197
+ scopeVerification: {
41198
+ ...nativeHookRegistry.getConfig().scopeVerification,
41199
+ enabled: true
41200
+ }
41201
+ });
41202
+ context.emit("text", `Scope verification enabled.
41203
+ `);
41204
+ context.emit("done");
41205
+ return { handled: true };
41206
+ }
41207
+ if (arg === "status") {
41208
+ const config = nativeHookRegistry.getConfig();
41209
+ const enabled = config.scopeVerification?.enabled !== false;
41210
+ const maxRetries = config.scopeVerification?.maxRetries ?? 2;
41211
+ context.emit("text", `Scope verification: ${enabled ? "enabled" : "disabled"}
41212
+ `);
41213
+ context.emit("text", `Max retries: ${maxRetries}
41214
+ `);
41215
+ context.emit("done");
41216
+ return { handled: true };
41217
+ }
41218
+ if (arg === "" || arg === "list") {
41219
+ const sessions2 = store.listRecent(10);
41220
+ if (sessions2.length === 0) {
41221
+ context.emit("text", `No verification sessions found.
41222
+ `);
41223
+ context.emit("done");
41224
+ return { handled: true };
41225
+ }
41226
+ context.emit("text", `Recent verification sessions:
41227
+
41228
+ `);
41229
+ for (const session of sessions2) {
41230
+ const date = new Date(session.createdAt).toLocaleString();
41231
+ const status = session.result === "pass" ? "\u2713" : session.result === "force-continue" ? "\u2192" : "\u2717";
41232
+ context.emit("text", `${status} ${session.id.slice(0, 8)} - ${date} - ${session.result}
41233
+ `);
41234
+ context.emit("text", ` Goals: ${session.goals.slice(0, 2).join(", ")}${session.goals.length > 2 ? "..." : ""}
41235
+ `);
41236
+ }
41237
+ context.emit("text", `
41238
+ Use /verification <id> to view details.
41239
+ `);
41240
+ context.emit("done");
41241
+ return { handled: true };
41242
+ }
41243
+ const sessions = store.listRecent(100);
41244
+ const match = sessions.find((s) => s.id.startsWith(arg) || s.id === arg);
41245
+ if (!match) {
41246
+ context.emit("text", `No verification session found matching "${arg}".
41247
+ `);
41248
+ context.emit("done");
41249
+ return { handled: true };
41250
+ }
41251
+ context.emit("text", `
41252
+ === Verification Session ${match.id} ===
41253
+
41254
+ `);
41255
+ context.emit("text", `Created: ${new Date(match.createdAt).toLocaleString()}
41256
+ `);
41257
+ context.emit("text", `Parent Session: ${match.parentSessionId}
41258
+ `);
41259
+ context.emit("text", `Result: ${match.result}
41260
+
41261
+ `);
41262
+ context.emit("text", `Goals:
41263
+ `);
41264
+ for (const goal of match.goals) {
41265
+ context.emit("text", ` \u2022 ${goal}
41266
+ `);
41267
+ }
41268
+ context.emit("text", `
41269
+ Analysis:
41270
+ `);
41271
+ for (const analysis of match.verificationResult.goalsAnalysis) {
41272
+ const icon = analysis.met ? "\u2713" : "\u2717";
41273
+ context.emit("text", ` ${icon} ${analysis.goal}
41274
+ `);
41275
+ context.emit("text", ` ${analysis.evidence}
41276
+ `);
41277
+ }
41278
+ context.emit("text", `
41279
+ Reason: ${match.reason}
41280
+ `);
41281
+ if (match.suggestions && match.suggestions.length > 0) {
41282
+ context.emit("text", `
41283
+ Suggestions:
41284
+ `);
41285
+ for (const suggestion of match.suggestions) {
41286
+ context.emit("text", ` \u2022 ${suggestion}
41287
+ `);
41288
+ }
41289
+ }
41290
+ context.emit("done");
41291
+ return { handled: true };
41292
+ }
41293
+ };
41294
+ }
40645
41295
  exitCommand() {
40646
41296
  return {
40647
41297
  name: "exit",
@@ -41608,9 +42258,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
41608
42258
  content: "",
41609
42259
  handler: async (args, context) => {
41610
42260
  const configPaths = [
41611
- join11(context.cwd, ".assistants", "config.json"),
41612
- join11(context.cwd, ".assistants", "config.local.json"),
41613
- join11(getConfigDir(), "config.json")
42261
+ join12(context.cwd, ".assistants", "config.json"),
42262
+ join12(context.cwd, ".assistants", "config.local.json"),
42263
+ join12(getConfigDir(), "config.json")
41614
42264
  ];
41615
42265
  let message = `
41616
42266
  **Configuration**
@@ -41628,9 +42278,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
41628
42278
  message += `
41629
42279
  **Commands Directories:**
41630
42280
  `;
41631
- message += ` - Project: ${join11(context.cwd, ".assistants", "commands")}
42281
+ message += ` - Project: ${join12(context.cwd, ".assistants", "commands")}
41632
42282
  `;
41633
- message += ` - Global: ${join11(homeDir, ".assistants", "commands")}
42283
+ message += ` - Global: ${join12(homeDir, ".assistants", "commands")}
41634
42284
  `;
41635
42285
  context.emit("text", message);
41636
42286
  context.emit("done");
@@ -41646,8 +42296,8 @@ Format the summary as a brief bullet-point list. This summary will replace the c
41646
42296
  selfHandled: true,
41647
42297
  content: "",
41648
42298
  handler: async (args, context) => {
41649
- const commandsDir = join11(context.cwd, ".assistants", "commands");
41650
- mkdirSync3(commandsDir, { recursive: true });
42299
+ const commandsDir = join12(context.cwd, ".assistants", "commands");
42300
+ mkdirSync4(commandsDir, { recursive: true });
41651
42301
  const exampleCommand = `---
41652
42302
  name: reflect
41653
42303
  description: Reflect on the conversation and suggest next steps
@@ -41662,9 +42312,9 @@ Please summarize the last interaction and suggest 2-3 next steps.
41662
42312
  - Focus on clarity
41663
42313
  - Ask a follow-up question if needed
41664
42314
  `;
41665
- const examplePath = join11(commandsDir, "reflect.md");
42315
+ const examplePath = join12(commandsDir, "reflect.md");
41666
42316
  if (!existsSync7(examplePath)) {
41667
- writeFileSync4(examplePath, exampleCommand);
42317
+ writeFileSync5(examplePath, exampleCommand);
41668
42318
  }
41669
42319
  let message = `
41670
42320
  **Initialized assistants**
@@ -41900,8 +42550,19 @@ Keep it concise but comprehensive.`
41900
42550
  handler: async (args, context) => {
41901
42551
  const id = args.trim();
41902
42552
  if (!id) {
41903
- context.emit("text", `Usage: /pause <id>
42553
+ const schedules = await listSchedules(context.cwd);
42554
+ if (schedules.length === 0) {
42555
+ context.emit("text", `No schedules found.
42556
+ `);
42557
+ } else {
42558
+ const lines = schedules.sort((a, b) => (a.nextRunAt || 0) - (b.nextRunAt || 0)).map((schedule) => `- ${schedule.id} [${schedule.status}] ${schedule.command}`);
42559
+ context.emit("text", `Usage: /pause <id>
42560
+
42561
+ Available schedules:
42562
+ ${lines.join(`
42563
+ `)}
41904
42564
  `);
42565
+ }
41905
42566
  context.emit("done");
41906
42567
  return { handled: true };
41907
42568
  }
@@ -41927,8 +42588,19 @@ Keep it concise but comprehensive.`
41927
42588
  handler: async (args, context) => {
41928
42589
  const id = args.trim();
41929
42590
  if (!id) {
41930
- context.emit("text", `Usage: /resume <id>
42591
+ const schedules = await listSchedules(context.cwd);
42592
+ if (schedules.length === 0) {
42593
+ context.emit("text", `No schedules found.
42594
+ `);
42595
+ } else {
42596
+ const lines = schedules.sort((a, b) => (a.nextRunAt || 0) - (b.nextRunAt || 0)).map((schedule) => `- ${schedule.id} [${schedule.status}] ${schedule.command}`);
42597
+ context.emit("text", `Usage: /resume <id>
42598
+
42599
+ Available schedules:
42600
+ ${lines.join(`
42601
+ `)}
41931
42602
  `);
42603
+ }
41932
42604
  context.emit("done");
41933
42605
  return { handled: true };
41934
42606
  }
@@ -42363,7 +43035,7 @@ async function createLLMClient(config) {
42363
43035
 
42364
43036
  // packages/core/src/heartbeat/manager.ts
42365
43037
  import { dirname as dirname5 } from "path";
42366
- import { mkdirSync as mkdirSync4 } from "fs";
43038
+ import { mkdirSync as mkdirSync5 } from "fs";
42367
43039
  import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
42368
43040
 
42369
43041
  class HeartbeatManager {
@@ -42385,7 +43057,7 @@ class HeartbeatManager {
42385
43057
  uptimeSeconds: 0
42386
43058
  };
42387
43059
  const dir = dirname5(config.persistPath);
42388
- mkdirSync4(dir, { recursive: true });
43060
+ mkdirSync5(dir, { recursive: true });
42389
43061
  }
42390
43062
  start(sessionId) {
42391
43063
  if (this.intervalId)
@@ -42459,14 +43131,14 @@ class HeartbeatManager {
42459
43131
  }
42460
43132
  // packages/core/src/heartbeat/persistence.ts
42461
43133
  import { dirname as dirname6 } from "path";
42462
- import { mkdirSync as mkdirSync5 } from "fs";
43134
+ import { mkdirSync as mkdirSync6 } from "fs";
42463
43135
  import { readFile as readFile5, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
42464
43136
 
42465
43137
  class StatePersistence {
42466
43138
  path;
42467
43139
  constructor(path2) {
42468
43140
  this.path = path2;
42469
- mkdirSync5(dirname6(path2), { recursive: true });
43141
+ mkdirSync6(dirname6(path2), { recursive: true });
42470
43142
  }
42471
43143
  async save(state) {
42472
43144
  try {
@@ -42686,14 +43358,14 @@ class EnergyManager {
42686
43358
  }
42687
43359
  // packages/core/src/energy/storage.ts
42688
43360
  import { dirname as dirname7 } from "path";
42689
- import { mkdirSync as mkdirSync6 } from "fs";
43361
+ import { mkdirSync as mkdirSync7 } from "fs";
42690
43362
  import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
42691
43363
 
42692
43364
  class EnergyStorage {
42693
43365
  path;
42694
43366
  constructor(path2) {
42695
43367
  this.path = path2;
42696
- mkdirSync6(dirname7(path2), { recursive: true });
43368
+ mkdirSync7(dirname7(path2), { recursive: true });
42697
43369
  }
42698
43370
  async save(state) {
42699
43371
  try {
@@ -42760,18 +43432,18 @@ function validateToolCalls(toolCalls, tools) {
42760
43432
  }
42761
43433
 
42762
43434
  // packages/core/src/voice/utils.ts
42763
- import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
43435
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
42764
43436
  import { homedir as homedir9 } from "os";
42765
- import { join as join13 } from "path";
43437
+ import { join as join14 } from "path";
42766
43438
  import { spawnSync } from "child_process";
42767
43439
  function loadApiKeyFromSecrets2(key) {
42768
43440
  const envHome = process.env.HOME || process.env.USERPROFILE;
42769
43441
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir9();
42770
- const secretsPath = join13(homeDir, ".secrets");
43442
+ const secretsPath = join14(homeDir, ".secrets");
42771
43443
  if (!existsSync9(secretsPath))
42772
43444
  return;
42773
43445
  try {
42774
- const content = readFileSync4(secretsPath, "utf-8");
43446
+ const content = readFileSync5(secretsPath, "utf-8");
42775
43447
  const match = content.match(new RegExp(`export\\s+${key}\\s*=\\s*['"]?([^'"\\n]+)['"]?`));
42776
43448
  return match?.[1];
42777
43449
  } catch {
@@ -42841,8 +43513,8 @@ class SystemSTT {
42841
43513
  // packages/core/src/voice/tts.ts
42842
43514
  import { spawnSync as spawnSync2 } from "child_process";
42843
43515
  import { tmpdir as tmpdir2 } from "os";
42844
- import { join as join14 } from "path";
42845
- import { readFileSync as readFileSync5, unlinkSync as unlinkSync2 } from "fs";
43516
+ import { join as join15 } from "path";
43517
+ import { readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
42846
43518
  class ElevenLabsTTS {
42847
43519
  apiKey;
42848
43520
  voiceId;
@@ -42945,7 +43617,7 @@ class SystemTTS {
42945
43617
  if (!say) {
42946
43618
  throw new Error('System TTS not available: missing "say" command.');
42947
43619
  }
42948
- const output = join14(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
43620
+ const output = join15(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
42949
43621
  const args = [];
42950
43622
  if (this.voiceId) {
42951
43623
  args.push("-v", this.voiceId);
@@ -42958,7 +43630,7 @@ class SystemTTS {
42958
43630
  if (result.status !== 0) {
42959
43631
  throw new Error(`System TTS failed: ${result.stderr || "unknown error"}`);
42960
43632
  }
42961
- const audio = readFileSync5(output);
43633
+ const audio = readFileSync6(output);
42962
43634
  unlinkSync2(output);
42963
43635
  return {
42964
43636
  audio: audio.buffer.slice(audio.byteOffset, audio.byteOffset + audio.byteLength),
@@ -42967,7 +43639,7 @@ class SystemTTS {
42967
43639
  }
42968
43640
  const espeak = findExecutable("espeak") || findExecutable("espeak-ng");
42969
43641
  if (espeak) {
42970
- const output = join14(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
43642
+ const output = join15(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
42971
43643
  const args = ["-w", output];
42972
43644
  if (this.voiceId) {
42973
43645
  args.push("-v", this.voiceId);
@@ -42980,7 +43652,7 @@ class SystemTTS {
42980
43652
  if (result.status !== 0) {
42981
43653
  throw new Error(`System TTS failed: ${result.stderr || "unknown error"}`);
42982
43654
  }
42983
- const audio = readFileSync5(output);
43655
+ const audio = readFileSync6(output);
42984
43656
  unlinkSync2(output);
42985
43657
  return {
42986
43658
  audio: audio.buffer.slice(audio.byteOffset, audio.byteOffset + audio.byteLength),
@@ -42994,15 +43666,15 @@ class SystemTTS {
42994
43666
  // packages/core/src/voice/player.ts
42995
43667
  import { spawn } from "child_process";
42996
43668
  import { tmpdir as tmpdir3 } from "os";
42997
- import { join as join15 } from "path";
42998
- import { unlink as unlink4, writeFileSync as writeFileSync5 } from "fs";
43669
+ import { join as join16 } from "path";
43670
+ import { unlink as unlink4, writeFileSync as writeFileSync6 } from "fs";
42999
43671
  class AudioPlayer {
43000
43672
  currentProcess = null;
43001
43673
  playing = false;
43002
43674
  async play(audio, options = {}) {
43003
43675
  const format = options.format ?? "mp3";
43004
- const tempFile = join15(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
43005
- writeFileSync5(tempFile, Buffer.from(audio));
43676
+ const tempFile = join16(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
43677
+ writeFileSync6(tempFile, Buffer.from(audio));
43006
43678
  const player = this.resolvePlayer(format);
43007
43679
  if (!player) {
43008
43680
  throw new Error("No supported audio player found. Install afplay, ffplay, mpg123, or aplay.");
@@ -43069,8 +43741,8 @@ class AudioPlayer {
43069
43741
  // packages/core/src/voice/recorder.ts
43070
43742
  import { spawn as spawn2 } from "child_process";
43071
43743
  import { tmpdir as tmpdir4 } from "os";
43072
- import { join as join16 } from "path";
43073
- import { readFileSync as readFileSync6, unlink as unlink5 } from "fs";
43744
+ import { join as join17 } from "path";
43745
+ import { readFileSync as readFileSync7, unlink as unlink5 } from "fs";
43074
43746
  class AudioRecorder {
43075
43747
  currentProcess = null;
43076
43748
  async record(options = {}) {
@@ -43080,7 +43752,7 @@ class AudioRecorder {
43080
43752
  const duration = options.durationSeconds ?? 5;
43081
43753
  const sampleRate = options.sampleRate ?? 16000;
43082
43754
  const channels = options.channels ?? 1;
43083
- const output = join16(tmpdir4(), `assistants-record-${Date.now()}.wav`);
43755
+ const output = join17(tmpdir4(), `assistants-record-${Date.now()}.wav`);
43084
43756
  const recorder = this.resolveRecorder(sampleRate, channels, duration, output);
43085
43757
  if (!recorder) {
43086
43758
  throw new Error("No supported audio recorder found. Install sox or ffmpeg.");
@@ -43100,7 +43772,7 @@ class AudioRecorder {
43100
43772
  reject(error);
43101
43773
  });
43102
43774
  });
43103
- const data = readFileSync6(output);
43775
+ const data = readFileSync7(output);
43104
43776
  unlink5(output, () => {});
43105
43777
  return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
43106
43778
  }
@@ -43274,13 +43946,13 @@ class VoiceManager {
43274
43946
  init_src();
43275
43947
  import { existsSync as existsSync11 } from "fs";
43276
43948
  import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile7, rm as rm2 } from "fs/promises";
43277
- import { join as join18 } from "path";
43949
+ import { join as join19 } from "path";
43278
43950
 
43279
43951
  // packages/core/src/identity/identity-manager.ts
43280
43952
  init_src();
43281
43953
  import { existsSync as existsSync10 } from "fs";
43282
43954
  import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile6, rm } from "fs/promises";
43283
- import { join as join17 } from "path";
43955
+ import { join as join18 } from "path";
43284
43956
  var DEFAULT_PROFILE = {
43285
43957
  displayName: "Assistant",
43286
43958
  timezone: "UTC",
@@ -43310,19 +43982,19 @@ class IdentityManager {
43310
43982
  this.basePath = basePath;
43311
43983
  }
43312
43984
  get identitiesRoot() {
43313
- return join17(this.basePath, "assistants", this.assistantId, "identities");
43985
+ return join18(this.basePath, "assistants", this.assistantId, "identities");
43314
43986
  }
43315
43987
  get indexPath() {
43316
- return join17(this.identitiesRoot, "index.json");
43988
+ return join18(this.identitiesRoot, "index.json");
43317
43989
  }
43318
43990
  get activePath() {
43319
- return join17(this.identitiesRoot, "active.json");
43991
+ return join18(this.identitiesRoot, "active.json");
43320
43992
  }
43321
43993
  identityPath(id) {
43322
- return join17(this.identitiesRoot, `${id}.json`);
43994
+ return join18(this.identitiesRoot, `${id}.json`);
43323
43995
  }
43324
43996
  assistantConfigPath() {
43325
- return join17(this.basePath, "assistants", this.assistantId, "config.json");
43997
+ return join18(this.basePath, "assistants", this.assistantId, "config.json");
43326
43998
  }
43327
43999
  async initialize() {
43328
44000
  await mkdir4(this.identitiesRoot, { recursive: true });
@@ -43506,16 +44178,16 @@ class AssistantManager {
43506
44178
  this.basePath = basePath;
43507
44179
  }
43508
44180
  get assistantsRoot() {
43509
- return join18(this.basePath, "assistants");
44181
+ return join19(this.basePath, "assistants");
43510
44182
  }
43511
44183
  get indexPath() {
43512
- return join18(this.assistantsRoot, "index.json");
44184
+ return join19(this.assistantsRoot, "index.json");
43513
44185
  }
43514
44186
  get activePath() {
43515
- return join18(this.basePath, "active.json");
44187
+ return join19(this.basePath, "active.json");
43516
44188
  }
43517
44189
  assistantConfigPath(id) {
43518
- return join18(this.assistantsRoot, id, "config.json");
44190
+ return join19(this.assistantsRoot, id, "config.json");
43519
44191
  }
43520
44192
  async initialize() {
43521
44193
  await mkdir5(this.assistantsRoot, { recursive: true });
@@ -43569,7 +44241,7 @@ class AssistantManager {
43569
44241
  if (!this.assistants.has(id)) {
43570
44242
  throw new Error(`Assistant ${id} not found`);
43571
44243
  }
43572
- await rm2(join18(this.assistantsRoot, id), { recursive: true, force: true });
44244
+ await rm2(join19(this.assistantsRoot, id), { recursive: true, force: true });
43573
44245
  this.assistants.delete(id);
43574
44246
  await this.removeFromIndex(id);
43575
44247
  if (this.activeId === id) {
@@ -43635,7 +44307,7 @@ class AssistantManager {
43635
44307
  }
43636
44308
  }
43637
44309
  async persistAssistant(assistant) {
43638
- const dir = join18(this.assistantsRoot, assistant.id);
44310
+ const dir = join19(this.assistantsRoot, assistant.id);
43639
44311
  await mkdir5(dir, { recursive: true });
43640
44312
  await writeFile7(this.assistantConfigPath(assistant.id), JSON.stringify(assistant, null, 2));
43641
44313
  }
@@ -43675,6 +44347,7 @@ class AgentLoop {
43675
44347
  skillExecutor;
43676
44348
  hookLoader;
43677
44349
  hookExecutor;
44350
+ scopeContextManager;
43678
44351
  commandLoader;
43679
44352
  commandExecutor;
43680
44353
  builtinCommands;
@@ -43716,6 +44389,7 @@ class AgentLoop {
43716
44389
  this.skillExecutor = new SkillExecutor;
43717
44390
  this.hookLoader = new HookLoader;
43718
44391
  this.hookExecutor = new HookExecutor;
44392
+ this.scopeContextManager = new ScopeContextManager;
43719
44393
  this.commandLoader = new CommandLoader(this.cwd);
43720
44394
  this.commandExecutor = new CommandExecutor(this.commandLoader);
43721
44395
  this.builtinCommands = new BuiltinCommands;
@@ -43781,6 +44455,14 @@ class AgentLoop {
43781
44455
  this.connectorBridge.registerAll(this.toolRegistry);
43782
44456
  this.builtinCommands.registerAll(this.commandLoader);
43783
44457
  this.hookLoader.load(hooksConfig);
44458
+ nativeHookRegistry.register(createScopeVerificationHook());
44459
+ const nativeConfig = hooksConfig?.native;
44460
+ if (nativeConfig) {
44461
+ nativeHookRegistry.setConfig(nativeConfig);
44462
+ if (nativeConfig.scopeVerification) {
44463
+ this.scopeContextManager.setConfig(nativeConfig.scopeVerification);
44464
+ }
44465
+ }
43784
44466
  this.hookExecutor.setAgentRunner((hook, input, timeout) => runHookAgent({ hook, input, timeout, cwd: this.cwd }));
43785
44467
  this.systemPrompt = systemPrompt || null;
43786
44468
  if (this.systemPrompt) {
@@ -43829,6 +44511,10 @@ class AgentLoop {
43829
44511
  return { ok: false, error: promptHookResult.stopReason || "Blocked by hook" };
43830
44512
  }
43831
44513
  }
44514
+ const explicitToolResult = await this.handleExplicitToolCommand(userMessage);
44515
+ if (explicitToolResult) {
44516
+ return explicitToolResult;
44517
+ }
43832
44518
  if (userMessage.startsWith("/")) {
43833
44519
  const parsed = this.commandExecutor.parseCommand(userMessage);
43834
44520
  const command = parsed ? this.commandLoader.getCommand(parsed.name) : undefined;
@@ -43863,6 +44549,12 @@ class AgentLoop {
43863
44549
  }
43864
44550
  const limits = getLimits();
43865
44551
  userMessage = enforceMessageLimit(userMessage, limits.maxUserMessageLength);
44552
+ if (source === "user") {
44553
+ const scopeContext = await this.scopeContextManager.createContext(userMessage, this.llmClient);
44554
+ if (scopeContext) {
44555
+ this.context.setScopeContext(scopeContext);
44556
+ }
44557
+ }
43866
44558
  this.context.addUserMessage(userMessage);
43867
44559
  await this.runLoop();
43868
44560
  this.contextManager?.refreshState(this.context.getMessages());
@@ -43880,6 +44572,47 @@ class AgentLoop {
43880
44572
  this.drainScheduledQueue();
43881
44573
  }
43882
44574
  }
44575
+ async handleExplicitToolCommand(userMessage) {
44576
+ const match = userMessage.match(/^!\[(\w+)\]\s*([\s\S]*)$/);
44577
+ if (!match)
44578
+ return null;
44579
+ const toolName = match[1].toLowerCase();
44580
+ const command = match[2].trim();
44581
+ if (!command) {
44582
+ this.emit({ type: "text", content: `Usage: ![${toolName}] <command>
44583
+ ` });
44584
+ this.emit({ type: "done" });
44585
+ return { ok: false, error: "Missing command" };
44586
+ }
44587
+ if (toolName !== "bash") {
44588
+ this.emit({ type: "text", content: `Unsupported tool: ${toolName}
44589
+ ` });
44590
+ this.emit({ type: "done" });
44591
+ return { ok: false, error: "Unsupported tool" };
44592
+ }
44593
+ const toolCall = {
44594
+ id: generateId(),
44595
+ name: "bash",
44596
+ type: "tool",
44597
+ input: {
44598
+ command,
44599
+ cwd: this.cwd,
44600
+ sessionId: this.sessionId
44601
+ }
44602
+ };
44603
+ this.context.addUserMessage(userMessage);
44604
+ this.context.addAssistantMessage("", [toolCall]);
44605
+ this.emit({ type: "tool_use", toolCall });
44606
+ const results = await this.executeToolCalls([toolCall]);
44607
+ this.context.addToolResults(results);
44608
+ this.emit({ type: "done" });
44609
+ const failed = results.some((result) => result.isError);
44610
+ if (failed) {
44611
+ const error = results.find((result) => result.isError)?.content;
44612
+ return { ok: false, error: error ? String(error) : "Tool execution failed" };
44613
+ }
44614
+ return { ok: true, summary: `Executed ${toolName}` };
44615
+ }
43883
44616
  async runLoop() {
43884
44617
  const maxTurns = 50;
43885
44618
  let turn = 0;
@@ -43945,6 +44678,21 @@ class AgentLoop {
43945
44678
  hook_event_name: "Stop",
43946
44679
  cwd: this.cwd
43947
44680
  });
44681
+ const verificationResult = await this.runScopeVerification();
44682
+ if (verificationResult && verificationResult.continue === false) {
44683
+ if (verificationResult.systemMessage) {
44684
+ this.context.addSystemMessage(verificationResult.systemMessage);
44685
+ }
44686
+ this.scopeContextManager.incrementAttempts();
44687
+ const scope = this.scopeContextManager.getContext();
44688
+ if (scope) {
44689
+ this.context.setScopeContext(scope);
44690
+ }
44691
+ await this.runLoop();
44692
+ return;
44693
+ }
44694
+ this.scopeContextManager.clear();
44695
+ this.context.clearScopeContext();
43948
44696
  this.emit({ type: "done" });
43949
44697
  }
43950
44698
  if (streamError) {
@@ -43976,6 +44724,36 @@ class AgentLoop {
43976
44724
  ` });
43977
44725
  }
43978
44726
  }
44727
+ async runScopeVerification() {
44728
+ if (!this.scopeContextManager.isEnabled()) {
44729
+ return null;
44730
+ }
44731
+ if (this.scopeContextManager.hasReachedMaxAttempts()) {
44732
+ return null;
44733
+ }
44734
+ const scopeContext = this.context.getScopeContext();
44735
+ if (!scopeContext) {
44736
+ return null;
44737
+ }
44738
+ const result = await nativeHookRegistry.execute("Stop", {
44739
+ session_id: this.sessionId,
44740
+ hook_event_name: "Stop",
44741
+ cwd: this.cwd
44742
+ }, {
44743
+ sessionId: this.sessionId,
44744
+ cwd: this.cwd,
44745
+ messages: this.context.getMessages(),
44746
+ scopeContext,
44747
+ llmClient: this.llmClient
44748
+ });
44749
+ if (!result) {
44750
+ return null;
44751
+ }
44752
+ return {
44753
+ continue: result.continue !== false,
44754
+ systemMessage: result.systemMessage
44755
+ };
44756
+ }
43979
44757
  async executeToolCalls(toolCalls) {
43980
44758
  const results = [];
43981
44759
  for (const toolCall of toolCalls) {
@@ -44365,7 +45143,7 @@ ${content.trim()}`);
44365
45143
  const heartbeatConfig = this.buildHeartbeatConfig(this.config);
44366
45144
  if (!heartbeatConfig)
44367
45145
  return;
44368
- const statePath = join19(getConfigDir(), "state", `${this.sessionId}.json`);
45146
+ const statePath = join20(getConfigDir(), "state", `${this.sessionId}.json`);
44369
45147
  this.heartbeatManager = new HeartbeatManager(heartbeatConfig);
44370
45148
  this.heartbeatPersistence = new StatePersistence(statePath);
44371
45149
  this.heartbeatRecovery = new RecoveryManager(this.heartbeatPersistence, heartbeatConfig.persistPath, heartbeatConfig.staleThresholdMs, {
@@ -44414,7 +45192,7 @@ ${content.trim()}`);
44414
45192
  async startEnergySystem() {
44415
45193
  if (!this.config || this.config.energy?.enabled === false)
44416
45194
  return;
44417
- const statePath = join19(getConfigDir(), "energy", "state.json");
45195
+ const statePath = join20(getConfigDir(), "energy", "state.json");
44418
45196
  this.energyManager = new EnergyManager(this.config.energy, new EnergyStorage(statePath));
44419
45197
  await this.energyManager.initialize();
44420
45198
  this.refreshEnergyEffects();
@@ -44481,7 +45259,24 @@ ${effects.message}
44481
45259
  await releaseScheduleLock(this.cwd, schedule.id, this.sessionId);
44482
45260
  continue;
44483
45261
  }
44484
- this.scheduledQueue.push(schedule);
45262
+ let scheduleToQueue = schedule;
45263
+ if (!schedule.sessionId) {
45264
+ const claimed = await updateSchedule(this.cwd, schedule.id, (current) => {
45265
+ if (current.sessionId)
45266
+ return current;
45267
+ return {
45268
+ ...current,
45269
+ sessionId: this.sessionId,
45270
+ updatedAt: Date.now()
45271
+ };
45272
+ });
45273
+ if (!claimed || claimed.sessionId && claimed.sessionId !== this.sessionId) {
45274
+ await releaseScheduleLock(this.cwd, schedule.id, this.sessionId);
45275
+ continue;
45276
+ }
45277
+ scheduleToQueue = claimed;
45278
+ }
45279
+ this.scheduledQueue.push(scheduleToQueue);
44485
45280
  }
44486
45281
  this.drainScheduledQueue();
44487
45282
  } catch (error) {
@@ -44643,7 +45438,7 @@ ${this.identityContext}`);
44643
45438
  return null;
44644
45439
  const intervalMs = Math.max(1000, config.heartbeat?.intervalMs ?? 15000);
44645
45440
  const staleThresholdMs = Math.max(intervalMs * 2, config.heartbeat?.staleThresholdMs ?? 120000);
44646
- const persistPath = config.heartbeat?.persistPath ?? join19(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
45441
+ const persistPath = config.heartbeat?.persistPath ?? join20(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
44647
45442
  return {
44648
45443
  intervalMs,
44649
45444
  staleThresholdMs,
@@ -44733,116 +45528,13 @@ function parseErrorCode(message) {
44733
45528
  }
44734
45529
  // packages/core/src/memory/sessions.ts
44735
45530
  init_src();
44736
- // packages/core/src/migration/migrate-to-assistants.ts
44737
- import { existsSync as existsSync12 } from "fs";
44738
- import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile8, rename, cp } from "fs/promises";
44739
- import { join as join20 } from "path";
44740
- import { homedir as homedir10 } from "os";
44741
- var MIGRATION_MARKER = ".migrated-from-oldpal";
44742
- async function ensureDir(path2) {
44743
- await mkdir6(path2, { recursive: true });
44744
- }
44745
- async function copyIfExists(source, destination) {
44746
- if (!existsSync12(source))
44747
- return false;
44748
- await ensureDir(join20(destination, ".."));
44749
- await cp(source, destination, { recursive: true });
44750
- return true;
44751
- }
44752
- async function readJson(path2) {
44753
- if (!existsSync12(path2))
44754
- return null;
44755
- try {
44756
- const raw = await readFile9(path2, "utf-8");
44757
- return JSON.parse(raw);
44758
- } catch {
44759
- return null;
44760
- }
44761
- }
44762
- async function migrateFromOldpal() {
44763
- const result = {
44764
- success: false,
44765
- migrated: [],
44766
- errors: []
44767
- };
44768
- const home = homedir10();
44769
- const oldPath = join20(home, ".oldpal");
44770
- const newPath = join20(home, ".assistants");
44771
- if (!existsSync12(oldPath)) {
44772
- result.success = true;
44773
- return result;
44774
- }
44775
- if (existsSync12(newPath)) {
44776
- const marker = join20(newPath, "migration", MIGRATION_MARKER);
44777
- if (existsSync12(marker)) {
44778
- result.success = true;
44779
- return result;
44780
- }
44781
- result.errors.push("Both ~/.oldpal and ~/.assistants exist. Manual merge required.");
44782
- return result;
44783
- }
44784
- try {
44785
- await ensureDir(newPath);
44786
- await ensureDir(join20(newPath, "assistants"));
44787
- await ensureDir(join20(newPath, "shared", "skills"));
44788
- await ensureDir(join20(newPath, "logs"));
44789
- await ensureDir(join20(newPath, "migration"));
44790
- const config = await readJson(join20(oldPath, "settings.json"));
44791
- if (config) {
44792
- await writeFile8(join20(newPath, "config.json"), JSON.stringify(config, null, 2));
44793
- result.migrated.push("config.json");
44794
- }
44795
- if (await copyIfExists(join20(oldPath, "settings.local.json"), join20(newPath, "config.local.json"))) {
44796
- result.migrated.push("config.local.json");
44797
- }
44798
- if (await copyIfExists(join20(oldPath, "hooks.json"), join20(newPath, "hooks.json"))) {
44799
- result.migrated.push("hooks.json");
44800
- }
44801
- if (await copyIfExists(join20(oldPath, "commands"), join20(newPath, "commands"))) {
44802
- result.migrated.push("commands");
44803
- }
44804
- if (await copyIfExists(join20(oldPath, "OLDPAL.md"), join20(newPath, "ASSISTANTS.md"))) {
44805
- result.migrated.push("ASSISTANTS.md");
44806
- }
44807
- if (await copyIfExists(join20(oldPath, "skills"), join20(newPath, "shared", "skills"))) {
44808
- result.migrated.push("skills");
44809
- }
44810
- if (await copyIfExists(join20(oldPath, "logs"), join20(newPath, "logs"))) {
44811
- result.migrated.push("logs");
44812
- }
44813
- const manager = new AssistantManager(newPath);
44814
- await manager.initialize();
44815
- const assistant = await manager.createAssistant({
44816
- name: config?.llm?.model ? `Assistant (${config.llm.model})` : "Default Assistant",
44817
- settings: config?.llm ? {
44818
- model: config.llm.model,
44819
- maxTokens: config.llm.maxTokens
44820
- } : undefined
44821
- });
44822
- const identityManager = manager.getIdentityManager(assistant.id);
44823
- await identityManager.initialize();
44824
- await identityManager.createIdentity({ name: "Default" });
44825
- if (await copyIfExists(join20(oldPath, "sessions"), join20(newPath, "assistants", assistant.id, "sessions"))) {
44826
- result.migrated.push("sessions");
44827
- }
44828
- await writeFile8(join20(newPath, "migration", MIGRATION_MARKER), JSON.stringify({ migratedAt: new Date().toISOString() }, null, 2));
44829
- const backupPath = `${oldPath}.backup`;
44830
- await rename(oldPath, backupPath);
44831
- result.backupPath = backupPath;
44832
- result.migrated.push("backup");
44833
- result.success = true;
44834
- } catch (error) {
44835
- result.errors.push(`Migration failed: ${error instanceof Error ? error.message : String(error)}`);
44836
- }
44837
- return result;
44838
- }
44839
45531
  // packages/core/src/index.ts
44840
45532
  init_anthropic();
44841
45533
  // packages/core/src/client.ts
44842
45534
  init_src();
44843
45535
 
44844
45536
  // packages/core/src/logger.ts
44845
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, appendFileSync, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
45537
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
44846
45538
  import { join as join21 } from "path";
44847
45539
  class Logger {
44848
45540
  logDir;
@@ -44856,8 +45548,8 @@ class Logger {
44856
45548
  this.logFile = join21(this.logDir, `${date}.log`);
44857
45549
  }
44858
45550
  ensureDir(dir) {
44859
- if (!existsSync13(dir)) {
44860
- mkdirSync7(dir, { recursive: true });
45551
+ if (!existsSync12(dir)) {
45552
+ mkdirSync8(dir, { recursive: true });
44861
45553
  }
44862
45554
  }
44863
45555
  write(level, message, data) {
@@ -44902,13 +45594,13 @@ class SessionStorage {
44902
45594
  this.sessionFile = join21(this.sessionsDir, `${sessionId}.json`);
44903
45595
  }
44904
45596
  ensureDir(dir) {
44905
- if (!existsSync13(dir)) {
44906
- mkdirSync7(dir, { recursive: true });
45597
+ if (!existsSync12(dir)) {
45598
+ mkdirSync8(dir, { recursive: true });
44907
45599
  }
44908
45600
  }
44909
45601
  save(data) {
44910
45602
  try {
44911
- writeFileSync6(this.sessionFile, JSON.stringify(data, null, 2));
45603
+ writeFileSync7(this.sessionFile, JSON.stringify(data, null, 2));
44912
45604
  } catch {}
44913
45605
  }
44914
45606
  getSessionId() {
@@ -44916,9 +45608,9 @@ class SessionStorage {
44916
45608
  }
44917
45609
  load() {
44918
45610
  try {
44919
- if (!existsSync13(this.sessionFile))
45611
+ if (!existsSync12(this.sessionFile))
44920
45612
  return null;
44921
- return JSON.parse(readFileSync7(this.sessionFile, "utf-8"));
45613
+ return JSON.parse(readFileSync8(this.sessionFile, "utf-8"));
44922
45614
  } catch {
44923
45615
  return null;
44924
45616
  }
@@ -44926,9 +45618,9 @@ class SessionStorage {
44926
45618
  static getActiveAssistantId() {
44927
45619
  try {
44928
45620
  const activePath = join21(getConfigDir(), "active.json");
44929
- if (!existsSync13(activePath))
45621
+ if (!existsSync12(activePath))
44930
45622
  return null;
44931
- const raw = readFileSync7(activePath, "utf-8");
45623
+ const raw = readFileSync8(activePath, "utf-8");
44932
45624
  const data = JSON.parse(raw);
44933
45625
  return data.id || null;
44934
45626
  } catch {
@@ -44940,7 +45632,7 @@ class SessionStorage {
44940
45632
  const resolvedId = assistantId ?? SessionStorage.getActiveAssistantId();
44941
45633
  if (resolvedId) {
44942
45634
  const assistantDir = join21(root, "assistants", resolvedId, "sessions");
44943
- if (existsSync13(assistantDir)) {
45635
+ if (existsSync12(assistantDir)) {
44944
45636
  return assistantDir;
44945
45637
  }
44946
45638
  }
@@ -44948,17 +45640,17 @@ class SessionStorage {
44948
45640
  }
44949
45641
  static listSessions(assistantId) {
44950
45642
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
44951
- if (!existsSync13(sessionsDir))
45643
+ if (!existsSync12(sessionsDir))
44952
45644
  return [];
44953
45645
  const sessions = [];
44954
- const files = readdirSync3(sessionsDir);
45646
+ const files = readdirSync4(sessionsDir);
44955
45647
  for (const file of files) {
44956
45648
  if (!file.endsWith(".json"))
44957
45649
  continue;
44958
45650
  try {
44959
45651
  const filePath = join21(sessionsDir, file);
44960
45652
  const stat = Bun.file(filePath);
44961
- const content = JSON.parse(readFileSync7(filePath, "utf-8"));
45653
+ const content = JSON.parse(readFileSync8(filePath, "utf-8"));
44962
45654
  sessions.push({
44963
45655
  id: file.replace(".json", ""),
44964
45656
  cwd: content.cwd,
@@ -44978,9 +45670,9 @@ class SessionStorage {
44978
45670
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
44979
45671
  const sessionFile = join21(sessionsDir, `${sessionId}.json`);
44980
45672
  try {
44981
- if (!existsSync13(sessionFile))
45673
+ if (!existsSync12(sessionFile))
44982
45674
  return null;
44983
- return JSON.parse(readFileSync7(sessionFile, "utf-8"));
45675
+ return JSON.parse(readFileSync8(sessionFile, "utf-8"));
44984
45676
  } catch {
44985
45677
  return null;
44986
45678
  }
@@ -45001,8 +45693,8 @@ function initAssistantsDir() {
45001
45693
  join21(baseDir, "migration")
45002
45694
  ];
45003
45695
  for (const dir of dirs) {
45004
- if (!existsSync13(dir)) {
45005
- mkdirSync7(dir, { recursive: true });
45696
+ if (!existsSync12(dir)) {
45697
+ mkdirSync8(dir, { recursive: true });
45006
45698
  }
45007
45699
  }
45008
45700
  }
@@ -45022,6 +45714,7 @@ class EmbeddedClient {
45022
45714
  assistantId = null;
45023
45715
  messageQueue = [];
45024
45716
  processingQueue = false;
45717
+ sawErrorChunk = false;
45025
45718
  constructor(cwd2, options) {
45026
45719
  initAssistantsDir();
45027
45720
  const sessionId = options?.sessionId || generateId();
@@ -45041,6 +45734,9 @@ class EmbeddedClient {
45041
45734
  for (const callback of this.chunkCallbacks) {
45042
45735
  callback(chunk);
45043
45736
  }
45737
+ if (chunk.type === "error") {
45738
+ this.sawErrorChunk = true;
45739
+ }
45044
45740
  if (chunk.type === "done" || chunk.type === "error") {
45045
45741
  queueMicrotask(() => {
45046
45742
  this.drainQueue();
@@ -45097,6 +45793,7 @@ class EmbeddedClient {
45097
45793
  }
45098
45794
  async processMessage(message) {
45099
45795
  this.logger.info("User message", { message });
45796
+ this.sawErrorChunk = false;
45100
45797
  try {
45101
45798
  await this.agent.process(message);
45102
45799
  const context = this.agent.getContext();
@@ -45113,6 +45810,9 @@ class EmbeddedClient {
45113
45810
  }
45114
45811
  this.saveSession();
45115
45812
  } catch (error) {
45813
+ if (this.sawErrorChunk) {
45814
+ return;
45815
+ }
45116
45816
  const err = error instanceof Error ? error : new Error(String(error));
45117
45817
  this.logger.error("Error processing message", { error: err.message });
45118
45818
  for (const callback of this.errorCallbacks) {
@@ -45267,6 +45967,9 @@ class SessionRegistry {
45267
45967
  const session = this.sessions.get(sessionId);
45268
45968
  if (session) {
45269
45969
  session.updatedAt = Date.now();
45970
+ if (chunk.type === "text" || chunk.type === "tool_use" || chunk.type === "tool_result") {
45971
+ session.isProcessing = true;
45972
+ }
45270
45973
  if (chunk.type === "done" || chunk.type === "error" || chunk.type === "exit") {
45271
45974
  session.isProcessing = false;
45272
45975
  }
@@ -45374,7 +46077,6 @@ init_errors();
45374
46077
  init_src();
45375
46078
 
45376
46079
  // packages/terminal/src/components/App.tsx
45377
- var import_react29 = __toESM(require_react(), 1);
45378
46080
  init_src();
45379
46081
 
45380
46082
  // packages/terminal/src/components/Input.tsx
@@ -46449,9 +47151,9 @@ function estimateToolPanelLines(toolCalls, toolResults, hasContent) {
46449
47151
  if (!toolCalls || toolCalls.length === 0) {
46450
47152
  return 0;
46451
47153
  }
46452
- const resultMap = new Set;
47154
+ const resultLines = new Map;
46453
47155
  for (const result of toolResults || []) {
46454
- resultMap.add(result.toolCallId);
47156
+ resultLines.set(result.toolCallId, estimateToolResultLines(result));
46455
47157
  }
46456
47158
  let lines = 3;
46457
47159
  if (hasContent) {
@@ -46459,26 +47161,53 @@ function estimateToolPanelLines(toolCalls, toolResults, hasContent) {
46459
47161
  }
46460
47162
  for (const call of toolCalls) {
46461
47163
  lines += 3;
46462
- if (resultMap.has(call.id)) {
46463
- lines += 1;
47164
+ const toolResultLines = resultLines.get(call.id);
47165
+ if (toolResultLines && toolResultLines > 0) {
47166
+ lines += toolResultLines;
46464
47167
  }
46465
47168
  }
46466
47169
  return lines;
46467
47170
  }
46468
- function estimateMessageLines(message) {
47171
+ function estimateToolResultLines(result, maxLines = 4) {
47172
+ const content = String(result.content || "");
47173
+ if (!content)
47174
+ return 1;
47175
+ const lines = content.replace(/\x1B\[[0-9;]*m/g, "").split(`
47176
+ `);
47177
+ if (lines.length <= maxLines)
47178
+ return Math.max(1, lines.length);
47179
+ return maxLines + 1;
47180
+ }
47181
+ function estimateMessageLines(message, maxWidth) {
46469
47182
  if (message.role === "system") {
46470
47183
  return 0;
46471
47184
  }
46472
47185
  const content = message.content ?? "";
46473
47186
  const contentLines = content.length > 0 ? content.split(`
46474
- `).length : 0;
46475
- const hasContent = contentLines > 0;
46476
- let lines = Math.max(1, contentLines);
47187
+ `) : [];
47188
+ const hasContent = contentLines.length > 0;
47189
+ const wrappedLines = contentLines.length > 0 ? countWrappedLines(contentLines, maxWidth) : 0;
47190
+ let lines = hasContent ? Math.max(1, wrappedLines) : 0;
46477
47191
  if (message.role === "assistant" && message.toolCalls?.length) {
46478
47192
  lines += estimateToolPanelLines(message.toolCalls, message.toolResults, hasContent);
46479
47193
  }
46480
47194
  return lines;
46481
47195
  }
47196
+ function countWrappedLines(lines, maxWidth) {
47197
+ if (!maxWidth || maxWidth <= 0) {
47198
+ return lines.length;
47199
+ }
47200
+ let total = 0;
47201
+ for (const line of lines) {
47202
+ const visible = stripAnsi3(line).length;
47203
+ const wrapped = Math.max(1, Math.ceil(visible / maxWidth));
47204
+ total += wrapped;
47205
+ }
47206
+ return total;
47207
+ }
47208
+ function stripAnsi3(text) {
47209
+ return text.replace(/\x1B\[[0-9;]*m/g, "");
47210
+ }
46482
47211
 
46483
47212
  // packages/terminal/src/components/Messages.tsx
46484
47213
  var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
@@ -46494,16 +47223,18 @@ function Messages4({
46494
47223
  queuedMessageIds
46495
47224
  }) {
46496
47225
  const [now2, setNow] = import_react24.useState(Date.now());
47226
+ const { columns } = use_stdout_default();
47227
+ const messageWidth = columns ? Math.max(10, columns - 2) : undefined;
46497
47228
  const combinedMessages = import_react24.useMemo(() => [...messages, ...streamingMessages], [messages, streamingMessages]);
46498
47229
  const lineSpans = import_react24.useMemo(() => {
46499
47230
  let cursor = 0;
46500
47231
  return combinedMessages.map((message, index) => {
46501
- const lines = estimateMessageLines(message);
47232
+ const lines = estimateMessageLines(message, messageWidth);
46502
47233
  const start = cursor;
46503
47234
  cursor += lines;
46504
47235
  return { message, index, start, end: cursor, lines };
46505
47236
  });
46506
- }, [combinedMessages]);
47237
+ }, [combinedMessages, messageWidth]);
46507
47238
  const totalLines = lineSpans.length > 0 ? lineSpans[lineSpans.length - 1].end : 0;
46508
47239
  const endLine = Math.max(0, totalLines - scrollOffsetLines);
46509
47240
  const startLine = Math.max(0, endLine - maxVisibleLines);
@@ -46511,6 +47242,7 @@ function Messages4({
46511
47242
  const historicalCount = messages.length;
46512
47243
  const visibleMessages = visibleSpans.filter((span) => span.index < historicalCount).map((span) => span.message);
46513
47244
  const visibleStreaming = visibleSpans.filter((span) => span.index >= historicalCount).map((span) => span.message);
47245
+ const showCurrentResponse = Boolean(currentResponse) && streamingMessages.length === 0;
46514
47246
  const groupedMessages = groupConsecutiveToolMessages(visibleMessages);
46515
47247
  const historicalItems = groupedMessages.map((group) => {
46516
47248
  if (group.type === "single") {
@@ -46636,7 +47368,7 @@ function Messages4({
46636
47368
  message,
46637
47369
  queuedMessageIds
46638
47370
  }, message.id, false, undefined, this)),
46639
- currentResponse && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
47371
+ showCurrentResponse && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
46640
47372
  marginY: 1,
46641
47373
  children: [
46642
47374
  /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
@@ -47597,7 +48329,7 @@ function SessionSelector({
47597
48329
 
47598
48330
  // packages/terminal/src/components/App.tsx
47599
48331
  var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
47600
- var SHOW_ERROR_CODES = process.env.ASSISTANTS_DEBUG === "1" || process.env.OLDPAL_DEBUG === "1";
48332
+ var SHOW_ERROR_CODES = process.env.ASSISTANTS_DEBUG === "1";
47601
48333
  function parseErrorMessage(error) {
47602
48334
  const lines = error.split(`
47603
48335
  `);
@@ -47642,15 +48374,30 @@ function wrapTextLines(text, wrapChars) {
47642
48374
  }
47643
48375
  return lines;
47644
48376
  }
47645
- function stripAnsi3(text) {
48377
+ function stripAnsi4(text) {
47646
48378
  return text.replace(/\x1B\[[0-9;]*m/g, "");
47647
48379
  }
48380
+ function truncateActivityContent(content, maxLines = 15, maxChars = 3000) {
48381
+ let output = content.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
48382
+ output = output.replace(/\t/g, " ");
48383
+ const lines = output.split(`
48384
+ `);
48385
+ if (lines.length > maxLines) {
48386
+ output = lines.slice(0, maxLines).join(`
48387
+ `) + `
48388
+ ... (${lines.length - maxLines} more lines)`;
48389
+ }
48390
+ if (output.length > maxChars) {
48391
+ output = output.slice(0, maxChars) + "...";
48392
+ }
48393
+ return output;
48394
+ }
47648
48395
  function chunkRenderedLines(lines, chunkLines) {
47649
48396
  const chunks = [];
47650
48397
  let current = [];
47651
48398
  let i = 0;
47652
- const isBoxStart = (line) => stripAnsi3(line).trimStart().startsWith("\u250C");
47653
- const isBoxEnd = (line) => stripAnsi3(line).trimStart().startsWith("\u2514");
48399
+ const isBoxStart = (line) => stripAnsi4(line).trimStart().startsWith("\u250C");
48400
+ const isBoxEnd = (line) => stripAnsi4(line).trimStart().startsWith("\u2514");
47654
48401
  while (i < lines.length) {
47655
48402
  const line = lines[i];
47656
48403
  if (isBoxStart(line)) {
@@ -47760,9 +48507,14 @@ function App2({ cwd: cwd2, version }) {
47760
48507
  const prevDisplayLineCountRef = import_react29.useRef(0);
47761
48508
  const skipNextDoneRef = import_react29.useRef(false);
47762
48509
  const isProcessingRef = import_react29.useRef(isProcessing);
48510
+ const processingStartTimeRef = import_react29.useRef(processingStartTime);
48511
+ const turnIdRef = import_react29.useRef(0);
47763
48512
  import_react29.useEffect(() => {
47764
48513
  isProcessingRef.current = isProcessing;
47765
48514
  }, [isProcessing]);
48515
+ import_react29.useEffect(() => {
48516
+ processingStartTimeRef.current = processingStartTime;
48517
+ }, [processingStartTime]);
47766
48518
  import_react29.useEffect(() => {
47767
48519
  if (isProcessing && !processingStartTime) {
47768
48520
  setProcessingStartTime(Date.now());
@@ -47815,8 +48567,8 @@ function App2({ cwd: cwd2, version }) {
47815
48567
 
47816
48568
  [error]` : "[error]";
47817
48569
  }
47818
- if (processingStartTime) {
47819
- const workedFor = formatElapsedDuration(Date.now() - processingStartTime);
48570
+ if (processingStartTimeRef.current) {
48571
+ const workedFor = formatElapsedDuration(Date.now() - processingStartTimeRef.current);
47820
48572
  content = content ? `${content}
47821
48573
 
47822
48574
  \u273B Worked for ${workedFor}` : `\u273B Worked for ${workedFor}`;
@@ -47833,7 +48585,7 @@ function App2({ cwd: cwd2, version }) {
47833
48585
  }
47834
48586
  ]);
47835
48587
  return true;
47836
- }, [buildFullResponse, processingStartTime]);
48588
+ }, [buildFullResponse]);
47837
48589
  const resetTurnState = import_react29.useCallback(() => {
47838
48590
  setCurrentResponse("");
47839
48591
  responseRef.current = "";
@@ -47902,9 +48654,14 @@ function App2({ cwd: cwd2, version }) {
47902
48654
  setAutoScroll(true);
47903
48655
  }, []);
47904
48656
  const handleChunk = import_react29.useCallback((chunk) => {
47905
- if (!isProcessingRef.current && (chunk.type === "text" || chunk.type === "tool_use" || chunk.type === "tool_result")) {
48657
+ const isStartChunk = chunk.type === "text" || chunk.type === "tool_use";
48658
+ const isTerminalChunk = chunk.type === "error" || chunk.type === "done";
48659
+ if (!isProcessingRef.current && (isStartChunk || isTerminalChunk)) {
47906
48660
  const active = registryRef.current.getActiveSession();
47907
48661
  if (active) {
48662
+ turnIdRef.current += 1;
48663
+ resetTurnState();
48664
+ setError(null);
47908
48665
  registryRef.current.setProcessing(active.id, true);
47909
48666
  setIsProcessing(true);
47910
48667
  isProcessingRef.current = true;
@@ -48004,8 +48761,11 @@ function App2({ cwd: cwd2, version }) {
48004
48761
  if (active) {
48005
48762
  registryRef.current.setProcessing(active.id, false);
48006
48763
  }
48764
+ const turnId = turnIdRef.current;
48007
48765
  queueMicrotask(() => {
48008
- resetTurnState();
48766
+ if (!isProcessingRef.current && turnIdRef.current === turnId) {
48767
+ resetTurnState();
48768
+ }
48009
48769
  });
48010
48770
  const activeSession2 = registry2.getActiveSession();
48011
48771
  if (activeSession2) {
@@ -48102,8 +48862,94 @@ function App2({ cwd: cwd2, version }) {
48102
48862
  const activeQueue = activeSessionId ? messageQueue.filter((msg) => msg.sessionId === activeSessionId) : [];
48103
48863
  const activeInline = activeSessionId ? inlinePending.filter((msg) => msg.sessionId === activeSessionId) : [];
48104
48864
  const queuedMessageIds = import_react29.useMemo(() => new Set(activeQueue.filter((msg) => msg.mode === "queued").map((msg) => msg.id)), [activeQueue]);
48865
+ const sessions = registry2.listSessions();
48866
+ const activeSession = registry2.getActiveSession();
48867
+ const sessionIndex = activeSessionId ? registry2.getSessionIndex(activeSessionId) : 0;
48868
+ const sessionCount = registry2.getSessionCount();
48869
+ const backgroundProcessingCount = registry2.getBackgroundProcessingSessions().length;
48870
+ const MAX_QUEUED_PREVIEW = 3;
48871
+ const truncateQueued = (text, maxLen = 80) => {
48872
+ if (text.length <= maxLen)
48873
+ return text;
48874
+ return text.slice(0, maxLen - 3) + "...";
48875
+ };
48876
+ const queuedCount = activeQueue.filter((msg) => msg.mode === "queued").length;
48877
+ const inlineCount = activeInline.length;
48878
+ const showWelcome = messages.length === 0 && !isProcessing;
48105
48879
  const wrapChars = columns ? Math.max(40, columns - 4) : MESSAGE_WRAP_CHARS;
48106
48880
  const renderWidth = columns ? Math.max(20, columns - 2) : undefined;
48881
+ const activityLogLineCount = import_react29.useMemo(() => {
48882
+ if (activityLog.length === 0)
48883
+ return 0;
48884
+ let lines = 0;
48885
+ for (const entry of activityLog) {
48886
+ if (entry.type === "text" && entry.content) {
48887
+ const rendered = renderMarkdown(entry.content, { maxWidth: renderWidth });
48888
+ const entryLines = wrapTextLines(stripAnsi4(rendered), wrapChars).length;
48889
+ lines += entryLines + 2;
48890
+ continue;
48891
+ }
48892
+ if (entry.type === "tool_call" && entry.toolCall) {
48893
+ lines += 4;
48894
+ continue;
48895
+ }
48896
+ if (entry.type === "tool_result" && entry.toolResult) {
48897
+ const truncated = truncateActivityContent(String(entry.toolResult.content ?? ""));
48898
+ const entryLines = wrapTextLines(truncated, wrapChars).length;
48899
+ lines += entryLines + 2;
48900
+ continue;
48901
+ }
48902
+ }
48903
+ return lines;
48904
+ }, [activityLog, renderWidth, wrapChars]);
48905
+ const reservedLines = import_react29.useMemo(() => {
48906
+ let lines = 0;
48907
+ if (showWelcome) {
48908
+ lines += 5;
48909
+ }
48910
+ if (backgroundProcessingCount > 0) {
48911
+ lines += 1;
48912
+ }
48913
+ if (scrollOffset > 0) {
48914
+ lines += 1;
48915
+ }
48916
+ if (activeQueue.length > 0 || inlineCount > 0) {
48917
+ const previewCount = Math.min(MAX_QUEUED_PREVIEW, activeQueue.length + inlineCount);
48918
+ const hasMore = activeQueue.length + inlineCount > MAX_QUEUED_PREVIEW;
48919
+ lines += 2;
48920
+ lines += 1;
48921
+ lines += previewCount;
48922
+ if (hasMore)
48923
+ lines += 1;
48924
+ }
48925
+ if (error) {
48926
+ const parsed = parseErrorMessage(error);
48927
+ const messageLines = parsed.message ? parsed.message.split(`
48928
+ `).length : 1;
48929
+ const suggestionLines = parsed.suggestion ? parsed.suggestion.split(`
48930
+ `).length : 0;
48931
+ lines += 2;
48932
+ lines += Math.max(1, messageLines) + suggestionLines;
48933
+ }
48934
+ if (isProcessing) {
48935
+ lines += 3;
48936
+ }
48937
+ lines += 1;
48938
+ lines += 1;
48939
+ if (isProcessing) {
48940
+ lines += 2;
48941
+ }
48942
+ lines += 2;
48943
+ return lines;
48944
+ }, [
48945
+ activeQueue.length,
48946
+ inlineCount,
48947
+ backgroundProcessingCount,
48948
+ scrollOffset,
48949
+ error,
48950
+ isProcessing,
48951
+ showWelcome
48952
+ ]);
48107
48953
  const displayMessages = import_react29.useMemo(() => buildDisplayMessages(messages, MESSAGE_CHUNK_LINES, wrapChars, { maxWidth: renderWidth }), [messages, wrapChars, renderWidth]);
48108
48954
  const streamingMessages = import_react29.useMemo(() => {
48109
48955
  if (!isProcessing || !currentResponse.trim())
@@ -48118,8 +48964,9 @@ function App2({ cwd: cwd2, version }) {
48118
48964
  }, [currentResponse, isProcessing, wrapChars, renderWidth]);
48119
48965
  const displayLineCount = import_react29.useMemo(() => {
48120
48966
  const combined = [...displayMessages, ...streamingMessages];
48121
- return combined.reduce((sum, msg) => sum + estimateMessageLines(msg), 0);
48122
- }, [displayMessages, streamingMessages]);
48967
+ return combined.reduce((sum, msg) => sum + estimateMessageLines(msg, renderWidth), 0);
48968
+ }, [displayMessages, streamingMessages, renderWidth]);
48969
+ const totalLineCount = displayLineCount + activityLogLineCount;
48123
48970
  import_react29.useEffect(() => {
48124
48971
  if (!isProcessing && activeQueue.length > 0) {
48125
48972
  processQueue();
@@ -48129,26 +48976,20 @@ function App2({ cwd: cwd2, version }) {
48129
48976
  if (autoScroll) {
48130
48977
  setScrollOffset(0);
48131
48978
  }
48132
- }, [displayLineCount, autoScroll]);
48979
+ }, [totalLineCount, autoScroll]);
48133
48980
  import_react29.useEffect(() => {
48134
48981
  const prevCount = prevDisplayLineCountRef.current;
48135
- if (!autoScroll && displayLineCount > prevCount) {
48136
- const delta = displayLineCount - prevCount;
48982
+ if (!autoScroll && totalLineCount > prevCount) {
48983
+ const delta = totalLineCount - prevCount;
48137
48984
  setScrollOffset((prev) => prev + delta);
48138
48985
  }
48139
- prevDisplayLineCountRef.current = displayLineCount;
48140
- }, [displayLineCount, autoScroll]);
48141
- const reservedLines = 8;
48142
- const maxVisibleLines = rows ? Math.max(6, rows - reservedLines) : 20;
48986
+ prevDisplayLineCountRef.current = totalLineCount;
48987
+ }, [totalLineCount, autoScroll]);
48988
+ const maxVisibleLines = rows ? Math.max(4, rows - reservedLines) : 20;
48143
48989
  import_react29.useEffect(() => {
48144
- const maxOffset = Math.max(0, displayLineCount - maxVisibleLines);
48990
+ const maxOffset = Math.max(0, totalLineCount - maxVisibleLines);
48145
48991
  setScrollOffset((prev) => Math.min(prev, maxOffset));
48146
- }, [displayLineCount, maxVisibleLines]);
48147
- const sessions = registry2.listSessions();
48148
- const activeSession = registry2.getActiveSession();
48149
- const sessionIndex = activeSessionId ? registry2.getSessionIndex(activeSessionId) : 0;
48150
- const sessionCount = registry2.getSessionCount();
48151
- const backgroundProcessingCount = registry2.getBackgroundProcessingSessions().length;
48992
+ }, [totalLineCount, maxVisibleLines]);
48152
48993
  const handleSessionSwitch = import_react29.useCallback(async (sessionId) => {
48153
48994
  setShowSessionSelector(false);
48154
48995
  if (sessionId === activeSessionId) {
@@ -48222,7 +49063,7 @@ function App2({ cwd: cwd2, version }) {
48222
49063
  }
48223
49064
  if (key.pageUp || key.shift && key.upArrow) {
48224
49065
  setScrollOffset((prev) => {
48225
- const maxOffset = Math.max(0, displayLineCount - maxVisibleLines);
49066
+ const maxOffset = Math.max(0, totalLineCount - maxVisibleLines);
48226
49067
  const step = Math.max(3, Math.floor(maxVisibleLines / 2));
48227
49068
  const newOffset = Math.min(prev + step, maxOffset);
48228
49069
  if (newOffset > 0)
@@ -48240,7 +49081,7 @@ function App2({ cwd: cwd2, version }) {
48240
49081
  });
48241
49082
  }
48242
49083
  if (key.ctrl && input === "u") {
48243
- const maxOffset = Math.max(0, displayLineCount - maxVisibleLines);
49084
+ const maxOffset = Math.max(0, totalLineCount - maxVisibleLines);
48244
49085
  setScrollOffset(maxOffset);
48245
49086
  setAutoScroll(false);
48246
49087
  }
@@ -48399,15 +49240,6 @@ function App2({ cwd: cwd2, version }) {
48399
49240
  return { toolCall: e.toolCall, result };
48400
49241
  });
48401
49242
  const isThinking = isProcessing && !currentResponse && !currentToolCall && toolCallEntries.length === 0;
48402
- const MAX_QUEUED_PREVIEW = 3;
48403
- const truncateQueued = (text, maxLen = 80) => {
48404
- if (text.length <= maxLen)
48405
- return text;
48406
- return text.slice(0, maxLen - 3) + "...";
48407
- };
48408
- const queuedCount = activeQueue.filter((msg) => msg.mode === "queued").length;
48409
- const inlineCount = activeInline.length;
48410
- const showWelcome = messages.length === 0 && !isProcessing;
48411
49243
  return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
48412
49244
  flexDirection: "column",
48413
49245
  padding: 1,
@@ -48714,7 +49546,7 @@ function formatStreamEvent(chunk) {
48714
49546
 
48715
49547
  // packages/terminal/src/index.tsx
48716
49548
  var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
48717
- var VERSION3 = "0.6.21";
49549
+ var VERSION3 = "0.6.22";
48718
49550
  process.env.ASSISTANTS_VERSION ??= VERSION3;
48719
49551
  function parseArgs(argv) {
48720
49552
  const args = argv.slice(2);
@@ -48841,7 +49673,6 @@ Interactive Mode:
48841
49673
  `);
48842
49674
  process.exit(0);
48843
49675
  }
48844
- migrateFromOldpal().catch(() => {});
48845
49676
  if (options.print !== null) {
48846
49677
  if (!options.print.trim()) {
48847
49678
  console.error("Error: Prompt is required with -p/--print flag");
@@ -48871,4 +49702,4 @@ if (options.print !== null) {
48871
49702
  });
48872
49703
  }
48873
49704
 
48874
- //# debugId=9F87F41E551C4D1964756E2164756E21
49705
+ //# debugId=A428BC3DC0A7465964756E2164756E21