@hasna/assistants 0.6.32 → 0.6.35

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
@@ -17712,14 +17712,13 @@ function sleep(ms) {
17712
17712
  return new Promise((resolve) => setTimeout(resolve, ms));
17713
17713
  }
17714
17714
  function parseFrontmatter(content) {
17715
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
17715
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
17716
17716
  if (!match) {
17717
17717
  return { frontmatter: {}, content };
17718
17718
  }
17719
17719
  const [, yamlContent, markdownContent] = match;
17720
17720
  const frontmatter = {};
17721
- const lines = yamlContent.split(`
17722
- `);
17721
+ const lines = yamlContent.split(/\r?\n/);
17723
17722
  for (const line of lines) {
17724
17723
  const colonIndex = line.indexOf(":");
17725
17724
  if (colonIndex === -1)
@@ -28327,14 +28326,14 @@ var exports_anthropic = {};
28327
28326
  __export(exports_anthropic, {
28328
28327
  AnthropicClient: () => AnthropicClient
28329
28328
  });
28330
- import { readFileSync as readFileSync4, existsSync as existsSync8 } from "fs";
28331
- import { homedir as homedir8 } from "os";
28332
- import { join as join13 } from "path";
28329
+ import { readFileSync as readFileSync4, existsSync as existsSync9 } from "fs";
28330
+ import { homedir as homedir11 } from "os";
28331
+ import { join as join14 } from "path";
28333
28332
  function loadApiKeyFromSecrets() {
28334
28333
  const envHome = process.env.HOME || process.env.USERPROFILE;
28335
- const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir8();
28336
- const secretsPath = join13(homeDir, ".secrets");
28337
- if (existsSync8(secretsPath)) {
28334
+ const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir11();
28335
+ const secretsPath = join14(homeDir, ".secrets");
28336
+ if (existsSync9(secretsPath)) {
28338
28337
  try {
28339
28338
  const content = readFileSync4(secretsPath, "utf-8");
28340
28339
  const match = content.match(/export\s+ANTHROPIC_API_KEY\s*=\s*["']?([^"'\n]+)["']?/);
@@ -36578,7 +36577,7 @@ var import_react29 = __toESM(require_react(), 1);
36578
36577
 
36579
36578
  // packages/core/src/agent/loop.ts
36580
36579
  init_src();
36581
- import { join as join20 } from "path";
36580
+ import { join as join21 } from "path";
36582
36581
 
36583
36582
  // packages/core/src/agent/context.ts
36584
36583
  init_src();
@@ -36650,6 +36649,9 @@ class AgentContext {
36650
36649
  try {
36651
36650
  const parsed = JSON.parse(content);
36652
36651
  if (parsed && parsed.__pdf_attachment__ === true) {
36652
+ if (!parsed.data || typeof parsed.data !== "string") {
36653
+ return null;
36654
+ }
36653
36655
  return {
36654
36656
  type: "pdf",
36655
36657
  source: {
@@ -36701,8 +36703,11 @@ class AgentContext {
36701
36703
  if (this.messages.length <= this.maxMessages) {
36702
36704
  return;
36703
36705
  }
36704
- const systemMessages = this.messages.filter((m) => m.role === "system");
36706
+ let systemMessages = this.messages.filter((m) => m.role === "system");
36705
36707
  const nonSystemMessages = this.messages.filter((m) => m.role !== "system");
36708
+ if (systemMessages.length > this.maxMessages) {
36709
+ systemMessages = systemMessages.slice(-this.maxMessages);
36710
+ }
36706
36711
  const targetCount = Math.max(0, this.maxMessages - systemMessages.length);
36707
36712
  let recentMessages = targetCount > 0 ? nonSystemMessages.slice(-targetCount) : [];
36708
36713
  while (recentMessages.length > 0 && recentMessages[0].toolResults) {
@@ -36871,7 +36876,33 @@ class ContextManager {
36871
36876
  summarizedCount: 0
36872
36877
  };
36873
36878
  }
36874
- const summary = await this.summarizer.summarize(toSummarize);
36879
+ let summary = "";
36880
+ try {
36881
+ summary = await this.summarizer.summarize(toSummarize);
36882
+ } catch {
36883
+ const tokens = this.tokenCounter.countMessages(messages);
36884
+ this.state.totalTokens = tokens;
36885
+ this.state.messageCount = messages.length;
36886
+ return {
36887
+ messages,
36888
+ summarized: false,
36889
+ tokensBefore: tokens,
36890
+ tokensAfter: tokens,
36891
+ summarizedCount: 0
36892
+ };
36893
+ }
36894
+ if (!summary.trim()) {
36895
+ const tokens = this.tokenCounter.countMessages(messages);
36896
+ this.state.totalTokens = tokens;
36897
+ this.state.messageCount = messages.length;
36898
+ return {
36899
+ messages,
36900
+ summarized: false,
36901
+ tokensBefore: tokens,
36902
+ tokensAfter: tokens,
36903
+ summarizedCount: 0
36904
+ };
36905
+ }
36875
36906
  const summaryMessage = {
36876
36907
  id: generateId(),
36877
36908
  role: "system",
@@ -37715,7 +37746,7 @@ function isErrorResult(result) {
37715
37746
  // packages/core/src/tools/connector.ts
37716
37747
  init_errors();
37717
37748
  import { homedir as homedir2 } from "os";
37718
- import { join as join3, delimiter, dirname as dirname2 } from "path";
37749
+ import { join as join3, delimiter, dirname as dirname2, extname } from "path";
37719
37750
  import { readdirSync, statSync, existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync as readFileSync2 } from "fs";
37720
37751
  function resolveTimeout(resolve) {
37721
37752
  resolve({ exitCode: 1 });
@@ -37803,7 +37834,11 @@ class ConnectorBridge {
37803
37834
  if (!stats.isFile()) {
37804
37835
  continue;
37805
37836
  }
37806
- const name = file.replace("connect-", "");
37837
+ const ext = extname(file);
37838
+ let name = file.replace("connect-", "");
37839
+ if (ext && [".exe", ".cmd", ".bat", ".ps1"].includes(ext.toLowerCase())) {
37840
+ name = name.slice(0, -ext.length);
37841
+ }
37807
37842
  if (name && !name.includes(".") && name.length > 1) {
37808
37843
  connectorNames.add(name);
37809
37844
  }
@@ -37857,6 +37892,7 @@ class ConnectorBridge {
37857
37892
  async discover(connectorNames) {
37858
37893
  const names = connectorNames && connectorNames.length > 0 ? connectorNames : this.autoDiscoverConnectorNames();
37859
37894
  if (names.length === 0) {
37895
+ this.connectors = new Map;
37860
37896
  return [];
37861
37897
  }
37862
37898
  const uncached = [];
@@ -37900,16 +37936,17 @@ class ConnectorBridge {
37900
37936
  };
37901
37937
  }
37902
37938
  async populateCache(name) {
37903
- const cli = `connect-${name}`;
37939
+ const cli = await this.resolveConnectorCli(name);
37904
37940
  try {
37905
37941
  let timeoutId = null;
37906
37942
  const timeoutPromise = new Promise((resolve) => {
37907
37943
  timeoutId = setTimeout(resolveTimeout, 500, resolve);
37908
37944
  });
37909
- const result = await Promise.race([
37910
- Bun.$`which ${cli}`.quiet().nothrow(),
37911
- timeoutPromise
37912
- ]);
37945
+ if (!cli) {
37946
+ ConnectorBridge.cache.set(name, null);
37947
+ return;
37948
+ }
37949
+ const result = await Promise.race([Bun.$`which ${cli}`.quiet().nothrow(), timeoutPromise]);
37913
37950
  if (timeoutId) {
37914
37951
  clearTimeout(timeoutId);
37915
37952
  }
@@ -37923,6 +37960,30 @@ class ConnectorBridge {
37923
37960
  ConnectorBridge.cache.set(name, null);
37924
37961
  }
37925
37962
  }
37963
+ async resolveConnectorCli(name) {
37964
+ const base2 = `connect-${name}`;
37965
+ const candidates = [base2];
37966
+ const extCandidates = [".exe", ".cmd", ".bat", ".ps1"];
37967
+ for (const ext of extCandidates) {
37968
+ candidates.push(`${base2}${ext}`);
37969
+ }
37970
+ for (const candidate of candidates) {
37971
+ try {
37972
+ let timeoutId = null;
37973
+ const timeoutPromise = new Promise((resolve) => {
37974
+ timeoutId = setTimeout(resolveTimeout, 500, resolve);
37975
+ });
37976
+ const result = await Promise.race([Bun.$`which ${candidate}`.quiet().nothrow(), timeoutPromise]);
37977
+ if (timeoutId) {
37978
+ clearTimeout(timeoutId);
37979
+ }
37980
+ if (result.exitCode === 0) {
37981
+ return candidate;
37982
+ }
37983
+ } catch {}
37984
+ }
37985
+ return null;
37986
+ }
37926
37987
  async discoverConnector(name, cli) {
37927
37988
  try {
37928
37989
  const helpResult = await Bun.$`${cli} --help`.quiet();
@@ -38462,14 +38523,16 @@ ${stderr || stdout}`.trim(), {
38462
38523
 
38463
38524
  // packages/core/src/tools/filesystem.ts
38464
38525
  import { join as join4, resolve as resolve3, dirname as dirname3, sep } from "path";
38526
+ import { homedir as homedir5 } from "os";
38465
38527
  init_errors();
38466
38528
  var {Glob } = globalThis.Bun;
38467
38529
 
38468
38530
  // packages/core/src/validation/paths.ts
38469
- import { resolve, normalize } from "path";
38531
+ import { resolve, normalize, relative, isAbsolute } from "path";
38532
+ import { homedir as homedir3 } from "os";
38470
38533
  import { lstat, realpath } from "fs/promises";
38471
38534
  async function validatePath(inputPath, options = {}) {
38472
- const normalized = normalize(inputPath);
38535
+ const normalized = normalize(expandHome(inputPath));
38473
38536
  const resolved = resolve(normalized);
38474
38537
  const allowedPaths = options.allowedPaths?.map((p) => resolve(p));
38475
38538
  const blockedPaths = options.blockedPaths?.map((p) => resolve(p));
@@ -38490,7 +38553,7 @@ async function validatePath(inputPath, options = {}) {
38490
38553
  }
38491
38554
  } catch {}
38492
38555
  }
38493
- if (blockedPaths && blockedPaths.some((blocked) => resolved.startsWith(blocked))) {
38556
+ if (blockedPaths && blockedPaths.some((blocked) => isWithinPath(resolved, blocked))) {
38494
38557
  return { valid: false, resolved, error: "Path is in blocked list" };
38495
38558
  }
38496
38559
  if (!isWithinAllowed(resolved, allowedPaths)) {
@@ -38498,15 +38561,35 @@ async function validatePath(inputPath, options = {}) {
38498
38561
  }
38499
38562
  return { valid: true, resolved };
38500
38563
  }
38564
+ function expandHome(value) {
38565
+ if (value === "~")
38566
+ return homedir3();
38567
+ if (value.startsWith("~/")) {
38568
+ return resolve(homedir3(), value.slice(2));
38569
+ }
38570
+ return value;
38571
+ }
38501
38572
  function isWithinAllowed(path, allowed) {
38502
38573
  if (!allowed || allowed.length === 0)
38503
38574
  return true;
38504
38575
  return allowed.some((allowedPath) => path === allowedPath || path.startsWith(`${allowedPath}/`));
38505
38576
  }
38577
+ function isWithinPath(target, base2) {
38578
+ if (target === base2)
38579
+ return true;
38580
+ const rel = relative(base2, target);
38581
+ if (!rel || rel === "")
38582
+ return true;
38583
+ if (rel.startsWith(".."))
38584
+ return false;
38585
+ if (isAbsolute(rel))
38586
+ return false;
38587
+ return true;
38588
+ }
38506
38589
 
38507
38590
  // packages/core/src/security/path-validator.ts
38508
- import { homedir as homedir3 } from "os";
38509
- import { resolve as resolve2 } from "path";
38591
+ import { homedir as homedir4 } from "os";
38592
+ import { resolve as resolve2, relative as relative2, isAbsolute as isAbsolute2 } from "path";
38510
38593
  import { lstat as lstat2, realpath as realpath2 } from "fs/promises";
38511
38594
  var PROTECTED_PATHS = [
38512
38595
  "/etc/passwd",
@@ -38518,17 +38601,16 @@ var PROTECTED_PATHS = [
38518
38601
  "~/.kube/config"
38519
38602
  ];
38520
38603
  async function isPathSafe(targetPath, operation, options = {}) {
38521
- const resolved = resolve2(targetPath);
38522
- const home = homedir3();
38604
+ const expandedTarget = expandHome2(targetPath);
38605
+ const resolved = resolve2(expandedTarget);
38606
+ const home = homedir4();
38523
38607
  for (const protectedPath of PROTECTED_PATHS) {
38524
38608
  const expanded = protectedPath.replace("~", home);
38525
- if (resolved.startsWith(expanded)) {
38526
- if (operation === "write" || operation === "delete") {
38527
- return {
38528
- safe: false,
38529
- reason: `Cannot ${operation} protected path: ${protectedPath}`
38530
- };
38531
- }
38609
+ if (isWithinPath2(resolved, expanded)) {
38610
+ return {
38611
+ safe: false,
38612
+ reason: `Cannot ${operation} protected path: ${protectedPath}`
38613
+ };
38532
38614
  }
38533
38615
  }
38534
38616
  try {
@@ -38546,6 +38628,24 @@ async function isPathSafe(targetPath, operation, options = {}) {
38546
38628
  } catch {}
38547
38629
  return { safe: true };
38548
38630
  }
38631
+ function expandHome2(value) {
38632
+ if (value === "~")
38633
+ return homedir4();
38634
+ if (value.startsWith("~/")) {
38635
+ return resolve2(homedir4(), value.slice(2));
38636
+ }
38637
+ return value;
38638
+ }
38639
+ function isWithinPath2(target, base2) {
38640
+ const rel = relative2(base2, target);
38641
+ if (rel === "")
38642
+ return true;
38643
+ if (rel.startsWith(".."))
38644
+ return false;
38645
+ if (isAbsolute2(rel))
38646
+ return false;
38647
+ return true;
38648
+ }
38549
38649
 
38550
38650
  // packages/core/src/tools/filesystem.ts
38551
38651
  var currentSessionId = "default";
@@ -38562,6 +38662,17 @@ function isInScriptsFolder(path, cwd2, sessionId) {
38562
38662
  }
38563
38663
 
38564
38664
  class FilesystemTools {
38665
+ static resolveInputPath(baseCwd, inputPath) {
38666
+ const envHome = process.env.HOME || process.env.USERPROFILE;
38667
+ const home = envHome && envHome.trim().length > 0 ? envHome : homedir5();
38668
+ if (inputPath === "~") {
38669
+ return home;
38670
+ }
38671
+ if (inputPath.startsWith("~/")) {
38672
+ return resolve3(home, inputPath.slice(2));
38673
+ }
38674
+ return resolve3(baseCwd, inputPath);
38675
+ }
38565
38676
  static registerAll(registry, sessionId) {
38566
38677
  if (sessionId) {
38567
38678
  currentSessionId = sessionId;
@@ -38603,9 +38714,21 @@ class FilesystemTools {
38603
38714
  };
38604
38715
  static readExecutor = async (input) => {
38605
38716
  const baseCwd = input.cwd || process.cwd();
38606
- const path = resolve3(baseCwd, input.path);
38717
+ const rawPath = String(input.path || "").trim();
38718
+ if (!rawPath) {
38719
+ throw new ToolExecutionError("File path is required", {
38720
+ toolName: "read",
38721
+ toolInput: input,
38722
+ code: ErrorCodes.VALIDATION_OUT_OF_RANGE,
38723
+ recoverable: false,
38724
+ retryable: false,
38725
+ suggestion: "Provide a valid file path."
38726
+ });
38727
+ }
38728
+ const path = FilesystemTools.resolveInputPath(baseCwd, rawPath);
38607
38729
  const offset = (input.offset || 1) - 1;
38608
- const limit = input.limit;
38730
+ const limitRaw = input.limit;
38731
+ const limit = typeof limitRaw === "number" && limitRaw > 0 ? Math.floor(limitRaw) : undefined;
38609
38732
  try {
38610
38733
  const safety = await isPathSafe(path, "read", { cwd: baseCwd });
38611
38734
  if (!safety.safe) {
@@ -38661,7 +38784,10 @@ class FilesystemTools {
38661
38784
  });
38662
38785
  }
38663
38786
  const content = await file.text();
38664
- const lines = content.split(`
38787
+ const normalized = content.replace(/\r\n/g, `
38788
+ `).replace(/\r/g, `
38789
+ `);
38790
+ const lines = normalized.split(`
38665
38791
  `);
38666
38792
  const startLine = Math.max(0, offset);
38667
38793
  const endLine = limit ? startLine + limit : lines.length;
@@ -38711,6 +38837,16 @@ class FilesystemTools {
38711
38837
  const filename = input.filename || input.path;
38712
38838
  const content = input.content;
38713
38839
  const baseCwd = input.cwd || process.cwd();
38840
+ if (typeof content !== "string") {
38841
+ throw new ToolExecutionError("Content must be a string", {
38842
+ toolName: "write",
38843
+ toolInput: input,
38844
+ code: ErrorCodes.VALIDATION_OUT_OF_RANGE,
38845
+ recoverable: false,
38846
+ retryable: false,
38847
+ suggestion: "Provide string content to write."
38848
+ });
38849
+ }
38714
38850
  const scriptsFolder = getScriptsFolder(baseCwd, input.sessionId);
38715
38851
  if (!filename || !filename.trim()) {
38716
38852
  throw new ToolExecutionError("Filename is required", {
@@ -38805,13 +38941,55 @@ class FilesystemTools {
38805
38941
  }
38806
38942
  };
38807
38943
  static globExecutor = async (input) => {
38808
- const pattern = input.pattern;
38944
+ const pattern = String(input.pattern || "").trim();
38809
38945
  const baseCwd = input.cwd || process.cwd();
38810
- const searchPath = resolve3(baseCwd, input.path || ".");
38946
+ const searchPath = FilesystemTools.resolveInputPath(baseCwd, input.path || ".");
38811
38947
  try {
38948
+ if (!pattern) {
38949
+ throw new ToolExecutionError("Pattern is required", {
38950
+ toolName: "glob",
38951
+ toolInput: input,
38952
+ code: ErrorCodes.VALIDATION_OUT_OF_RANGE,
38953
+ recoverable: false,
38954
+ retryable: false
38955
+ });
38956
+ }
38957
+ const safety = await isPathSafe(searchPath, "read", { cwd: baseCwd });
38958
+ if (!safety.safe) {
38959
+ getSecurityLogger().log({
38960
+ eventType: "path_violation",
38961
+ severity: "high",
38962
+ details: {
38963
+ tool: "glob",
38964
+ path: searchPath,
38965
+ reason: safety.reason || "Blocked path"
38966
+ },
38967
+ sessionId: input.sessionId || "unknown"
38968
+ });
38969
+ throw new ToolExecutionError(safety.reason || "Blocked path", {
38970
+ toolName: "glob",
38971
+ toolInput: input,
38972
+ code: ErrorCodes.TOOL_PERMISSION_DENIED,
38973
+ recoverable: false,
38974
+ retryable: false
38975
+ });
38976
+ }
38977
+ const validated = await validatePath(searchPath, {
38978
+ allowSymlinks: true,
38979
+ allowedPaths: [baseCwd, searchPath]
38980
+ });
38981
+ if (!validated.valid) {
38982
+ throw new ToolExecutionError(validated.error || "Invalid path", {
38983
+ toolName: "glob",
38984
+ toolInput: input,
38985
+ code: ErrorCodes.VALIDATION_OUT_OF_RANGE,
38986
+ recoverable: false,
38987
+ retryable: false
38988
+ });
38989
+ }
38812
38990
  const glob = new Glob(pattern);
38813
38991
  const matches = [];
38814
- for await (const file of glob.scan({ cwd: searchPath })) {
38992
+ for await (const file of glob.scan({ cwd: validated.resolved })) {
38815
38993
  matches.push(file);
38816
38994
  if (matches.length >= 1000)
38817
38995
  break;
@@ -38864,18 +39042,60 @@ class FilesystemTools {
38864
39042
  }
38865
39043
  };
38866
39044
  static grepExecutor = async (input) => {
38867
- const pattern = input.pattern;
39045
+ const pattern = String(input.pattern || "").trim();
38868
39046
  const baseCwd = input.cwd || process.cwd();
38869
- const searchPath = resolve3(baseCwd, input.path || ".");
39047
+ const searchPath = FilesystemTools.resolveInputPath(baseCwd, input.path || ".");
38870
39048
  const globPattern = input.glob || "**/*";
38871
39049
  const caseSensitive = input.caseSensitive || false;
38872
39050
  try {
39051
+ if (!pattern) {
39052
+ throw new ToolExecutionError("Pattern is required", {
39053
+ toolName: "grep",
39054
+ toolInput: input,
39055
+ code: ErrorCodes.VALIDATION_OUT_OF_RANGE,
39056
+ recoverable: false,
39057
+ retryable: false
39058
+ });
39059
+ }
39060
+ const safety = await isPathSafe(searchPath, "read", { cwd: baseCwd });
39061
+ if (!safety.safe) {
39062
+ getSecurityLogger().log({
39063
+ eventType: "path_violation",
39064
+ severity: "high",
39065
+ details: {
39066
+ tool: "grep",
39067
+ path: searchPath,
39068
+ reason: safety.reason || "Blocked path"
39069
+ },
39070
+ sessionId: input.sessionId || "unknown"
39071
+ });
39072
+ throw new ToolExecutionError(safety.reason || "Blocked path", {
39073
+ toolName: "grep",
39074
+ toolInput: input,
39075
+ code: ErrorCodes.TOOL_PERMISSION_DENIED,
39076
+ recoverable: false,
39077
+ retryable: false
39078
+ });
39079
+ }
39080
+ const validated = await validatePath(searchPath, {
39081
+ allowSymlinks: true,
39082
+ allowedPaths: [baseCwd, searchPath]
39083
+ });
39084
+ if (!validated.valid) {
39085
+ throw new ToolExecutionError(validated.error || "Invalid path", {
39086
+ toolName: "grep",
39087
+ toolInput: input,
39088
+ code: ErrorCodes.VALIDATION_OUT_OF_RANGE,
39089
+ recoverable: false,
39090
+ retryable: false
39091
+ });
39092
+ }
38873
39093
  const flags = caseSensitive ? "" : "i";
38874
39094
  const regex2 = new RegExp(pattern, flags);
38875
39095
  const results = [];
38876
39096
  const glob = new Glob(globPattern);
38877
- for await (const file of glob.scan({ cwd: searchPath })) {
38878
- const filePath = join4(searchPath, file);
39097
+ for await (const file of glob.scan({ cwd: validated.resolved })) {
39098
+ const filePath = join4(validated.resolved, file);
38879
39099
  try {
38880
39100
  const content = await Bun.file(filePath).text();
38881
39101
  const lines = content.split(`
@@ -38928,7 +39148,7 @@ class FilesystemTools {
38928
39148
  };
38929
39149
  static readPdfExecutor = async (input) => {
38930
39150
  const baseCwd = input.cwd || process.cwd();
38931
- const path = resolve3(baseCwd, input.path);
39151
+ const path = FilesystemTools.resolveInputPath(baseCwd, String(input.path || ""));
38932
39152
  try {
38933
39153
  const safety = await isPathSafe(path, "read", { cwd: baseCwd });
38934
39154
  if (!safety.safe) {
@@ -39847,13 +40067,19 @@ function getNextCronRun(expression, fromTime, timeZone) {
39847
40067
 
39848
40068
  // packages/core/src/scheduler/store.ts
39849
40069
  var DEFAULT_LOCK_TTL_MS = 10 * 60 * 1000;
40070
+ var SAFE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
39850
40071
  function schedulesDir(cwd2) {
39851
40072
  return join6(getProjectConfigDir(cwd2), "schedules");
39852
40073
  }
39853
40074
  function locksDir(cwd2) {
39854
40075
  return join6(schedulesDir(cwd2), "locks");
39855
40076
  }
40077
+ function isSafeId(id) {
40078
+ return SAFE_ID_PATTERN.test(id);
40079
+ }
39856
40080
  function schedulePath(cwd2, id) {
40081
+ if (!isSafeId(id))
40082
+ return null;
39857
40083
  return join6(schedulesDir(cwd2), `${id}.json`);
39858
40084
  }
39859
40085
  function lockPath(cwd2, id) {
@@ -39886,11 +40112,17 @@ async function listSchedules(cwd2) {
39886
40112
  async function saveSchedule(cwd2, schedule) {
39887
40113
  await ensureDirs(cwd2);
39888
40114
  const path = schedulePath(cwd2, schedule.id);
40115
+ if (!path) {
40116
+ throw new Error(`Invalid schedule id: ${schedule.id}`);
40117
+ }
39889
40118
  await writeFile(path, JSON.stringify(schedule, null, 2), "utf-8");
39890
40119
  }
39891
40120
  async function deleteSchedule(cwd2, id) {
39892
40121
  try {
39893
- await unlink(schedulePath(cwd2, id));
40122
+ const path = schedulePath(cwd2, id);
40123
+ if (!path)
40124
+ return false;
40125
+ await unlink(path);
39894
40126
  return true;
39895
40127
  } catch {
39896
40128
  return false;
@@ -39902,7 +40134,10 @@ function computeNextRun(schedule, fromTime) {
39902
40134
  if (schedule.schedule.kind === "once") {
39903
40135
  if (!schedule.schedule.at)
39904
40136
  return;
39905
- return parseScheduledTime(schedule.schedule.at, validTimezone);
40137
+ const next = parseScheduledTime(schedule.schedule.at, validTimezone);
40138
+ if (!next || next <= fromTime)
40139
+ return;
40140
+ return next;
39906
40141
  }
39907
40142
  if (schedule.schedule.kind === "cron") {
39908
40143
  if (!schedule.schedule.cron)
@@ -39918,12 +40153,17 @@ async function getDueSchedules(cwd2, nowTime) {
39918
40153
  return false;
39919
40154
  if (!schedule.nextRunAt)
39920
40155
  return false;
40156
+ if (!Number.isFinite(schedule.nextRunAt))
40157
+ return false;
39921
40158
  return schedule.nextRunAt <= nowTime;
39922
40159
  });
39923
40160
  }
39924
40161
  async function updateSchedule(cwd2, id, updater) {
39925
40162
  try {
39926
- const raw = await readFile(schedulePath(cwd2, id), "utf-8");
40163
+ const path = schedulePath(cwd2, id);
40164
+ if (!path)
40165
+ return null;
40166
+ const raw = await readFile(path, "utf-8");
39927
40167
  const schedule = JSON.parse(raw);
39928
40168
  const updated = updater(schedule);
39929
40169
  await saveSchedule(cwd2, updated);
@@ -39933,6 +40173,8 @@ async function updateSchedule(cwd2, id, updater) {
39933
40173
  }
39934
40174
  }
39935
40175
  async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_MS, allowRetry = true) {
40176
+ if (!isSafeId(id))
40177
+ return false;
39936
40178
  await ensureDirs(cwd2);
39937
40179
  const path = lockPath(cwd2, id);
39938
40180
  const now2 = Date.now();
@@ -39965,6 +40207,8 @@ async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_M
39965
40207
  return false;
39966
40208
  }
39967
40209
  async function releaseScheduleLock(cwd2, id, ownerId) {
40210
+ if (!isSafeId(id))
40211
+ return;
39968
40212
  const path = lockPath(cwd2, id);
39969
40213
  try {
39970
40214
  const raw = await readFile(path, "utf-8");
@@ -39975,6 +40219,8 @@ async function releaseScheduleLock(cwd2, id, ownerId) {
39975
40219
  } catch {}
39976
40220
  }
39977
40221
  async function refreshScheduleLock(cwd2, id, ownerId) {
40222
+ if (!isSafeId(id))
40223
+ return;
39978
40224
  const path = lockPath(cwd2, id);
39979
40225
  try {
39980
40226
  const raw = await readFile(path, "utf-8");
@@ -39987,7 +40233,10 @@ async function refreshScheduleLock(cwd2, id, ownerId) {
39987
40233
  }
39988
40234
  async function readSchedule(cwd2, id) {
39989
40235
  try {
39990
- const raw = await readFile(schedulePath(cwd2, id), "utf-8");
40236
+ const path = schedulePath(cwd2, id);
40237
+ if (!path)
40238
+ return null;
40239
+ const raw = await readFile(path, "utf-8");
39991
40240
  const schedule = JSON.parse(raw);
39992
40241
  if (!schedule?.id)
39993
40242
  return null;
@@ -40033,6 +40282,9 @@ function parseDateTime(value) {
40033
40282
  if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day) || !Number.isFinite(hour) || !Number.isFinite(minute) || !Number.isFinite(second)) {
40034
40283
  return null;
40035
40284
  }
40285
+ if (month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
40286
+ return null;
40287
+ }
40036
40288
  return { year, month, day, hour, minute, second };
40037
40289
  }
40038
40290
  function hasTimeZoneOffset(value) {
@@ -40171,11 +40423,21 @@ class SchedulerTool {
40171
40423
  const id = String(input.id || "").trim();
40172
40424
  if (!id)
40173
40425
  return "Error: id is required.";
40426
+ let nextRunAt;
40427
+ if (action === "resume") {
40428
+ const schedule = await readSchedule(cwd2, id);
40429
+ if (!schedule)
40430
+ return `Schedule ${id} not found.`;
40431
+ nextRunAt = computeNextRun(schedule, Date.now());
40432
+ if (!nextRunAt) {
40433
+ return `Error: unable to compute next run for schedule ${id}.`;
40434
+ }
40435
+ }
40174
40436
  const updated = await updateSchedule(cwd2, id, (s) => ({
40175
40437
  ...s,
40176
40438
  status: action === "pause" ? "paused" : "active",
40177
40439
  updatedAt: Date.now(),
40178
- nextRunAt: action === "resume" ? computeNextRun(s, Date.now()) : s.nextRunAt
40440
+ nextRunAt: action === "resume" ? nextRunAt : s.nextRunAt
40179
40441
  }));
40180
40442
  if (!updated)
40181
40443
  return `Schedule ${id} not found.`;
@@ -40190,7 +40452,7 @@ init_src();
40190
40452
  import { existsSync as existsSync4, writeFileSync as writeFileSync3, unlinkSync } from "fs";
40191
40453
  import { tmpdir } from "os";
40192
40454
  import { join as join7 } from "path";
40193
- import { homedir as homedir4 } from "os";
40455
+ import { homedir as homedir6 } from "os";
40194
40456
  async function getViuPath() {
40195
40457
  const explicitPath = process.env.ASSISTANTS_VIU_PATH || process.env.VIU_PATH;
40196
40458
  if (explicitPath) {
@@ -40202,7 +40464,7 @@ async function getViuPath() {
40202
40464
  } catch {}
40203
40465
  }
40204
40466
  const envHome = process.env.HOME || process.env.USERPROFILE;
40205
- const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir4();
40467
+ const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir6();
40206
40468
  const locations = [
40207
40469
  "viu",
40208
40470
  join7(homeDir, ".cargo", "bin", "viu"),
@@ -40351,14 +40613,14 @@ Respond with ALLOW or DENY on the first line, followed by a short reason.`,
40351
40613
  // packages/core/src/skills/loader.ts
40352
40614
  init_src();
40353
40615
  import { join as join8, basename, dirname as dirname4 } from "path";
40354
- import { homedir as homedir5 } from "os";
40616
+ import { homedir as homedir7 } from "os";
40355
40617
  var {Glob: Glob2 } = globalThis.Bun;
40356
40618
 
40357
40619
  class SkillLoader {
40358
40620
  skills = new Map;
40359
40621
  async loadAll(projectDir = process.cwd()) {
40360
40622
  const envHome = process.env.HOME || process.env.USERPROFILE;
40361
- const userHome = envHome && envHome.trim().length > 0 ? envHome : homedir5();
40623
+ const userHome = envHome && envHome.trim().length > 0 ? envHome : homedir7();
40362
40624
  const userSkillsDir = join8(userHome, ".assistants", "shared", "skills");
40363
40625
  await this.loadFromDirectory(userSkillsDir);
40364
40626
  const projectSkillsDir = join8(projectDir, ".assistants", "skills");
@@ -40574,6 +40836,7 @@ class HookLoader {
40574
40836
  }
40575
40837
  // packages/core/src/hooks/executor.ts
40576
40838
  init_src();
40839
+ import { existsSync as existsSync5 } from "fs";
40577
40840
  function killSpawnedProcess(proc) {
40578
40841
  proc.kill();
40579
40842
  }
@@ -40659,7 +40922,9 @@ class HookExecutor {
40659
40922
  if (!hook.command)
40660
40923
  return null;
40661
40924
  try {
40925
+ const cwd2 = input.cwd && existsSync5(input.cwd) ? input.cwd : process.cwd();
40662
40926
  const proc = Bun.spawn(["bash", "-c", hook.command], {
40927
+ cwd: cwd2,
40663
40928
  stdin: "pipe",
40664
40929
  stdout: "pipe",
40665
40930
  stderr: "pipe"
@@ -41036,7 +41301,7 @@ init_src();
41036
41301
  // packages/core/src/sessions/verification.ts
41037
41302
  init_src();
41038
41303
  import { join as join9 } from "path";
41039
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2 } from "fs";
41304
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2 } from "fs";
41040
41305
 
41041
41306
  class VerificationSessionStore {
41042
41307
  basePath;
@@ -41047,7 +41312,7 @@ class VerificationSessionStore {
41047
41312
  this.ensureDirectory();
41048
41313
  }
41049
41314
  ensureDirectory() {
41050
- if (!existsSync5(this.basePath)) {
41315
+ if (!existsSync6(this.basePath)) {
41051
41316
  mkdirSync3(this.basePath, { recursive: true });
41052
41317
  }
41053
41318
  }
@@ -41073,7 +41338,7 @@ class VerificationSessionStore {
41073
41338
  }
41074
41339
  get(id) {
41075
41340
  const filePath = join9(this.basePath, `${id}.json`);
41076
- if (!existsSync5(filePath)) {
41341
+ if (!existsSync6(filePath)) {
41077
41342
  return null;
41078
41343
  }
41079
41344
  try {
@@ -41121,7 +41386,7 @@ class VerificationSessionStore {
41121
41386
  this.save(session);
41122
41387
  }
41123
41388
  listFiles() {
41124
- if (!existsSync5(this.basePath)) {
41389
+ if (!existsSync6(this.basePath)) {
41125
41390
  return [];
41126
41391
  }
41127
41392
  return readdirSync2(this.basePath).filter((f) => f.endsWith(".json"));
@@ -41312,9 +41577,9 @@ function createScopeVerificationHook() {
41312
41577
  };
41313
41578
  }
41314
41579
  // packages/core/src/commands/loader.ts
41315
- import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
41316
- import { join as join10, basename as basename2, extname } from "path";
41317
- import { homedir as homedir6 } from "os";
41580
+ import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
41581
+ import { join as join10, basename as basename2, extname as extname2 } from "path";
41582
+ import { homedir as homedir8 } from "os";
41318
41583
 
41319
41584
  class CommandLoader {
41320
41585
  commands = new Map;
@@ -41325,14 +41590,14 @@ class CommandLoader {
41325
41590
  async loadAll() {
41326
41591
  this.commands.clear();
41327
41592
  const envHome = process.env.HOME || process.env.USERPROFILE;
41328
- const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir6();
41593
+ const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir8();
41329
41594
  const globalDir = join10(homeDir, ".assistants", "commands");
41330
41595
  await this.loadFromDirectory(globalDir, "global");
41331
41596
  const projectDir = join10(this.cwd, ".assistants", "commands");
41332
41597
  await this.loadFromDirectory(projectDir, "project");
41333
41598
  }
41334
41599
  async loadFromDirectory(dir, source, prefix = "") {
41335
- if (!existsSync6(dir))
41600
+ if (!existsSync7(dir))
41336
41601
  return;
41337
41602
  const entries = readdirSync3(dir);
41338
41603
  for (const entry of entries) {
@@ -41341,7 +41606,7 @@ class CommandLoader {
41341
41606
  if (stat.isDirectory()) {
41342
41607
  const newPrefix = prefix ? `${prefix}:${entry}` : entry;
41343
41608
  await this.loadFromDirectory(fullPath, source, newPrefix);
41344
- } else if (stat.isFile() && extname(entry) === ".md") {
41609
+ } else if (stat.isFile() && extname2(entry) === ".md") {
41345
41610
  const command = await this.loadCommandFile(fullPath, prefix);
41346
41611
  if (command) {
41347
41612
  this.commands.set(command.name, command);
@@ -41372,15 +41637,14 @@ class CommandLoader {
41372
41637
  }
41373
41638
  }
41374
41639
  parseFrontmatter(content) {
41375
- const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
41640
+ const frontmatterRegex = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n?([\s\S]*)$/;
41376
41641
  const match = content.match(frontmatterRegex);
41377
41642
  if (!match) {
41378
41643
  return { frontmatter: {}, body: content };
41379
41644
  }
41380
41645
  const [, yamlContent, body] = match;
41381
41646
  const frontmatter = {};
41382
- const lines = yamlContent.split(`
41383
- `);
41647
+ const lines = yamlContent.split(/\r?\n/);
41384
41648
  let currentListKey = null;
41385
41649
  for (const rawLine of lines) {
41386
41650
  const line = rawLine.trimEnd();
@@ -41512,10 +41776,16 @@ Use /help to see available commands.
41512
41776
  }
41513
41777
  if (!inCodeBlock && trimmed.startsWith("!")) {
41514
41778
  const command = trimmed.slice(1).trim();
41779
+ if (!command) {
41780
+ processedLines.push(line);
41781
+ continue;
41782
+ }
41515
41783
  const output = await this.executeShell(command, cwd2);
41516
- processedLines.push(`\`\`\`
41517
- ${output}
41518
- \`\`\``);
41784
+ const indent = line.match(/^\s*/)?.[0] ?? "";
41785
+ const fenced = [`${indent}\`\`\``, ...output.split(`
41786
+ `).map((o) => `${indent}${o}`), `${indent}\`\`\``];
41787
+ processedLines.push(fenced.join(`
41788
+ `));
41519
41789
  } else {
41520
41790
  processedLines.push(line);
41521
41791
  }
@@ -41567,19 +41837,25 @@ ${stderr}`;
41567
41837
  }
41568
41838
  }
41569
41839
  // packages/core/src/commands/builtin.ts
41570
- import { join as join12 } from "path";
41571
- import { homedir as homedir7, platform as platform2, release, arch } from "os";
41572
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
41840
+ import { join as join13 } from "path";
41841
+ import { homedir as homedir10, platform as platform2, release, arch } from "os";
41842
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
41573
41843
  init_src();
41574
41844
 
41575
41845
  // packages/core/src/projects/store.ts
41576
41846
  init_src();
41577
41847
  import { join as join11 } from "path";
41578
41848
  import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
41849
+ var SAFE_ID_PATTERN2 = /^[a-zA-Z0-9_-]+$/;
41579
41850
  function projectsDir(cwd2) {
41580
41851
  return join11(cwd2, ".assistants", "projects");
41581
41852
  }
41853
+ function isSafeId2(id) {
41854
+ return SAFE_ID_PATTERN2.test(id);
41855
+ }
41582
41856
  function projectPath(cwd2, id) {
41857
+ if (!isSafeId2(id))
41858
+ return null;
41583
41859
  return join11(projectsDir(cwd2), `${id}.json`);
41584
41860
  }
41585
41861
  async function ensureProjectsDir(cwd2) {
@@ -41611,7 +41887,10 @@ async function listProjects(cwd2) {
41611
41887
  }
41612
41888
  async function readProject(cwd2, id) {
41613
41889
  try {
41614
- const raw = await readFile2(projectPath(cwd2, id), "utf-8");
41890
+ const path = projectPath(cwd2, id);
41891
+ if (!path)
41892
+ return null;
41893
+ const raw = await readFile2(path, "utf-8");
41615
41894
  const project = JSON.parse(raw);
41616
41895
  if (!project?.id || !project?.name)
41617
41896
  return null;
@@ -41627,11 +41906,18 @@ async function findProjectByName(cwd2, name) {
41627
41906
  }
41628
41907
  async function saveProject(cwd2, project) {
41629
41908
  await ensureProjectsDir(cwd2);
41630
- await writeFile2(projectPath(cwd2, project.id), JSON.stringify(project, null, 2), "utf-8");
41909
+ const path = projectPath(cwd2, project.id);
41910
+ if (!path) {
41911
+ throw new Error(`Invalid project id: ${project.id}`);
41912
+ }
41913
+ await writeFile2(path, JSON.stringify(project, null, 2), "utf-8");
41631
41914
  }
41632
41915
  async function deleteProject(cwd2, id) {
41633
41916
  try {
41634
- await unlink2(projectPath(cwd2, id));
41917
+ const path = projectPath(cwd2, id);
41918
+ if (!path)
41919
+ return false;
41920
+ await unlink2(path);
41635
41921
  return true;
41636
41922
  } catch {
41637
41923
  return false;
@@ -41672,23 +41958,28 @@ function hasProjectNameConflict(projects, name) {
41672
41958
 
41673
41959
  // packages/core/src/projects/context.ts
41674
41960
  import { readFile as readFile3 } from "fs/promises";
41675
- import { resolve as resolve4 } from "path";
41961
+ import { homedir as homedir9 } from "os";
41962
+ import { resolve as resolve4, join as join12 } from "path";
41676
41963
  var DEFAULT_MAX_FILE_BYTES = 12000;
41964
+ function singleLine(value) {
41965
+ return value.replace(/[\r\n]+/g, " ").trim();
41966
+ }
41677
41967
  function formatPlan(plan) {
41678
41968
  const lines = [];
41679
- lines.push(`- ${plan.title} (${plan.steps.length} steps)`);
41969
+ lines.push(`- ${singleLine(plan.title)} (${plan.steps.length} steps)`);
41680
41970
  for (const step of plan.steps) {
41681
- lines.push(` - [${step.status}] ${step.text}`);
41971
+ lines.push(` - [${step.status}] ${singleLine(step.text)}`);
41682
41972
  }
41683
41973
  return lines.join(`
41684
41974
  `);
41685
41975
  }
41686
41976
  function normalizeEntryLabel(entry) {
41687
- return entry.label ? entry.label.trim() : entry.value.trim();
41977
+ return entry.label ? singleLine(entry.label) : singleLine(entry.value);
41688
41978
  }
41689
41979
  async function renderFileEntry(entry, options) {
41690
41980
  const rawPath = entry.value.trim();
41691
- const resolved = resolve4(options.cwd, rawPath);
41981
+ const expandedPath = rawPath === "~" ? homedir9() : rawPath.startsWith("~/") ? join12(homedir9(), rawPath.slice(2)) : rawPath;
41982
+ const resolved = resolve4(options.cwd, expandedPath);
41692
41983
  const validation = await validatePath(resolved, { allowedPaths: [options.cwd] });
41693
41984
  if (!validation.valid) {
41694
41985
  return `- File: ${rawPath} (unavailable: ${validation.error || "invalid path"})`;
@@ -41820,7 +42111,7 @@ function splitArgs(input) {
41820
42111
  args.push(current);
41821
42112
  return args;
41822
42113
  }
41823
- function singleLine(value) {
42114
+ function singleLine2(value) {
41824
42115
  return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
41825
42116
  }
41826
42117
 
@@ -42659,12 +42950,12 @@ No context entries for project "${project.name}".
42659
42950
  return { handled: true };
42660
42951
  }
42661
42952
  let output = `
42662
- **Context Entries (${singleLine(project.name)})**
42953
+ **Context Entries (${singleLine2(project.name)})**
42663
42954
 
42664
42955
  `;
42665
42956
  for (const entry of project.context) {
42666
- const label = entry.label ? ` (${singleLine(entry.label)})` : "";
42667
- output += `- ${entry.id} [${entry.type}] ${singleLine(entry.value)}${label}
42957
+ const label = entry.label ? ` (${singleLine2(entry.label)})` : "";
42958
+ output += `- ${entry.id} [${entry.type}] ${singleLine2(entry.value)}${label}
42668
42959
  `;
42669
42960
  }
42670
42961
  context.emit("text", output);
@@ -42811,7 +43102,7 @@ No projects found. Use /projects new <name>.
42811
43102
  `;
42812
43103
  for (const project of projects) {
42813
43104
  const marker = project.id === activeId ? "*" : " ";
42814
- output += `${marker} ${singleLine(project.name)} (${project.id})
43105
+ output += `${marker} ${singleLine2(project.name)} (${project.id})
42815
43106
  `;
42816
43107
  }
42817
43108
  context.emit("text", output);
@@ -42873,13 +43164,13 @@ No projects found. Use /projects new <name>.
42873
43164
  return { handled: true };
42874
43165
  }
42875
43166
  let output = `
42876
- **Project: ${singleLine(project.name)}**
43167
+ **Project: ${singleLine2(project.name)}**
42877
43168
 
42878
43169
  `;
42879
43170
  output += `ID: ${project.id}
42880
43171
  `;
42881
43172
  if (project.description) {
42882
- output += `Description: ${singleLine(project.description)}
43173
+ output += `Description: ${singleLine2(project.description)}
42883
43174
  `;
42884
43175
  }
42885
43176
  output += `Context entries: ${project.context.length}
@@ -43005,11 +43296,11 @@ No plans for project "${project.name}".
43005
43296
  return { handled: true };
43006
43297
  }
43007
43298
  let output = `
43008
- **Plans (${singleLine(project.name)})**
43299
+ **Plans (${singleLine2(project.name)})**
43009
43300
 
43010
43301
  `;
43011
43302
  for (const plan of project.plans) {
43012
- output += `- ${plan.id} ${singleLine(plan.title)} (${plan.steps.length} steps)
43303
+ output += `- ${plan.id} ${singleLine2(plan.title)} (${plan.steps.length} steps)
43013
43304
  `;
43014
43305
  }
43015
43306
  context.emit("text", output);
@@ -43065,7 +43356,7 @@ No plans for project "${project.name}".
43065
43356
  return { handled: true };
43066
43357
  }
43067
43358
  let output = `
43068
- **Plan: ${singleLine(plan.title)}**
43359
+ **Plan: ${singleLine2(plan.title)}**
43069
43360
 
43070
43361
  `;
43071
43362
  output += `ID: ${plan.id}
@@ -43075,7 +43366,7 @@ No plans for project "${project.name}".
43075
43366
  `;
43076
43367
  } else {
43077
43368
  for (const step of plan.steps) {
43078
- output += `- ${step.id} [${step.status}] ${singleLine(step.text)}
43369
+ output += `- ${step.id} [${step.status}] ${singleLine2(step.text)}
43079
43370
  `;
43080
43371
  }
43081
43372
  }
@@ -43508,9 +43799,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
43508
43799
  content: "",
43509
43800
  handler: async (args, context) => {
43510
43801
  const configPaths = [
43511
- join12(context.cwd, ".assistants", "config.json"),
43512
- join12(context.cwd, ".assistants", "config.local.json"),
43513
- join12(getConfigDir(), "config.json")
43802
+ join13(context.cwd, ".assistants", "config.json"),
43803
+ join13(context.cwd, ".assistants", "config.local.json"),
43804
+ join13(getConfigDir(), "config.json")
43514
43805
  ];
43515
43806
  let message = `
43516
43807
  **Configuration**
@@ -43519,18 +43810,18 @@ Format the summary as a brief bullet-point list. This summary will replace the c
43519
43810
  message += `**Config File Locations:**
43520
43811
  `;
43521
43812
  for (const path of configPaths) {
43522
- const exists = existsSync7(path);
43813
+ const exists = existsSync8(path);
43523
43814
  message += ` ${exists ? "\u2713" : "\u25CB"} ${path}
43524
43815
  `;
43525
43816
  }
43526
43817
  const envHome = process.env.HOME || process.env.USERPROFILE;
43527
- const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir7();
43818
+ const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir10();
43528
43819
  message += `
43529
43820
  **Commands Directories:**
43530
43821
  `;
43531
- message += ` - Project: ${join12(context.cwd, ".assistants", "commands")}
43822
+ message += ` - Project: ${join13(context.cwd, ".assistants", "commands")}
43532
43823
  `;
43533
- message += ` - Global: ${join12(homeDir, ".assistants", "commands")}
43824
+ message += ` - Global: ${join13(homeDir, ".assistants", "commands")}
43534
43825
  `;
43535
43826
  context.emit("text", message);
43536
43827
  context.emit("done");
@@ -43546,7 +43837,7 @@ Format the summary as a brief bullet-point list. This summary will replace the c
43546
43837
  selfHandled: true,
43547
43838
  content: "",
43548
43839
  handler: async (args, context) => {
43549
- const commandsDir = join12(context.cwd, ".assistants", "commands");
43840
+ const commandsDir = join13(context.cwd, ".assistants", "commands");
43550
43841
  mkdirSync4(commandsDir, { recursive: true });
43551
43842
  const exampleCommand = `---
43552
43843
  name: reflect
@@ -43562,8 +43853,8 @@ Please summarize the last interaction and suggest 2-3 next steps.
43562
43853
  - Focus on clarity
43563
43854
  - Ask a follow-up question if needed
43564
43855
  `;
43565
- const examplePath = join12(commandsDir, "reflect.md");
43566
- if (!existsSync7(examplePath)) {
43856
+ const examplePath = join13(commandsDir, "reflect.md");
43857
+ if (!existsSync8(examplePath)) {
43567
43858
  writeFileSync5(examplePath, exampleCommand);
43568
43859
  }
43569
43860
  let message = `
@@ -44727,15 +45018,15 @@ function validateToolCalls(toolCalls, tools) {
44727
45018
  }
44728
45019
 
44729
45020
  // packages/core/src/voice/utils.ts
44730
- import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
44731
- import { homedir as homedir9 } from "os";
44732
- import { join as join14 } from "path";
45021
+ import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
45022
+ import { homedir as homedir12 } from "os";
45023
+ import { join as join15 } from "path";
44733
45024
  import { spawnSync } from "child_process";
44734
45025
  function loadApiKeyFromSecrets2(key) {
44735
45026
  const envHome = process.env.HOME || process.env.USERPROFILE;
44736
- const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir9();
44737
- const secretsPath = join14(homeDir, ".secrets");
44738
- if (!existsSync9(secretsPath))
45027
+ const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir12();
45028
+ const secretsPath = join15(homeDir, ".secrets");
45029
+ if (!existsSync10(secretsPath))
44739
45030
  return;
44740
45031
  try {
44741
45032
  const content = readFileSync5(secretsPath, "utf-8");
@@ -44808,7 +45099,7 @@ class SystemSTT {
44808
45099
  // packages/core/src/voice/tts.ts
44809
45100
  import { spawnSync as spawnSync2 } from "child_process";
44810
45101
  import { tmpdir as tmpdir2 } from "os";
44811
- import { join as join15 } from "path";
45102
+ import { join as join16 } from "path";
44812
45103
  import { readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
44813
45104
  class ElevenLabsTTS {
44814
45105
  apiKey;
@@ -44912,7 +45203,7 @@ class SystemTTS {
44912
45203
  if (!say) {
44913
45204
  throw new Error('System TTS not available: missing "say" command.');
44914
45205
  }
44915
- const output = join15(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
45206
+ const output = join16(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
44916
45207
  const args = [];
44917
45208
  if (this.voiceId) {
44918
45209
  args.push("-v", this.voiceId);
@@ -44934,7 +45225,7 @@ class SystemTTS {
44934
45225
  }
44935
45226
  const espeak = findExecutable("espeak") || findExecutable("espeak-ng");
44936
45227
  if (espeak) {
44937
- const output = join15(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
45228
+ const output = join16(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
44938
45229
  const args = ["-w", output];
44939
45230
  if (this.voiceId) {
44940
45231
  args.push("-v", this.voiceId);
@@ -44961,14 +45252,14 @@ class SystemTTS {
44961
45252
  // packages/core/src/voice/player.ts
44962
45253
  import { spawn } from "child_process";
44963
45254
  import { tmpdir as tmpdir3 } from "os";
44964
- import { join as join16 } from "path";
45255
+ import { join as join17 } from "path";
44965
45256
  import { unlink as unlink4, writeFileSync as writeFileSync6 } from "fs";
44966
45257
  class AudioPlayer {
44967
45258
  currentProcess = null;
44968
45259
  playing = false;
44969
45260
  async play(audio, options = {}) {
44970
45261
  const format = options.format ?? "mp3";
44971
- const tempFile = join16(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
45262
+ const tempFile = join17(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
44972
45263
  writeFileSync6(tempFile, Buffer.from(audio));
44973
45264
  const player = this.resolvePlayer(format);
44974
45265
  if (!player) {
@@ -45036,7 +45327,7 @@ class AudioPlayer {
45036
45327
  // packages/core/src/voice/recorder.ts
45037
45328
  import { spawn as spawn2 } from "child_process";
45038
45329
  import { tmpdir as tmpdir4 } from "os";
45039
- import { join as join17 } from "path";
45330
+ import { join as join18 } from "path";
45040
45331
  import { readFileSync as readFileSync7, unlink as unlink5 } from "fs";
45041
45332
  class AudioRecorder {
45042
45333
  currentProcess = null;
@@ -45047,7 +45338,7 @@ class AudioRecorder {
45047
45338
  const duration = options.durationSeconds ?? 5;
45048
45339
  const sampleRate = options.sampleRate ?? 16000;
45049
45340
  const channels = options.channels ?? 1;
45050
- const output = join17(tmpdir4(), `assistants-record-${Date.now()}.wav`);
45341
+ const output = join18(tmpdir4(), `assistants-record-${Date.now()}.wav`);
45051
45342
  const recorder = this.resolveRecorder(sampleRate, channels, duration, output);
45052
45343
  if (!recorder) {
45053
45344
  throw new Error("No supported audio recorder found. Install sox or ffmpeg.");
@@ -45239,15 +45530,15 @@ class VoiceManager {
45239
45530
  }
45240
45531
  // packages/core/src/identity/assistant-manager.ts
45241
45532
  init_src();
45242
- import { existsSync as existsSync11 } from "fs";
45533
+ import { existsSync as existsSync12 } from "fs";
45243
45534
  import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile7, rm as rm2 } from "fs/promises";
45244
- import { join as join19 } from "path";
45535
+ import { join as join20 } from "path";
45245
45536
 
45246
45537
  // packages/core/src/identity/identity-manager.ts
45247
45538
  init_src();
45248
- import { existsSync as existsSync10 } from "fs";
45539
+ import { existsSync as existsSync11 } from "fs";
45249
45540
  import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile6, rm } from "fs/promises";
45250
- import { join as join18 } from "path";
45541
+ import { join as join19 } from "path";
45251
45542
  var DEFAULT_PROFILE = {
45252
45543
  displayName: "Assistant",
45253
45544
  timezone: "UTC",
@@ -45277,19 +45568,19 @@ class IdentityManager {
45277
45568
  this.basePath = basePath;
45278
45569
  }
45279
45570
  get identitiesRoot() {
45280
- return join18(this.basePath, "assistants", this.assistantId, "identities");
45571
+ return join19(this.basePath, "assistants", this.assistantId, "identities");
45281
45572
  }
45282
45573
  get indexPath() {
45283
- return join18(this.identitiesRoot, "index.json");
45574
+ return join19(this.identitiesRoot, "index.json");
45284
45575
  }
45285
45576
  get activePath() {
45286
- return join18(this.identitiesRoot, "active.json");
45577
+ return join19(this.identitiesRoot, "active.json");
45287
45578
  }
45288
45579
  identityPath(id) {
45289
- return join18(this.identitiesRoot, `${id}.json`);
45580
+ return join19(this.identitiesRoot, `${id}.json`);
45290
45581
  }
45291
45582
  assistantConfigPath() {
45292
- return join18(this.basePath, "assistants", this.assistantId, "config.json");
45583
+ return join19(this.basePath, "assistants", this.assistantId, "config.json");
45293
45584
  }
45294
45585
  async initialize() {
45295
45586
  await mkdir4(this.identitiesRoot, { recursive: true });
@@ -45395,7 +45686,7 @@ class IdentityManager {
45395
45686
  `);
45396
45687
  }
45397
45688
  async readIndex() {
45398
- if (!existsSync10(this.indexPath)) {
45689
+ if (!existsSync11(this.indexPath)) {
45399
45690
  return { identities: [] };
45400
45691
  }
45401
45692
  try {
@@ -45420,7 +45711,7 @@ class IdentityManager {
45420
45711
  }
45421
45712
  async readIdentity(id) {
45422
45713
  const path2 = this.identityPath(id);
45423
- if (!existsSync10(path2))
45714
+ if (!existsSync11(path2))
45424
45715
  return null;
45425
45716
  try {
45426
45717
  const raw = await readFile7(path2, "utf-8");
@@ -45434,7 +45725,7 @@ class IdentityManager {
45434
45725
  await writeFile6(this.identityPath(identity.id), JSON.stringify(identity, null, 2));
45435
45726
  }
45436
45727
  async readActive() {
45437
- if (!existsSync10(this.activePath))
45728
+ if (!existsSync11(this.activePath))
45438
45729
  return null;
45439
45730
  try {
45440
45731
  const raw = await readFile7(this.activePath, "utf-8");
@@ -45449,7 +45740,7 @@ class IdentityManager {
45449
45740
  await writeFile6(this.activePath, JSON.stringify({ id }, null, 2));
45450
45741
  }
45451
45742
  async loadAssistant() {
45452
- if (!existsSync10(this.assistantConfigPath()))
45743
+ if (!existsSync11(this.assistantConfigPath()))
45453
45744
  return null;
45454
45745
  try {
45455
45746
  const raw = await readFile7(this.assistantConfigPath(), "utf-8");
@@ -45473,16 +45764,16 @@ class AssistantManager {
45473
45764
  this.basePath = basePath;
45474
45765
  }
45475
45766
  get assistantsRoot() {
45476
- return join19(this.basePath, "assistants");
45767
+ return join20(this.basePath, "assistants");
45477
45768
  }
45478
45769
  get indexPath() {
45479
- return join19(this.assistantsRoot, "index.json");
45770
+ return join20(this.assistantsRoot, "index.json");
45480
45771
  }
45481
45772
  get activePath() {
45482
- return join19(this.basePath, "active.json");
45773
+ return join20(this.basePath, "active.json");
45483
45774
  }
45484
45775
  assistantConfigPath(id) {
45485
- return join19(this.assistantsRoot, id, "config.json");
45776
+ return join20(this.assistantsRoot, id, "config.json");
45486
45777
  }
45487
45778
  async initialize() {
45488
45779
  await mkdir5(this.assistantsRoot, { recursive: true });
@@ -45536,7 +45827,7 @@ class AssistantManager {
45536
45827
  if (!this.assistants.has(id)) {
45537
45828
  throw new Error(`Assistant ${id} not found`);
45538
45829
  }
45539
- await rm2(join19(this.assistantsRoot, id), { recursive: true, force: true });
45830
+ await rm2(join20(this.assistantsRoot, id), { recursive: true, force: true });
45540
45831
  this.assistants.delete(id);
45541
45832
  await this.removeFromIndex(id);
45542
45833
  if (this.activeId === id) {
@@ -45567,7 +45858,7 @@ class AssistantManager {
45567
45858
  return new IdentityManager(assistantId, this.basePath);
45568
45859
  }
45569
45860
  async readIndex() {
45570
- if (!existsSync11(this.indexPath)) {
45861
+ if (!existsSync12(this.indexPath)) {
45571
45862
  return { assistants: [] };
45572
45863
  }
45573
45864
  try {
@@ -45592,7 +45883,7 @@ class AssistantManager {
45592
45883
  }
45593
45884
  async readAssistant(id) {
45594
45885
  const configPath = this.assistantConfigPath(id);
45595
- if (!existsSync11(configPath))
45886
+ if (!existsSync12(configPath))
45596
45887
  return null;
45597
45888
  try {
45598
45889
  const raw = await readFile8(configPath, "utf-8");
@@ -45602,12 +45893,12 @@ class AssistantManager {
45602
45893
  }
45603
45894
  }
45604
45895
  async persistAssistant(assistant) {
45605
- const dir = join19(this.assistantsRoot, assistant.id);
45896
+ const dir = join20(this.assistantsRoot, assistant.id);
45606
45897
  await mkdir5(dir, { recursive: true });
45607
45898
  await writeFile7(this.assistantConfigPath(assistant.id), JSON.stringify(assistant, null, 2));
45608
45899
  }
45609
45900
  async readActive() {
45610
- if (!existsSync11(this.activePath))
45901
+ if (!existsSync12(this.activePath))
45611
45902
  return null;
45612
45903
  try {
45613
45904
  const raw = await readFile8(this.activePath, "utf-8");
@@ -46062,7 +46353,7 @@ class AgentLoop {
46062
46353
  if (toolInput.cwd === undefined) {
46063
46354
  toolInput.cwd = this.cwd;
46064
46355
  }
46065
- if (toolInput.sessionId === undefined) {
46356
+ if (typeof toolInput.sessionId !== "string" || toolInput.sessionId.length === 0) {
46066
46357
  toolInput.sessionId = this.sessionId;
46067
46358
  }
46068
46359
  toolCall.input = toolInput;
@@ -46098,7 +46389,7 @@ class AgentLoop {
46098
46389
  if (input.cwd === undefined) {
46099
46390
  input.cwd = this.cwd;
46100
46391
  }
46101
- if (input.sessionId === undefined) {
46392
+ if (typeof input.sessionId !== "string" || input.sessionId.length === 0) {
46102
46393
  input.sessionId = this.sessionId;
46103
46394
  }
46104
46395
  if (preHookResult?.continue === false || preHookResult?.permissionDecision === "deny") {
@@ -46444,7 +46735,7 @@ ${content.trim()}`);
46444
46735
  const heartbeatConfig = this.buildHeartbeatConfig(this.config);
46445
46736
  if (!heartbeatConfig)
46446
46737
  return;
46447
- const statePath = join20(getConfigDir(), "state", `${this.sessionId}.json`);
46738
+ const statePath = join21(getConfigDir(), "state", `${this.sessionId}.json`);
46448
46739
  this.heartbeatManager = new HeartbeatManager(heartbeatConfig);
46449
46740
  this.heartbeatPersistence = new StatePersistence(statePath);
46450
46741
  this.heartbeatRecovery = new RecoveryManager(this.heartbeatPersistence, heartbeatConfig.persistPath, heartbeatConfig.staleThresholdMs, {
@@ -46493,7 +46784,7 @@ ${content.trim()}`);
46493
46784
  async startEnergySystem() {
46494
46785
  if (!this.config || this.config.energy?.enabled === false)
46495
46786
  return;
46496
- const statePath = join20(getConfigDir(), "energy", "state.json");
46787
+ const statePath = join21(getConfigDir(), "energy", "state.json");
46497
46788
  this.energyManager = new EnergyManager(this.config.energy, new EnergyStorage(statePath));
46498
46789
  await this.energyManager.initialize();
46499
46790
  this.refreshEnergyEffects();
@@ -46739,7 +47030,7 @@ ${this.identityContext}`);
46739
47030
  return null;
46740
47031
  const intervalMs = Math.max(1000, config.heartbeat?.intervalMs ?? 15000);
46741
47032
  const staleThresholdMs = Math.max(intervalMs * 2, config.heartbeat?.staleThresholdMs ?? 120000);
46742
- const persistPath = config.heartbeat?.persistPath ?? join20(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
47033
+ const persistPath = config.heartbeat?.persistPath ?? join21(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
46743
47034
  return {
46744
47035
  intervalMs,
46745
47036
  staleThresholdMs,
@@ -46835,21 +47126,21 @@ init_anthropic();
46835
47126
  init_src();
46836
47127
 
46837
47128
  // packages/core/src/logger.ts
46838
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
46839
- import { join as join21 } from "path";
47129
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
47130
+ import { join as join22 } from "path";
46840
47131
  class Logger {
46841
47132
  logDir;
46842
47133
  logFile;
46843
47134
  sessionId;
46844
47135
  constructor(sessionId, basePath) {
46845
47136
  this.sessionId = sessionId;
46846
- this.logDir = join21(basePath || getConfigDir(), "logs");
47137
+ this.logDir = join22(basePath || getConfigDir(), "logs");
46847
47138
  this.ensureDir(this.logDir);
46848
47139
  const date = new Date().toISOString().split("T")[0];
46849
- this.logFile = join21(this.logDir, `${date}.log`);
47140
+ this.logFile = join22(this.logDir, `${date}.log`);
46850
47141
  }
46851
47142
  ensureDir(dir) {
46852
- if (!existsSync12(dir)) {
47143
+ if (!existsSync13(dir)) {
46853
47144
  mkdirSync8(dir, { recursive: true });
46854
47145
  }
46855
47146
  }
@@ -46890,12 +47181,12 @@ class SessionStorage {
46890
47181
  constructor(sessionId, basePath, assistantId) {
46891
47182
  this.sessionId = sessionId;
46892
47183
  const root = basePath || getConfigDir();
46893
- this.sessionsDir = assistantId ? join21(root, "assistants", assistantId, "sessions") : join21(root, "sessions");
47184
+ this.sessionsDir = assistantId ? join22(root, "assistants", assistantId, "sessions") : join22(root, "sessions");
46894
47185
  this.ensureDir(this.sessionsDir);
46895
- this.sessionFile = join21(this.sessionsDir, `${sessionId}.json`);
47186
+ this.sessionFile = join22(this.sessionsDir, `${sessionId}.json`);
46896
47187
  }
46897
47188
  ensureDir(dir) {
46898
- if (!existsSync12(dir)) {
47189
+ if (!existsSync13(dir)) {
46899
47190
  mkdirSync8(dir, { recursive: true });
46900
47191
  }
46901
47192
  }
@@ -46909,7 +47200,7 @@ class SessionStorage {
46909
47200
  }
46910
47201
  load() {
46911
47202
  try {
46912
- if (!existsSync12(this.sessionFile))
47203
+ if (!existsSync13(this.sessionFile))
46913
47204
  return null;
46914
47205
  return JSON.parse(readFileSync8(this.sessionFile, "utf-8"));
46915
47206
  } catch {
@@ -46918,8 +47209,8 @@ class SessionStorage {
46918
47209
  }
46919
47210
  static getActiveAssistantId() {
46920
47211
  try {
46921
- const activePath = join21(getConfigDir(), "active.json");
46922
- if (!existsSync12(activePath))
47212
+ const activePath = join22(getConfigDir(), "active.json");
47213
+ if (!existsSync13(activePath))
46923
47214
  return null;
46924
47215
  const raw = readFileSync8(activePath, "utf-8");
46925
47216
  const data = JSON.parse(raw);
@@ -46932,16 +47223,16 @@ class SessionStorage {
46932
47223
  const root = getConfigDir();
46933
47224
  const resolvedId = assistantId ?? SessionStorage.getActiveAssistantId();
46934
47225
  if (resolvedId) {
46935
- const assistantDir = join21(root, "assistants", resolvedId, "sessions");
46936
- if (existsSync12(assistantDir)) {
47226
+ const assistantDir = join22(root, "assistants", resolvedId, "sessions");
47227
+ if (existsSync13(assistantDir)) {
46937
47228
  return assistantDir;
46938
47229
  }
46939
47230
  }
46940
- return join21(root, "sessions");
47231
+ return join22(root, "sessions");
46941
47232
  }
46942
47233
  static listSessions(assistantId) {
46943
47234
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
46944
- if (!existsSync12(sessionsDir))
47235
+ if (!existsSync13(sessionsDir))
46945
47236
  return [];
46946
47237
  const sessions = [];
46947
47238
  const files = readdirSync4(sessionsDir);
@@ -46949,7 +47240,7 @@ class SessionStorage {
46949
47240
  if (!file.endsWith(".json"))
46950
47241
  continue;
46951
47242
  try {
46952
- const filePath = join21(sessionsDir, file);
47243
+ const filePath = join22(sessionsDir, file);
46953
47244
  const stat = Bun.file(filePath);
46954
47245
  const content = JSON.parse(readFileSync8(filePath, "utf-8"));
46955
47246
  sessions.push({
@@ -46969,9 +47260,9 @@ class SessionStorage {
46969
47260
  }
46970
47261
  static loadSession(sessionId, assistantId) {
46971
47262
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
46972
- const sessionFile = join21(sessionsDir, `${sessionId}.json`);
47263
+ const sessionFile = join22(sessionsDir, `${sessionId}.json`);
46973
47264
  try {
46974
- if (!existsSync12(sessionFile))
47265
+ if (!existsSync13(sessionFile))
46975
47266
  return null;
46976
47267
  return JSON.parse(readFileSync8(sessionFile, "utf-8"));
46977
47268
  } catch {
@@ -46983,18 +47274,18 @@ function initAssistantsDir() {
46983
47274
  const baseDir = getConfigDir();
46984
47275
  const dirs = [
46985
47276
  baseDir,
46986
- join21(baseDir, "logs"),
46987
- join21(baseDir, "assistants"),
46988
- join21(baseDir, "shared", "skills"),
46989
- join21(baseDir, "commands"),
46990
- join21(baseDir, "temp"),
46991
- join21(baseDir, "heartbeats"),
46992
- join21(baseDir, "state"),
46993
- join21(baseDir, "energy"),
46994
- join21(baseDir, "migration")
47277
+ join22(baseDir, "logs"),
47278
+ join22(baseDir, "assistants"),
47279
+ join22(baseDir, "shared", "skills"),
47280
+ join22(baseDir, "commands"),
47281
+ join22(baseDir, "temp"),
47282
+ join22(baseDir, "heartbeats"),
47283
+ join22(baseDir, "state"),
47284
+ join22(baseDir, "energy"),
47285
+ join22(baseDir, "migration")
46995
47286
  ];
46996
47287
  for (const dir of dirs) {
46997
- if (!existsSync12(dir)) {
47288
+ if (!existsSync13(dir)) {
46998
47289
  mkdirSync8(dir, { recursive: true });
46999
47290
  }
47000
47291
  }
@@ -47647,7 +47938,7 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands, skills = []
47647
47938
  const visibleSkills = getVisibleItems(filteredSkills);
47648
47939
  const visibleCommands = getVisibleItems(filteredCommands);
47649
47940
  const { stdout } = use_stdout_default();
47650
- const terminalWidth = stdout?.columns ?? 80;
47941
+ const terminalWidth = Math.max(10, (stdout?.columns ?? 80) - 2);
47651
47942
  const lines = value.split(`
47652
47943
  `);
47653
47944
  const lineCount = lines.length;
@@ -47657,8 +47948,8 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands, skills = []
47657
47948
  children: [
47658
47949
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
47659
47950
  children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
47660
- color: "gray",
47661
- children: "\u2500".repeat(terminalWidth)
47951
+ color: "#666666",
47952
+ children: "-".repeat(terminalWidth)
47662
47953
  }, undefined, false, undefined, this)
47663
47954
  }, undefined, false, undefined, this),
47664
47955
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
@@ -47692,8 +47983,8 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands, skills = []
47692
47983
  }, undefined, false, undefined, this),
47693
47984
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
47694
47985
  children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
47695
- color: "gray",
47696
- children: "\u2500".repeat(terminalWidth)
47986
+ color: "#666666",
47987
+ children: "-".repeat(terminalWidth)
47697
47988
  }, undefined, false, undefined, this)
47698
47989
  }, undefined, false, undefined, this),
47699
47990
  isProcessing && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
@@ -47818,14 +48109,19 @@ function parseMarkdown(text, options) {
47818
48109
  codeBlocks.push(match);
47819
48110
  return `@@CODEBLOCK${codeBlocks.length - 1}@@`;
47820
48111
  });
48112
+ const inlineCodeBlocks = [];
48113
+ result = result.replace(/`([^`\n]+)`/g, (_, code) => {
48114
+ inlineCodeBlocks.push(code);
48115
+ return `@@INLINECODE${inlineCodeBlocks.length - 1}@@`;
48116
+ });
47821
48117
  const blockSections = [];
47822
48118
  if (!options?.skipBlocks) {
47823
48119
  result = extractBlockSections(result, blockSections);
47824
48120
  }
47825
- result = result.replace(/\*\*(.+?)\*\*/g, (_, text2) => source_default.bold(text2));
47826
- result = result.replace(/__(.+?)__/g, (_, text2) => source_default.bold(text2));
47827
- result = result.replace(/\*(.+?)\*/g, (_, text2) => source_default.italic(text2));
47828
- result = result.replace(/_(.+?)_/g, (_, text2) => source_default.italic(text2));
48121
+ result = result.replace(/(^|[\s([{>])\*\*([^*]+?)\*\*(?=[\s)\]}<.,!?;:]|$)/gm, (_, lead, text2) => `${lead}${source_default.bold(text2)}`);
48122
+ result = result.replace(/(^|[\s([{>])__([^_]+?)__(?=[\s)\]}<.,!?;:]|$)/gm, (_, lead, text2) => `${lead}${source_default.bold(text2)}`);
48123
+ result = result.replace(/(^|[\s([{>])\*([^*\n]+?)\*(?=[\s)\]}<.,!?;:]|$)/gm, (_, lead, text2) => `${lead}${source_default.italic(text2)}`);
48124
+ result = result.replace(/(^|[\s([{>])_([^_\n]+?)_(?=[\s)\]}<.,!?;:]|$)/gm, (_, lead, text2) => `${lead}${source_default.italic(text2)}`);
47829
48125
  result = result.replace(/`([^`]+)`/g, (_, code) => source_default.dim(code));
47830
48126
  result = result.replace(/^### (.+)$/gm, (_, text2) => source_default.bold(text2));
47831
48127
  result = result.replace(/^## (.+)$/gm, (_, text2) => source_default.bold(text2));
@@ -47848,6 +48144,10 @@ function parseMarkdown(text, options) {
47848
48144
  const code = block.replace(/```\w*\n?/g, "").replace(/```$/g, "").trim();
47849
48145
  return source_default.dim(code);
47850
48146
  });
48147
+ result = result.replace(/@@INLINECODE(\d+)@@/g, (_, index) => {
48148
+ const code = inlineCodeBlocks[parseInt(index, 10)] ?? "";
48149
+ return source_default.dim(code);
48150
+ });
47851
48151
  return result.trimEnd();
47852
48152
  }
47853
48153
  var ALLOWED_BLOCK_TYPES = new Set(["info", "success", "warning", "error", "note", "command"]);
@@ -48083,7 +48383,7 @@ function renderBlockSection(section, maxWidth) {
48083
48383
  if (section.kind === "grid") {
48084
48384
  const adjustedWidth2 = maxWidth ? Math.max(20, maxWidth - section.indent.length) : undefined;
48085
48385
  if (section.cards.length === 0) {
48086
- return renderCard({ type: "note", title: "Grid", body: section.body }, adjustedWidth2, section.indent).join(`
48386
+ return renderCard({ type: "note", title: "Grid", body: section.body }, adjustedWidth2, section.indent, Boolean(adjustedWidth2)).join(`
48087
48387
  `);
48088
48388
  }
48089
48389
  return renderCardGrid(section.cards, section.columns, adjustedWidth2, section.indent);
@@ -48215,7 +48515,15 @@ function formatMarkdownTables(text, maxWidth) {
48215
48515
  const lines = text.split(`
48216
48516
  `);
48217
48517
  const output = [];
48218
- const isSeparator = (line) => /^\s*\|?[\s:-]+\|?[\s:-]*$/.test(line);
48518
+ const isSeparator = (line) => {
48519
+ const trimmed = line.trim();
48520
+ if (!trimmed)
48521
+ return false;
48522
+ const withoutPipes = trimmed.replace(/\|/g, "");
48523
+ if (!withoutPipes)
48524
+ return false;
48525
+ return /^[\s:-]+$/.test(withoutPipes);
48526
+ };
48219
48527
  const hasPipes = (line) => line.includes("|");
48220
48528
  let i = 0;
48221
48529
  while (i < lines.length) {
@@ -48242,7 +48550,31 @@ function formatMarkdownTables(text, maxWidth) {
48242
48550
  function parseTableRow(line) {
48243
48551
  const trimmed = line.trim();
48244
48552
  const withoutEdges = trimmed.replace(/^\|/, "").replace(/\|$/, "");
48245
- return withoutEdges.split("|").map((cell) => cell.trim());
48553
+ const cells = [];
48554
+ let current = "";
48555
+ let escaping = false;
48556
+ for (let i = 0;i < withoutEdges.length; i += 1) {
48557
+ const ch = withoutEdges[i];
48558
+ if (escaping) {
48559
+ current += ch;
48560
+ escaping = false;
48561
+ continue;
48562
+ }
48563
+ if (ch === "\\") {
48564
+ escaping = true;
48565
+ continue;
48566
+ }
48567
+ if (ch === "|") {
48568
+ cells.push(current);
48569
+ current = "";
48570
+ continue;
48571
+ }
48572
+ current += ch;
48573
+ }
48574
+ if (escaping)
48575
+ current += "\\";
48576
+ cells.push(current);
48577
+ return cells.map((cell) => cell.replace(/[\r\n]+/g, " ").trim());
48246
48578
  }
48247
48579
  function renderTable(header, rows, maxWidth) {
48248
48580
  const colCount = Math.max(header.length, ...rows.map((r) => r.length));
@@ -48456,11 +48788,17 @@ function wrapAnsiLine(line, width) {
48456
48788
  const result = [];
48457
48789
  let current = "";
48458
48790
  let visible = 0;
48791
+ let activeAnsi = "";
48459
48792
  let i = 0;
48460
48793
  while (i < line.length) {
48461
48794
  const match = line.slice(i).match(/^\x1b\[[0-9;]*m/);
48462
48795
  if (match) {
48463
48796
  current += match[0];
48797
+ if (match[0] === "\x1B[0m") {
48798
+ activeAnsi = "";
48799
+ } else {
48800
+ activeAnsi += match[0];
48801
+ }
48464
48802
  i += match[0].length;
48465
48803
  continue;
48466
48804
  }
@@ -48469,7 +48807,7 @@ function wrapAnsiLine(line, width) {
48469
48807
  i += 1;
48470
48808
  if (visible >= width) {
48471
48809
  result.push(current);
48472
- current = "";
48810
+ current = activeAnsi;
48473
48811
  visible = 0;
48474
48812
  }
48475
48813
  }
@@ -48480,6 +48818,7 @@ function wrapAnsiLine(line, width) {
48480
48818
  function truncateAnsi(line, width) {
48481
48819
  if (stripAnsi2(line).length <= width)
48482
48820
  return line;
48821
+ const hasAnsi = /\x1b\[[0-9;]*m/.test(line);
48483
48822
  if (width <= 3) {
48484
48823
  let result = "";
48485
48824
  let visible2 = 0;
@@ -48495,7 +48834,7 @@ function truncateAnsi(line, width) {
48495
48834
  visible2 += 1;
48496
48835
  i2 += 1;
48497
48836
  }
48498
- return result;
48837
+ return hasAnsi && !result.endsWith("\x1B[0m") ? result + "\x1B[0m" : result;
48499
48838
  }
48500
48839
  const suffix = "...";
48501
48840
  const target = Math.max(0, width - suffix.length);
@@ -48513,7 +48852,8 @@ function truncateAnsi(line, width) {
48513
48852
  visible += 1;
48514
48853
  i += 1;
48515
48854
  }
48516
- return current + suffix;
48855
+ const truncated = current + suffix;
48856
+ return hasAnsi && !truncated.endsWith("\x1B[0m") ? truncated + "\x1B[0m" : truncated;
48517
48857
  }
48518
48858
 
48519
48859
  // packages/terminal/src/components/toolDisplay.ts
@@ -48537,7 +48877,7 @@ function truncateToolResult(toolResult, maxLines = 15, maxChars = 3000) {
48537
48877
  if (content.length > maxChars) {
48538
48878
  content = content.slice(0, maxChars) + "...";
48539
48879
  }
48540
- return prefix + content.trim();
48880
+ return prefix + content.trimEnd();
48541
48881
  }
48542
48882
  function formatToolResultNicely(toolName, content, isError) {
48543
48883
  if (isError) {
@@ -48706,7 +49046,7 @@ function estimateMessageLines(message, maxWidth) {
48706
49046
  const hasContent = contentLines.length > 0;
48707
49047
  const prefixWidth = message.role === "user" || message.role === "assistant" ? 2 : 0;
48708
49048
  const effectiveWidth = maxWidth ? Math.max(1, maxWidth - prefixWidth) : maxWidth;
48709
- const wrappedLines = contentLines.length > 0 ? countWrappedLines(contentLines, effectiveWidth) : 0;
49049
+ const wrappedLines = typeof message.__lineCount === "number" ? message.__lineCount : contentLines.length > 0 ? countWrappedLines(contentLines, effectiveWidth) : 0;
48710
49050
  let lines = hasContent ? Math.max(1, wrappedLines) : 0;
48711
49051
  if (message.role === "assistant" && message.toolCalls?.length) {
48712
49052
  lines += estimateToolPanelLines(message.toolCalls, message.toolResults, hasContent);
@@ -48786,11 +49126,13 @@ function Messages3({
48786
49126
  const columns = stdout?.columns ?? 80;
48787
49127
  const messageWidth = Math.max(1, columns - 2);
48788
49128
  const wrapWidth = messageWidth;
49129
+ const messageGroups = import_react24.useMemo(() => groupConsecutiveToolMessages(messages), [messages]);
49130
+ const messageItems = import_react24.useMemo(() => {
49131
+ return messageGroups.map((group) => group.type === "single" ? { kind: "message", message: group.message } : { kind: "grouped", messages: group.messages });
49132
+ }, [messageGroups]);
48789
49133
  const items = import_react24.useMemo(() => {
48790
49134
  const output = [];
48791
- for (const message of messages) {
48792
- output.push({ kind: "message", message });
48793
- }
49135
+ output.push(...messageItems);
48794
49136
  for (const entry of activityLog) {
48795
49137
  output.push({ kind: "activity", entry });
48796
49138
  }
@@ -48802,7 +49144,7 @@ function Messages3({
48802
49144
  const lineSpans = import_react24.useMemo(() => {
48803
49145
  let cursor = 0;
48804
49146
  return items.map((item, index) => {
48805
- const lines = item.kind === "activity" ? estimateActivityEntryLines(item.entry, wrapWidth, messageWidth) : estimateMessageLines(item.message, messageWidth);
49147
+ const lines = item.kind === "activity" ? estimateActivityEntryLines(item.entry, wrapWidth, messageWidth) : item.kind === "grouped" ? estimateGroupedToolLines(item.messages, messageWidth) : estimateMessageLines(item.message, messageWidth);
48806
49148
  const start = cursor;
48807
49149
  cursor += lines;
48808
49150
  return { item, index, start, end: cursor, lines };
@@ -48812,16 +49154,15 @@ function Messages3({
48812
49154
  const endLine = Math.max(0, totalLines - scrollOffsetLines);
48813
49155
  const startLine = Math.max(0, endLine - maxVisibleLines);
48814
49156
  const visibleSpans = lineSpans.filter((span) => span.end > startLine && span.start < endLine);
48815
- const visibleMessages = visibleSpans.filter((span) => span.item.kind === "message").map((span) => span.item.message);
49157
+ const visibleMessageItems = visibleSpans.filter((span) => span.item.kind === "message" || span.item.kind === "grouped").map((span) => span.item);
48816
49158
  const visibleActivity = visibleSpans.filter((span) => span.item.kind === "activity").map((span) => span.item.entry);
48817
49159
  const visibleStreaming = visibleSpans.filter((span) => span.item.kind === "streaming").map((span) => span.item.message);
48818
49160
  const showCurrentResponse = Boolean(currentResponse) && streamingMessages.length === 0;
48819
- const groupedMessages = groupConsecutiveToolMessages(visibleMessages);
48820
- const historicalItems = groupedMessages.map((group) => {
48821
- if (group.type === "single") {
48822
- return { id: group.message.id, group };
49161
+ const historicalItems = visibleMessageItems.map((item) => {
49162
+ if (item.kind === "message") {
49163
+ return { id: item.message.id, item };
48823
49164
  }
48824
- return { id: group.messages[0].id, group };
49165
+ return { id: item.messages[0].id, item };
48825
49166
  });
48826
49167
  const toolResultMap = import_react24.useMemo(() => {
48827
49168
  const map = new Map;
@@ -48855,14 +49196,14 @@ function Messages3({
48855
49196
  width: "100%",
48856
49197
  children: [
48857
49198
  historicalItems.map((item) => {
48858
- if (item.group.type === "single") {
49199
+ if (item.item.kind === "message") {
48859
49200
  return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(MessageBubble, {
48860
- message: item.group.message,
49201
+ message: item.item.message,
48861
49202
  queuedMessageIds
48862
49203
  }, item.id, false, undefined, this);
48863
49204
  }
48864
49205
  return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(CombinedToolMessage, {
48865
- messages: item.group.messages
49206
+ messages: item.item.messages
48866
49207
  }, item.id, false, undefined, this);
48867
49208
  }),
48868
49209
  visibleActivity.map((entry) => {
@@ -49006,11 +49347,35 @@ function CombinedToolMessage({ messages }) {
49006
49347
  allToolResults.push(...msg.toolResults);
49007
49348
  }
49008
49349
  }
49009
- return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ToolCallPanel, {
49010
- toolCalls: allToolCalls,
49011
- toolResults: allToolResults
49350
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
49351
+ marginY: 1,
49352
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ToolCallPanel, {
49353
+ toolCalls: allToolCalls,
49354
+ toolResults: allToolResults
49355
+ }, undefined, false, undefined, this)
49012
49356
  }, undefined, false, undefined, this);
49013
49357
  }
49358
+ function estimateGroupedToolLines(messages, messageWidth) {
49359
+ const toolCalls = [];
49360
+ const toolResults = [];
49361
+ for (const msg of messages) {
49362
+ if (msg.toolCalls)
49363
+ toolCalls.push(...msg.toolCalls);
49364
+ if (msg.toolResults)
49365
+ toolResults.push(...msg.toolResults);
49366
+ }
49367
+ if (toolCalls.length === 0)
49368
+ return 0;
49369
+ const synthetic = {
49370
+ id: "grouped-tool-call",
49371
+ role: "assistant",
49372
+ content: "",
49373
+ timestamp: 0,
49374
+ toolCalls,
49375
+ toolResults: toolResults.length > 0 ? toolResults : undefined
49376
+ };
49377
+ return estimateMessageLines(synthetic, messageWidth);
49378
+ }
49014
49379
  function MessageBubble({ message, queuedMessageIds }) {
49015
49380
  const isUser = message.role === "user";
49016
49381
  const isSystem = message.role === "system";
@@ -49157,7 +49522,7 @@ function ToolCallPanel({
49157
49522
  const statusColor = result ? result.isError ? "red" : "green" : "yellow";
49158
49523
  const displayName = getToolDisplayName(toolCall);
49159
49524
  const context2 = getToolContext(toolCall);
49160
- const maxLine = Math.max(16, innerWidth - 2);
49525
+ const maxLine = Math.max(1, innerWidth - 2);
49161
49526
  const summaryLine = truncate(formatToolCall(toolCall), maxLine);
49162
49527
  const resultText = result ? indentMultiline(truncateToolResult(result, 4, 400), " ") : "";
49163
49528
  return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
@@ -49333,8 +49698,9 @@ function formatScheduleCall(input) {
49333
49698
  return "Listing scheduled tasks";
49334
49699
  case "create":
49335
49700
  const cmd = truncate(String(input.command || ""), 30);
49336
- const schedule = String(input.schedule || "");
49337
- return `Creating schedule: "${cmd}" (${schedule})`;
49701
+ const when = input.cron ? `cron ${input.cron}` : String(input.at || "");
49702
+ const schedule = when ? ` (${truncate(String(when), 40)})` : "";
49703
+ return `Creating schedule: "${cmd}"${schedule}`;
49338
49704
  case "update":
49339
49705
  return `Updating schedule: ${input.id || "unknown"}`;
49340
49706
  case "delete":
@@ -49589,7 +49955,7 @@ function ProcessingIndicator({
49589
49955
  var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
49590
49956
  function WelcomeBanner({ version, model, directory }) {
49591
49957
  const homeDir = process.env.HOME || "";
49592
- const displayDir = directory.startsWith(homeDir) ? "~" + directory.slice(homeDir.length) : directory;
49958
+ const displayDir = homeDir && directory.startsWith(homeDir) ? "~" + directory.slice(homeDir.length) : directory;
49593
49959
  return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
49594
49960
  flexDirection: "column",
49595
49961
  marginBottom: 1,
@@ -49675,7 +50041,7 @@ function formatSessionTime(timestamp) {
49675
50041
  }
49676
50042
  function formatPath(cwd2) {
49677
50043
  const home = process.env.HOME || "";
49678
- if (cwd2.startsWith(home)) {
50044
+ if (home && cwd2.startsWith(home)) {
49679
50045
  return "~" + cwd2.slice(home.length);
49680
50046
  }
49681
50047
  return cwd2;
@@ -49831,6 +50197,16 @@ function wrapTextLines(text, wrapChars) {
49831
50197
  function stripAnsi6(text) {
49832
50198
  return text.replace(/\x1B\[[0-9;]*m/g, "");
49833
50199
  }
50200
+ function countWrappedLines2(lines, maxWidth) {
50201
+ if (!maxWidth || maxWidth <= 0)
50202
+ return lines.length;
50203
+ let total = 0;
50204
+ for (const line of lines) {
50205
+ const visible = stripAnsi6(line).length;
50206
+ total += Math.max(1, Math.ceil(visible / maxWidth));
50207
+ }
50208
+ return total;
50209
+ }
49834
50210
  function chunkRenderedLines(lines, chunkLines) {
49835
50211
  const chunks = [];
49836
50212
  let current = [];
@@ -49881,31 +50257,36 @@ function buildDisplayMessages(messages, chunkLines, wrapChars, options) {
49881
50257
  continue;
49882
50258
  }
49883
50259
  if (msg.role === "assistant") {
49884
- const rendered = renderMarkdown(content, { maxWidth: options?.maxWidth });
50260
+ const assistantWidth = options?.maxWidth ? Math.max(1, options.maxWidth - 2) : undefined;
50261
+ const rendered = renderMarkdown(content, { maxWidth: assistantWidth });
49885
50262
  const renderedLines = rendered.split(`
49886
50263
  `);
49887
50264
  if (renderedLines.length <= chunkLines) {
49888
- display.push({ ...msg, content: rendered, __rendered: true });
50265
+ const lineCount = countWrappedLines2(renderedLines, assistantWidth);
50266
+ display.push({ ...msg, content: rendered, __rendered: true, __lineCount: lineCount });
49889
50267
  continue;
49890
50268
  }
49891
50269
  const chunks2 = chunkRenderedLines(renderedLines, chunkLines);
49892
50270
  for (let i = 0;i < chunks2.length; i++) {
49893
50271
  const chunkContent = chunks2[i].join(`
49894
50272
  `);
50273
+ const lineCount = countWrappedLines2(chunks2[i], assistantWidth);
49895
50274
  display.push({
49896
50275
  ...msg,
49897
50276
  id: `${msg.id}::chunk-${i}`,
49898
50277
  content: chunkContent,
49899
50278
  __rendered: true,
50279
+ __lineCount: lineCount,
49900
50280
  toolCalls: i === chunks2.length - 1 ? msg.toolCalls : undefined,
49901
50281
  toolResults: i === chunks2.length - 1 ? msg.toolResults : undefined
49902
50282
  });
49903
50283
  }
49904
50284
  continue;
49905
50285
  }
49906
- const lines = wrapTextLines(content, wrapChars);
50286
+ const effectiveWrap = msg.role === "user" ? Math.max(1, wrapChars - 2) : wrapChars;
50287
+ const lines = wrapTextLines(content, effectiveWrap);
49907
50288
  if (lines.length <= chunkLines) {
49908
- display.push(msg);
50289
+ display.push({ ...msg, __lineCount: lines.length });
49909
50290
  continue;
49910
50291
  }
49911
50292
  const chunks = chunkRenderedLines(lines, chunkLines);
@@ -49916,6 +50297,7 @@ function buildDisplayMessages(messages, chunkLines, wrapChars, options) {
49916
50297
  ...msg,
49917
50298
  id: `${msg.id}::chunk-${i}`,
49918
50299
  content: chunkContent,
50300
+ __lineCount: chunks[i].length,
49919
50301
  toolCalls: i === chunks.length - 1 ? msg.toolCalls : undefined,
49920
50302
  toolResults: i === chunks.length - 1 ? msg.toolResults : undefined
49921
50303
  });
@@ -49951,6 +50333,7 @@ function App2({ cwd: cwd2, version }) {
49951
50333
  const [scrollOffset, setScrollOffset] = import_react29.useState(0);
49952
50334
  const [autoScroll, setAutoScroll] = import_react29.useState(true);
49953
50335
  const [skills, setSkills] = import_react29.useState([]);
50336
+ const [commands, setCommands] = import_react29.useState([]);
49954
50337
  const responseRef = import_react29.useRef("");
49955
50338
  const toolCallsRef = import_react29.useRef([]);
49956
50339
  const toolResultsRef = import_react29.useRef([]);
@@ -49979,6 +50362,25 @@ function App2({ cwd: cwd2, version }) {
49979
50362
  return parts.join(`
49980
50363
  `).trim();
49981
50364
  }, []);
50365
+ const loadSessionMetadata = import_react29.useCallback(async (session) => {
50366
+ try {
50367
+ const [loadedSkills, loadedCommands] = await Promise.all([
50368
+ session.client.getSkills(),
50369
+ session.client.getCommands()
50370
+ ]);
50371
+ setSkills(loadedSkills.map((s) => ({
50372
+ name: s.name,
50373
+ description: s.description || "",
50374
+ argumentHint: s.argumentHint
50375
+ })));
50376
+ setCommands(loadedCommands.map((cmd) => ({
50377
+ name: cmd.name.startsWith("/") ? cmd.name : `/${cmd.name}`,
50378
+ description: cmd.description || ""
50379
+ })));
50380
+ } catch (err) {
50381
+ setError(err instanceof Error ? err.message : String(err));
50382
+ }
50383
+ }, []);
49982
50384
  const finalizeResponse = import_react29.useCallback((status) => {
49983
50385
  const baseContent = buildFullResponse();
49984
50386
  const hasContent = baseContent.length > 0;
@@ -50247,12 +50649,7 @@ function App2({ cwd: cwd2, version }) {
50247
50649
  });
50248
50650
  const session = await registry.createSession(cwd2);
50249
50651
  setActiveSessionId(session.id);
50250
- const loadedSkills = await session.client.getSkills();
50251
- setSkills(loadedSkills.map((s) => ({
50252
- name: s.name,
50253
- description: s.description || "",
50254
- argumentHint: s.argumentHint
50255
- })));
50652
+ await loadSessionMetadata(session);
50256
50653
  setEnergyState(session.client.getEnergyState() ?? undefined);
50257
50654
  setVoiceState(session.client.getVoiceState() ?? undefined);
50258
50655
  setIdentityInfo(session.client.getIdentityInfo() ?? undefined);
@@ -50266,7 +50663,7 @@ function App2({ cwd: cwd2, version }) {
50266
50663
  return () => {
50267
50664
  registry.closeAll();
50268
50665
  };
50269
- }, [cwd2, registry, handleChunk, finalizeResponse, resetTurnState]);
50666
+ }, [cwd2, registry, handleChunk, finalizeResponse, resetTurnState, loadSessionMetadata]);
50270
50667
  const processQueue = import_react29.useCallback(async () => {
50271
50668
  const activeSession2 = registryRef.current.getActiveSession();
50272
50669
  if (!activeSession2 || !activeSessionId)
@@ -50336,6 +50733,7 @@ function App2({ cwd: cwd2, version }) {
50336
50733
  }, [activityLog, renderWidth, wrapChars]);
50337
50734
  const reservedLines = import_react29.useMemo(() => {
50338
50735
  let lines = 0;
50736
+ lines += 2;
50339
50737
  if (showWelcome) {
50340
50738
  lines += 4;
50341
50739
  }
@@ -50367,9 +50765,9 @@ function App2({ cwd: cwd2, version }) {
50367
50765
  lines += 3;
50368
50766
  }
50369
50767
  lines += 1;
50370
- lines += 1;
50768
+ lines += 3;
50371
50769
  if (isProcessing) {
50372
- lines += 2;
50770
+ lines += 1;
50373
50771
  }
50374
50772
  lines += 2;
50375
50773
  return lines;
@@ -50436,6 +50834,7 @@ function App2({ cwd: cwd2, version }) {
50436
50834
  setEnergyState(session.client.getEnergyState() ?? undefined);
50437
50835
  setVoiceState(session.client.getVoiceState() ?? undefined);
50438
50836
  setIdentityInfo(session.client.getIdentityInfo() ?? undefined);
50837
+ await loadSessionMetadata(session);
50439
50838
  }
50440
50839
  await registry.switchSession(sessionId);
50441
50840
  setActiveSessionId(sessionId);
@@ -50453,6 +50852,7 @@ function App2({ cwd: cwd2, version }) {
50453
50852
  setEnergyState(newSession.client.getEnergyState() ?? undefined);
50454
50853
  setVoiceState(newSession.client.getVoiceState() ?? undefined);
50455
50854
  setIdentityInfo(newSession.client.getIdentityInfo() ?? undefined);
50855
+ await loadSessionMetadata(newSession);
50456
50856
  } catch (err) {
50457
50857
  setError(err instanceof Error ? err.message : "Failed to create session");
50458
50858
  }
@@ -50783,6 +51183,7 @@ function App2({ cwd: cwd2, version }) {
50783
51183
  onSubmit: handleSubmit,
50784
51184
  isProcessing,
50785
51185
  queueLength: activeQueue.length + inlineCount,
51186
+ commands,
50786
51187
  skills
50787
51188
  }, undefined, false, undefined, this),
50788
51189
  /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Status, {
@@ -50978,7 +51379,7 @@ function formatStreamEvent(chunk) {
50978
51379
 
50979
51380
  // packages/terminal/src/index.tsx
50980
51381
  var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
50981
- var VERSION3 = "0.6.32";
51382
+ var VERSION3 = "0.6.35";
50982
51383
  process.env.ASSISTANTS_VERSION ??= VERSION3;
50983
51384
  function parseArgs(argv) {
50984
51385
  const args = argv.slice(2);
@@ -51134,4 +51535,4 @@ if (options.print !== null) {
51134
51535
  });
51135
51536
  }
51136
51537
 
51137
- //# debugId=E7E53BFBEA38084764756E2164756E21
51538
+ //# debugId=2F2CC5B1781D557164756E2164756E21