@hasna/assistants 0.6.15 → 0.6.17

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,14 +27290,14 @@ var exports_anthropic = {};
27290
27290
  __export(exports_anthropic, {
27291
27291
  AnthropicClient: () => AnthropicClient
27292
27292
  });
27293
- import { readFileSync as readFileSync2, existsSync as existsSync5 } from "fs";
27293
+ import { readFileSync as readFileSync2, existsSync as existsSync7 } from "fs";
27294
27294
  import { homedir as homedir8 } from "os";
27295
27295
  import { join as join11 } 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
27299
  const secretsPath = join11(homeDir, ".secrets");
27300
- if (existsSync5(secretsPath)) {
27300
+ if (existsSync7(secretsPath)) {
27301
27301
  try {
27302
27302
  const content = readFileSync2(secretsPath, "utf-8");
27303
27303
  const match = content.match(/export\s+ANTHROPIC_API_KEY\s*=\s*["']?([^"'\n]+)["']?/);
@@ -36133,6 +36133,25 @@ import { dirname, join as join2 } from "path";
36133
36133
  // packages/core/src/config.ts
36134
36134
  import { join } from "path";
36135
36135
  import { homedir } from "os";
36136
+ var DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant running in the terminal.
36137
+
36138
+ ## Runtime Environment
36139
+ - Use **Bun** as the default runtime for JavaScript/TypeScript scripts
36140
+ - When creating scripts, use the shebang \`#!/usr/bin/env bun\`
36141
+ - Prefer Bun APIs (Bun.file, Bun.write, etc.) over Node.js equivalents when available
36142
+ - For package management, prefer \`bun install\` over \`npm install\`
36143
+
36144
+ ## Code Style
36145
+ - Write clean, readable code with meaningful variable names
36146
+ - Add comments only when the logic isn't self-evident
36147
+ - Prefer simple solutions over complex abstractions
36148
+ - Use TypeScript when type safety is beneficial
36149
+
36150
+ ## Communication
36151
+ - Be concise and direct in responses
36152
+ - Ask clarifying questions when requirements are ambiguous
36153
+ - Explain your reasoning when making architectural decisions
36154
+ `;
36136
36155
  var DEFAULT_CONFIG = {
36137
36156
  llm: {
36138
36157
  provider: "anthropic",
@@ -36361,7 +36380,7 @@ async function loadSystemPrompt(cwd2 = process.cwd()) {
36361
36380
  if (projectPrompt)
36362
36381
  prompts.push(projectPrompt);
36363
36382
  if (prompts.length === 0) {
36364
- return null;
36383
+ return DEFAULT_SYSTEM_PROMPT;
36365
36384
  }
36366
36385
  return prompts.join(`
36367
36386
 
@@ -36989,6 +37008,34 @@ function validateBashCommand(command) {
36989
37008
  function killProcess(proc) {
36990
37009
  proc.kill();
36991
37010
  }
37011
+ function stripQuotedSegments(input) {
37012
+ let result = "";
37013
+ let quote = null;
37014
+ let escaped = false;
37015
+ for (let i = 0;i < input.length; i += 1) {
37016
+ const char = input[i];
37017
+ if (quote) {
37018
+ if (quote === '"' && !escaped && char === "\\") {
37019
+ escaped = true;
37020
+ continue;
37021
+ }
37022
+ if (!escaped && char === quote) {
37023
+ quote = null;
37024
+ result += char;
37025
+ continue;
37026
+ }
37027
+ escaped = false;
37028
+ continue;
37029
+ }
37030
+ if (char === '"' || char === "'") {
37031
+ quote = char;
37032
+ result += char;
37033
+ continue;
37034
+ }
37035
+ result += char;
37036
+ }
37037
+ return result;
37038
+ }
36992
37039
 
36993
37040
  class BashTool {
36994
37041
  static tool = {
@@ -37109,6 +37156,7 @@ class BashTool {
37109
37156
  const cwd2 = input.cwd || process.cwd();
37110
37157
  const timeout = input.timeout || 30000;
37111
37158
  const commandForChecks = command.replace(/\s*2>&1\s*/g, " ");
37159
+ const commandSansQuotes = stripQuotedSegments(commandForChecks);
37112
37160
  const securityCheck = validateBashCommand(commandForChecks);
37113
37161
  if (!securityCheck.valid) {
37114
37162
  getSecurityLogger().log({
@@ -37131,7 +37179,7 @@ class BashTool {
37131
37179
  });
37132
37180
  }
37133
37181
  for (const pattern of this.BLOCKED_PATTERNS) {
37134
- if (pattern.test(commandForChecks)) {
37182
+ if (pattern.test(commandSansQuotes)) {
37135
37183
  getSecurityLogger().log({
37136
37184
  eventType: "blocked_command",
37137
37185
  severity: "high",
@@ -37220,6 +37268,7 @@ ${stderr || stdout}`.trim(), {
37220
37268
 
37221
37269
  // packages/core/src/tools/filesystem.ts
37222
37270
  import { join as join4, resolve as resolve3, dirname as dirname2, sep } from "path";
37271
+ import { existsSync as existsSync2 } from "fs";
37223
37272
  init_errors();
37224
37273
  var {Glob } = globalThis.Bun;
37225
37274
 
@@ -37308,6 +37357,10 @@ async function isPathSafe(targetPath, operation, options = {}) {
37308
37357
  // packages/core/src/tools/filesystem.ts
37309
37358
  var currentSessionId = "default";
37310
37359
  function getScriptsFolder(cwd2) {
37360
+ const legacyDir = join4(cwd2, ".oldpal");
37361
+ if (existsSync2(legacyDir)) {
37362
+ return join4(legacyDir, "scripts", currentSessionId);
37363
+ }
37311
37364
  return join4(getProjectConfigDir(cwd2), "scripts", currentSessionId);
37312
37365
  }
37313
37366
  function isInScriptsFolder(path2, cwd2) {
@@ -37443,13 +37496,13 @@ class FilesystemTools {
37443
37496
  };
37444
37497
  static writeTool = {
37445
37498
  name: "write",
37446
- description: "Write content to a file. RESTRICTED: Can only write to .assistants/scripts/{session}/ in the current project. Provide a filename and it will be saved under the scripts folder.",
37499
+ 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.",
37447
37500
  parameters: {
37448
37501
  type: "object",
37449
37502
  properties: {
37450
37503
  filename: {
37451
37504
  type: "string",
37452
- description: "The filename to write to (will be saved in .assistants/scripts)"
37505
+ description: "The filename to write to (saved in the project scripts folder)"
37453
37506
  },
37454
37507
  content: {
37455
37508
  type: "string",
@@ -37487,7 +37540,7 @@ class FilesystemTools {
37487
37540
  code: ErrorCodes.TOOL_PERMISSION_DENIED,
37488
37541
  recoverable: false,
37489
37542
  retryable: false,
37490
- suggestion: "Write only within the .assistants/scripts/ folder."
37543
+ suggestion: "Write only within the project scripts folder."
37491
37544
  });
37492
37545
  }
37493
37546
  try {
@@ -38230,7 +38283,7 @@ function isPrivateIPv4(octets) {
38230
38283
  // packages/core/src/tools/feedback.ts
38231
38284
  init_src();
38232
38285
  import { join as join5 } from "path";
38233
- import { mkdirSync, writeFileSync } from "fs";
38286
+ import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
38234
38287
  function normalizeTags(value) {
38235
38288
  if (Array.isArray(value)) {
38236
38289
  const tags = value.map((t) => String(t).trim()).filter(Boolean);
@@ -38242,8 +38295,16 @@ function normalizeTags(value) {
38242
38295
  }
38243
38296
  return;
38244
38297
  }
38245
- function saveFeedbackEntry(entry) {
38246
- const feedbackDir = join5(getConfigDir(), "feedback");
38298
+ function resolveFeedbackDir(cwd2) {
38299
+ const baseCwd = cwd2 && cwd2.trim().length > 0 ? cwd2 : process.cwd();
38300
+ const legacyDir = join5(baseCwd, ".oldpal");
38301
+ if (existsSync3(legacyDir)) {
38302
+ return join5(legacyDir, "feedback");
38303
+ }
38304
+ return join5(getConfigDir(), "feedback");
38305
+ }
38306
+ function saveFeedbackEntry(entry, cwd2) {
38307
+ const feedbackDir = resolveFeedbackDir(cwd2);
38247
38308
  mkdirSync(feedbackDir, { recursive: true });
38248
38309
  const path2 = join5(feedbackDir, `${entry.id}.json`);
38249
38310
  writeFileSync(path2, JSON.stringify(entry, null, 2));
@@ -38330,7 +38391,7 @@ class FeedbackTool {
38330
38391
  static executor = async (input) => {
38331
38392
  try {
38332
38393
  const entry = buildEntry(input, { source: input.source || "tool" });
38333
- const { path: path2 } = saveFeedbackEntry(entry);
38394
+ const { path: path2 } = saveFeedbackEntry(entry, typeof input.cwd === "string" ? input.cwd : undefined);
38334
38395
  return `Feedback saved locally.
38335
38396
  ID: ${entry.id}
38336
38397
  Path: ${path2}`;
@@ -38802,7 +38863,7 @@ class SchedulerTool {
38802
38863
 
38803
38864
  // packages/core/src/tools/image.ts
38804
38865
  init_src();
38805
- import { existsSync as existsSync2, writeFileSync as writeFileSync2, unlinkSync } from "fs";
38866
+ import { existsSync as existsSync4, writeFileSync as writeFileSync2, unlinkSync } from "fs";
38806
38867
  import { tmpdir } from "os";
38807
38868
  import { join as join7 } from "path";
38808
38869
  import { homedir as homedir4 } from "os";
@@ -38889,7 +38950,7 @@ class ImageDisplayTool {
38889
38950
  return `Error: Failed to fetch image: ${error instanceof Error ? error.message : String(error)}`;
38890
38951
  }
38891
38952
  }
38892
- if (!existsSync2(localPath)) {
38953
+ if (!existsSync4(localPath)) {
38893
38954
  return `Error: Image file not found: ${localPath}`;
38894
38955
  }
38895
38956
  try {
@@ -38914,7 +38975,7 @@ class ImageDisplayTool {
38914
38975
  } catch (error) {
38915
38976
  return `Error: ${error instanceof Error ? error.message : String(error)}`;
38916
38977
  } finally {
38917
- if (tempFile && existsSync2(tempFile)) {
38978
+ if (tempFile && existsSync4(tempFile)) {
38918
38979
  try {
38919
38980
  unlinkSync(tempFile);
38920
38981
  } catch {}
@@ -39418,7 +39479,7 @@ Respond with JSON only: {"allow": boolean, "reason": string}`;
39418
39479
  }
39419
39480
  }
39420
39481
  // packages/core/src/commands/loader.ts
39421
- import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
39482
+ import { existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
39422
39483
  import { join as join9, basename as basename2, extname } from "path";
39423
39484
  import { homedir as homedir6 } from "os";
39424
39485
 
@@ -39442,7 +39503,7 @@ class CommandLoader {
39442
39503
  await this.loadFromDirectory(legacyProjectDir, "project");
39443
39504
  }
39444
39505
  async loadFromDirectory(dir, source, prefix = "") {
39445
- if (!existsSync3(dir))
39506
+ if (!existsSync5(dir))
39446
39507
  return;
39447
39508
  const entries = readdirSync2(dir);
39448
39509
  for (const entry of entries) {
@@ -39673,7 +39734,7 @@ ${stderr}`;
39673
39734
  // packages/core/src/commands/builtin.ts
39674
39735
  import { join as join10 } from "path";
39675
39736
  import { homedir as homedir7, platform as platform2, release, arch } from "os";
39676
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
39737
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
39677
39738
  init_src();
39678
39739
  var VERSION = "0.6.14";
39679
39740
  function resolveAuthTimeout(resolve4) {
@@ -40608,7 +40669,7 @@ Format the summary as a brief bullet-point list. This summary will replace the c
40608
40669
  message += `**Config File Locations:**
40609
40670
  `;
40610
40671
  for (const path2 of configPaths) {
40611
- const exists = existsSync4(path2);
40672
+ const exists = existsSync6(path2);
40612
40673
  message += ` ${exists ? "\u2713" : "\u25CB"} ${path2}
40613
40674
  `;
40614
40675
  }
@@ -40652,7 +40713,7 @@ Please summarize the last interaction and suggest 2-3 next steps.
40652
40713
  - Ask a follow-up question if needed
40653
40714
  `;
40654
40715
  const examplePath = join10(commandsDir, "reflect.md");
40655
- if (!existsSync4(examplePath)) {
40716
+ if (!existsSync6(examplePath)) {
40656
40717
  writeFileSync3(examplePath, exampleCommand);
40657
40718
  }
40658
40719
  let message = `
@@ -41230,7 +41291,7 @@ No security events recorded.
41230
41291
  };
41231
41292
  let localPath = "";
41232
41293
  try {
41233
- const saved = saveFeedbackEntry(localEntry);
41294
+ const saved = saveFeedbackEntry(localEntry, context.cwd);
41234
41295
  localPath = saved.path;
41235
41296
  } catch {
41236
41297
  localPath = "";
@@ -41719,7 +41780,7 @@ function validateToolCalls(toolCalls, tools) {
41719
41780
  }
41720
41781
 
41721
41782
  // packages/core/src/voice/utils.ts
41722
- import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
41783
+ import { existsSync as existsSync8, readFileSync as readFileSync3 } from "fs";
41723
41784
  import { homedir as homedir9 } from "os";
41724
41785
  import { join as join12 } from "path";
41725
41786
  import { spawnSync } from "child_process";
@@ -41727,7 +41788,7 @@ function loadApiKeyFromSecrets2(key) {
41727
41788
  const envHome = process.env.HOME || process.env.USERPROFILE;
41728
41789
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir9();
41729
41790
  const secretsPath = join12(homeDir, ".secrets");
41730
- if (!existsSync6(secretsPath))
41791
+ if (!existsSync8(secretsPath))
41731
41792
  return;
41732
41793
  try {
41733
41794
  const content = readFileSync3(secretsPath, "utf-8");
@@ -42231,13 +42292,13 @@ class VoiceManager {
42231
42292
  }
42232
42293
  // packages/core/src/identity/assistant-manager.ts
42233
42294
  init_src();
42234
- import { existsSync as existsSync8 } from "fs";
42295
+ import { existsSync as existsSync10 } from "fs";
42235
42296
  import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile6, rm as rm2 } from "fs/promises";
42236
42297
  import { join as join17 } from "path";
42237
42298
 
42238
42299
  // packages/core/src/identity/identity-manager.ts
42239
42300
  init_src();
42240
- import { existsSync as existsSync7 } from "fs";
42301
+ import { existsSync as existsSync9 } from "fs";
42241
42302
  import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile5, rm } from "fs/promises";
42242
42303
  import { join as join16 } from "path";
42243
42304
  var DEFAULT_PROFILE = {
@@ -42387,7 +42448,7 @@ class IdentityManager {
42387
42448
  `);
42388
42449
  }
42389
42450
  async readIndex() {
42390
- if (!existsSync7(this.indexPath)) {
42451
+ if (!existsSync9(this.indexPath)) {
42391
42452
  return { identities: [] };
42392
42453
  }
42393
42454
  try {
@@ -42412,7 +42473,7 @@ class IdentityManager {
42412
42473
  }
42413
42474
  async readIdentity(id) {
42414
42475
  const path2 = this.identityPath(id);
42415
- if (!existsSync7(path2))
42476
+ if (!existsSync9(path2))
42416
42477
  return null;
42417
42478
  try {
42418
42479
  const raw = await readFile5(path2, "utf-8");
@@ -42426,7 +42487,7 @@ class IdentityManager {
42426
42487
  await writeFile5(this.identityPath(identity.id), JSON.stringify(identity, null, 2));
42427
42488
  }
42428
42489
  async readActive() {
42429
- if (!existsSync7(this.activePath))
42490
+ if (!existsSync9(this.activePath))
42430
42491
  return null;
42431
42492
  try {
42432
42493
  const raw = await readFile5(this.activePath, "utf-8");
@@ -42441,7 +42502,7 @@ class IdentityManager {
42441
42502
  await writeFile5(this.activePath, JSON.stringify({ id }, null, 2));
42442
42503
  }
42443
42504
  async loadAssistant() {
42444
- if (!existsSync7(this.assistantConfigPath()))
42505
+ if (!existsSync9(this.assistantConfigPath()))
42445
42506
  return null;
42446
42507
  try {
42447
42508
  const raw = await readFile5(this.assistantConfigPath(), "utf-8");
@@ -42559,7 +42620,7 @@ class AssistantManager {
42559
42620
  return new IdentityManager(assistantId, this.basePath);
42560
42621
  }
42561
42622
  async readIndex() {
42562
- if (!existsSync8(this.indexPath)) {
42623
+ if (!existsSync10(this.indexPath)) {
42563
42624
  return { assistants: [] };
42564
42625
  }
42565
42626
  try {
@@ -42584,7 +42645,7 @@ class AssistantManager {
42584
42645
  }
42585
42646
  async readAssistant(id) {
42586
42647
  const configPath = this.assistantConfigPath(id);
42587
- if (!existsSync8(configPath))
42648
+ if (!existsSync10(configPath))
42588
42649
  return null;
42589
42650
  try {
42590
42651
  const raw = await readFile6(configPath, "utf-8");
@@ -42599,7 +42660,7 @@ class AssistantManager {
42599
42660
  await writeFile6(this.assistantConfigPath(assistant.id), JSON.stringify(assistant, null, 2));
42600
42661
  }
42601
42662
  async readActive() {
42602
- if (!existsSync8(this.activePath))
42663
+ if (!existsSync10(this.activePath))
42603
42664
  return null;
42604
42665
  try {
42605
42666
  const raw = await readFile6(this.activePath, "utf-8");
@@ -42700,6 +42761,7 @@ class AgentLoop {
42700
42761
  await this.initializeIdentitySystem();
42701
42762
  const connectorNames = this.config.connectors && this.config.connectors.length > 0 && !this.config.connectors.includes("*") ? this.config.connectors : undefined;
42702
42763
  this.connectorBridge.fastDiscover(connectorNames);
42764
+ this.connectorBridge.registerAll(this.toolRegistry);
42703
42765
  this.connectorDiscovery = this.connectorBridge.discover(connectorNames).then(() => {
42704
42766
  this.connectorBridge.registerAll(this.toolRegistry);
42705
42767
  }).catch(() => {});
@@ -43657,7 +43719,7 @@ function parseErrorCode(message) {
43657
43719
  // packages/core/src/memory/sessions.ts
43658
43720
  init_src();
43659
43721
  // packages/core/src/migration/migrate-to-assistants.ts
43660
- import { existsSync as existsSync9 } from "fs";
43722
+ import { existsSync as existsSync11 } from "fs";
43661
43723
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile7, rename, cp } from "fs/promises";
43662
43724
  import { join as join19 } from "path";
43663
43725
  import { homedir as homedir10 } from "os";
@@ -43666,14 +43728,14 @@ async function ensureDir(path2) {
43666
43728
  await mkdir5(path2, { recursive: true });
43667
43729
  }
43668
43730
  async function copyIfExists(source, destination) {
43669
- if (!existsSync9(source))
43731
+ if (!existsSync11(source))
43670
43732
  return false;
43671
43733
  await ensureDir(join19(destination, ".."));
43672
43734
  await cp(source, destination, { recursive: true });
43673
43735
  return true;
43674
43736
  }
43675
43737
  async function readJson(path2) {
43676
- if (!existsSync9(path2))
43738
+ if (!existsSync11(path2))
43677
43739
  return null;
43678
43740
  try {
43679
43741
  const raw = await readFile7(path2, "utf-8");
@@ -43691,13 +43753,13 @@ async function migrateFromOldpal() {
43691
43753
  const home = homedir10();
43692
43754
  const oldPath = join19(home, ".oldpal");
43693
43755
  const newPath = join19(home, ".assistants");
43694
- if (!existsSync9(oldPath)) {
43756
+ if (!existsSync11(oldPath)) {
43695
43757
  result.success = true;
43696
43758
  return result;
43697
43759
  }
43698
- if (existsSync9(newPath)) {
43760
+ if (existsSync11(newPath)) {
43699
43761
  const marker = join19(newPath, "migration", MIGRATION_MARKER);
43700
- if (existsSync9(marker)) {
43762
+ if (existsSync11(marker)) {
43701
43763
  result.success = true;
43702
43764
  return result;
43703
43765
  }
@@ -43765,7 +43827,7 @@ init_anthropic();
43765
43827
  init_src();
43766
43828
 
43767
43829
  // packages/core/src/logger.ts
43768
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, appendFileSync, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
43830
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, appendFileSync, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
43769
43831
  import { join as join20 } from "path";
43770
43832
  class Logger {
43771
43833
  logDir;
@@ -43779,7 +43841,7 @@ class Logger {
43779
43841
  this.logFile = join20(this.logDir, `${date}.log`);
43780
43842
  }
43781
43843
  ensureDir(dir) {
43782
- if (!existsSync10(dir)) {
43844
+ if (!existsSync12(dir)) {
43783
43845
  mkdirSync6(dir, { recursive: true });
43784
43846
  }
43785
43847
  }
@@ -43825,7 +43887,7 @@ class SessionStorage {
43825
43887
  this.sessionFile = join20(this.sessionsDir, `${sessionId}.json`);
43826
43888
  }
43827
43889
  ensureDir(dir) {
43828
- if (!existsSync10(dir)) {
43890
+ if (!existsSync12(dir)) {
43829
43891
  mkdirSync6(dir, { recursive: true });
43830
43892
  }
43831
43893
  }
@@ -43839,7 +43901,7 @@ class SessionStorage {
43839
43901
  }
43840
43902
  load() {
43841
43903
  try {
43842
- if (!existsSync10(this.sessionFile))
43904
+ if (!existsSync12(this.sessionFile))
43843
43905
  return null;
43844
43906
  return JSON.parse(readFileSync6(this.sessionFile, "utf-8"));
43845
43907
  } catch {
@@ -43849,7 +43911,7 @@ class SessionStorage {
43849
43911
  static getActiveAssistantId() {
43850
43912
  try {
43851
43913
  const activePath = join20(getConfigDir(), "active.json");
43852
- if (!existsSync10(activePath))
43914
+ if (!existsSync12(activePath))
43853
43915
  return null;
43854
43916
  const raw = readFileSync6(activePath, "utf-8");
43855
43917
  const data = JSON.parse(raw);
@@ -43863,7 +43925,7 @@ class SessionStorage {
43863
43925
  const resolvedId = assistantId ?? SessionStorage.getActiveAssistantId();
43864
43926
  if (resolvedId) {
43865
43927
  const assistantDir = join20(root, "assistants", resolvedId, "sessions");
43866
- if (existsSync10(assistantDir)) {
43928
+ if (existsSync12(assistantDir)) {
43867
43929
  return assistantDir;
43868
43930
  }
43869
43931
  }
@@ -43871,7 +43933,7 @@ class SessionStorage {
43871
43933
  }
43872
43934
  static listSessions(assistantId) {
43873
43935
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
43874
- if (!existsSync10(sessionsDir))
43936
+ if (!existsSync12(sessionsDir))
43875
43937
  return [];
43876
43938
  const sessions = [];
43877
43939
  const files = readdirSync3(sessionsDir);
@@ -43901,7 +43963,7 @@ class SessionStorage {
43901
43963
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
43902
43964
  const sessionFile = join20(sessionsDir, `${sessionId}.json`);
43903
43965
  try {
43904
- if (!existsSync10(sessionFile))
43966
+ if (!existsSync12(sessionFile))
43905
43967
  return null;
43906
43968
  return JSON.parse(readFileSync6(sessionFile, "utf-8"));
43907
43969
  } catch {
@@ -43924,7 +43986,7 @@ function initAssistantsDir() {
43924
43986
  join20(baseDir, "migration")
43925
43987
  ];
43926
43988
  for (const dir of dirs) {
43927
- if (!existsSync10(dir)) {
43989
+ if (!existsSync12(dir)) {
43928
43990
  mkdirSync6(dir, { recursive: true });
43929
43991
  }
43930
43992
  }
@@ -43943,6 +44005,8 @@ class EmbeddedClient {
43943
44005
  startedAt;
43944
44006
  initialMessages = null;
43945
44007
  assistantId = null;
44008
+ messageQueue = [];
44009
+ processingQueue = false;
43946
44010
  constructor(cwd2, options) {
43947
44011
  initAssistantsDir();
43948
44012
  const sessionId = options?.sessionId || generateId();
@@ -44004,6 +44068,15 @@ class EmbeddedClient {
44004
44068
  if (!this.initialized) {
44005
44069
  await this.initialize();
44006
44070
  }
44071
+ if (this.agent.isProcessing()) {
44072
+ this.logger.info("Queuing message (agent busy)", { message, queueLength: this.messageQueue.length + 1 });
44073
+ this.messageQueue.push(message);
44074
+ return;
44075
+ }
44076
+ await this.processMessage(message);
44077
+ await this.drainQueue();
44078
+ }
44079
+ async processMessage(message) {
44007
44080
  this.logger.info("User message", { message });
44008
44081
  try {
44009
44082
  await this.agent.process(message);
@@ -44028,6 +44101,21 @@ class EmbeddedClient {
44028
44101
  }
44029
44102
  }
44030
44103
  }
44104
+ async drainQueue() {
44105
+ if (this.processingQueue)
44106
+ return;
44107
+ this.processingQueue = true;
44108
+ try {
44109
+ while (this.messageQueue.length > 0 && !this.agent.isProcessing()) {
44110
+ const nextMessage = this.messageQueue.shift();
44111
+ if (nextMessage) {
44112
+ await this.processMessage(nextMessage);
44113
+ }
44114
+ }
44115
+ } finally {
44116
+ this.processingQueue = false;
44117
+ }
44118
+ }
44031
44119
  saveSession() {
44032
44120
  this.session.save({
44033
44121
  messages: this.messages,
@@ -44109,6 +44197,13 @@ class EmbeddedClient {
44109
44197
  getMessages() {
44110
44198
  return [...this.messages];
44111
44199
  }
44200
+ getQueueLength() {
44201
+ return this.messageQueue.length;
44202
+ }
44203
+ clearQueue() {
44204
+ this.messageQueue = [];
44205
+ this.logger.info("Message queue cleared");
44206
+ }
44112
44207
  }
44113
44208
  // packages/core/src/sessions/registry.ts
44114
44209
  class SessionRegistry {
@@ -44684,7 +44779,13 @@ function extractBlockSections(text, blocks) {
44684
44779
  const indent = gridMatch[1] ?? "";
44685
44780
  const header = gridMatch[2] ?? "";
44686
44781
  const attrs = parseAttributes(header);
44687
- const columns = Math.max(1, Math.min(4, Number(attrs.columns || attrs.cols || 2)));
44782
+ const rawColumns = attrs.columns || attrs.cols;
44783
+ let columns = Number(rawColumns ?? 2);
44784
+ if (!Number.isFinite(columns) || columns <= 0) {
44785
+ output.push(createMalformedBlock(blocks, indent, "grid", `Invalid columns value "${rawColumns ?? ""}". Using 2.`));
44786
+ columns = 2;
44787
+ }
44788
+ columns = Math.max(1, Math.min(4, Math.round(columns)));
44688
44789
  const parsed = parseDelimitedBlock(lines, i, indent);
44689
44790
  if (!parsed) {
44690
44791
  output.push(line);
@@ -44750,24 +44851,38 @@ function extractCards(body) {
44750
44851
  let i = 0;
44751
44852
  while (i < lines.length) {
44752
44853
  const line = lines[i];
44753
- const match = line.match(/^\s*:::card(.*)$/);
44854
+ const match = line.match(/^(\s*):::card(.*)$/);
44754
44855
  if (!match) {
44755
44856
  i += 1;
44756
44857
  continue;
44757
44858
  }
44758
- const attrs = parseAttributes(match[1] ?? "");
44859
+ const indent = match[1] ?? "";
44860
+ const attrs = parseAttributes(match[2] ?? "");
44759
44861
  const type = String(attrs.type || "note");
44760
44862
  const title = attrs.title ? String(attrs.title) : undefined;
44761
44863
  const bodyLines = [];
44864
+ let closed = false;
44762
44865
  i += 1;
44763
- while (i < lines.length && lines[i].trim() !== ":::") {
44764
- bodyLines.push(lines[i]);
44866
+ while (i < lines.length) {
44867
+ const current = lines[i];
44868
+ if (current.trim() === ":::" && current.startsWith(indent)) {
44869
+ closed = true;
44870
+ i += 1;
44871
+ break;
44872
+ }
44873
+ bodyLines.push(current);
44765
44874
  i += 1;
44766
44875
  }
44767
- if (i < lines.length && lines[i].trim() === ":::") {
44768
- i += 1;
44876
+ if (!closed) {
44877
+ cards.push({
44878
+ type: "warning",
44879
+ title: "Malformed card",
44880
+ body: "Missing closing ::: for card."
44881
+ });
44882
+ break;
44769
44883
  }
44770
- cards.push({ type, title, body: bodyLines.join(`
44884
+ const stripped = stripIndent(bodyLines, indent);
44885
+ cards.push({ type, title, body: stripped.join(`
44771
44886
  `) });
44772
44887
  }
44773
44888
  return cards;
@@ -44804,12 +44919,12 @@ function stripIndent(lines, indent) {
44804
44919
  return lines;
44805
44920
  return lines.map((line) => line.startsWith(indent) ? line.slice(indent.length) : line);
44806
44921
  }
44807
- function createMalformedBlock(blocks, indent, kind2) {
44922
+ function createMalformedBlock(blocks, indent, kind2, message) {
44808
44923
  blocks.push({
44809
44924
  kind: "block",
44810
44925
  type: "warning",
44811
44926
  title: "Malformed block",
44812
- body: `Missing closing ::: for ${kind2}.`,
44927
+ body: message || `Missing closing ::: for ${kind2}.`,
44813
44928
  indent
44814
44929
  });
44815
44930
  return `@@BLOCKSECTION${blocks.length - 1}@@`;
@@ -44848,7 +44963,7 @@ function renderBlock(type, title, body, maxWidth, indent = "") {
44848
44963
  const content = parseMarkdown(body, { skipBlocks: true, maxWidth });
44849
44964
  const lines = content ? content.split(`
44850
44965
  `) : [];
44851
- return renderBox(header, lines, type, maxWidth, indent);
44966
+ return renderBox(header, lines, type, maxWidth, indent, Boolean(maxWidth));
44852
44967
  }
44853
44968
  function renderCard(card, maxWidth, indent = "", forceWidth = false) {
44854
44969
  const header = formatBlockHeader(card.type, card.title);
@@ -44860,11 +44975,17 @@ function renderCard(card, maxWidth, indent = "", forceWidth = false) {
44860
44975
  function renderCardGrid(cards, columns, maxWidth, indent = "") {
44861
44976
  const gap = 2;
44862
44977
  const totalWidth = maxWidth ? Math.max(20, maxWidth) : undefined;
44863
- const cardTotalWidth = totalWidth ? Math.max(20, Math.floor((totalWidth - gap * (columns - 1)) / columns)) : undefined;
44978
+ let effectiveColumns = columns;
44979
+ if (totalWidth) {
44980
+ const minCardWidth = 18;
44981
+ const maxColumns = Math.max(1, Math.floor((totalWidth + gap) / (minCardWidth + gap)));
44982
+ effectiveColumns = Math.min(columns, maxColumns);
44983
+ }
44984
+ const cardTotalWidth = totalWidth ? Math.max(8, Math.floor((totalWidth - gap * (effectiveColumns - 1)) / effectiveColumns)) : undefined;
44864
44985
  const cardLines = cards.map((card) => renderCard(card, cardTotalWidth, "", true));
44865
44986
  const rows = [];
44866
- for (let i = 0;i < cardLines.length; i += columns) {
44867
- rows.push(cardLines.slice(i, i + columns));
44987
+ for (let i = 0;i < cardLines.length; i += effectiveColumns) {
44988
+ rows.push(cardLines.slice(i, i + effectiveColumns));
44868
44989
  }
44869
44990
  const output = [];
44870
44991
  for (const row of rows) {
@@ -44907,8 +45028,8 @@ function getBlockIcon(type) {
44907
45028
  return "\u2139";
44908
45029
  }
44909
45030
  }
44910
- function renderBox(header, lines, type, maxWidth, indent = "") {
44911
- return renderBoxLines(header, lines, type, maxWidth, indent).join(`
45031
+ function renderBox(header, lines, type, maxWidth, indent = "", forceWidth = false) {
45032
+ return renderBoxLines(header, lines, type, maxWidth, indent, forceWidth).join(`
44912
45033
  `);
44913
45034
  }
44914
45035
  function renderBoxLines(header, lines, type, maxWidth, indent = "", forceWidth = false) {
@@ -45227,6 +45348,7 @@ var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
45227
45348
  function Messages4({
45228
45349
  messages,
45229
45350
  currentResponse,
45351
+ streamingMessages = [],
45230
45352
  currentToolCall,
45231
45353
  lastToolResult,
45232
45354
  activityLog = [],
@@ -45235,9 +45357,14 @@ function Messages4({
45235
45357
  queuedMessageIds
45236
45358
  }) {
45237
45359
  const [now2, setNow] = import_react24.useState(Date.now());
45238
- const endIndex = messages.length - scrollOffset;
45360
+ const combinedMessages = import_react24.useMemo(() => [...messages, ...streamingMessages], [messages, streamingMessages]);
45361
+ const endIndex = combinedMessages.length - scrollOffset;
45239
45362
  const startIndex = Math.max(0, endIndex - maxVisible);
45240
- const visibleMessages = messages.slice(startIndex, endIndex);
45363
+ const visibleCombined = combinedMessages.slice(startIndex, endIndex);
45364
+ const historicalCount = messages.length;
45365
+ const splitIndex = Math.max(0, Math.min(visibleCombined.length, historicalCount - startIndex));
45366
+ const visibleMessages = visibleCombined.slice(0, splitIndex);
45367
+ const visibleStreaming = visibleCombined.slice(splitIndex);
45241
45368
  const groupedMessages = groupConsecutiveToolMessages(visibleMessages);
45242
45369
  const historicalItems = groupedMessages.map((group) => {
45243
45370
  if (group.type === "single") {
@@ -45311,19 +45438,30 @@ function Messages4({
45311
45438
  const elapsedText = formatDuration(elapsedMs);
45312
45439
  return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
45313
45440
  marginY: 1,
45441
+ flexDirection: "column",
45314
45442
  children: [
45315
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
45316
- dimColor: true,
45317
- children: "\u2699 "
45318
- }, undefined, false, undefined, this),
45319
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
45320
- dimColor: true,
45443
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
45321
45444
  children: [
45322
- formatToolCall(entry.toolCall),
45323
- " \xB7 ",
45324
- elapsedText
45445
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
45446
+ dimColor: true,
45447
+ children: "\u2699 "
45448
+ }, undefined, false, undefined, this),
45449
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
45450
+ dimColor: true,
45451
+ children: formatToolCall(entry.toolCall)
45452
+ }, undefined, false, undefined, this)
45325
45453
  ]
45326
- }, undefined, true, undefined, this)
45454
+ }, undefined, true, undefined, this),
45455
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
45456
+ marginLeft: 2,
45457
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
45458
+ dimColor: true,
45459
+ children: [
45460
+ elapsedText,
45461
+ " elapsed"
45462
+ ]
45463
+ }, undefined, true, undefined, this)
45464
+ }, undefined, false, undefined, this)
45327
45465
  ]
45328
45466
  }, entry.id, true, undefined, this);
45329
45467
  }
@@ -45348,6 +45486,10 @@ function Messages4({
45348
45486
  }
45349
45487
  return null;
45350
45488
  }),
45489
+ visibleStreaming.map((message) => /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(MessageBubble, {
45490
+ message,
45491
+ queuedMessageIds
45492
+ }, message.id, false, undefined, this)),
45351
45493
  currentResponse && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
45352
45494
  marginY: 1,
45353
45495
  children: [
@@ -46472,6 +46614,10 @@ function App2({ cwd: cwd2, version }) {
46472
46614
  setError(chunk.error);
46473
46615
  setIsProcessing(false);
46474
46616
  isProcessingRef.current = false;
46617
+ const active = registryRef.current.getActiveSession();
46618
+ if (active) {
46619
+ registryRef.current.setProcessing(active.id, false);
46620
+ }
46475
46621
  } else if (chunk.type === "exit") {
46476
46622
  registry2.closeAll();
46477
46623
  exit();
@@ -46486,6 +46632,10 @@ function App2({ cwd: cwd2, version }) {
46486
46632
  }
46487
46633
  setIsProcessing(false);
46488
46634
  isProcessingRef.current = false;
46635
+ const active = registryRef.current.getActiveSession();
46636
+ if (active) {
46637
+ registryRef.current.setProcessing(active.id, false);
46638
+ }
46489
46639
  queueMicrotask(() => {
46490
46640
  resetTurnState();
46491
46641
  });
@@ -46511,6 +46661,10 @@ function App2({ cwd: cwd2, version }) {
46511
46661
  setError(err.message);
46512
46662
  setIsProcessing(false);
46513
46663
  isProcessingRef.current = false;
46664
+ const active = registryRef.current.getActiveSession();
46665
+ if (active) {
46666
+ registryRef.current.setProcessing(active.id, false);
46667
+ }
46514
46668
  });
46515
46669
  const session = await registry2.createSession(cwd2);
46516
46670
  setActiveSessionId(session.id);
@@ -46581,6 +46735,18 @@ function App2({ cwd: cwd2, version }) {
46581
46735
  const queuedMessageIds = import_react29.useMemo(() => new Set(activeQueue.map((msg) => msg.id)), [activeQueue]);
46582
46736
  const wrapChars = columns ? Math.max(40, columns - 4) : MESSAGE_WRAP_CHARS;
46583
46737
  const displayMessages = import_react29.useMemo(() => buildDisplayMessages(messages, MESSAGE_CHUNK_LINES, wrapChars), [messages, wrapChars]);
46738
+ const streamingMessages = import_react29.useMemo(() => {
46739
+ if (!isProcessing || !currentResponse.trim())
46740
+ return [];
46741
+ const streamingMessage = {
46742
+ id: "streaming-response",
46743
+ role: "assistant",
46744
+ content: currentResponse,
46745
+ timestamp: now()
46746
+ };
46747
+ return buildDisplayMessages([streamingMessage], MESSAGE_CHUNK_LINES, wrapChars);
46748
+ }, [currentResponse, isProcessing, wrapChars]);
46749
+ const displayCount = displayMessages.length + streamingMessages.length;
46584
46750
  import_react29.useEffect(() => {
46585
46751
  if (!isProcessing && activeQueue.length > 0) {
46586
46752
  processQueue();
@@ -46590,23 +46756,23 @@ function App2({ cwd: cwd2, version }) {
46590
46756
  if (autoScroll) {
46591
46757
  setScrollOffset(0);
46592
46758
  }
46593
- }, [displayMessages.length, autoScroll]);
46759
+ }, [displayCount, autoScroll]);
46594
46760
  import_react29.useEffect(() => {
46595
46761
  const prevCount = prevDisplayCountRef.current;
46596
- if (!autoScroll && displayMessages.length > prevCount) {
46597
- const delta = displayMessages.length - prevCount;
46762
+ if (!autoScroll && displayCount > prevCount) {
46763
+ const delta = displayCount - prevCount;
46598
46764
  setScrollOffset((prev) => prev + delta);
46599
46765
  }
46600
- prevDisplayCountRef.current = displayMessages.length;
46601
- }, [displayMessages.length, autoScroll]);
46766
+ prevDisplayCountRef.current = displayCount;
46767
+ }, [displayCount, autoScroll]);
46602
46768
  const reservedLines = 8;
46603
46769
  const baseMaxVisible = rows ? Math.max(3, rows - reservedLines) : 10;
46604
46770
  const toolCallsHeight = isProcessing ? Math.min(toolCallsRef.current.length, 5) : 0;
46605
46771
  const maxVisibleMessages = Math.max(3, baseMaxVisible - toolCallsHeight);
46606
46772
  import_react29.useEffect(() => {
46607
- const maxOffset = Math.max(0, displayMessages.length - maxVisibleMessages);
46773
+ const maxOffset = Math.max(0, displayCount - maxVisibleMessages);
46608
46774
  setScrollOffset((prev) => Math.min(prev, maxOffset));
46609
- }, [displayMessages.length, maxVisibleMessages]);
46775
+ }, [displayCount, maxVisibleMessages]);
46610
46776
  const sessions = registry2.listSessions();
46611
46777
  const activeSession = registry2.getActiveSession();
46612
46778
  const sessionIndex = activeSessionId ? registry2.getSessionIndex(activeSessionId) : 0;
@@ -46685,7 +46851,7 @@ function App2({ cwd: cwd2, version }) {
46685
46851
  }
46686
46852
  if (key.pageUp || key.shift && key.upArrow) {
46687
46853
  setScrollOffset((prev) => {
46688
- const maxOffset = Math.max(0, displayMessages.length - maxVisibleMessages);
46854
+ const maxOffset = Math.max(0, displayCount - maxVisibleMessages);
46689
46855
  const newOffset = Math.min(prev + 3, maxOffset);
46690
46856
  if (newOffset > 0)
46691
46857
  setAutoScroll(false);
@@ -46701,7 +46867,7 @@ function App2({ cwd: cwd2, version }) {
46701
46867
  });
46702
46868
  }
46703
46869
  if (key.ctrl && input === "u") {
46704
- const maxOffset = Math.max(0, displayMessages.length - maxVisibleMessages);
46870
+ const maxOffset = Math.max(0, displayCount - maxVisibleMessages);
46705
46871
  setScrollOffset(maxOffset);
46706
46872
  setAutoScroll(false);
46707
46873
  }
@@ -46760,6 +46926,7 @@ function App2({ cwd: cwd2, version }) {
46760
46926
  resetTurnState();
46761
46927
  setIsProcessing(false);
46762
46928
  isProcessingRef.current = false;
46929
+ registry2.setProcessing(activeSession.id, false);
46763
46930
  await new Promise((r) => setTimeout(r, 100));
46764
46931
  }
46765
46932
  const userMessage = {
@@ -46861,13 +47028,14 @@ function App2({ cwd: cwd2, version }) {
46861
47028
  children: [
46862
47029
  "\u2191 ",
46863
47030
  scrollOffset,
46864
- " more messages above (Shift+\u2193 or Ctrl+D to scroll down)"
47031
+ " more messages above (Shift+\u2193 or Page Down to scroll down)"
46865
47032
  ]
46866
47033
  }, undefined, true, undefined, this)
46867
47034
  }, undefined, false, undefined, this),
46868
47035
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Messages4, {
46869
47036
  messages: displayMessages,
46870
- currentResponse: isProcessing ? currentResponse : undefined,
47037
+ currentResponse: undefined,
47038
+ streamingMessages,
46871
47039
  currentToolCall: undefined,
46872
47040
  lastToolResult: undefined,
46873
47041
  activityLog: isProcessing ? activityLog : [],
@@ -47166,7 +47334,7 @@ function formatStreamEvent(chunk) {
47166
47334
 
47167
47335
  // packages/terminal/src/index.tsx
47168
47336
  var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
47169
- var VERSION3 = "0.6.15";
47337
+ var VERSION3 = "0.6.17";
47170
47338
  function parseArgs(argv) {
47171
47339
  const args = argv.slice(2);
47172
47340
  const options = {
@@ -47329,4 +47497,4 @@ if (options.print !== null) {
47329
47497
  });
47330
47498
  }
47331
47499
 
47332
- //# debugId=E5501B17CB0867B264756E2164756E21
47500
+ //# debugId=36B3EC68850C35ED64756E2164756E21