@lobehub/cli 0.0.13 → 0.0.16-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +2049 -501
  2. package/man/man1/lh.1 +1 -4
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
3
  import { EventEmitter } from "node:events";
4
- import { execFile, spawn } from "node:child_process";
5
- import * as path$1 from "node:path";
6
- import path from "node:path";
4
+ import { execFile, execFileSync, spawn } from "node:child_process";
5
+ import path, { basename, extname } from "node:path";
7
6
  import fs, { createReadStream, existsSync, readFileSync, writeFileSync } from "node:fs";
8
- import os, { tmpdir } from "node:os";
7
+ import os, { platform, tmpdir } from "node:os";
9
8
  import crypto$1, { createHash, randomUUID } from "node:crypto";
10
9
  import { createInterface } from "node:readline";
11
10
  import { text } from "node:stream/consumers";
12
- import fs$1, { access, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
11
+ import fs$1, { access, mkdir, open, readFile, readdir, stat, writeFile } from "node:fs/promises";
13
12
 
14
13
  //#region \0rolldown/runtime.js
15
14
  var __create$2 = Object.create;
@@ -1034,7 +1033,7 @@ var require_suggestSimilar = /* @__PURE__ */ __commonJSMin(((exports) => {
1034
1033
  var require_command = /* @__PURE__ */ __commonJSMin(((exports) => {
1035
1034
  const EventEmitter$7 = __require("node:events").EventEmitter;
1036
1035
  const childProcess = __require("node:child_process");
1037
- const path$12 = __require("node:path");
1036
+ const path$11 = __require("node:path");
1038
1037
  const fs$12 = __require("node:fs");
1039
1038
  const process$1 = __require("node:process");
1040
1039
  const { Argument, humanReadableArgName } = require_argument();
@@ -1941,9 +1940,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1941
1940
  ".cjs"
1942
1941
  ];
1943
1942
  function findFile(baseDir, baseName) {
1944
- const localBin = path$12.resolve(baseDir, baseName);
1943
+ const localBin = path$11.resolve(baseDir, baseName);
1945
1944
  if (fs$12.existsSync(localBin)) return localBin;
1946
- if (sourceExt.includes(path$12.extname(baseName))) return void 0;
1945
+ if (sourceExt.includes(path$11.extname(baseName))) return void 0;
1947
1946
  const foundExt = sourceExt.find((ext) => fs$12.existsSync(`${localBin}${ext}`));
1948
1947
  if (foundExt) return `${localBin}${foundExt}`;
1949
1948
  }
@@ -1958,17 +1957,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
1958
1957
  } catch {
1959
1958
  resolvedScriptPath = this._scriptPath;
1960
1959
  }
1961
- executableDir = path$12.resolve(path$12.dirname(resolvedScriptPath), executableDir);
1960
+ executableDir = path$11.resolve(path$11.dirname(resolvedScriptPath), executableDir);
1962
1961
  }
1963
1962
  if (executableDir) {
1964
1963
  let localFile = findFile(executableDir, executableFile);
1965
1964
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1966
- const legacyName = path$12.basename(this._scriptPath, path$12.extname(this._scriptPath));
1965
+ const legacyName = path$11.basename(this._scriptPath, path$11.extname(this._scriptPath));
1967
1966
  if (legacyName !== this._name) localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
1968
1967
  }
1969
1968
  executableFile = localFile || executableFile;
1970
1969
  }
1971
- launchWithNode = sourceExt.includes(path$12.extname(executableFile));
1970
+ launchWithNode = sourceExt.includes(path$11.extname(executableFile));
1972
1971
  let proc;
1973
1972
  if (process$1.platform !== "win32") if (launchWithNode) {
1974
1973
  args.unshift(executableFile);
@@ -2672,7 +2671,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2672
2671
  * @return {Command}
2673
2672
  */
2674
2673
  nameFromFilename(filename) {
2675
- this._name = path$12.basename(filename, path$12.extname(filename));
2674
+ this._name = path$11.basename(filename, path$11.extname(filename));
2676
2675
  return this;
2677
2676
  }
2678
2677
  /**
@@ -6949,7 +6948,10 @@ function registerAgentCommand(program) {
6949
6948
  return;
6950
6949
  }
6951
6950
  }
6952
- const input = { prompt: options.prompt };
6951
+ const input = {
6952
+ prompt: options.prompt,
6953
+ trigger: "cli"
6954
+ };
6953
6955
  if (options.agentId) input.agentId = options.agentId;
6954
6956
  if (deviceId) input.deviceId = deviceId;
6955
6957
  if (options.slug) input.slug = options.slug;
@@ -7346,8 +7348,7 @@ const DEFAULT_AGENT_SEARCH_FC_MODEL = {
7346
7348
  provider: DEFAULT_PROVIDER
7347
7349
  };
7348
7350
  const DEFAULT_AGENT_CHAT_CONFIG = {
7349
- autoCreateTopicThreshold: 2,
7350
- enableAutoCreateTopic: true,
7351
+ enableAgentMode: true,
7351
7352
  enableCompressHistory: true,
7352
7353
  enableContextCompression: true,
7353
7354
  enableHistoryCount: false,
@@ -7414,7 +7415,6 @@ const KeyEnum = {
7414
7415
  };
7415
7416
  const HotkeyEnum = {
7416
7417
  AddUserMessage: "addUserMessage",
7417
- ClearCurrentMessages: "clearCurrentMessages",
7418
7418
  CommandPalette: "commandPalette",
7419
7419
  DeleteAndRegenerateMessage: "deleteAndRegenerateMessage",
7420
7420
  DeleteLastMessage: "deleteLastMessage",
@@ -7543,16 +7543,6 @@ const HOTKEYS_REGISTRATION = [
7543
7543
  nonEditable: true,
7544
7544
  scopes: [HotkeyScopeEnum.Chat]
7545
7545
  },
7546
- {
7547
- group: HotkeyGroupEnum.Conversation,
7548
- id: HotkeyEnum.ClearCurrentMessages,
7549
- keys: combineKeys([
7550
- KeyEnum.Alt,
7551
- KeyEnum.Shift,
7552
- KeyEnum.Backspace
7553
- ]),
7554
- scopes: [HotkeyScopeEnum.Chat]
7555
- },
7556
7546
  {
7557
7547
  group: HotkeyGroupEnum.Essential,
7558
7548
  id: HotkeyEnum.SaveDocument,
@@ -7589,6 +7579,29 @@ const DEFAULT_INPUT_COMPLETION_SYSTEM_AGENT_ITEM = {
7589
7579
  //#region ../../packages/const/src/discover.ts
7590
7580
  const DEFAULT_CREATED_AT = (/* @__PURE__ */ new Date()).toISOString();
7591
7581
 
7582
+ //#endregion
7583
+ //#region ../../packages/const/src/interests.ts
7584
+ const INTEREST_AREA_KEYS = [
7585
+ "writing",
7586
+ "coding",
7587
+ "design",
7588
+ "education",
7589
+ "business",
7590
+ "marketing",
7591
+ "product",
7592
+ "sales",
7593
+ "operations",
7594
+ "hr",
7595
+ "finance-legal",
7596
+ "creator",
7597
+ "investing",
7598
+ "parenting",
7599
+ "health",
7600
+ "hobbies",
7601
+ "personal"
7602
+ ];
7603
+ const interestAreaKeySet = new Set(INTEREST_AREA_KEYS);
7604
+
7592
7605
  //#endregion
7593
7606
  //#region ../../../node_modules/.pnpm/react@19.2.4/node_modules/react/cjs/react-jsx-runtime.production.js
7594
7607
  /**
@@ -10011,16 +10024,6 @@ const KLAVIS_SERVER_TYPES = [
10011
10024
  label: "Google Calendar",
10012
10025
  serverName: McpServerName.GoogleCalendar
10013
10026
  },
10014
- {
10015
- author: "Klavis",
10016
- authorUrl: "https://klavis.io",
10017
- description: "Notion is a collaborative productivity and note-taking application",
10018
- icon: "https://hub-apac-1.lobeobjects.space/assets/logos/notion.svg",
10019
- identifier: "notion",
10020
- readme: "Connect to Notion to access and manage your workspace. Create pages, search content, update databases, and organize your knowledge base—all through natural conversation with your AI assistant.",
10021
- label: "Notion",
10022
- serverName: McpServerName.Notion
10023
- },
10024
10027
  {
10025
10028
  author: "Klavis",
10026
10029
  authorUrl: "https://klavis.io",
@@ -10213,6 +10216,15 @@ const KLAVIS_SERVER_TYPES = [
10213
10216
  }
10214
10217
  ];
10215
10218
 
10219
+ //#endregion
10220
+ //#region ../../package.json
10221
+ var version$3 = "2.2.0";
10222
+
10223
+ //#endregion
10224
+ //#region ../../packages/const/src/version.ts
10225
+ const CURRENT_VERSION = version$3;
10226
+ const isDesktop = typeof __ELECTRON__ !== "undefined" && !!__ELECTRON__;
10227
+
10216
10228
  //#endregion
10217
10229
  //#region ../../../node_modules/.pnpm/es-toolkit@1.46.1/node_modules/es-toolkit/dist/predicate/isLength.mjs
10218
10230
  function isLength(value) {
@@ -10621,6 +10633,669 @@ const DEFAULT_INBOX_SESSION = merge(DEFAULT_AGENT_LOBE_SESSION, {
10621
10633
  meta: { avatar: DEFAULT_INBOX_AVATAR }
10622
10634
  });
10623
10635
 
10636
+ //#endregion
10637
+ //#region ../../packages/const/src/taskTemplate.ts
10638
+ const taskTemplates = [
10639
+ {
10640
+ id: "daily-topic-pick",
10641
+ category: "content-creation",
10642
+ cronPattern: "0 9 * * *",
10643
+ interests: ["writing"]
10644
+ },
10645
+ {
10646
+ id: "hot-topic-radar",
10647
+ category: "content-creation",
10648
+ cronPattern: "0 10 * * *",
10649
+ interests: ["writing"]
10650
+ },
10651
+ {
10652
+ id: "headline-inspiration",
10653
+ category: "content-creation",
10654
+ cronPattern: "0 10 * * *",
10655
+ interests: ["writing"]
10656
+ },
10657
+ {
10658
+ id: "viral-content-breakdown",
10659
+ category: "content-creation",
10660
+ cronPattern: "0 10 * * *",
10661
+ interests: ["writing"]
10662
+ },
10663
+ {
10664
+ id: "twitter-weekly-recap",
10665
+ category: "content-creation",
10666
+ cronPattern: "0 10 * * 1",
10667
+ interests: ["writing", "creator"],
10668
+ requiresSkills: [{
10669
+ provider: "twitter",
10670
+ source: "lobehub"
10671
+ }]
10672
+ },
10673
+ {
10674
+ id: "youtube-weekly-recap",
10675
+ category: "content-creation",
10676
+ cronPattern: "0 9 * * 1",
10677
+ interests: ["writing", "creator"],
10678
+ requiresSkills: [{
10679
+ provider: "youtube",
10680
+ source: "klavis"
10681
+ }]
10682
+ },
10683
+ {
10684
+ id: "competitor-creator-tracking",
10685
+ category: "content-creation",
10686
+ cronPattern: "0 9 * * *",
10687
+ interests: ["writing", "creator"]
10688
+ },
10689
+ {
10690
+ id: "content-calendar-weekly",
10691
+ category: "content-creation",
10692
+ cronPattern: "0 20 * * 0",
10693
+ interests: ["writing", "creator"],
10694
+ optionalSkills: [{
10695
+ provider: "notion",
10696
+ source: "lobehub"
10697
+ }]
10698
+ },
10699
+ {
10700
+ id: "oss-intel-daily",
10701
+ category: "engineering",
10702
+ cronPattern: "0 9 * * *",
10703
+ icon: "github",
10704
+ interests: ["coding"]
10705
+ },
10706
+ {
10707
+ id: "repo-health-weekly",
10708
+ category: "engineering",
10709
+ cronPattern: "0 9 * * 1",
10710
+ interests: ["coding"],
10711
+ requiresSkills: [{
10712
+ provider: "github",
10713
+ source: "lobehub"
10714
+ }]
10715
+ },
10716
+ {
10717
+ id: "dependency-security-weekly",
10718
+ category: "engineering",
10719
+ cronPattern: "0 10 * * 1",
10720
+ interests: ["coding"],
10721
+ requiresSkills: [{
10722
+ provider: "github",
10723
+ source: "lobehub"
10724
+ }]
10725
+ },
10726
+ {
10727
+ id: "vercel-health-weekly",
10728
+ category: "engineering",
10729
+ cronPattern: "0 10 * * 1",
10730
+ interests: ["coding"],
10731
+ requiresSkills: [{
10732
+ provider: "vercel",
10733
+ source: "lobehub"
10734
+ }]
10735
+ },
10736
+ {
10737
+ id: "linear-sprint-daily",
10738
+ category: "engineering",
10739
+ cronPattern: "30 8 * * *",
10740
+ interests: ["coding", "product"],
10741
+ requiresSkills: [{
10742
+ provider: "linear",
10743
+ source: "lobehub"
10744
+ }]
10745
+ },
10746
+ {
10747
+ id: "tech-trend-weekly",
10748
+ category: "engineering",
10749
+ cronPattern: "0 8 * * 1",
10750
+ interests: ["coding"]
10751
+ },
10752
+ {
10753
+ id: "keyword-tech-feed",
10754
+ category: "engineering",
10755
+ cronPattern: "0 10 * * *",
10756
+ interests: ["coding"]
10757
+ },
10758
+ {
10759
+ id: "daily-design-inspiration",
10760
+ category: "design",
10761
+ cronPattern: "0 9 * * *",
10762
+ interests: ["design"]
10763
+ },
10764
+ {
10765
+ id: "design-trend-weekly",
10766
+ category: "design",
10767
+ cronPattern: "0 9 * * 1",
10768
+ interests: ["design"]
10769
+ },
10770
+ {
10771
+ id: "figma-files-cleanup",
10772
+ category: "design",
10773
+ cronPattern: "0 17 * * 5",
10774
+ interests: ["design"],
10775
+ requiresSkills: [{
10776
+ provider: "figma",
10777
+ source: "klavis"
10778
+ }]
10779
+ },
10780
+ {
10781
+ id: "aigc-prompt-inspiration",
10782
+ category: "design",
10783
+ cronPattern: "0 10 * * *",
10784
+ interests: ["design"]
10785
+ },
10786
+ {
10787
+ id: "brand-watch-weekly",
10788
+ category: "design",
10789
+ cronPattern: "0 10 * * 1",
10790
+ interests: ["design"]
10791
+ },
10792
+ {
10793
+ id: "font-color-weekly",
10794
+ category: "design",
10795
+ cronPattern: "0 10 * * 3",
10796
+ interests: ["design"]
10797
+ },
10798
+ {
10799
+ id: "arxiv-curated-daily",
10800
+ category: "learning-research",
10801
+ cronPattern: "0 9 * * *",
10802
+ interests: ["education"]
10803
+ },
10804
+ {
10805
+ id: "must-read-papers-weekly",
10806
+ category: "learning-research",
10807
+ cronPattern: "0 20 * * 0",
10808
+ interests: ["education"]
10809
+ },
10810
+ {
10811
+ id: "language-morning-bite",
10812
+ category: "learning-research",
10813
+ cronPattern: "30 7 * * *",
10814
+ interests: ["education"]
10815
+ },
10816
+ {
10817
+ id: "industry-research-weekly",
10818
+ category: "learning-research",
10819
+ cronPattern: "0 9 * * 1",
10820
+ interests: ["education", "business"]
10821
+ },
10822
+ {
10823
+ id: "industry-morning-brief",
10824
+ category: "business",
10825
+ cronPattern: "0 8 * * *",
10826
+ interests: ["business"]
10827
+ },
10828
+ {
10829
+ id: "competitor-radar-daily",
10830
+ category: "business",
10831
+ cronPattern: "0 9 * * *",
10832
+ interests: ["business"]
10833
+ },
10834
+ {
10835
+ id: "funding-intel-daily",
10836
+ category: "business",
10837
+ cronPattern: "0 10 * * *",
10838
+ interests: ["business"]
10839
+ },
10840
+ {
10841
+ id: "macro-economy-weekly",
10842
+ category: "business",
10843
+ cronPattern: "0 8 * * 1",
10844
+ interests: ["business", "investing"]
10845
+ },
10846
+ {
10847
+ id: "weekly-meeting-brief",
10848
+ category: "business",
10849
+ cronPattern: "30 8 * * 1",
10850
+ interests: ["business"]
10851
+ },
10852
+ {
10853
+ id: "marketing-hot-radar",
10854
+ category: "marketing",
10855
+ cronPattern: "0 10 * * *",
10856
+ interests: ["marketing"]
10857
+ },
10858
+ {
10859
+ id: "ad-creative-inspiration",
10860
+ category: "marketing",
10861
+ cronPattern: "0 10 * * *",
10862
+ interests: ["marketing"]
10863
+ },
10864
+ {
10865
+ id: "brand-mention-daily",
10866
+ category: "marketing",
10867
+ cronPattern: "0 18 * * *",
10868
+ interests: ["marketing"],
10869
+ requiresSkills: [{
10870
+ provider: "twitter",
10871
+ source: "lobehub"
10872
+ }]
10873
+ },
10874
+ {
10875
+ id: "seo-weekly-report",
10876
+ category: "marketing",
10877
+ cronPattern: "0 9 * * 1",
10878
+ interests: ["marketing"]
10879
+ },
10880
+ {
10881
+ id: "newsletter-perf-weekly",
10882
+ category: "marketing",
10883
+ cronPattern: "0 10 * * 1",
10884
+ interests: ["marketing"],
10885
+ requiresSkills: [{
10886
+ provider: "gmail",
10887
+ source: "klavis"
10888
+ }]
10889
+ },
10890
+ {
10891
+ id: "kol-collab-calendar",
10892
+ category: "marketing",
10893
+ cronPattern: "0 9 * * 1",
10894
+ interests: ["marketing"],
10895
+ requiresSkills: [{
10896
+ provider: "airtable",
10897
+ source: "klavis"
10898
+ }]
10899
+ },
10900
+ {
10901
+ id: "hubspot-funnel-daily",
10902
+ category: "marketing",
10903
+ cronPattern: "0 9 * * *",
10904
+ interests: ["marketing", "sales"],
10905
+ requiresSkills: [{
10906
+ provider: "hubspot",
10907
+ source: "klavis"
10908
+ }]
10909
+ },
10910
+ {
10911
+ id: "user-feedback-daily",
10912
+ category: "product",
10913
+ cronPattern: "0 9 * * *",
10914
+ interests: ["product"]
10915
+ },
10916
+ {
10917
+ id: "competitor-update-daily",
10918
+ category: "product",
10919
+ cronPattern: "0 10 * * *",
10920
+ interests: ["product"]
10921
+ },
10922
+ {
10923
+ id: "standup-brief",
10924
+ category: "product",
10925
+ cronPattern: "30 8 * * *",
10926
+ interests: ["product"],
10927
+ requiresSkills: [{
10928
+ provider: "linear",
10929
+ source: "lobehub"
10930
+ }]
10931
+ },
10932
+ {
10933
+ id: "iteration-recap-weekly",
10934
+ category: "product",
10935
+ cronPattern: "0 17 * * 5",
10936
+ interests: ["product"],
10937
+ requiresSkills: [{
10938
+ provider: "linear",
10939
+ source: "lobehub"
10940
+ }]
10941
+ },
10942
+ {
10943
+ id: "core-metric-daily",
10944
+ category: "product",
10945
+ cronPattern: "0 9 * * *",
10946
+ interests: ["product"]
10947
+ },
10948
+ {
10949
+ id: "user-interview-schedule",
10950
+ category: "product",
10951
+ cronPattern: "0 9 * * 1",
10952
+ interests: ["product"],
10953
+ requiresSkills: [{
10954
+ provider: "google-calendar",
10955
+ source: "klavis"
10956
+ }]
10957
+ },
10958
+ {
10959
+ id: "prd-review-reminder",
10960
+ category: "product",
10961
+ cronPattern: "0 15 * * 5",
10962
+ interests: ["product"],
10963
+ requiresSkills: [{
10964
+ provider: "notion",
10965
+ source: "lobehub"
10966
+ }]
10967
+ },
10968
+ {
10969
+ id: "daily-followup-list",
10970
+ category: "sales-customer",
10971
+ cronPattern: "0 9 * * *",
10972
+ interests: ["sales"],
10973
+ requiresSkills: [{
10974
+ provider: "hubspot",
10975
+ source: "klavis"
10976
+ }]
10977
+ },
10978
+ {
10979
+ id: "renewal-risk-weekly",
10980
+ category: "sales-customer",
10981
+ cronPattern: "0 9 * * 1",
10982
+ interests: ["sales"],
10983
+ requiresSkills: [{
10984
+ provider: "hubspot",
10985
+ source: "klavis"
10986
+ }]
10987
+ },
10988
+ {
10989
+ id: "deal-pipeline-weekly",
10990
+ category: "sales-customer",
10991
+ cronPattern: "0 16 * * 5",
10992
+ interests: ["sales"],
10993
+ requiresSkills: [{
10994
+ provider: "hubspot",
10995
+ source: "klavis"
10996
+ }]
10997
+ },
10998
+ {
10999
+ id: "key-account-radar",
11000
+ category: "sales-customer",
11001
+ cronPattern: "0 9 * * *",
11002
+ interests: ["sales"]
11003
+ },
11004
+ {
11005
+ id: "zendesk-ticket-daily",
11006
+ category: "sales-customer",
11007
+ cronPattern: "0 9 * * *",
11008
+ interests: ["sales"],
11009
+ requiresSkills: [{
11010
+ provider: "zendesk",
11011
+ source: "klavis"
11012
+ }]
11013
+ },
11014
+ {
11015
+ id: "morning-brief",
11016
+ category: "operations",
11017
+ cronPattern: "0 8 * * *",
11018
+ interests: ["operations"],
11019
+ requiresSkills: [{
11020
+ provider: "google-calendar",
11021
+ source: "klavis"
11022
+ }]
11023
+ },
11024
+ {
11025
+ id: "meeting-brief",
11026
+ category: "operations",
11027
+ cronPattern: "30 8 * * *",
11028
+ interests: ["operations"],
11029
+ requiresSkills: [{
11030
+ provider: "google-calendar",
11031
+ source: "klavis"
11032
+ }]
11033
+ },
11034
+ {
11035
+ id: "calendar-conflict-check",
11036
+ category: "operations",
11037
+ cronPattern: "30 7 * * *",
11038
+ interests: ["operations"],
11039
+ requiresSkills: [{
11040
+ provider: "google-calendar",
11041
+ source: "klavis"
11042
+ }]
11043
+ },
11044
+ {
11045
+ id: "friday-wrap-list",
11046
+ category: "operations",
11047
+ cronPattern: "0 16 * * 5",
11048
+ interests: ["operations"],
11049
+ requiresSkills: [{
11050
+ provider: "linear",
11051
+ source: "lobehub"
11052
+ }]
11053
+ },
11054
+ {
11055
+ id: "recruit-funnel-daily",
11056
+ category: "hr",
11057
+ cronPattern: "0 9 * * *",
11058
+ interests: ["hr"],
11059
+ requiresSkills: [{
11060
+ provider: "airtable",
11061
+ source: "klavis"
11062
+ }]
11063
+ },
11064
+ {
11065
+ id: "onboarding-buddy-weekly",
11066
+ category: "hr",
11067
+ cronPattern: "0 9 * * 1",
11068
+ interests: ["hr"],
11069
+ requiresSkills: [{
11070
+ provider: "notion",
11071
+ source: "lobehub"
11072
+ }]
11073
+ },
11074
+ {
11075
+ id: "team-status-weekly",
11076
+ category: "hr",
11077
+ cronPattern: "0 9 * * 1",
11078
+ interests: ["hr"],
11079
+ requiresSkills: [{
11080
+ provider: "google-calendar",
11081
+ source: "klavis"
11082
+ }]
11083
+ },
11084
+ {
11085
+ id: "precious-metals-daily",
11086
+ category: "finance-legal",
11087
+ cronPattern: "0 16 * * *",
11088
+ interests: ["finance-legal", "investing"]
11089
+ },
11090
+ {
11091
+ id: "pre-market-brief",
11092
+ category: "finance-legal",
11093
+ cronPattern: "0 9 * * *",
11094
+ interests: ["finance-legal", "investing"]
11095
+ },
11096
+ {
11097
+ id: "cashflow-weekly",
11098
+ category: "finance-legal",
11099
+ cronPattern: "0 9 * * 1",
11100
+ interests: ["finance-legal"],
11101
+ requiresSkills: [{
11102
+ provider: "airtable",
11103
+ source: "klavis"
11104
+ }]
11105
+ },
11106
+ {
11107
+ id: "contract-expiry-weekly",
11108
+ category: "finance-legal",
11109
+ cronPattern: "0 9 * * 1",
11110
+ interests: ["finance-legal"],
11111
+ requiresSkills: [{
11112
+ provider: "notion",
11113
+ source: "lobehub"
11114
+ }]
11115
+ },
11116
+ {
11117
+ id: "regulation-watch-weekly",
11118
+ category: "finance-legal",
11119
+ cronPattern: "0 10 * * 1",
11120
+ interests: ["finance-legal"]
11121
+ },
11122
+ {
11123
+ id: "invoice-collection-daily",
11124
+ category: "finance-legal",
11125
+ cronPattern: "0 10 * * *",
11126
+ interests: ["finance-legal"],
11127
+ requiresSkills: [{
11128
+ provider: "gmail",
11129
+ source: "klavis"
11130
+ }]
11131
+ },
11132
+ {
11133
+ id: "cross-platform-engagement-daily",
11134
+ category: "creator",
11135
+ cronPattern: "0 9 * * *",
11136
+ interests: ["creator"],
11137
+ requiresSkills: [{
11138
+ provider: "twitter",
11139
+ source: "lobehub"
11140
+ }]
11141
+ },
11142
+ {
11143
+ id: "brand-collab-weekly",
11144
+ category: "creator",
11145
+ cronPattern: "0 10 * * 1",
11146
+ interests: ["creator"]
11147
+ },
11148
+ {
11149
+ id: "follower-growth-weekly",
11150
+ category: "creator",
11151
+ cronPattern: "0 10 * * 1",
11152
+ interests: ["creator"],
11153
+ requiresSkills: [{
11154
+ provider: "twitter",
11155
+ source: "lobehub"
11156
+ }]
11157
+ },
11158
+ {
11159
+ id: "youtube-channel-weekly",
11160
+ category: "creator",
11161
+ cronPattern: "0 9 * * 1",
11162
+ interests: ["creator"],
11163
+ requiresSkills: [{
11164
+ provider: "youtube",
11165
+ source: "klavis"
11166
+ }]
11167
+ },
11168
+ {
11169
+ id: "monetization-opportunity-weekly",
11170
+ category: "creator",
11171
+ cronPattern: "0 10 * * 3",
11172
+ interests: ["creator"]
11173
+ },
11174
+ {
11175
+ id: "portfolio-daily",
11176
+ category: "investing",
11177
+ cronPattern: "0 16 * * *",
11178
+ interests: ["investing"]
11179
+ },
11180
+ {
11181
+ id: "crypto-market-daily",
11182
+ category: "investing",
11183
+ cronPattern: "0 9 * * *",
11184
+ interests: ["investing"]
11185
+ },
11186
+ {
11187
+ id: "child-growth-weekly",
11188
+ category: "parenting",
11189
+ cronPattern: "0 9 * * 1",
11190
+ interests: ["parenting"]
11191
+ },
11192
+ {
11193
+ id: "child-study-weekly",
11194
+ category: "parenting",
11195
+ cronPattern: "0 20 * * 0",
11196
+ interests: ["parenting", "education"]
11197
+ },
11198
+ {
11199
+ id: "family-finance-weekly",
11200
+ category: "parenting",
11201
+ cronPattern: "0 20 * * 0",
11202
+ interests: ["parenting", "finance-legal"],
11203
+ requiresSkills: [{
11204
+ provider: "google-sheets",
11205
+ source: "klavis"
11206
+ }]
11207
+ },
11208
+ {
11209
+ id: "family-task-schedule",
11210
+ category: "parenting",
11211
+ cronPattern: "0 8 * * 1",
11212
+ interests: ["parenting"],
11213
+ optionalSkills: [{
11214
+ provider: "google-calendar",
11215
+ source: "klavis"
11216
+ }]
11217
+ },
11218
+ {
11219
+ id: "diet-log-companion",
11220
+ category: "health",
11221
+ cronPattern: "0 21 * * *",
11222
+ interests: ["health"]
11223
+ },
11224
+ {
11225
+ id: "podcast-new-episodes",
11226
+ category: "hobbies",
11227
+ cronPattern: "0 9 * * 1",
11228
+ interests: ["hobbies"]
11229
+ },
11230
+ {
11231
+ id: "newsletter-aggregator",
11232
+ category: "hobbies",
11233
+ cronPattern: "0 20 * * 0",
11234
+ interests: ["hobbies"],
11235
+ requiresSkills: [{
11236
+ provider: "gmail",
11237
+ source: "klavis"
11238
+ }]
11239
+ },
11240
+ {
11241
+ id: "series-update-weekly",
11242
+ category: "hobbies",
11243
+ cronPattern: "0 9 * * 1",
11244
+ interests: ["hobbies"]
11245
+ },
11246
+ {
11247
+ id: "travel-inspiration-weekly",
11248
+ category: "hobbies",
11249
+ cronPattern: "0 10 * * 3",
11250
+ interests: ["hobbies"]
11251
+ },
11252
+ {
11253
+ id: "watchlist-friday",
11254
+ category: "hobbies",
11255
+ cronPattern: "0 18 * * 5",
11256
+ interests: ["hobbies"]
11257
+ },
11258
+ {
11259
+ id: "exhibition-event-weekly",
11260
+ category: "hobbies",
11261
+ cronPattern: "0 10 * * 1",
11262
+ interests: ["hobbies"]
11263
+ },
11264
+ {
11265
+ id: "daily-learning-bite",
11266
+ category: "personal-life",
11267
+ cronPattern: "30 7 * * *",
11268
+ interests: ["education", "personal"]
11269
+ },
11270
+ {
11271
+ id: "sunday-reflection",
11272
+ category: "personal-life",
11273
+ cronPattern: "0 21 * * 0",
11274
+ interests: ["personal"]
11275
+ },
11276
+ {
11277
+ id: "morning-ritual",
11278
+ category: "personal-life",
11279
+ cronPattern: "0 7 * * *",
11280
+ interests: ["personal"],
11281
+ optionalSkills: [{
11282
+ provider: "google-calendar",
11283
+ source: "klavis"
11284
+ }]
11285
+ },
11286
+ {
11287
+ id: "bedtime-gratitude",
11288
+ category: "personal-life",
11289
+ cronPattern: "0 22 * * *",
11290
+ interests: ["personal"],
11291
+ optionalSkills: [{
11292
+ provider: "notion",
11293
+ source: "lobehub"
11294
+ }]
11295
+ }
11296
+ ];
11297
+ const KNOWN_TASK_TEMPLATE_IDS = new Set(taskTemplates.map((t) => t.id));
11298
+
10624
11299
  //#endregion
10625
11300
  //#region ../../packages/const/src/url.ts
10626
11301
  const isDev = process.env.NODE_ENV === "development";
@@ -10671,21 +11346,74 @@ const MEMORY_SEARCH_TOP_K_LIMITS = {
10671
11346
  medium: { ...DEFAULT_SEARCH_USER_MEMORY_TOP_K }
10672
11347
  };
10673
11348
 
10674
- //#endregion
10675
- //#region ../../package.json
10676
- var version$3 = "2.1.56";
10677
-
10678
- //#endregion
10679
- //#region ../../packages/const/src/version.ts
10680
- const CURRENT_VERSION = version$3;
10681
- const isDesktop = typeof __ELECTRON__ !== "undefined" && !!__ELECTRON__;
10682
-
10683
11349
  //#endregion
10684
11350
  //#region src/commands/botMessage.ts
11351
+ const MIME_EXT_MAP = {
11352
+ ".bmp": "image/bmp",
11353
+ ".gif": "image/gif",
11354
+ ".jpeg": "image/jpeg",
11355
+ ".jpg": "image/jpeg",
11356
+ ".m4a": "audio/mp4",
11357
+ ".mp3": "audio/mpeg",
11358
+ ".mp4": "video/mp4",
11359
+ ".ogg": "audio/ogg",
11360
+ ".pdf": "application/pdf",
11361
+ ".png": "image/png",
11362
+ ".svg": "image/svg+xml",
11363
+ ".txt": "text/plain",
11364
+ ".wav": "audio/wav",
11365
+ ".webm": "video/webm",
11366
+ ".webp": "image/webp"
11367
+ };
11368
+ const inferMime = (path) => MIME_EXT_MAP[extname(path).toLowerCase()];
11369
+ const inferAttachmentType = (mimeType) => {
11370
+ if (!mimeType) return "file";
11371
+ if (mimeType.startsWith("image/")) return "image";
11372
+ if (mimeType.startsWith("video/")) return "video";
11373
+ if (mimeType.startsWith("audio/")) return "audio";
11374
+ return "file";
11375
+ };
11376
+ /**
11377
+ * Parse a single `--attachment <value>` argument. Accepted forms:
11378
+ * - `https://…` / `http://…` → fetchUrl, type inferred from extension
11379
+ * - any other string → treated as a local file path;
11380
+ * bytes are read + base64-encoded
11381
+ */
11382
+ const parseAttachmentArg = async (raw) => {
11383
+ if (/^https?:\/\//.test(raw)) {
11384
+ const pathname = new URL(raw).pathname;
11385
+ const mimeType = inferMime(pathname);
11386
+ return {
11387
+ fetchUrl: raw,
11388
+ mimeType,
11389
+ name: basename(pathname) || void 0,
11390
+ type: inferAttachmentType(mimeType)
11391
+ };
11392
+ }
11393
+ const bytes = await readFile(raw);
11394
+ const mimeType = inferMime(raw);
11395
+ return {
11396
+ data: bytes.toString("base64"),
11397
+ mimeType,
11398
+ name: basename(raw),
11399
+ type: inferAttachmentType(mimeType)
11400
+ };
11401
+ };
10685
11402
  function registerBotMessageCommands(bot) {
10686
11403
  const message = bot.command("message").description("Send and manage messages on connected platforms");
10687
- message.command("send <botId>").description("Send a message to a channel").requiredOption("--target <channelId>", "Target channel / conversation ID").requiredOption("--message <text>", "Message content").option("--reply-to <messageId>", "Reply to a specific message").option("--json", "Output JSON").action(async (botId, options) => {
11404
+ message.command("send <botId>").description("Send a message to a channel").requiredOption("--target <channelId>", "Target channel / conversation ID").requiredOption("--message <text>", "Message content").option("--attachment <pathOrUrl>", "Attach a file by local path or remote URL (repeatable). Local paths are base64-encoded; http(s) URLs are passed as fetchUrl.", collectOptions, []).option("--reply-to <messageId>", "Reply to a specific message").option("--json", "Output JSON").action(async (botId, options) => {
11405
+ let attachments;
11406
+ if (options.attachment.length > 0) {
11407
+ attachments = [];
11408
+ for (const raw of options.attachment) try {
11409
+ attachments.push(await parseAttachmentArg(raw));
11410
+ } catch (error) {
11411
+ log$7.error(`Failed to load attachment "${raw}": ${error.message}`);
11412
+ process.exit(1);
11413
+ }
11414
+ }
10688
11415
  const result = await (await getTrpcClient()).botMessage.sendMessage.mutate({
11416
+ attachments,
10689
11417
  botId,
10690
11418
  channelId: options.target,
10691
11419
  content: options.message,
@@ -10696,7 +11424,8 @@ function registerBotMessageCommands(bot) {
10696
11424
  return;
10697
11425
  }
10698
11426
  const r = result;
10699
- console.log(`${import_picocolors.default.green("✓")} Message sent${r.messageId ? ` (${import_picocolors.default.dim(r.messageId)})` : ""}`);
11427
+ const suffix = attachments?.length ? ` with ${attachments.length} attachment(s)` : "";
11428
+ console.log(`${import_picocolors.default.green("✓")} Message sent${r.messageId ? ` (${import_picocolors.default.dim(r.messageId)})` : ""}${suffix}`);
10700
11429
  });
10701
11430
  message.command("read <botId>").description("Read messages from a channel").requiredOption("--target <channelId>", "Target channel / conversation ID").option("--limit <n>", "Max messages to fetch", String(50)).option("--before <messageId>", "Read messages before this ID").option("--after <messageId>", "Read messages after this ID").option("--start-time <timestamp>", "Start time as Unix seconds (Feishu/Lark)").option("--end-time <timestamp>", "End time as Unix seconds (Feishu/Lark)").option("--cursor <token>", "Pagination cursor from a previous response (Feishu/Lark)").option("--json", "Output JSON").action(async (botId, options) => {
10702
11431
  const result = await (await getTrpcClient()).botMessage.readMessages.query({
@@ -11144,6 +11873,134 @@ function registerAllowlistCommand(bot, opts) {
11144
11873
  console.log(`${import_picocolors.default.green("✓")} Cleared ${opts.fieldKey} on bot ${import_picocolors.default.bold(botId)}`);
11145
11874
  });
11146
11875
  }
11876
+ /**
11877
+ * Normalise `settings.watchKeywords` into the canonical
11878
+ * `{keyword, instruction?}[]` shape. Mirrors `extractWatchKeywordEntries`
11879
+ * in `src/server/services/bot/platforms/const.ts` so the CLI accepts the
11880
+ * same legacy on-disk shapes (`string`, `string[]`, `{keyword, …}[]`)
11881
+ * the runtime is forgiving about — including the rare comma/whitespace
11882
+ * separated string from a hand-pasted upgrade.
11883
+ */
11884
+ function normalizeWatchKeywords(raw) {
11885
+ const push = (out, keyword, instruction) => {
11886
+ if (typeof keyword !== "string") return;
11887
+ const normalised = keyword.trim().toLowerCase();
11888
+ if (!normalised) return;
11889
+ const trimmedInstruction = typeof instruction === "string" && instruction.trim() ? instruction.trim() : void 0;
11890
+ const existing = out.get(normalised);
11891
+ if (!existing) {
11892
+ out.set(normalised, {
11893
+ instruction: trimmedInstruction,
11894
+ keyword: normalised
11895
+ });
11896
+ return;
11897
+ }
11898
+ if (!existing.instruction && trimmedInstruction) existing.instruction = trimmedInstruction;
11899
+ };
11900
+ const collected = /* @__PURE__ */ new Map();
11901
+ if (typeof raw === "string") for (const piece of raw.split(/[\s,]+/)) push(collected, piece);
11902
+ else if (Array.isArray(raw)) for (const entry of raw) {
11903
+ if (typeof entry === "string") {
11904
+ push(collected, entry);
11905
+ continue;
11906
+ }
11907
+ if (entry && typeof entry === "object" && "keyword" in entry) {
11908
+ const obj = entry;
11909
+ push(collected, obj.keyword, obj.instruction);
11910
+ }
11911
+ }
11912
+ return [...collected.values()];
11913
+ }
11914
+ /**
11915
+ * Build a `list / add / remove / clear` subcommand group around
11916
+ * `settings.watchKeywords`. Shape differs from the user/channel allowlists
11917
+ * (`{keyword, instruction?}` vs `{id, name?}`), so we duplicate the
11918
+ * scaffolding instead of squeezing both shapes through one factory — the
11919
+ * help text, column headers, and `--instruction` flag are all keyword-
11920
+ * specific and would just bloat the unified version.
11921
+ */
11922
+ function registerWatchKeywordsCommand(bot) {
11923
+ const group = bot.command("watch-keywords").description("Manage watch keywords (non-mention channel triggers; the optional instruction is prepended to the user message before being sent to the AI)");
11924
+ const readEntries = (bot) => normalizeWatchKeywords(bot.settings?.watchKeywords);
11925
+ const buildPayload = (bot, nextEntries) => ({
11926
+ id: bot.id,
11927
+ settings: {
11928
+ ...bot.settings,
11929
+ watchKeywords: nextEntries
11930
+ }
11931
+ });
11932
+ group.command("list <botId>").description("List watch-keyword entries").option("--json", "Output JSON").action(async (botId, options) => {
11933
+ const entries = readEntries(await findBot(await getTrpcClient(), botId));
11934
+ if (options.json) {
11935
+ outputJson(entries);
11936
+ return;
11937
+ }
11938
+ if (entries.length === 0) {
11939
+ console.log(`${import_picocolors.default.dim("No watch-keyword entries.")}`);
11940
+ return;
11941
+ }
11942
+ printTable(entries.map((e) => [e.keyword, e.instruction ?? import_picocolors.default.dim("-")]), ["KEYWORD", "INSTRUCTION"]);
11943
+ });
11944
+ group.command("add <botId> <keyword>").description("Add a watch keyword (with optional instruction prefix)").option("--instruction <text>", "Prompt prepended to the user message when this keyword fires (omit for \"just wake the bot\")").action(async (botId, keyword, options) => {
11945
+ const trimmedKeyword = keyword.trim().toLowerCase();
11946
+ if (!trimmedKeyword) {
11947
+ log$7.error("Keyword cannot be empty.");
11948
+ process.exit(1);
11949
+ return;
11950
+ }
11951
+ const trimmedInstruction = options.instruction?.trim();
11952
+ const client = await getTrpcClient();
11953
+ const b = await findBot(client, botId);
11954
+ const entries = readEntries(b);
11955
+ const existing = entries.find((e) => e.keyword === trimmedKeyword);
11956
+ if (existing) {
11957
+ if (trimmedInstruction && existing.instruction !== trimmedInstruction) {
11958
+ existing.instruction = trimmedInstruction;
11959
+ await client.agentBotProvider.update.mutate(buildPayload(b, entries));
11960
+ console.log(`${import_picocolors.default.green("✓")} Updated instruction for ${import_picocolors.default.bold(trimmedKeyword)} (${entries.length} entr${entries.length === 1 ? "y" : "ies"})`);
11961
+ return;
11962
+ }
11963
+ log$7.info(`${trimmedKeyword} is already on watchKeywords — nothing to do.`);
11964
+ return;
11965
+ }
11966
+ const next = [...entries, trimmedInstruction ? {
11967
+ instruction: trimmedInstruction,
11968
+ keyword: trimmedKeyword
11969
+ } : { keyword: trimmedKeyword }];
11970
+ await client.agentBotProvider.update.mutate(buildPayload(b, next));
11971
+ console.log(`${import_picocolors.default.green("✓")} Added ${import_picocolors.default.bold(trimmedKeyword)}${trimmedInstruction ? " (with instruction)" : ""} to watchKeywords (now ${next.length} entr${next.length === 1 ? "y" : "ies"})`);
11972
+ });
11973
+ group.command("remove <botId> <keyword>").description("Remove a watch keyword").action(async (botId, keyword) => {
11974
+ const trimmedKeyword = keyword.trim().toLowerCase();
11975
+ const client = await getTrpcClient();
11976
+ const b = await findBot(client, botId);
11977
+ const entries = readEntries(b);
11978
+ const next = entries.filter((e) => e.keyword !== trimmedKeyword);
11979
+ if (next.length === entries.length) {
11980
+ log$7.info(`${trimmedKeyword} is not on watchKeywords — nothing to do.`);
11981
+ return;
11982
+ }
11983
+ await client.agentBotProvider.update.mutate(buildPayload(b, next));
11984
+ console.log(`${import_picocolors.default.green("✓")} Removed ${import_picocolors.default.bold(trimmedKeyword)} from watchKeywords (${next.length} entr${next.length === 1 ? "y" : "ies"} left)`);
11985
+ });
11986
+ group.command("clear <botId>").description("Clear all watch keywords").option("--yes", "Skip confirmation prompt").action(async (botId, options) => {
11987
+ const client = await getTrpcClient();
11988
+ const b = await findBot(client, botId);
11989
+ const entries = readEntries(b);
11990
+ if (entries.length === 0) {
11991
+ log$7.info("watchKeywords is already empty — nothing to do.");
11992
+ return;
11993
+ }
11994
+ if (!options.yes) {
11995
+ if (!await confirm(`Clear all ${entries.length} watch-keyword entr${entries.length === 1 ? "y" : "ies"} from this bot?`)) {
11996
+ console.log("Cancelled.");
11997
+ return;
11998
+ }
11999
+ }
12000
+ await client.agentBotProvider.update.mutate(buildPayload(b, []));
12001
+ console.log(`${import_picocolors.default.green("✓")} Cleared watchKeywords on bot ${import_picocolors.default.bold(botId)}`);
12002
+ });
12003
+ }
11147
12004
  function registerBotCommand(program) {
11148
12005
  const bot = program.command("bot").description("Manage bot integrations");
11149
12006
  registerBotMessageCommands(bot);
@@ -11333,6 +12190,7 @@ function registerBotCommand(program) {
11333
12190
  idLabel: "channel / group / thread ID",
11334
12191
  name: "group-allowlist"
11335
12192
  });
12193
+ registerWatchKeywordsCommand(bot);
11336
12194
  bot.command("remove <botId>").description("Remove a bot integration").option("--yes", "Skip confirmation prompt").action(async (botId, options) => {
11337
12195
  if (!options.yes) {
11338
12196
  if (!await confirm("Are you sure you want to remove this bot integration?")) {
@@ -16388,7 +17246,7 @@ var require_path = /* @__PURE__ */ __commonJSMin(((exports) => {
16388
17246
  Object.defineProperty(exports, "__esModule", { value: true });
16389
17247
  exports.convertPosixPathToPattern = exports.convertWindowsPathToPattern = exports.convertPathToPattern = exports.escapePosixPath = exports.escapeWindowsPath = exports.escape = exports.removeLeadingDotSegment = exports.makeAbsolute = exports.unixify = void 0;
16390
17248
  const os$3 = __require("os");
16391
- const path$11 = __require("path");
17249
+ const path$10 = __require("path");
16392
17250
  const IS_WINDOWS_PLATFORM = os$3.platform() === "win32";
16393
17251
  const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2;
16394
17252
  /**
@@ -16417,7 +17275,7 @@ var require_path = /* @__PURE__ */ __commonJSMin(((exports) => {
16417
17275
  }
16418
17276
  exports.unixify = unixify;
16419
17277
  function makeAbsolute(cwd, filepath) {
16420
- return path$11.resolve(cwd, filepath);
17278
+ return path$10.resolve(cwd, filepath);
16421
17279
  }
16422
17280
  exports.makeAbsolute = makeAbsolute;
16423
17281
  function removeLeadingDotSegment(entry) {
@@ -17683,7 +18541,7 @@ var require_braces = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17683
18541
  //#endregion
17684
18542
  //#region node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/constants.js
17685
18543
  var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17686
- const path$10 = __require("path");
18544
+ const path$9 = __require("path");
17687
18545
  const WIN_SLASH = "\\\\/";
17688
18546
  const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
17689
18547
  const DEFAULT_MAX_EXTGLOB_RECURSION = 0;
@@ -17812,7 +18670,7 @@ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17812
18670
  CHAR_UNDERSCORE: 95,
17813
18671
  CHAR_VERTICAL_LINE: 124,
17814
18672
  CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
17815
- SEP: path$10.sep,
18673
+ SEP: path$9.sep,
17816
18674
  /**
17817
18675
  * Create EXTGLOB_CHARS
17818
18676
  */
@@ -17857,7 +18715,7 @@ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17857
18715
  //#endregion
17858
18716
  //#region node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/utils.js
17859
18717
  var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
17860
- const path$9 = __require("path");
18718
+ const path$8 = __require("path");
17861
18719
  const win32 = process.platform === "win32";
17862
18720
  const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = require_constants$2();
17863
18721
  exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
@@ -17877,7 +18735,7 @@ var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
17877
18735
  };
17878
18736
  exports.isWindows = (options) => {
17879
18737
  if (options && typeof options.windows === "boolean") return options.windows;
17880
- return win32 === true || path$9.sep === "\\";
18738
+ return win32 === true || path$8.sep === "\\";
17881
18739
  };
17882
18740
  exports.escapeLast = (input, char, lastIdx) => {
17883
18741
  const idx = input.lastIndexOf(char, lastIdx);
@@ -19250,7 +20108,7 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19250
20108
  //#endregion
19251
20109
  //#region node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/picomatch.js
19252
20110
  var require_picomatch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19253
- const path$8 = __require("path");
20111
+ const path$7 = __require("path");
19254
20112
  const scan = require_scan();
19255
20113
  const parse = require_parse();
19256
20114
  const utils = require_utils$3();
@@ -19389,7 +20247,7 @@ var require_picomatch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19389
20247
  * @api public
19390
20248
  */
19391
20249
  picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
19392
- return (glob instanceof RegExp ? glob : picomatch.makeRe(glob, options)).test(path$8.basename(input));
20250
+ return (glob instanceof RegExp ? glob : picomatch.makeRe(glob, options)).test(path$7.basename(input));
19393
20251
  };
19394
20252
  /**
19395
20253
  * Returns true if **any** of the given glob `patterns` match the specified `string`.
@@ -19947,7 +20805,7 @@ var require_micromatch = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19947
20805
  var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
19948
20806
  Object.defineProperty(exports, "__esModule", { value: true });
19949
20807
  exports.isAbsolute = exports.partitionAbsoluteAndRelative = exports.removeDuplicateSlashes = exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0;
19950
- const path$7 = __require("path");
20808
+ const path$6 = __require("path");
19951
20809
  const globParent = require_glob_parent();
19952
20810
  const micromatch = require_micromatch();
19953
20811
  const GLOBSTAR = "**";
@@ -20055,7 +20913,7 @@ var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
20055
20913
  }
20056
20914
  exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
20057
20915
  function isAffectDepthOfReadingPattern(pattern) {
20058
- const basename = path$7.basename(pattern);
20916
+ const basename = path$6.basename(pattern);
20059
20917
  return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
20060
20918
  }
20061
20919
  exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
@@ -20129,7 +20987,7 @@ var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
20129
20987
  }
20130
20988
  exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
20131
20989
  function isAbsolute(pattern) {
20132
- return path$7.isAbsolute(pattern);
20990
+ return path$6.isAbsolute(pattern);
20133
20991
  }
20134
20992
  exports.isAbsolute = isAbsolute;
20135
20993
  }));
@@ -20788,7 +21646,7 @@ var require_fs = /* @__PURE__ */ __commonJSMin(((exports) => {
20788
21646
  //#region node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/settings.js
20789
21647
  var require_settings$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
20790
21648
  Object.defineProperty(exports, "__esModule", { value: true });
20791
- const path$6 = __require("path");
21649
+ const path$5 = __require("path");
20792
21650
  const fsStat = require_out$3();
20793
21651
  const fs = require_fs();
20794
21652
  var Settings = class {
@@ -20796,7 +21654,7 @@ var require_settings$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
20796
21654
  this._options = _options;
20797
21655
  this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
20798
21656
  this.fs = fs.createFileSystemAdapter(this._options.fs);
20799
- this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$6.sep);
21657
+ this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$5.sep);
20800
21658
  this.stats = this._getValue(this._options.stats, false);
20801
21659
  this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
20802
21660
  this.fsStatSettings = new fsStat.Settings({
@@ -21401,7 +22259,7 @@ var require_sync$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
21401
22259
  //#region node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/settings.js
21402
22260
  var require_settings$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
21403
22261
  Object.defineProperty(exports, "__esModule", { value: true });
21404
- const path$5 = __require("path");
22262
+ const path$4 = __require("path");
21405
22263
  const fsScandir = require_out$2();
21406
22264
  var Settings = class {
21407
22265
  constructor(_options = {}) {
@@ -21411,7 +22269,7 @@ var require_settings$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
21411
22269
  this.deepFilter = this._getValue(this._options.deepFilter, null);
21412
22270
  this.entryFilter = this._getValue(this._options.entryFilter, null);
21413
22271
  this.errorFilter = this._getValue(this._options.errorFilter, null);
21414
- this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$5.sep);
22272
+ this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$4.sep);
21415
22273
  this.fsScandirSettings = new fsScandir.Settings({
21416
22274
  followSymbolicLinks: this._options.followSymbolicLinks,
21417
22275
  fs: this._options.fs,
@@ -21465,7 +22323,7 @@ var require_out$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
21465
22323
  //#region node_modules/.pnpm/fast-glob@3.3.3/node_modules/fast-glob/out/readers/reader.js
21466
22324
  var require_reader$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
21467
22325
  Object.defineProperty(exports, "__esModule", { value: true });
21468
- const path$4 = __require("path");
22326
+ const path$3 = __require("path");
21469
22327
  const fsStat = require_out$3();
21470
22328
  const utils = require_utils$2();
21471
22329
  var Reader = class {
@@ -21478,7 +22336,7 @@ var require_reader$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
21478
22336
  });
21479
22337
  }
21480
22338
  _getFullEntryPath(filepath) {
21481
- return path$4.resolve(this._settings.cwd, filepath);
22339
+ return path$3.resolve(this._settings.cwd, filepath);
21482
22340
  }
21483
22341
  _makeEntry(stats, pattern) {
21484
22342
  const entry = {
@@ -21822,7 +22680,7 @@ var require_entry = /* @__PURE__ */ __commonJSMin(((exports) => {
21822
22680
  //#region node_modules/.pnpm/fast-glob@3.3.3/node_modules/fast-glob/out/providers/provider.js
21823
22681
  var require_provider = /* @__PURE__ */ __commonJSMin(((exports) => {
21824
22682
  Object.defineProperty(exports, "__esModule", { value: true });
21825
- const path$3 = __require("path");
22683
+ const path$2 = __require("path");
21826
22684
  const deep_1 = require_deep();
21827
22685
  const entry_1 = require_entry$1();
21828
22686
  const error_1 = require_error();
@@ -21836,7 +22694,7 @@ var require_provider = /* @__PURE__ */ __commonJSMin(((exports) => {
21836
22694
  this.entryTransformer = new entry_2.default(this._settings);
21837
22695
  }
21838
22696
  _getRootDirectory(task) {
21839
- return path$3.resolve(this._settings.cwd, task.base);
22697
+ return path$2.resolve(this._settings.cwd, task.base);
21840
22698
  }
21841
22699
  _getReaderOptions(task) {
21842
22700
  const basePath = task.base === "." ? "" : task.base;
@@ -22148,16 +23006,36 @@ var require_out = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22148
23006
  module.exports = FastGlob;
22149
23007
  }));
22150
23008
 
23009
+ //#endregion
23010
+ //#region ../../packages/local-file-shell/src/file/hasHiddenSegment.ts
23011
+ /**
23012
+ * Detect whether a glob pattern (or path) contains a dot-prefixed segment such
23013
+ * as `.github`, `.husky`, or `foo/.config`. Used to auto-enable hidden-file
23014
+ * matching when the caller's intent is clearly to traverse a hidden directory
23015
+ * — otherwise `fast-glob` (`dot: false`) and `ripgrep` (no `--hidden`) silently
23016
+ * return zero results.
23017
+ *
23018
+ * Skips `.` and `..` (relative path indicators), which are not hidden segments.
23019
+ */
23020
+ const HIDDEN_SEGMENT_RE = /(?:^|\/)\.[^./]/;
23021
+ const hasHiddenSegment = (pattern) => !!pattern && HIDDEN_SEGMENT_RE.test(pattern);
23022
+
22151
23023
  //#endregion
22152
23024
  //#region ../../packages/local-file-shell/src/file/glob.ts
22153
23025
  var import_out = /* @__PURE__ */ __toESM$2(require_out());
22154
23026
  async function globLocalFiles({ pattern, cwd }) {
22155
23027
  try {
22156
- return { files: await (0, import_out.default)(pattern, {
23028
+ const wantsHidden = hasHiddenSegment(pattern);
23029
+ const files = await (0, import_out.default)(pattern, {
22157
23030
  cwd: expandTilde(cwd) || process.cwd(),
22158
- dot: false,
23031
+ dot: wantsHidden,
22159
23032
  ignore: ["**/node_modules/**", "**/.git/**"]
22160
- }) };
23033
+ });
23034
+ if (wantsHidden) return {
23035
+ files,
23036
+ hint: `Auto-enabled hidden-file matching because pattern contains a dot-prefixed segment.`
23037
+ };
23038
+ return { files };
22161
23039
  } catch (error) {
22162
23040
  return {
22163
23041
  error: error.message,
@@ -22169,8 +23047,11 @@ async function globLocalFiles({ pattern, cwd }) {
22169
23047
  //#endregion
22170
23048
  //#region ../../packages/local-file-shell/src/file/grep.ts
22171
23049
  async function grepContent({ pattern, cwd, filePattern }) {
23050
+ const wantsHidden = hasHiddenSegment(filePattern);
23051
+ const hint = wantsHidden ? `Auto-enabled hidden-file matching because filePattern contains a dot-prefixed segment.` : void 0;
22172
23052
  return new Promise((resolve) => {
22173
23053
  const args = ["--json", "-n"];
23054
+ if (wantsHidden) args.push("--hidden", "--glob", "!**/.git/**");
22174
23055
  if (filePattern) args.push("--glob", filePattern);
22175
23056
  args.push(pattern);
22176
23057
  const child = spawn("rg", args, { cwd: expandTilde(cwd) || process.cwd() });
@@ -22182,6 +23063,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
22182
23063
  child.on("close", (code) => {
22183
23064
  if (code !== 0 && code !== 1) {
22184
23065
  resolve({
23066
+ hint,
22185
23067
  matches: [],
22186
23068
  success: false
22187
23069
  });
@@ -22189,6 +23071,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
22189
23071
  }
22190
23072
  try {
22191
23073
  resolve({
23074
+ hint,
22192
23075
  matches: stdout.split("\n").filter(Boolean).map((line) => {
22193
23076
  try {
22194
23077
  return JSON.parse(line);
@@ -22200,6 +23083,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
22200
23083
  });
22201
23084
  } catch {
22202
23085
  resolve({
23086
+ hint,
22203
23087
  matches: [],
22204
23088
  success: true
22205
23089
  });
@@ -22207,6 +23091,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
22207
23091
  });
22208
23092
  child.on("error", () => {
22209
23093
  resolve({
23094
+ hint,
22210
23095
  matches: [],
22211
23096
  success: false
22212
23097
  });
@@ -22971,6 +23856,120 @@ var require_src = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22971
23856
  else module.exports = require_node$1();
22972
23857
  }));
22973
23858
 
23859
+ //#endregion
23860
+ //#region ../../packages/file-loaders/src/utils/detectUtf16.ts
23861
+ const HEURISTIC_SAMPLE_BYTES = 512;
23862
+ const HEURISTIC_THRESHOLD = .3;
23863
+ /**
23864
+ * Detect UTF-16 without BOM by sampling and counting ASCII-shaped code-unit
23865
+ * pairs. ASCII chars in UTF-16 produce a 0x00 byte at the high half: at
23866
+ * odd index for LE, at even index for BE.
23867
+ *
23868
+ * Used both by `TextLoader` (to pick the right decoder) and by the binary
23869
+ * sniffer (so a UTF-16 text file without BOM isn't mistaken for binary
23870
+ * because of its alternating null bytes).
23871
+ */
23872
+ const detectUtf16NoBom = (buffer) => {
23873
+ const sample = buffer.subarray(0, Math.min(HEURISTIC_SAMPLE_BYTES, buffer.length));
23874
+ if (sample.length < 4 || sample.length % 2 !== 0) return null;
23875
+ let leAsciiPairs = 0;
23876
+ let beAsciiPairs = 0;
23877
+ const totalPairs = sample.length / 2;
23878
+ for (let i = 0; i < sample.length; i += 2) {
23879
+ const lo = sample[i];
23880
+ const hi = sample[i + 1];
23881
+ if (hi === 0 && lo !== 0) leAsciiPairs++;
23882
+ else if (lo === 0 && hi !== 0) beAsciiPairs++;
23883
+ }
23884
+ if (leAsciiPairs > beAsciiPairs && leAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16le";
23885
+ if (beAsciiPairs > leAsciiPairs && beAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16be";
23886
+ return null;
23887
+ };
23888
+
23889
+ //#endregion
23890
+ //#region ../../packages/file-loaders/src/loaders/text/index.ts
23891
+ var import_src$5 = /* @__PURE__ */ __toESM$2(require_src());
23892
+ const log$6 = (0, import_src$5.default)("file-loaders:text");
23893
+ /**
23894
+ * Read a text file with automatic encoding detection.
23895
+ * Detects UTF-8, UTF-16LE, and UTF-16BE via BOM, with a heuristic fallback
23896
+ * for UTF-16 without BOM (common in some Windows exports). Falls back to UTF-8.
23897
+ */
23898
+ const readTextFile = async (filePath) => {
23899
+ const buffer = await readFile(filePath);
23900
+ if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) {
23901
+ log$6("Detected UTF-16LE BOM");
23902
+ return new TextDecoder("utf-16le").decode(buffer.subarray(2));
23903
+ }
23904
+ if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) {
23905
+ log$6("Detected UTF-16BE BOM");
23906
+ return new TextDecoder("utf-16be").decode(buffer.subarray(2));
23907
+ }
23908
+ if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
23909
+ log$6("Detected UTF-8 BOM");
23910
+ return buffer.subarray(3).toString("utf8");
23911
+ }
23912
+ const variant = detectUtf16NoBom(buffer);
23913
+ if (variant) {
23914
+ log$6("Detected %s without BOM (heuristic)", variant);
23915
+ return new TextDecoder(variant).decode(buffer);
23916
+ }
23917
+ return buffer.toString("utf8");
23918
+ };
23919
+ /**
23920
+ * Loader for loading plain text files.
23921
+ */
23922
+ var TextLoader = class {
23923
+ async loadPages(filePath) {
23924
+ log$6("Loading text file:", filePath);
23925
+ try {
23926
+ const fileContent = await readTextFile(filePath);
23927
+ log$6("Text file loaded successfully, size:", fileContent.length, "bytes");
23928
+ const lineCount = fileContent.split("\n").length;
23929
+ const charCount = fileContent.length;
23930
+ log$6("Text file stats:", {
23931
+ charCount,
23932
+ lineCount
23933
+ });
23934
+ const page = {
23935
+ charCount,
23936
+ lineCount,
23937
+ metadata: {
23938
+ lineNumberEnd: lineCount,
23939
+ lineNumberStart: 1
23940
+ },
23941
+ pageContent: fileContent
23942
+ };
23943
+ log$6("Text page created successfully");
23944
+ return [page];
23945
+ } catch (e) {
23946
+ const error = e;
23947
+ log$6("Error encountered while loading text file");
23948
+ console.error(`Error loading text file ${filePath}: ${error.message}`);
23949
+ const errorPage = {
23950
+ charCount: 0,
23951
+ lineCount: 0,
23952
+ metadata: { error: `Failed to load text file: ${error.message}` },
23953
+ pageContent: ""
23954
+ };
23955
+ log$6("Created error page for failed text file loading");
23956
+ return [errorPage];
23957
+ }
23958
+ }
23959
+ /**
23960
+ * For plain text, simply concatenate the content of all pages.
23961
+ * (Although TextLoader typically has only one page, this maintains interface consistency)
23962
+ * @param pages Array of pages
23963
+ * @returns Aggregated content
23964
+ */
23965
+ async aggregateContent(pages) {
23966
+ log$6("Aggregating content from", pages.length, "text pages");
23967
+ const result = pages.map((page) => page.pageContent).join("\n");
23968
+ log$6("Content aggregated successfully, length:", result.length);
23969
+ return result;
23970
+ }
23971
+ };
23972
+
22974
23973
  //#endregion
22975
23974
  //#region node_modules/.pnpm/word-extractor@1.0.4/node_modules/word-extractor/lib/ole-header.js
22976
23975
  var require_ole_header = /* @__PURE__ */ __commonJSMin(((exports, module) => {
@@ -27169,7 +28168,7 @@ var require_open_office_extractor = /* @__PURE__ */ __commonJSMin(((exports, mod
27169
28168
  * Note that [WordOleExtractor]{@link module:word-ole-extractor~WordOleExtractor} is
27170
28169
  * used for older, OLE-style, compound document files.
27171
28170
  */
27172
- const path$2 = __require("path");
28171
+ const path$1 = __require("path");
27173
28172
  const SAXES = require_saxes();
27174
28173
  const yauzl = require_yauzl$1();
27175
28174
  const BufferReader = require_buffer_reader();
@@ -27204,7 +28203,7 @@ var require_open_office_extractor = /* @__PURE__ */ __commonJSMin(((exports, mod
27204
28203
  }
27205
28204
  shouldProcess(filename) {
27206
28205
  if (this._actions[filename]) return true;
27207
- const extension = path$2.posix.extname(filename).replace(/^\./, "");
28206
+ const extension = path$1.posix.extname(filename).replace(/^\./, "");
27208
28207
  if (!extension) return false;
27209
28208
  const defaultType = this._defaults[extension];
27210
28209
  if (defaultType && this._streamTypes[defaultType]) return true;
@@ -27457,14 +28456,14 @@ var require_word = /* @__PURE__ */ __commonJSMin(((exports, module) => {
27457
28456
  //#endregion
27458
28457
  //#region ../../packages/file-loaders/src/loaders/doc/index.ts
27459
28458
  var doc_exports = /* @__PURE__ */ __exportAll({ DocLoader: () => DocLoader });
27460
- var import_src$6, import_word, log$6, DocLoader;
28459
+ var import_src$4, import_word, log$5, DocLoader;
27461
28460
  var init_doc = __esmMin((() => {
27462
- import_src$6 = /* @__PURE__ */ __toESM$2(require_src());
28461
+ import_src$4 = /* @__PURE__ */ __toESM$2(require_src());
27463
28462
  import_word = /* @__PURE__ */ __toESM$2(require_word());
27464
- log$6 = (0, import_src$6.default)("file-loaders:doc");
28463
+ log$5 = (0, import_src$4.default)("file-loaders:doc");
27465
28464
  DocLoader = class {
27466
28465
  async loadPages(filePath) {
27467
- log$6("Loading DOC file:", filePath);
28466
+ log$5("Loading DOC file:", filePath);
27468
28467
  try {
27469
28468
  const extracted = await new import_word.default().extract(filePath);
27470
28469
  const pageContent = extracted && typeof extracted.getBody === "function" ? extracted.getBody() : extracted?.text ?? "";
@@ -27475,11 +28474,11 @@ var init_doc = __esmMin((() => {
27475
28474
  metadata: { pageNumber: 1 },
27476
28475
  pageContent
27477
28476
  };
27478
- log$6("DOC loading completed");
28477
+ log$5("DOC loading completed");
27479
28478
  return [page];
27480
28479
  } catch (e) {
27481
28480
  const error = e;
27482
- log$6("Error encountered while loading DOC file");
28481
+ log$5("Error encountered while loading DOC file");
27483
28482
  console.error(`Error loading DOC file ${filePath}: ${error.message}`);
27484
28483
  return [{
27485
28484
  charCount: 0,
@@ -27490,7 +28489,7 @@ var init_doc = __esmMin((() => {
27490
28489
  }
27491
28490
  }
27492
28491
  async aggregateContent(pages) {
27493
- log$6("Aggregating content from", pages.length, "DOC pages");
28492
+ log$5("Aggregating content from", pages.length, "DOC pages");
27494
28493
  return pages.map((p) => p.pageContent).join("\n\n");
27495
28494
  }
27496
28495
  };
@@ -61735,23 +62734,23 @@ var require_lib$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
61735
62734
  //#endregion
61736
62735
  //#region ../../packages/file-loaders/src/loaders/docx/index.ts
61737
62736
  var docx_exports = /* @__PURE__ */ __exportAll({ DocxLoader: () => DocxLoader });
61738
- var import_src$5, import_lib$1, log$5, DocxLoader;
62737
+ var import_src$3, import_lib$1, log$4, DocxLoader;
61739
62738
  var init_docx = __esmMin((() => {
61740
- import_src$5 = /* @__PURE__ */ __toESM$2(require_src());
62739
+ import_src$3 = /* @__PURE__ */ __toESM$2(require_src());
61741
62740
  import_lib$1 = /* @__PURE__ */ __toESM$2(require_lib$1());
61742
- log$5 = (0, import_src$5.default)("file-loaders:docx");
62741
+ log$4 = (0, import_src$3.default)("file-loaders:docx");
61743
62742
  DocxLoader = class {
61744
62743
  async loadPages(filePath) {
61745
- log$5("Loading DOCX file:", filePath);
62744
+ log$4("Loading DOCX file:", filePath);
61746
62745
  try {
61747
62746
  const buffer = await fs$1.readFile(filePath);
61748
- log$5("File buffer read, size:", buffer.length);
62747
+ log$4("File buffer read, size:", buffer.length);
61749
62748
  const result = await import_lib$1.extractRawText({ buffer });
61750
62749
  const pageContent = result.value;
61751
- log$5("Text extracted, length:", pageContent.length);
62750
+ log$4("Text extracted, length:", pageContent.length);
61752
62751
  const lineCount = pageContent.split("\n").length;
61753
62752
  const charCount = pageContent.length;
61754
- log$5("DOCX document processed, lines:", lineCount, "chars:", charCount);
62753
+ log$4("DOCX document processed, lines:", lineCount, "chars:", charCount);
61755
62754
  const page = {
61756
62755
  charCount,
61757
62756
  lineCount,
@@ -61761,15 +62760,15 @@ var init_docx = __esmMin((() => {
61761
62760
  if (result.messages.length > 0) {
61762
62761
  const warnings = result.messages.filter((msg) => msg.type === "warning");
61763
62762
  if (warnings.length > 0) {
61764
- log$5("Extraction warnings:", warnings.length);
61765
- warnings.forEach((warning) => log$5("Warning:", warning.message));
62763
+ log$4("Extraction warnings:", warnings.length);
62764
+ warnings.forEach((warning) => log$4("Warning:", warning.message));
61766
62765
  }
61767
62766
  }
61768
- log$5("DOCX loading completed");
62767
+ log$4("DOCX loading completed");
61769
62768
  return [page];
61770
62769
  } catch (e) {
61771
62770
  const error = e;
61772
- log$5("Error encountered while loading DOCX file");
62771
+ log$4("Error encountered while loading DOCX file");
61773
62772
  console.error(`Error loading DOCX file ${filePath}: ${error.message}`);
61774
62773
  const errorPage = {
61775
62774
  charCount: 0,
@@ -61777,7 +62776,7 @@ var init_docx = __esmMin((() => {
61777
62776
  metadata: { error: `Failed to load DOCX file: ${error.message}` },
61778
62777
  pageContent: ""
61779
62778
  };
61780
- log$5("Created error page for failed DOCX loading");
62779
+ log$4("Created error page for failed DOCX loading");
61781
62780
  return [errorPage];
61782
62781
  }
61783
62782
  }
@@ -61788,9 +62787,9 @@ var init_docx = __esmMin((() => {
61788
62787
  * @returns Aggregated content as a string.
61789
62788
  */
61790
62789
  async aggregateContent(pages) {
61791
- log$5("Aggregating content from", pages.length, "DOCX pages");
62790
+ log$4("Aggregating content from", pages.length, "DOCX pages");
61792
62791
  const result = pages.map((page) => page.pageContent).join("\n\n");
61793
- log$5("DOCX content aggregated successfully, length:", result.length);
62792
+ log$4("DOCX content aggregated successfully, length:", result.length);
61794
62793
  return result;
61795
62794
  }
61796
62795
  };
@@ -85153,68 +86152,68 @@ var excel_exports = /* @__PURE__ */ __exportAll({ ExcelLoader: () => ExcelLoader
85153
86152
  * Handles empty sheets and escapes pipe characters.
85154
86153
  */
85155
86154
  function sheetToMarkdownTable(jsonData) {
85156
- log$4("Converting sheet data to Markdown table, rows:", jsonData?.length || 0);
86155
+ log$3("Converting sheet data to Markdown table, rows:", jsonData?.length || 0);
85157
86156
  if (!jsonData || jsonData.length === 0) {
85158
- log$4("Sheet is empty, returning placeholder message");
86157
+ log$3("Sheet is empty, returning placeholder message");
85159
86158
  return "*Sheet is empty or contains no data.*";
85160
86159
  }
85161
86160
  const headers = Object.keys(jsonData[0] || {});
85162
- log$4("Sheet headers:", headers);
86161
+ log$3("Sheet headers:", headers);
85163
86162
  if (headers.length === 0) {
85164
- log$4("Sheet has no headers, returning placeholder message");
86163
+ log$3("Sheet has no headers, returning placeholder message");
85165
86164
  return "*Sheet has headers but no data.*";
85166
86165
  }
85167
86166
  const headerRow = `| ${headers.join(" | ")} |`;
85168
86167
  const separatorRow = `| ${headers.map(() => "---").join(" | ")} |`;
85169
- log$4("Building data rows for Markdown table");
86168
+ log$3("Building data rows for Markdown table");
85170
86169
  const result = `${headerRow}\n${separatorRow}\n${jsonData.map((row) => {
85171
86170
  return `| ${headers.map((header) => {
85172
86171
  const value = row[header];
85173
86172
  return (value === null || value === void 0 ? "" : String(value).replaceAll("|", "\\|")).trim();
85174
86173
  }).join(" | ")} |`;
85175
86174
  }).join("\n")}`;
85176
- log$4("Markdown table created, length:", result.length);
86175
+ log$3("Markdown table created, length:", result.length);
85177
86176
  return result;
85178
86177
  }
85179
- var import_src$4, log$4, ExcelLoader;
86178
+ var import_src$2, log$3, ExcelLoader;
85180
86179
  var init_excel = __esmMin((() => {
85181
- import_src$4 = /* @__PURE__ */ __toESM$2(require_src());
86180
+ import_src$2 = /* @__PURE__ */ __toESM$2(require_src());
85182
86181
  init_xlsx();
85183
86182
  init_prompt$1();
85184
- log$4 = (0, import_src$4.default)("file-loaders:excel");
86183
+ log$3 = (0, import_src$2.default)("file-loaders:excel");
85185
86184
  ExcelLoader = class {
85186
86185
  async loadPages(filePath) {
85187
- log$4("Loading Excel file:", filePath);
86186
+ log$3("Loading Excel file:", filePath);
85188
86187
  const pages = [];
85189
86188
  try {
85190
- log$4("Reading Excel file as buffer");
86189
+ log$3("Reading Excel file as buffer");
85191
86190
  const dataBuffer = await readFile(filePath);
85192
- log$4("Excel file read successfully, size:", dataBuffer.length, "bytes");
85193
- log$4("Parsing Excel workbook");
86191
+ log$3("Excel file read successfully, size:", dataBuffer.length, "bytes");
86192
+ log$3("Parsing Excel workbook");
85194
86193
  const workbook = readSync(dataBuffer, { type: "buffer" });
85195
- log$4("Excel workbook parsed successfully, sheets:", workbook.SheetNames.length);
86194
+ log$3("Excel workbook parsed successfully, sheets:", workbook.SheetNames.length);
85196
86195
  for (const sheetName of workbook.SheetNames) {
85197
- log$4(`Processing sheet: ${sheetName}`);
86196
+ log$3(`Processing sheet: ${sheetName}`);
85198
86197
  const worksheet = workbook.Sheets[sheetName];
85199
86198
  const jsonData = utils.sheet_to_json(worksheet, {
85200
86199
  defval: "",
85201
86200
  raw: false
85202
86201
  });
85203
- log$4(`Sheet ${sheetName} converted to JSON, rows:`, jsonData.length);
86202
+ log$3(`Sheet ${sheetName} converted to JSON, rows:`, jsonData.length);
85204
86203
  const tableMarkdown = sheetToMarkdownTable(jsonData);
85205
86204
  const lineCount = tableMarkdown.split("\n").length;
85206
86205
  const charCount = tableMarkdown.length;
85207
- log$4(`Sheet ${sheetName} converted to Markdown, lines: ${lineCount}, chars: ${charCount}`);
86206
+ log$3(`Sheet ${sheetName} converted to Markdown, lines: ${lineCount}, chars: ${charCount}`);
85208
86207
  pages.push({
85209
86208
  charCount,
85210
86209
  lineCount,
85211
86210
  metadata: { sheetName },
85212
86211
  pageContent: tableMarkdown.trim()
85213
86212
  });
85214
- log$4(`Added sheet ${sheetName} as page`);
86213
+ log$3(`Added sheet ${sheetName} as page`);
85215
86214
  }
85216
86215
  if (pages.length === 0) {
85217
- log$4("Excel file contains no sheets, creating empty page with error");
86216
+ log$3("Excel file contains no sheets, creating empty page with error");
85218
86217
  pages.push({
85219
86218
  charCount: 0,
85220
86219
  lineCount: 0,
@@ -85222,11 +86221,11 @@ var init_excel = __esmMin((() => {
85222
86221
  pageContent: ""
85223
86222
  });
85224
86223
  }
85225
- log$4("Excel loading completed, total pages:", pages.length);
86224
+ log$3("Excel loading completed, total pages:", pages.length);
85226
86225
  return pages;
85227
86226
  } catch (e) {
85228
86227
  const error = e;
85229
- log$4("Error encountered while loading Excel file");
86228
+ log$3("Error encountered while loading Excel file");
85230
86229
  console.error(`Error loading Excel file ${filePath}: ${error.message}`);
85231
86230
  const errorPage = {
85232
86231
  charCount: 0,
@@ -85234,7 +86233,7 @@ var init_excel = __esmMin((() => {
85234
86233
  metadata: { error: `Failed to load Excel file: ${error.message}` },
85235
86234
  pageContent: ""
85236
86235
  };
85237
- log$4("Created error page for failed Excel loading");
86236
+ log$3("Created error page for failed Excel loading");
85238
86237
  return [errorPage];
85239
86238
  }
85240
86239
  }
@@ -85245,9 +86244,9 @@ var init_excel = __esmMin((() => {
85245
86244
  * @returns Aggregated content as a string.
85246
86245
  */
85247
86246
  async aggregateContent(pages) {
85248
- log$4("Aggregating content from", pages.length, "Excel pages");
86247
+ log$3("Aggregating content from", pages.length, "Excel pages");
85249
86248
  const result = promptTemplate$1(pages);
85250
- log$4("Excel content aggregated successfully, length:", result.length);
86249
+ log$3("Excel content aggregated successfully, length:", result.length);
85251
86250
  return result;
85252
86251
  }
85253
86252
  };
@@ -193505,40 +194504,40 @@ ${page.pageContent}
193505
194504
  //#endregion
193506
194505
  //#region ../../packages/file-loaders/src/loaders/pdf/index.ts
193507
194506
  var pdf_exports = /* @__PURE__ */ __exportAll({ PdfLoader: () => PdfLoader });
193508
- var import_src$3, log$3, PdfLoader;
194507
+ var import_src$1, log$2, PdfLoader;
193509
194508
  var init_pdf = __esmMin((() => {
193510
- import_src$3 = /* @__PURE__ */ __toESM$2(require_src());
194509
+ import_src$1 = /* @__PURE__ */ __toESM$2(require_src());
193511
194510
  init_pdf$1();
193512
194511
  init_pdf_worker();
193513
194512
  init_prompt();
193514
- log$3 = (0, import_src$3.default)("file-loaders:pdf");
194513
+ log$2 = (0, import_src$1.default)("file-loaders:pdf");
193515
194514
  PdfLoader = class {
193516
194515
  pdfInstance = null;
193517
194516
  pdfjsWorker = pdf_worker_exports;
193518
194517
  async getPDFFile(filePath) {
193519
- log$3("Reading PDF file:", filePath);
194518
+ log$2("Reading PDF file:", filePath);
193520
194519
  const dataBuffer = await readFile(filePath);
193521
- log$3("PDF file read successfully, size:", dataBuffer.length, "bytes");
194520
+ log$2("PDF file read successfully, size:", dataBuffer.length, "bytes");
193522
194521
  const loadingTask = getDocument({
193523
194522
  data: new Uint8Array(dataBuffer.buffer, dataBuffer.byteOffset, dataBuffer.length),
193524
194523
  useSystemFonts: true
193525
194524
  });
193526
- log$3("PDF document loading task created");
194525
+ log$2("PDF document loading task created");
193527
194526
  const pdf = await loadingTask.promise;
193528
- log$3("PDF document loaded successfully, pages:", pdf.numPages);
194527
+ log$2("PDF document loaded successfully, pages:", pdf.numPages);
193529
194528
  return pdf;
193530
194529
  }
193531
194530
  async loadPages(filePath) {
193532
- log$3("Starting to load PDF pages from:", filePath);
194531
+ log$2("Starting to load PDF pages from:", filePath);
193533
194532
  try {
193534
194533
  const pdf = await this.getPDFFile(filePath);
193535
194534
  const pages = [];
193536
- log$3(`Processing ${pdf.numPages} PDF pages`);
194535
+ log$2(`Processing ${pdf.numPages} PDF pages`);
193537
194536
  for (let i = 1; i <= pdf.numPages; i += 1) {
193538
- log$3(`Loading page ${i}/${pdf.numPages}`);
194537
+ log$2(`Loading page ${i}/${pdf.numPages}`);
193539
194538
  const page = await pdf.getPage(i);
193540
194539
  const content = await page.getTextContent();
193541
- log$3(`Page ${i} text content retrieved, items:`, content.items.length);
194540
+ log$2(`Page ${i} text content retrieved, items:`, content.items.length);
193542
194541
  let lastY;
193543
194542
  const textItems = [];
193544
194543
  for (const item of content.items) if ("str" in item) {
@@ -193549,23 +194548,23 @@ var init_pdf = __esmMin((() => {
193549
194548
  const cleanedPageContent = textItems.join("").replaceAll("\0", "");
193550
194549
  const lineCount = cleanedPageContent.split("\n").length;
193551
194550
  const charCount = cleanedPageContent.length;
193552
- log$3(`Page ${i} processed, lines: ${lineCount}, chars: ${charCount}`);
194551
+ log$2(`Page ${i} processed, lines: ${lineCount}, chars: ${charCount}`);
193553
194552
  pages.push({
193554
194553
  charCount,
193555
194554
  lineCount,
193556
194555
  metadata: { pageNumber: i },
193557
194556
  pageContent: cleanedPageContent
193558
194557
  });
193559
- log$3(`Cleaning up page ${i} resources`);
194558
+ log$2(`Cleaning up page ${i} resources`);
193560
194559
  page.cleanup();
193561
194560
  }
193562
- log$3("Cleaning up PDF document resources");
194561
+ log$2("Cleaning up PDF document resources");
193563
194562
  await pdf.destroy();
193564
- log$3(`PDF loading completed for ${filePath}, total pages:`, pages.length);
194563
+ log$2(`PDF loading completed for ${filePath}, total pages:`, pages.length);
193565
194564
  return pages;
193566
194565
  } catch (e) {
193567
194566
  const error = e;
193568
- log$3("Error encountered while loading PDF file");
194567
+ log$2("Error encountered while loading PDF file");
193569
194568
  console.error(`Error loading PDF file ${filePath} using pdfjs-dist: ${error.message}`, error.stack);
193570
194569
  const errorPage = {
193571
194570
  charCount: 0,
@@ -193576,7 +194575,7 @@ var init_pdf = __esmMin((() => {
193576
194575
  },
193577
194576
  pageContent: ""
193578
194577
  };
193579
- log$3("Created error page for failed PDF loading");
194578
+ log$2("Created error page for failed PDF loading");
193580
194579
  return [errorPage];
193581
194580
  }
193582
194581
  }
@@ -193587,25 +194586,25 @@ var init_pdf = __esmMin((() => {
193587
194586
  * @returns Aggregated content as a string.
193588
194587
  */
193589
194588
  async aggregateContent(pages) {
193590
- log$3("Aggregating content from", pages.length, "PDF pages");
194589
+ log$2("Aggregating content from", pages.length, "PDF pages");
193591
194590
  const validPages = pages.filter((page) => !page.metadata.error);
193592
- log$3(`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} pages with errors filtered out)`);
194591
+ log$2(`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} pages with errors filtered out)`);
193593
194592
  const result = promptTemplate(validPages);
193594
- log$3("PDF content aggregated successfully, length:", result.length);
194593
+ log$2("PDF content aggregated successfully, length:", result.length);
193595
194594
  return result;
193596
194595
  }
193597
194596
  async attachDocumentMetadata(filePath) {
193598
- log$3("Attaching document metadata for PDF:", filePath);
194597
+ log$2("Attaching document metadata for PDF:", filePath);
193599
194598
  const pdf = await this.getPDFFile(filePath);
193600
- log$3("Getting PDF metadata");
194599
+ log$2("Getting PDF metadata");
193601
194600
  const pdfMetadata = await pdf.getMetadata().catch((err) => {
193602
- log$3("Error retrieving PDF metadata");
194601
+ log$2("Error retrieving PDF metadata");
193603
194602
  console.error(`Error getting PDF metadata: ${err.message}`);
193604
194603
  return null;
193605
194604
  }) ?? null;
193606
194605
  const pdfInfo = pdfMetadata?.info ?? {};
193607
194606
  const metadata = pdfMetadata?.metadata ?? null;
193608
- log$3("PDF metadata retrieved:", {
194607
+ log$2("PDF metadata retrieved:", {
193609
194608
  hasInfo: !!Object.keys(pdfInfo).length,
193610
194609
  hasMetadata: !!metadata
193611
194610
  });
@@ -204656,11 +205655,11 @@ var init_parser_utils = __esmMin((() => {
204656
205655
  //#endregion
204657
205656
  //#region ../../packages/file-loaders/src/loaders/pptx/index.ts
204658
205657
  var pptx_exports = /* @__PURE__ */ __exportAll({ PptxLoader: () => PptxLoader });
204659
- var import_src$2, log$2, PptxLoader;
205658
+ var import_src, log$1, PptxLoader;
204660
205659
  var init_pptx = __esmMin((() => {
204661
- import_src$2 = /* @__PURE__ */ __toESM$2(require_src());
205660
+ import_src = /* @__PURE__ */ __toESM$2(require_src());
204662
205661
  init_parser_utils();
204663
- log$2 = (0, import_src$2.default)("file-loaders:pptx");
205662
+ log$1 = (0, import_src.default)("file-loaders:pptx");
204664
205663
  PptxLoader = class {
204665
205664
  /**
204666
205665
  * Loads pages from the specified PPTX file path.
@@ -204671,33 +205670,33 @@ var init_pptx = __esmMin((() => {
204671
205670
  * `DocumentPage` object with error information in its metadata.
204672
205671
  */
204673
205672
  async loadPages(filePath) {
204674
- log$2("Loading PPTX file:", filePath);
205673
+ log$1("Loading PPTX file:", filePath);
204675
205674
  const sourceFileName = path.basename(filePath);
204676
- log$2("Source file name:", sourceFileName);
205675
+ log$1("Source file name:", sourceFileName);
204677
205676
  try {
204678
205677
  const slidesRegex = /ppt\/slides\/slide\d+\.xml/g;
204679
205678
  const slideNumberRegex = /slide(\d+)\.xml/;
204680
- log$2("Extracting slide XML files from PPTX");
205679
+ log$1("Extracting slide XML files from PPTX");
204681
205680
  const slideFiles = await extractFiles(filePath, (fileName) => slidesRegex.test(fileName));
204682
- log$2("Extracted slide files:", slideFiles.length);
205681
+ log$1("Extracted slide files:", slideFiles.length);
204683
205682
  if (slideFiles.length === 0) {
204684
- log$2("No slide XML files found in the PPTX file");
205683
+ log$1("No slide XML files found in the PPTX file");
204685
205684
  console.warn(`No slide XML files found in ${sourceFileName}. May be corrupted or empty.`);
204686
205685
  return [this.createErrorPage("No slides found. The PPTX file might be empty, corrupted, or does not contain standard slide XMLs.", sourceFileName)];
204687
205686
  }
204688
- log$2("Sorting slide files by slide number");
205687
+ log$1("Sorting slide files by slide number");
204689
205688
  slideFiles.sort((a, b) => {
204690
205689
  const matchA = a.path.match(slideNumberRegex);
204691
205690
  const matchB = b.path.match(slideNumberRegex);
204692
205691
  return (matchA ? parseInt(matchA[1], 10) : Infinity) - (matchB ? parseInt(matchB[1], 10) : Infinity);
204693
205692
  });
204694
- log$2("Slide files sorted");
204695
- log$2("Creating document pages from slide files");
205693
+ log$1("Slide files sorted");
205694
+ log$1("Creating document pages from slide files");
204696
205695
  const pages = slideFiles.map((slideFile, index) => {
204697
205696
  try {
204698
- log$2(`Processing slide ${index + 1}/${slideFiles.length}, path: ${slideFile.path}`);
205697
+ log$1(`Processing slide ${index + 1}/${slideFiles.length}, path: ${slideFile.path}`);
204699
205698
  const paragraphNodes = parseString(slideFile.content).getElementsByTagName("a:p");
204700
- log$2(`Found ${paragraphNodes.length} paragraph nodes in slide ${index + 1}`);
205699
+ log$1(`Found ${paragraphNodes.length} paragraph nodes in slide ${index + 1}`);
204701
205700
  const slideText = Array.from(paragraphNodes).map((pNode) => {
204702
205701
  const textNodes = pNode.getElementsByTagName("a:t");
204703
205702
  return Array.from(textNodes).map((tNode) => tNode.childNodes[0] ? tNode.childNodes[0].nodeValue : "").join("");
@@ -204705,7 +205704,7 @@ var init_pptx = __esmMin((() => {
204705
205704
  const lines = slideText.split("\n");
204706
205705
  const slideNumberMatch = slideFile.path.match(slideNumberRegex);
204707
205706
  const slideNumber = slideNumberMatch ? parseInt(slideNumberMatch[1], 10) : index + 1;
204708
- log$2(`Slide ${index + 1} text extracted, lines: ${lines.length}, characters: ${slideText.length}`);
205707
+ log$1(`Slide ${index + 1} text extracted, lines: ${lines.length}, characters: ${slideText.length}`);
204709
205708
  const metadata = {
204710
205709
  pageCount: slideFiles.length,
204711
205710
  slideNumber,
@@ -204718,26 +205717,26 @@ var init_pptx = __esmMin((() => {
204718
205717
  pageContent: slideText.trim()
204719
205718
  };
204720
205719
  } catch (parseError) {
204721
- log$2(`Error parsing slide ${slideFile.path}`);
205720
+ log$1(`Error parsing slide ${slideFile.path}`);
204722
205721
  console.error(`Failed to parse XML for slide ${slideFile.path} in ${sourceFileName}: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
204723
205722
  return this.createErrorPage(`Error parsing slide ${slideFile.path}: ${parseError instanceof Error ? parseError.message : String(parseError)}`, sourceFileName, slideFile.path);
204724
205723
  }
204725
205724
  }).filter((page) => page !== null);
204726
- log$2(`Created ${pages.length} document pages from slides`);
205725
+ log$1(`Created ${pages.length} document pages from slides`);
204727
205726
  if (pages.length === 0) {
204728
- log$2("Parsing resulted in zero valid pages");
205727
+ log$1("Parsing resulted in zero valid pages");
204729
205728
  console.warn(`Parsing resulted in zero valid pages for ${sourceFileName}`);
204730
205729
  return [this.createErrorPage("Parsing resulted in zero valid pages.", sourceFileName)];
204731
205730
  }
204732
205731
  if (pages.every((page) => page.metadata?.error)) {
204733
- log$2("All slides failed to parse");
205732
+ log$1("All slides failed to parse");
204734
205733
  console.warn(`All slides failed to parse for ${sourceFileName}`);
204735
205734
  return [this.createErrorPage("All slides failed to parse correctly.", sourceFileName)];
204736
205735
  }
204737
- log$2("PPTX loading completed successfully");
205736
+ log$1("PPTX loading completed successfully");
204738
205737
  return pages;
204739
205738
  } catch (error) {
204740
- log$2("Error loading or processing PPTX file");
205739
+ log$1("Error loading or processing PPTX file");
204741
205740
  const errorMessage = `Failed to load or process PPTX file: ${error instanceof Error ? error.message : String(error)}`;
204742
205741
  console.error(errorMessage, { filePath });
204743
205742
  return [this.createErrorPage(errorMessage, sourceFileName)];
@@ -204753,11 +205752,11 @@ var init_pptx = __esmMin((() => {
204753
205752
  * @returns A Promise resolving to the aggregated content string.
204754
205753
  */
204755
205754
  async aggregateContent(pages) {
204756
- log$2("Aggregating content from", pages.length, "PPTX pages");
205755
+ log$1("Aggregating content from", pages.length, "PPTX pages");
204757
205756
  const validPages = pages.filter((page) => !page.metadata?.error);
204758
- log$2(`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} error pages filtered out)`);
205757
+ log$1(`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} error pages filtered out)`);
204759
205758
  if (validPages.length === 0) {
204760
- log$2("No valid pages found, returning content of first page (may be error page)");
205759
+ log$1("No valid pages found, returning content of first page (may be error page)");
204761
205760
  return pages[0]?.pageContent || "";
204762
205761
  }
204763
205762
  const result = validPages.map((page) => {
@@ -204766,7 +205765,7 @@ var init_pptx = __esmMin((() => {
204766
205765
  ${page.pageContent}
204767
205766
  </slide_page>`;
204768
205767
  }).join("\n\n");
204769
- log$2("PPTX content aggregated successfully, length:", result.length);
205768
+ log$1("PPTX content aggregated successfully, length:", result.length);
204770
205769
  return result;
204771
205770
  }
204772
205771
  /**
@@ -204778,7 +205777,7 @@ ${page.pageContent}
204778
205777
  * @returns A `DocumentPage` object representing the error state.
204779
205778
  */
204780
205779
  createErrorPage(errorInfo, sourceFileName, sourceFilePath) {
204781
- log$2("Creating error page:", errorInfo);
205780
+ log$1("Creating error page:", errorInfo);
204782
205781
  return {
204783
205782
  charCount: 0,
204784
205783
  lineCount: 0,
@@ -204794,104 +205793,6 @@ ${page.pageContent}
204794
205793
  };
204795
205794
  }));
204796
205795
 
204797
- //#endregion
204798
- //#region ../../packages/file-loaders/src/loaders/text/index.ts
204799
- var text_exports = /* @__PURE__ */ __exportAll({ TextLoader: () => TextLoader });
204800
- var import_src$1, log$1, HEURISTIC_SAMPLE_BYTES, HEURISTIC_THRESHOLD, detectUtf16NoBom, readTextFile, TextLoader;
204801
- var init_text = __esmMin((() => {
204802
- import_src$1 = /* @__PURE__ */ __toESM$2(require_src());
204803
- log$1 = (0, import_src$1.default)("file-loaders:text");
204804
- HEURISTIC_SAMPLE_BYTES = 512;
204805
- HEURISTIC_THRESHOLD = .3;
204806
- detectUtf16NoBom = (buffer) => {
204807
- const sample = buffer.subarray(0, Math.min(HEURISTIC_SAMPLE_BYTES, buffer.length));
204808
- if (sample.length < 4 || sample.length % 2 !== 0) return null;
204809
- let leAsciiPairs = 0;
204810
- let beAsciiPairs = 0;
204811
- const totalPairs = sample.length / 2;
204812
- for (let i = 0; i < sample.length; i += 2) {
204813
- const lo = sample[i];
204814
- const hi = sample[i + 1];
204815
- if (hi === 0 && lo !== 0) leAsciiPairs++;
204816
- else if (lo === 0 && hi !== 0) beAsciiPairs++;
204817
- }
204818
- if (leAsciiPairs > beAsciiPairs && leAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16le";
204819
- if (beAsciiPairs > leAsciiPairs && beAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16be";
204820
- return null;
204821
- };
204822
- readTextFile = async (filePath) => {
204823
- const buffer = await readFile(filePath);
204824
- if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) {
204825
- log$1("Detected UTF-16LE BOM");
204826
- return new TextDecoder("utf-16le").decode(buffer.subarray(2));
204827
- }
204828
- if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) {
204829
- log$1("Detected UTF-16BE BOM");
204830
- return new TextDecoder("utf-16be").decode(buffer.subarray(2));
204831
- }
204832
- if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
204833
- log$1("Detected UTF-8 BOM");
204834
- return buffer.subarray(3).toString("utf8");
204835
- }
204836
- const variant = detectUtf16NoBom(buffer);
204837
- if (variant) {
204838
- log$1("Detected %s without BOM (heuristic)", variant);
204839
- return new TextDecoder(variant).decode(buffer);
204840
- }
204841
- return buffer.toString("utf8");
204842
- };
204843
- TextLoader = class {
204844
- async loadPages(filePath) {
204845
- log$1("Loading text file:", filePath);
204846
- try {
204847
- const fileContent = await readTextFile(filePath);
204848
- log$1("Text file loaded successfully, size:", fileContent.length, "bytes");
204849
- const lineCount = fileContent.split("\n").length;
204850
- const charCount = fileContent.length;
204851
- log$1("Text file stats:", {
204852
- charCount,
204853
- lineCount
204854
- });
204855
- const page = {
204856
- charCount,
204857
- lineCount,
204858
- metadata: {
204859
- lineNumberEnd: lineCount,
204860
- lineNumberStart: 1
204861
- },
204862
- pageContent: fileContent
204863
- };
204864
- log$1("Text page created successfully");
204865
- return [page];
204866
- } catch (e) {
204867
- const error = e;
204868
- log$1("Error encountered while loading text file");
204869
- console.error(`Error loading text file ${filePath}: ${error.message}`);
204870
- const errorPage = {
204871
- charCount: 0,
204872
- lineCount: 0,
204873
- metadata: { error: `Failed to load text file: ${error.message}` },
204874
- pageContent: ""
204875
- };
204876
- log$1("Created error page for failed text file loading");
204877
- return [errorPage];
204878
- }
204879
- }
204880
- /**
204881
- * For plain text, simply concatenate the content of all pages.
204882
- * (Although TextLoader typically has only one page, this maintains interface consistency)
204883
- * @param pages Array of pages
204884
- * @returns Aggregated content
204885
- */
204886
- async aggregateContent(pages) {
204887
- log$1("Aggregating content from", pages.length, "text pages");
204888
- const result = pages.map((page) => page.pageContent).join("\n");
204889
- log$1("Content aggregated successfully, length:", result.length);
204890
- return result;
204891
- }
204892
- };
204893
- }));
204894
-
204895
205796
  //#endregion
204896
205797
  //#region ../../packages/file-loaders/src/loaders/index.ts
204897
205798
  const lazyFileLoaders = {
@@ -204923,23 +205824,18 @@ const lazyFileLoaders = {
204923
205824
  pptx: async () => {
204924
205825
  const { PptxLoader } = await Promise.resolve().then(() => (init_pptx(), pptx_exports));
204925
205826
  return PptxLoader;
204926
- },
204927
- txt: async () => {
204928
- const { TextLoader } = await Promise.resolve().then(() => (init_text(), text_exports));
204929
- return TextLoader;
204930
205827
  }
204931
205828
  };
204932
205829
  /**
204933
205830
  * Get a file loader class for the specified file type.
204934
205831
  * Uses dynamic imports to avoid loading heavy dependencies (like pdfjs-dist) until needed.
204935
- * Falls back to TextLoader if no specific loader is found.
205832
+ * TextLoader is returned synchronously (statically imported) for `txt` and as the
205833
+ * fallback for unknown types.
204936
205834
  */
204937
205835
  const getFileLoader = async (fileType) => {
205836
+ if (fileType === "txt") return TextLoader;
204938
205837
  const loaderFactory = lazyFileLoaders[fileType];
204939
- if (!loaderFactory) {
204940
- const { TextLoader } = await Promise.resolve().then(() => (init_text(), text_exports));
204941
- return TextLoader;
204942
- }
205838
+ if (!loaderFactory) return TextLoader;
204943
205839
  return loaderFactory();
204944
205840
  };
204945
205841
 
@@ -204951,6 +205847,8 @@ const TEXT_READABLE_FILE_TYPES = [
204951
205847
  "markdown",
204952
205848
  "mdx",
204953
205849
  "json",
205850
+ "jsonc",
205851
+ "json5",
204954
205852
  "xml",
204955
205853
  "yaml",
204956
205854
  "yml",
@@ -204959,6 +205857,8 @@ const TEXT_READABLE_FILE_TYPES = [
204959
205857
  "cfg",
204960
205858
  "conf",
204961
205859
  "csv",
205860
+ "env",
205861
+ "properties",
204962
205862
  "html",
204963
205863
  "htm",
204964
205864
  "css",
@@ -204969,6 +205869,9 @@ const TEXT_READABLE_FILE_TYPES = [
204969
205869
  "ts",
204970
205870
  "tsx",
204971
205871
  "mjs",
205872
+ "cjs",
205873
+ "mts",
205874
+ "cts",
204972
205875
  "vue",
204973
205876
  "svelte",
204974
205877
  "svg",
@@ -204989,6 +205892,11 @@ const TEXT_READABLE_FILE_TYPES = [
204989
205892
  "bash",
204990
205893
  "bat",
204991
205894
  "ps1",
205895
+ "lua",
205896
+ "dart",
205897
+ "scala",
205898
+ "groovy",
205899
+ "gradle",
204992
205900
  "log",
204993
205901
  "sql",
204994
205902
  "patch",
@@ -204996,6 +205904,18 @@ const TEXT_READABLE_FILE_TYPES = [
204996
205904
  "db"
204997
205905
  ];
204998
205906
  /**
205907
+ * Extensions that have dedicated parsers in `loadFile`. These are not text but
205908
+ * are explicitly supported file types that we know how to extract text from.
205909
+ */
205910
+ const SPECIAL_PARSED_FILE_TYPES = [
205911
+ "pdf",
205912
+ "doc",
205913
+ "docx",
205914
+ "xls",
205915
+ "xlsx",
205916
+ "pptx"
205917
+ ];
205918
+ /**
204999
205919
  * Determine if a file can be read as text based on its extension.
205000
205920
  * @param fileType File extension (without the leading dot)
205001
205921
  * @returns Whether the file is likely text-readable
@@ -205003,11 +205923,29 @@ const TEXT_READABLE_FILE_TYPES = [
205003
205923
  function isTextReadableFile(fileType) {
205004
205924
  return TEXT_READABLE_FILE_TYPES.includes(fileType.toLowerCase());
205005
205925
  }
205926
+ /**
205927
+ * Whether the agent's `readFile` should be willing to attempt reading this
205928
+ * extension at all. True for known text formats and for the special parsed
205929
+ * binary formats (pdf/doc/etc.) that have dedicated loaders. Anything else —
205930
+ * `.bin`, `.zip`, `.b64`, `.exe`, … — should be hard-rejected before the file
205931
+ * is opened, to avoid feeding a binary blob to the LLM.
205932
+ */
205933
+ function isReadableFileType(fileType) {
205934
+ const ext = fileType.toLowerCase();
205935
+ return TEXT_READABLE_FILE_TYPES.includes(ext) || SPECIAL_PARSED_FILE_TYPES.includes(ext);
205936
+ }
205006
205937
 
205007
205938
  //#endregion
205008
205939
  //#region ../../packages/file-loaders/src/loadFile.ts
205009
- var import_src = /* @__PURE__ */ __toESM$2(require_src());
205010
- const log = (0, import_src.default)("file-loaders:loadFile");
205940
+ const log = (0, import_src$5.default)("file-loaders:loadFile");
205941
+ var UnsupportedFileTypeError = class extends Error {
205942
+ fileType;
205943
+ constructor(fileType, filename) {
205944
+ super(`Unsupported file type '${fileType || "unknown"}' for file '${filename}'.`);
205945
+ this.name = "UnsupportedFileTypeError";
205946
+ this.fileType = fileType;
205947
+ }
205948
+ };
205011
205949
  /**
205012
205950
  * Determines the file type based on the filename extension.
205013
205951
  * @param filePath The path to the file.
@@ -205015,7 +205953,7 @@ const log = (0, import_src.default)("file-loaders:loadFile");
205015
205953
  */
205016
205954
  const getFileType = (filePath) => {
205017
205955
  log("Determining file type for:", filePath);
205018
- const extension = path$1.extname(filePath).toLowerCase().replace(".", "");
205956
+ const extension = path.extname(filePath).toLowerCase().replace(".", "");
205019
205957
  if (!extension) {
205020
205958
  log("No extension found, treating as txt");
205021
205959
  return "txt";
@@ -205070,8 +206008,8 @@ const loadFile = async (filePath, fileMetadata) => {
205070
206008
  fsError = `Failed to access file stats: ${error.message}`;
205071
206009
  }
205072
206010
  log("Determining base file info");
205073
- const fileExtension = path$1.extname(filePath).slice(1).toLowerCase();
205074
- const baseFilename = path$1.basename(filePath);
206011
+ const fileExtension = path.extname(filePath).slice(1).toLowerCase();
206012
+ const baseFilename = path.basename(filePath);
205075
206013
  const source = fileMetadata?.source ?? filePath;
205076
206014
  const filename = fileMetadata?.filename ?? baseFilename;
205077
206015
  const fileType = fileMetadata?.fileType ?? fileExtension;
@@ -205086,10 +206024,13 @@ const loadFile = async (filePath, fileMetadata) => {
205086
206024
  });
205087
206025
  const parserType = getFileType(filePath);
205088
206026
  log("Parser type determined as:", parserType);
206027
+ if (!parserType && !fsError) {
206028
+ console.warn(`No specific loader found for file type '${fileType}'. Rejecting unsupported file type.`);
206029
+ throw new UnsupportedFileTypeError(fileType, filename);
206030
+ }
205089
206031
  const LoaderClass = await getFileLoader(parserType ?? "txt");
205090
206032
  log("Selected loader class:", LoaderClass.name);
205091
- if (!parserType) console.warn(`No specific loader found for file type '${fileType}'. Using default loader (TextLoader) as fallback.`);
205092
- let pages = [];
206033
+ let pages;
205093
206034
  let aggregatedContent = "";
205094
206035
  let loaderError;
205095
206036
  let aggregationError;
@@ -205187,6 +206128,69 @@ const loadFile = async (filePath, fileMetadata) => {
205187
206128
  return fileDocument;
205188
206129
  };
205189
206130
 
206131
+ //#endregion
206132
+ //#region ../../packages/file-loaders/src/utils/isBinaryContent.ts
206133
+ const SNIFF_BYTES = 8192;
206134
+ const NON_PRINTABLE_THRESHOLD = .3;
206135
+ const hasUtf8Bom = (buf) => buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191;
206136
+ const hasUtf16Bom = (buf) => buf.length >= 2 && (buf[0] === 255 && buf[1] === 254 || buf[0] === 254 && buf[1] === 255);
206137
+ /**
206138
+ * Heuristically determine if a buffer looks like binary data.
206139
+ *
206140
+ * - UTF-8 / UTF-16 BOM → text
206141
+ * - UTF-16 detected without BOM (Windows-style exports) → text, decoded for
206142
+ * the printable-ratio check
206143
+ * - Any null byte (and not UTF-16) → binary
206144
+ * - More than 30% of decoded chars are control or U+FFFD replacement → binary
206145
+ *
206146
+ * Note: this only catches truly binary content. Text-encoded blobs (e.g., a
206147
+ * single 27KB line of base64) will pass this check — the extension whitelist
206148
+ * and the post-load char cap are what stop those.
206149
+ */
206150
+ const sniffBinaryBuffer = (buffer) => {
206151
+ if (buffer.length === 0) return { isBinary: false };
206152
+ if (hasUtf8Bom(buffer) || hasUtf16Bom(buffer)) return { isBinary: false };
206153
+ const utf16Variant = detectUtf16NoBom(buffer);
206154
+ if (utf16Variant) return checkPrintableRatio(new TextDecoder(utf16Variant, { fatal: false }).decode(buffer), buffer.length);
206155
+ if (buffer.includes(0)) return {
206156
+ isBinary: true,
206157
+ reason: "contains null byte"
206158
+ };
206159
+ return checkPrintableRatio(new TextDecoder("utf-8", { fatal: false }).decode(buffer), buffer.length);
206160
+ };
206161
+ const REPLACEMENT_CHAR = "�";
206162
+ const checkPrintableRatio = (text, sampledBytes) => {
206163
+ if (text.length === 0) return { isBinary: false };
206164
+ let suspect = 0;
206165
+ for (const ch of text) {
206166
+ if (ch === REPLACEMENT_CHAR) {
206167
+ suspect++;
206168
+ continue;
206169
+ }
206170
+ const code = ch.codePointAt(0);
206171
+ if (code < 32 && code !== 9 && code !== 10 && code !== 13) suspect++;
206172
+ }
206173
+ const ratio = suspect / text.length;
206174
+ if (ratio > NON_PRINTABLE_THRESHOLD) return {
206175
+ isBinary: true,
206176
+ reason: `${(ratio * 100).toFixed(1)}% non-printable chars in first ${sampledBytes} bytes`
206177
+ };
206178
+ return { isBinary: false };
206179
+ };
206180
+ /**
206181
+ * Read up to the leading 8KB of a file and run the binary heuristic on it.
206182
+ */
206183
+ const sniffBinaryFile = async (filePath) => {
206184
+ const fd = await open(filePath, "r");
206185
+ try {
206186
+ const buffer = Buffer.alloc(SNIFF_BYTES);
206187
+ const { bytesRead } = await fd.read(buffer, 0, SNIFF_BYTES, 0);
206188
+ return sniffBinaryBuffer(buffer.subarray(0, bytesRead));
206189
+ } finally {
206190
+ await fd.close();
206191
+ }
206192
+ };
206193
+
205190
206194
  //#endregion
205191
206195
  //#region ../../packages/local-file-shell/src/file/list.ts
205192
206196
  async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrder = "desc", limit = 100 }, options) {
@@ -205247,9 +206251,52 @@ async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrde
205247
206251
 
205248
206252
  //#endregion
205249
206253
  //#region ../../packages/local-file-shell/src/file/read.ts
206254
+ /** Hard cap on file size we will read into memory at all (10MB). */
206255
+ const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
206256
+ /** Cap on the total chars returned to the agent. */
206257
+ const MAX_OUTPUT_CHARS = 5e5;
206258
+ /** Cap on chars per line. Keeps a single 27KB base64 line from blowing up the response. */
206259
+ const MAX_LINE_CHARS = 8e3;
206260
+ const inferFileType = (filePath) => path.extname(filePath).toLowerCase().replace(".", "") || "unknown";
206261
+ const buildErrorResult = (filePath, message) => ({
206262
+ charCount: 0,
206263
+ content: message,
206264
+ createdTime: /* @__PURE__ */ new Date(),
206265
+ fileType: inferFileType(filePath),
206266
+ filename: path.basename(filePath),
206267
+ lineCount: 0,
206268
+ loc: [0, 0],
206269
+ modifiedTime: /* @__PURE__ */ new Date(),
206270
+ totalCharCount: 0,
206271
+ totalLineCount: 0
206272
+ });
206273
+ const truncateLine = (line) => {
206274
+ if (line.length <= MAX_LINE_CHARS) return {
206275
+ line,
206276
+ truncated: false
206277
+ };
206278
+ return {
206279
+ line: `${line.slice(0, MAX_LINE_CHARS)}… [line truncated: was ${line.length} chars, kept first ${MAX_LINE_CHARS}]`,
206280
+ truncated: true
206281
+ };
206282
+ };
205250
206283
  async function readLocalFile({ path: rawPath, loc, fullContent }) {
205251
206284
  const filePath = expandTilde(rawPath) ?? rawPath;
205252
206285
  const effectiveLoc = fullContent ? void 0 : loc ?? [0, 200];
206286
+ let stats;
206287
+ try {
206288
+ stats = await stat(filePath);
206289
+ } catch (error) {
206290
+ return buildErrorResult(filePath, `Error accessing or processing file: ${error.message}`);
206291
+ }
206292
+ if (stats.isDirectory()) return buildErrorResult(filePath, "This is a directory and cannot be read as plain text.");
206293
+ if (stats.size > MAX_FILE_SIZE_BYTES) return buildErrorResult(filePath, `Error: File is too large to read (${stats.size} bytes, limit ${MAX_FILE_SIZE_BYTES}). Use grep / shell tools to inspect specific parts.`);
206294
+ const extension = path.extname(filePath).toLowerCase().replace(".", "");
206295
+ if (extension && !isReadableFileType(extension)) return buildErrorResult(filePath, `Error: Unsupported binary file type: .${extension}. Use a different tool (e.g., 'runCommand' with file/hexdump/strings) to inspect binary files.`);
206296
+ if (!SPECIAL_PARSED_FILE_TYPES.includes(extension)) try {
206297
+ const sniff = await sniffBinaryFile(filePath);
206298
+ if (sniff.isBinary) return buildErrorResult(filePath, `Error: File appears to be binary (${sniff.reason}). Refusing to read as text.`);
206299
+ } catch {}
205253
206300
  try {
205254
206301
  const fileDocument = await loadFile(filePath);
205255
206302
  if (fileDocument.metadata?.error) return {
@@ -205267,59 +206314,45 @@ async function readLocalFile({ path: rawPath, loc, fullContent }) {
205267
206314
  const lines = fileDocument.content.split("\n");
205268
206315
  const totalLineCount = lines.length;
205269
206316
  const totalCharCount = fileDocument.content.length;
205270
- let content;
205271
- let charCount;
205272
- let lineCount;
206317
+ let workingLines;
205273
206318
  let actualLoc;
205274
206319
  if (effectiveLoc === void 0) {
205275
- content = fileDocument.content;
205276
- charCount = totalCharCount;
205277
- lineCount = totalLineCount;
206320
+ workingLines = lines;
205278
206321
  actualLoc = [0, totalLineCount];
205279
206322
  } else {
205280
206323
  const [startLine, endLine] = effectiveLoc;
205281
- const selectedLines = lines.slice(startLine, endLine);
205282
- content = selectedLines.join("\n");
205283
- charCount = content.length;
205284
- lineCount = selectedLines.length;
206324
+ workingLines = lines.slice(startLine, endLine);
205285
206325
  actualLoc = effectiveLoc;
205286
206326
  }
205287
- const fileType = fileDocument.fileType || "unknown";
206327
+ let linesTruncated = 0;
206328
+ let content = workingLines.map((line) => {
206329
+ const r = truncateLine(line);
206330
+ if (r.truncated) linesTruncated++;
206331
+ return r.line;
206332
+ }).join("\n");
206333
+ let truncated = false;
206334
+ if (content.length > MAX_OUTPUT_CHARS) {
206335
+ const originalLength = content.length;
206336
+ content = `${content.slice(0, MAX_OUTPUT_CHARS)}\n[content truncated: response was ${originalLength} chars, kept first ${MAX_OUTPUT_CHARS}. Use a smaller line range or grep to narrow down.]`;
206337
+ truncated = true;
206338
+ }
205288
206339
  const result = {
205289
- charCount,
206340
+ charCount: content.length,
205290
206341
  content,
205291
206342
  createdTime: fileDocument.createdTime,
205292
- fileType,
206343
+ fileType: fileDocument.fileType || "unknown",
205293
206344
  filename: fileDocument.filename,
205294
- lineCount,
206345
+ lineCount: workingLines.length,
205295
206346
  loc: actualLoc,
205296
206347
  modifiedTime: fileDocument.modifiedTime,
205297
206348
  totalCharCount,
205298
206349
  totalLineCount
205299
206350
  };
205300
- try {
205301
- if ((await stat(filePath)).isDirectory()) {
205302
- result.content = "This is a directory and cannot be read as plain text.";
205303
- result.charCount = 0;
205304
- result.lineCount = 0;
205305
- result.totalCharCount = 0;
205306
- result.totalLineCount = 0;
205307
- }
205308
- } catch {}
206351
+ if (truncated) result.truncated = true;
206352
+ if (linesTruncated > 0) result.linesTruncated = linesTruncated;
205309
206353
  return result;
205310
206354
  } catch (error) {
205311
- return {
205312
- charCount: 0,
205313
- content: `Error accessing or processing file: ${error.message}`,
205314
- createdTime: /* @__PURE__ */ new Date(),
205315
- fileType: path.extname(filePath).toLowerCase().replace(".", "") || "unknown",
205316
- filename: path.basename(filePath),
205317
- lineCount: 0,
205318
- loc: [0, 0],
205319
- modifiedTime: /* @__PURE__ */ new Date(),
205320
- totalCharCount: 0,
205321
- totalLineCount: 0
205322
- };
206355
+ return buildErrorResult(filePath, `Error accessing or processing file: ${error.message}`);
205323
206356
  }
205324
206357
  }
205325
206358
 
@@ -205328,9 +206361,10 @@ async function readLocalFile({ path: rawPath, loc, fullContent }) {
205328
206361
  async function searchLocalFiles({ keywords, directory, contentContains, limit = 30 }) {
205329
206362
  try {
205330
206363
  const cwd = expandTilde(directory) || process.cwd();
206364
+ const wantsHidden = keywords.startsWith(".");
205331
206365
  let results = (await (0, import_out.default)(`**/*${keywords}*`, {
205332
206366
  cwd,
205333
- dot: false,
206367
+ dot: wantsHidden,
205334
206368
  ignore: ["**/node_modules/**", "**/.git/**"]
205335
206369
  })).map((f) => ({
205336
206370
  name: path.basename(f),
@@ -205471,7 +206505,7 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
205471
206505
  cwd,
205472
206506
  timeout
205473
206507
  });
205474
- const effectiveTimeout = Math.min(Math.max(timeout, 1e3), 6e5);
206508
+ const effectiveTimeout = Math.min(Math.max(timeout, 1e3), 8e5);
205475
206509
  const shellConfig = getShellConfig(command);
205476
206510
  const childEnv = extraEnv ? {
205477
206511
  ...process.env,
@@ -205571,6 +206605,207 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
205571
206605
  }
205572
206606
  }
205573
206607
 
206608
+ //#endregion
206609
+ //#region src/daemon/taskRegistry.ts
206610
+ function getRegistryPath() {
206611
+ return path.join(os.homedir(), ".lobehub", "task-registry.json");
206612
+ }
206613
+ function readRegistry() {
206614
+ try {
206615
+ return JSON.parse(fs.readFileSync(getRegistryPath(), "utf8"));
206616
+ } catch {
206617
+ return {};
206618
+ }
206619
+ }
206620
+ function writeRegistry(entries) {
206621
+ const dir = path.dirname(getRegistryPath());
206622
+ fs.mkdirSync(dir, {
206623
+ mode: 448,
206624
+ recursive: true
206625
+ });
206626
+ fs.writeFileSync(getRegistryPath(), JSON.stringify(entries, null, 2), { mode: 384 });
206627
+ }
206628
+ function saveTask(entry) {
206629
+ const registry = readRegistry();
206630
+ registry[entry.taskId] = entry;
206631
+ writeRegistry(registry);
206632
+ }
206633
+ function getTask(taskId) {
206634
+ return readRegistry()[taskId];
206635
+ }
206636
+ function removeTask(taskId) {
206637
+ const registry = readRegistry();
206638
+ delete registry[taskId];
206639
+ writeRegistry(registry);
206640
+ }
206641
+
206642
+ //#endregion
206643
+ //#region src/tools/heteroTask.ts
206644
+ const DEFAULT_HERMES_PORT = 3456;
206645
+ /** Resolve the absolute path to the `lh` binary to avoid PATH issues in child processes. */
206646
+ function resolveLhPath() {
206647
+ try {
206648
+ return execFileSync("which", ["lh"], { encoding: "utf8" }).trim();
206649
+ } catch {
206650
+ return "lh";
206651
+ }
206652
+ }
206653
+ function getHermesPort() {
206654
+ const env = process.env.HERMES_GATEWAY_PORT;
206655
+ if (env) {
206656
+ const parsed = Number.parseInt(env, 10);
206657
+ if (!Number.isNaN(parsed)) return parsed;
206658
+ }
206659
+ return DEFAULT_HERMES_PORT;
206660
+ }
206661
+ async function isHermesGatewayRunning(port) {
206662
+ try {
206663
+ return (await fetch(`http://localhost:${port}/health`)).ok;
206664
+ } catch {
206665
+ return false;
206666
+ }
206667
+ }
206668
+ async function startHermesGateway(port) {
206669
+ spawn("hermes", ["gateway", "start"], {
206670
+ detached: true,
206671
+ env: { ...process.env },
206672
+ stdio: "ignore"
206673
+ }).unref();
206674
+ const deadline = Date.now() + 1e4;
206675
+ while (Date.now() < deadline) {
206676
+ await new Promise((r) => setTimeout(r, 500));
206677
+ if (await isHermesGatewayRunning(port)) return;
206678
+ }
206679
+ throw new Error(`Hermes gateway did not start within 10s on port ${port}`);
206680
+ }
206681
+ async function sendAutoNotify(topicId, taskId, text, agentId) {
206682
+ try {
206683
+ await (await getTrpcClient()).agentNotify.notify.mutate({
206684
+ agentId,
206685
+ content: JSON.stringify({
206686
+ stage: "error",
206687
+ taskId,
206688
+ text
206689
+ }),
206690
+ topicId
206691
+ });
206692
+ } catch (err) {
206693
+ log$7.error("Failed to send auto-notify:", err instanceof Error ? err.message : String(err));
206694
+ }
206695
+ }
206696
+ async function runHeteroTask(params) {
206697
+ const { agentId, agentType, cwd, operationId, prompt, taskId, topicId } = params;
206698
+ const workDir = cwd || process.cwd();
206699
+ if (agentType === "openclaw") {
206700
+ const enrichedPrompt = `${prompt}\n\nWhen your task is complete, run this shell command to report back:\n${resolveLhPath()} notify --topic ${topicId} --content '${JSON.stringify({
206701
+ stage: "done",
206702
+ taskId,
206703
+ text: "Task completed"
206704
+ })}'`;
206705
+ const openclawAgent = process.env.OPENCLAW_AGENT_ID ?? "main";
206706
+ const child = spawn("openclaw", [
206707
+ "agent",
206708
+ "--agent",
206709
+ openclawAgent,
206710
+ "--message",
206711
+ enrichedPrompt,
206712
+ "--local"
206713
+ ], {
206714
+ cwd: workDir,
206715
+ detached: true,
206716
+ env: { ...process.env },
206717
+ stdio: "ignore"
206718
+ });
206719
+ const pid = child.pid;
206720
+ if (pid === void 0) throw new Error("Failed to get PID for openclaw process");
206721
+ child.unref();
206722
+ saveTask({
206723
+ agentId,
206724
+ agentType,
206725
+ operationId,
206726
+ pid,
206727
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
206728
+ taskId,
206729
+ topicId
206730
+ });
206731
+ log$7.info(`OpenClaw task started: taskId=${taskId} pid=${pid} agent=${openclawAgent}`);
206732
+ child.on("close", (code, signal) => {
206733
+ removeTask(taskId);
206734
+ if (code !== 0 || signal !== null) sendAutoNotify(topicId, taskId, signal ? `Task cancelled (signal: ${signal})` : `Task failed (exit code: ${code})`, agentId);
206735
+ });
206736
+ return JSON.stringify({
206737
+ pid,
206738
+ taskId
206739
+ });
206740
+ }
206741
+ if (agentType === "hermes") {
206742
+ const port = getHermesPort();
206743
+ if (!await isHermesGatewayRunning(port)) {
206744
+ log$7.info(`Hermes gateway not running on port ${port}, starting...`);
206745
+ await startHermesGateway(port);
206746
+ }
206747
+ const res = await fetch(`http://localhost:${port}/message`, {
206748
+ body: JSON.stringify({
206749
+ content: prompt,
206750
+ operationId
206751
+ }),
206752
+ headers: { "Content-Type": "application/json" },
206753
+ method: "POST"
206754
+ });
206755
+ if (!res.ok) throw new Error(`Hermes gateway returned ${res.status}: ${await res.text()}`);
206756
+ saveTask({
206757
+ agentId,
206758
+ agentType,
206759
+ operationId,
206760
+ pid: 0,
206761
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
206762
+ taskId,
206763
+ topicId
206764
+ });
206765
+ log$7.info(`Hermes task dispatched: taskId=${taskId} operationId=${operationId}`);
206766
+ return JSON.stringify({
206767
+ operationId,
206768
+ taskId
206769
+ });
206770
+ }
206771
+ throw new Error(`Unsupported agentType: ${agentType}`);
206772
+ }
206773
+ async function cancelHeteroTask(params) {
206774
+ const { signal = "SIGINT", taskId } = params;
206775
+ const entry = getTask(taskId);
206776
+ if (!entry) return JSON.stringify({
206777
+ message: `No task found with taskId: ${taskId}`,
206778
+ success: false
206779
+ });
206780
+ if (entry.agentType === "hermes") {
206781
+ const port = getHermesPort();
206782
+ try {
206783
+ await fetch(`http://localhost:${port}/stop`, {
206784
+ body: JSON.stringify({ operationId: entry.operationId }),
206785
+ headers: { "Content-Type": "application/json" },
206786
+ method: "POST"
206787
+ });
206788
+ } catch (err) {
206789
+ log$7.warn(`Failed to send /stop to Hermes gateway: ${err instanceof Error ? err.message : String(err)}`);
206790
+ }
206791
+ removeTask(taskId);
206792
+ await sendAutoNotify(entry.topicId, taskId, "Task cancelled", entry.agentId);
206793
+ return JSON.stringify({ taskId });
206794
+ }
206795
+ try {
206796
+ process.kill(entry.pid, signal);
206797
+ } catch (err) {
206798
+ log$7.warn(`Failed to send ${signal} to pid ${entry.pid}: ${err instanceof Error ? err.message : String(err)}`);
206799
+ removeTask(taskId);
206800
+ await sendAutoNotify(entry.topicId, taskId, "Task already completed or cancelled", entry.agentId);
206801
+ }
206802
+ return JSON.stringify({
206803
+ pid: entry.pid,
206804
+ signal,
206805
+ taskId
206806
+ });
206807
+ }
206808
+
205574
206809
  //#endregion
205575
206810
  //#region src/tools/shell.ts
205576
206811
  const processManager = new ShellProcessManager();
@@ -205593,6 +206828,7 @@ async function killCommand(params) {
205593
206828
  //#endregion
205594
206829
  //#region src/tools/index.ts
205595
206830
  const methodMap = {
206831
+ cancelHeteroTask,
205596
206832
  editFile: editLocalFile,
205597
206833
  getCommandOutput,
205598
206834
  globFiles: globLocalFiles,
@@ -205601,6 +206837,7 @@ const methodMap = {
205601
206837
  listFiles: listLocalFiles,
205602
206838
  readFile: readLocalFile,
205603
206839
  runCommand,
206840
+ runHeteroTask,
205604
206841
  searchFiles: searchLocalFiles,
205605
206842
  writeFile: writeLocalFile,
205606
206843
  editLocalFile,
@@ -205939,141 +207176,6 @@ function collectSystemInfo() {
205939
207176
  };
205940
207177
  }
205941
207178
 
205942
- //#endregion
205943
- //#region src/commands/cron.ts
205944
- function registerCronCommand(program) {
205945
- const cron = program.command("cron").description("Manage agent cron jobs");
205946
- cron.command("list").description("List cron jobs").option("--agent-id <id>", "Filter by agent ID").option("--enabled", "Only show enabled jobs").option("--disabled", "Only show disabled jobs").option("-L, --limit <n>", "Page size", "20").option("--offset <n>", "Offset", "0").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
205947
- const client = await getTrpcClient();
205948
- const input = {};
205949
- if (options.agentId) input.agentId = options.agentId;
205950
- if (options.enabled) input.enabled = true;
205951
- if (options.disabled) input.enabled = false;
205952
- if (options.limit) input.limit = Number.parseInt(options.limit, 10);
205953
- if (options.offset) input.offset = Number.parseInt(options.offset, 10);
205954
- const items = (await client.agentCronJob.list.query(input)).data ?? [];
205955
- if (options.json !== void 0) {
205956
- outputJson(items, typeof options.json === "string" ? options.json : void 0);
205957
- return;
205958
- }
205959
- if (items.length === 0) {
205960
- console.log("No cron jobs found.");
205961
- return;
205962
- }
205963
- printTable(items.map((j) => [
205964
- j.id || "",
205965
- truncate(j.name || "", 30),
205966
- j.schedule || "",
205967
- j.enabled ? import_picocolors.default.green("enabled") : import_picocolors.default.dim("disabled"),
205968
- `${j.executionCount ?? 0}/${j.maxExecutions ?? "∞"}`,
205969
- j.updatedAt ? timeAgo(j.updatedAt) : ""
205970
- ]), [
205971
- "ID",
205972
- "NAME",
205973
- "SCHEDULE",
205974
- "STATUS",
205975
- "EXECUTIONS",
205976
- "UPDATED"
205977
- ]);
205978
- });
205979
- cron.command("view <id>").description("View cron job details").option("--json [fields]", "Output JSON").action(async (id, options) => {
205980
- const job = (await (await getTrpcClient()).agentCronJob.findById.query({ id })).data;
205981
- if (options.json !== void 0) {
205982
- outputJson(job, typeof options.json === "string" ? options.json : void 0);
205983
- return;
205984
- }
205985
- if (!job) {
205986
- log$7.error("Cron job not found.");
205987
- process.exit(1);
205988
- }
205989
- console.log(`${import_picocolors.default.bold("ID:")} ${job.id}`);
205990
- console.log(`${import_picocolors.default.bold("Name:")} ${job.name || ""}`);
205991
- console.log(`${import_picocolors.default.bold("Agent ID:")} ${job.agentId || ""}`);
205992
- console.log(`${import_picocolors.default.bold("Schedule:")} ${job.schedule || ""}`);
205993
- console.log(`${import_picocolors.default.bold("Status:")} ${job.enabled ? import_picocolors.default.green("enabled") : import_picocolors.default.dim("disabled")}`);
205994
- console.log(`${import_picocolors.default.bold("Executions:")} ${job.executionCount ?? 0}/${job.maxExecutions ?? "∞"}`);
205995
- if (job.prompt) console.log(`${import_picocolors.default.bold("Prompt:")} ${truncate(job.prompt, 80)}`);
205996
- if (job.createdAt) console.log(`${import_picocolors.default.bold("Created:")} ${timeAgo(job.createdAt)}`);
205997
- if (job.updatedAt) console.log(`${import_picocolors.default.bold("Updated:")} ${timeAgo(job.updatedAt)}`);
205998
- });
205999
- cron.command("create").description("Create a cron job").requiredOption("--agent-id <id>", "Agent ID").requiredOption("-s, --schedule <cron>", "Cron schedule expression").option("-n, --name <name>", "Job name").option("-p, --prompt <prompt>", "Prompt text").option("--max-executions <n>", "Maximum number of executions").option("--json", "Output JSON").action(async (options) => {
206000
- const client = await getTrpcClient();
206001
- const input = {
206002
- agentId: options.agentId,
206003
- cronPattern: options.schedule
206004
- };
206005
- if (options.name) input.name = options.name;
206006
- if (options.prompt) input.content = options.prompt;
206007
- if (options.maxExecutions) input.maxExecutions = Number.parseInt(options.maxExecutions, 10);
206008
- const result = await client.agentCronJob.create.mutate(input);
206009
- if (options.json) {
206010
- console.log(JSON.stringify(result, null, 2));
206011
- return;
206012
- }
206013
- const data = result.data;
206014
- console.log(`${import_picocolors.default.green("✓")} Created cron job ${import_picocolors.default.bold(data?.id || "")}`);
206015
- });
206016
- cron.command("edit <id>").description("Update a cron job").option("-n, --name <name>", "Job name").option("-s, --schedule <cron>", "Cron schedule expression").option("-p, --prompt <prompt>", "Prompt text").option("--max-executions <n>", "Maximum number of executions").option("--enable", "Enable the job").option("--disable", "Disable the job").action(async (id, options) => {
206017
- const data = {};
206018
- if (options.name) data.name = options.name;
206019
- if (options.schedule) data.cronPattern = options.schedule;
206020
- if (options.prompt) data.content = options.prompt;
206021
- if (options.maxExecutions) data.maxExecutions = Number.parseInt(options.maxExecutions, 10);
206022
- if (options.enable) data.enabled = true;
206023
- if (options.disable) data.enabled = false;
206024
- if (Object.keys(data).length === 0) {
206025
- log$7.error("No changes specified. Use --name, --schedule, --prompt, --enable, or --disable.");
206026
- process.exit(1);
206027
- }
206028
- await (await getTrpcClient()).agentCronJob.update.mutate({
206029
- data,
206030
- id
206031
- });
206032
- console.log(`${import_picocolors.default.green("✓")} Updated cron job ${import_picocolors.default.bold(id)}`);
206033
- });
206034
- cron.command("delete <id>").description("Delete a cron job").option("--yes", "Skip confirmation prompt").action(async (id, options) => {
206035
- if (!options.yes) {
206036
- if (!await confirm("Are you sure you want to delete this cron job?")) {
206037
- console.log("Cancelled.");
206038
- return;
206039
- }
206040
- }
206041
- await (await getTrpcClient()).agentCronJob.delete.mutate({ id });
206042
- console.log(`${import_picocolors.default.green("✓")} Deleted cron job ${import_picocolors.default.bold(id)}`);
206043
- });
206044
- cron.command("toggle <ids...>").description("Batch enable or disable cron jobs").option("--enable", "Enable the jobs").option("--disable", "Disable the jobs").action(async (ids, options) => {
206045
- if (!options.enable && !options.disable) {
206046
- log$7.error("Specify --enable or --disable.");
206047
- process.exit(1);
206048
- }
206049
- const enabled = !!options.enable;
206050
- const count = (await (await getTrpcClient()).agentCronJob.batchUpdateStatus.mutate({
206051
- enabled,
206052
- ids
206053
- })).data?.updatedCount ?? ids.length;
206054
- console.log(`${import_picocolors.default.green("✓")} ${enabled ? "Enabled" : "Disabled"} ${count} cron job(s)`);
206055
- });
206056
- cron.command("reset <id>").description("Reset execution count for a cron job").option("--max <n>", "Set new max executions").action(async (id, options) => {
206057
- const client = await getTrpcClient();
206058
- const input = { id };
206059
- if (options.max) input.newMaxExecutions = Number.parseInt(options.max, 10);
206060
- await client.agentCronJob.resetExecutions.mutate(input);
206061
- console.log(`${import_picocolors.default.green("✓")} Reset execution count for ${import_picocolors.default.bold(id)}`);
206062
- });
206063
- cron.command("stats").description("Get cron job execution statistics").option("--json", "Output JSON").action(async (options) => {
206064
- const stats = (await (await getTrpcClient()).agentCronJob.getStats.query()).data;
206065
- if (options.json) {
206066
- console.log(JSON.stringify(stats, null, 2));
206067
- return;
206068
- }
206069
- if (!stats) {
206070
- console.log("No statistics available.");
206071
- return;
206072
- }
206073
- for (const [key, value] of Object.entries(stats)) console.log(`${import_picocolors.default.bold(key + ":")} ${value}`);
206074
- });
206075
- }
206076
-
206077
207179
  //#endregion
206078
207180
  //#region src/commands/device.ts
206079
207181
  function registerDeviceCommand(program) {
@@ -207203,6 +208305,58 @@ function colorStatus(status) {
207203
208305
  * regardless of whether they share a constant.
207204
208306
  */
207205
208307
  const CC_TODO_WRITE_TOOL_NAME = "TodoWrite";
208308
+ /**
208309
+ * CC 2.1.143+ replaced the declarative {@link CC_TODO_WRITE_TOOL_NAME} with
208310
+ * an imperative trio: one task per `TaskCreate` (server-assigned numeric id),
208311
+ * field-merge mutations via `TaskUpdate`, and `TaskList` as the only
208312
+ * full-state read. The adapter accumulates these into a per-session map and
208313
+ * synthesizes the shared `pluginState.todos` shape on each task-tool
208314
+ * tool_result so the existing TodoProgress UI keeps working.
208315
+ *
208316
+ * The old TodoWrite path stays alongside — resumed sessions started on an
208317
+ * older CC may still emit it, and CC's recent SDK reminder text doesn't
208318
+ * forbid the model from using TodoWrite if it really wants to.
208319
+ */
208320
+ const CC_TASK_CREATE_TOOL_NAME = "TaskCreate";
208321
+ const CC_TASK_UPDATE_TOOL_NAME = "TaskUpdate";
208322
+ const CC_TASK_LIST_TOOL_NAME = "TaskList";
208323
+ /**
208324
+ * tool_result confirmation emitted by CC for a successful `TaskCreate`.
208325
+ * Observed shape on CC 2.1.143: `Task #1 created successfully: <subject>`.
208326
+ * The numeric id is the only place we can read the CC-assigned handle —
208327
+ * `TaskCreate.input` itself does not echo it.
208328
+ */
208329
+ const TASK_CREATE_RESULT_PATTERN = /^Task #(\d+) created successfully/;
208330
+ /**
208331
+ * tool_result confirmation emitted by CC for a successful `TaskUpdate`.
208332
+ * Suffix varies (`status`, blank if no field changed, etc.); we only need
208333
+ * the id to confirm the mutation landed — the field deltas are already
208334
+ * carried by the cached `TaskUpdate.input`.
208335
+ */
208336
+ const TASK_UPDATE_RESULT_PATTERN = /^Updated task #\d+/;
208337
+ /**
208338
+ * One line of `TaskList`'s plain-text output: `#1 [in_progress] read hosts`.
208339
+ * Used as the resume reconciliation path — when this adapter joins a CC
208340
+ * session mid-stream and missed earlier Create / Update events, parsing
208341
+ * TaskList rebuilds id / subject / status. `activeForm` and `description`
208342
+ * cannot be recovered (CC omits them) so resumed in_progress tasks fall
208343
+ * back to the subject text, same as TodoWrite's content-fallback.
208344
+ */
208345
+ const TASK_LIST_LINE_PATTERN = /^#(\d+) \[(pending|in_progress|completed)\] (.+)$/;
208346
+ /**
208347
+ * Tool name CC sees for the LobeHub-hosted MCP `ask_user_question` server.
208348
+ * Source of truth lives in `../askUser/constants.ts`; replicated here as a
208349
+ * literal so the adapter compiles in browser bundles without dragging in
208350
+ * any of the askUser package's runtime (node:http, MCP SDK, etc.) by
208351
+ * accident. Keep in sync.
208352
+ */
208353
+ const ASK_USER_MCP_TOOL_NAME = "mcp__lobe_cc__ask_user_question";
208354
+ /**
208355
+ * apiName the adapter rewrites the MCP tool to so the renderer routes on
208356
+ * a stable key, not the wire-prefixed MCP name. Source of truth same as
208357
+ * above.
208358
+ */
208359
+ const ASK_USER_API_NAME = "askUserQuestion";
207206
208360
  const CLAUDE_CODE_CLI_INSTALL_DOCS_URL = "https://docs.anthropic.com/en/docs/claude-code/setup";
207207
208361
  const CLI_AUTH_REQUIRED_PATTERNS = [
207208
208362
  /failed to authenticate/i,
@@ -207260,24 +208414,40 @@ const getRateLimitTerminalError = (result, rateLimitInfo, apiErrorStatus) => {
207260
208414
  stderr: rawMessage
207261
208415
  };
207262
208416
  };
208417
+ const toSynthesizedStatus = (status) => status === "in_progress" ? "processing" : status === "pending" ? "todo" : "completed";
208418
+ const synthesizeTodoWritePluginState = (args) => {
208419
+ return { todos: {
208420
+ items: (args.todos || []).map((todo) => {
208421
+ const text = todo.status === "in_progress" ? todo.activeForm || todo.content : todo.content;
208422
+ return {
208423
+ status: toSynthesizedStatus(todo.status),
208424
+ text
208425
+ };
208426
+ }),
208427
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
208428
+ } };
208429
+ };
207263
208430
  /**
207264
- * CC's TodoWrite is a declarative state-write tool: its `tool_use.input` IS
207265
- * the target todos list, and the `tool_result` content is just a confirmation
207266
- * string. Translating the input into the shared `StepContextTodos` shape lets
207267
- * the Gateway/ACP-aligned `pluginState.todos` contract light up the
207268
- * TodoProgress card without any CC-specific knowledge leaking into selectors
207269
- * or executors.
208431
+ * Snapshot the running `claudeCodeTasks` accumulator into the shared
208432
+ * `pluginState.todos` shape. Sorted by numeric id so the rendered order
208433
+ * matches CC's own TaskList output (insertion order = id order in practice,
208434
+ * but TaskUpdate can rearrange status without rearranging ids). Carries the
208435
+ * `id` per item so the TaskUpdate inspector can resolve `args.taskId` →
208436
+ * subject text without falling back to a `#N` label.
207270
208437
  *
207271
- * Word mapping: CC `pending|in_progress|completed` shared `todo|processing|completed`.
207272
- * Text field: use `activeForm` while in progress (present-continuous is what
207273
- * the header surfaces), fall back to `content` for every other state.
208438
+ * Text resolution mirrors {@link synthesizeTodoWritePluginState}: use
208439
+ * `activeForm` while in progress so the spinner reads "Running tests"
208440
+ * rather than "Run tests"; fall back to `subject` whenever activeForm is
208441
+ * missing (TaskList-reconciled entries, or a TaskCreate that omitted it).
207274
208442
  */
207275
- const synthesizeTodoWritePluginState = (args) => {
208443
+ const synthesizeTaskPluginState = (tasks) => {
207276
208444
  return { todos: {
207277
- items: (args.todos || []).map((todo) => {
208445
+ items: [...tasks.entries()].sort(([a], [b]) => Number(a) - Number(b)).map(([id, entry]) => {
208446
+ const text = entry.status === "in_progress" ? entry.activeForm || entry.subject : entry.subject;
207278
208447
  return {
207279
- status: todo.status === "in_progress" ? "processing" : todo.status === "pending" ? "todo" : "completed",
207280
- text: todo.status === "in_progress" ? todo.activeForm || todo.content : todo.content
208448
+ id,
208449
+ status: toSynthesizedStatus(entry.status),
208450
+ text
207281
208451
  };
207282
208452
  }),
207283
208453
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -207338,10 +208508,10 @@ var ClaudeCodeAdapter = class {
207338
208508
  * authoritative usage on `message_delta`.
207339
208509
  */
207340
208510
  currentStreamEventModel;
207341
- /** message.ids whose text has already been streamed as deltas skip the full-block emission */
207342
- messagesWithStreamedText = /* @__PURE__ */ new Set();
207343
- /** message.ids whose thinking has already been streamed as deltas skip the full-block emission */
207344
- messagesWithStreamedThinking = /* @__PURE__ */ new Set();
208511
+ /** Cumulative text streamed via partial-message deltas, keyed by message.id. */
208512
+ streamedTextByMessageId = /* @__PURE__ */ new Map();
208513
+ /** Cumulative thinking streamed via partial-message deltas, keyed by message.id. */
208514
+ streamedThinkingByMessageId = /* @__PURE__ */ new Map();
207345
208515
  /**
207346
208516
  * Cumulative tool_use blocks per message.id. CC streams each tool_use in
207347
208517
  * its OWN assistant event, and the handler's in-memory assistant.tools
@@ -207359,6 +208529,34 @@ var ClaudeCodeAdapter = class {
207359
208529
  */
207360
208530
  todoWriteInputs = /* @__PURE__ */ new Map();
207361
208531
  /**
208532
+ * Cached `TaskCreate.input` keyed by `tool_use.id`. Drained in `handleUser`
208533
+ * once the matching tool_result lands: at that point we parse the
208534
+ * CC-assigned numeric id from `Task #N created successfully` and push the
208535
+ * cached fields into {@link claudeCodeTasks}. Cleared even on error to
208536
+ * keep long sessions bounded — failed creates never reach the accumulator.
208537
+ */
208538
+ taskCreateInputs = /* @__PURE__ */ new Map();
208539
+ /**
208540
+ * Cached `TaskUpdate.input` keyed by `tool_use.id`. Drained on
208541
+ * tool_result; on success the cached fields merge into the targeted entry
208542
+ * in {@link claudeCodeTasks}. `status: 'deleted'` removes the entry.
208543
+ */
208544
+ taskUpdateInputs = /* @__PURE__ */ new Map();
208545
+ /**
208546
+ * Tool_use ids of `TaskList` calls awaiting their tool_result. Used to
208547
+ * dispatch the reconciliation parser without re-checking the tool name on
208548
+ * every user event. `TaskList.input` is empty so no payload to cache.
208549
+ */
208550
+ pendingTaskListCalls = /* @__PURE__ */ new Set();
208551
+ /**
208552
+ * Adapter's running mirror of CC's task list, keyed by the CC-assigned
208553
+ * numeric task id. Survives across `result` events because CC keeps the
208554
+ * task list alive between turns within one session; cleared only when
208555
+ * the adapter is destroyed. This is what `synthesizeTaskPluginState`
208556
+ * snapshots on each task-tool tool_result.
208557
+ */
208558
+ claudeCodeTasks = /* @__PURE__ */ new Map();
208559
+ /**
207362
208560
  * Cached inputs for main-agent tool_uses keyed by their tool_use.id.
207363
208561
  * Populated for every main-agent tool_use (not just `Task`) because
207364
208562
  * CC uses multiple tool names for subagent delegation — real traces
@@ -207379,6 +208577,63 @@ var ClaudeCodeAdapter = class {
207379
208577
  * recreate the Thread on every chunk.
207380
208578
  */
207381
208579
  announcedSpawns = /* @__PURE__ */ new Set();
208580
+ /**
208581
+ * Tool name keyed by main-agent `tool_use.id`. Used to label the
208582
+ * resulting {@link ExternalSignalContext} when a Monitor-style task
208583
+ * fires a callback turn.
208584
+ *
208585
+ * Populated for every main-agent tool_use; subagent inner tools are
208586
+ * excluded because their tool_results route through `subagent.parentToolCallId`,
208587
+ * not the main-agent signal detector.
208588
+ */
208589
+ mainToolNamesById = /* @__PURE__ */ new Map();
208590
+ /**
208591
+ * Active CC tasks (long-running tools registered via `system task_started`).
208592
+ * Keyed by `task_id`, carries the originating `tool_use_id`, the resolved
208593
+ * tool name, and a counter incremented for each signal callback turn
208594
+ * the adapter attributes to this task.
208595
+ *
208596
+ * A task lives from `task_started` until `task_notification` /
208597
+ * `task_completed`. While alive, any `message_start` that opens a turn
208598
+ * WITHOUT a preceding `user` event is a signal callback and gets tagged.
208599
+ */
208600
+ activeTasks = /* @__PURE__ */ new Map();
208601
+ /**
208602
+ * True after a `user` event has been seen but the next turn hasn't yet
208603
+ * opened (`message_start` not yet fired). Carries the "this next turn
208604
+ * is a natural follow-up to a tool_result, not a signal callback"
208605
+ * intent across the gap between the tool_result event and the
208606
+ * resulting assistant turn.
208607
+ *
208608
+ * Reset to `false` once a `message_start` consumes it. After that, any
208609
+ * further `message_start` that opens while {@link activeTasks} is
208610
+ * non-empty is treated as a signal callback (CC re-invoked the LLM
208611
+ * because a long-running tool pushed an update).
208612
+ */
208613
+ hasUnhandledUserInput = false;
208614
+ /**
208615
+ * {@link ExternalSignalContext} to attach to the NEXT `stream_start(newStep)`.
208616
+ *
208617
+ * Armed by `message_start` when {@link hasUnhandledUserInput} is false
208618
+ * AND {@link activeTasks} is non-empty — i.e. CC opened a new turn
208619
+ * without fresh user input while a long-running tool is alive. Cleared
208620
+ * on the next `tool_use` (LLM is back on the main chain).
208621
+ */
208622
+ pendingExternalSignal;
208623
+ /**
208624
+ * Source-tool lineage of the most recently completed long-running task,
208625
+ * waiting to be stamped on the post-task summary turn with
208626
+ * `type: 'task-completion'`.
208627
+ *
208628
+ * Armed when `system task_notification` ends an active task; consumed
208629
+ * by the NEXT `message_start` that takes the natural-turn branch
208630
+ * (no other active task triggering a callback). Cleared on `result`
208631
+ * so it never leaks across LLM runs.
208632
+ *
208633
+ * Lets the renderer keep the summary inside the same AssistantGroup as
208634
+ * the preceding callbacks instead of letting it spawn a separate group.
208635
+ */
208636
+ pendingTaskCompletion;
207382
208637
  adapt(raw) {
207383
208638
  if (!raw || typeof raw !== "object") return [];
207384
208639
  switch (raw.type) {
@@ -207400,6 +208655,25 @@ var ClaudeCodeAdapter = class {
207400
208655
  return events;
207401
208656
  }
207402
208657
  handleSystem(raw) {
208658
+ if (raw.subtype === "task_started" && raw.task_id && raw.tool_use_id) {
208659
+ const toolUseId = raw.tool_use_id;
208660
+ this.activeTasks.set(raw.task_id, {
208661
+ callbackCount: 0,
208662
+ sourceToolName: this.mainToolNamesById.get(toolUseId) ?? "unknown",
208663
+ toolUseId
208664
+ });
208665
+ return [];
208666
+ }
208667
+ if (raw.subtype === "task_notification" && raw.task_id) {
208668
+ const ending = this.activeTasks.get(raw.task_id);
208669
+ if (ending) this.pendingTaskCompletion = {
208670
+ sourceToolCallId: ending.toolUseId,
208671
+ sourceToolName: ending.sourceToolName
208672
+ };
208673
+ this.activeTasks.delete(raw.task_id);
208674
+ return [];
208675
+ }
208676
+ if (raw.subtype === "task_updated") return [];
207403
208677
  if (raw.subtype !== "init") return [];
207404
208678
  this.sessionId = raw.session_id;
207405
208679
  this.started = true;
@@ -207428,9 +208702,10 @@ var ClaudeCodeAdapter = class {
207428
208702
  case "thinking":
207429
208703
  if (block.thinking) reasoningParts.push(block.thinking);
207430
208704
  break;
207431
- case "tool_use":
208705
+ case "tool_use": {
208706
+ const apiName = block.name === ASK_USER_MCP_TOOL_NAME ? ASK_USER_API_NAME : block.name;
207432
208707
  newToolCalls.push({
207433
- apiName: block.name,
208708
+ apiName,
207434
208709
  arguments: JSON.stringify(block.input || {}),
207435
208710
  id: block.id,
207436
208711
  identifier: "claude-code",
@@ -207438,19 +208713,29 @@ var ClaudeCodeAdapter = class {
207438
208713
  });
207439
208714
  this.pendingToolCalls.add(block.id);
207440
208715
  if (block.input) this.mainToolInputsById.set(block.id, block.input);
208716
+ if (block.name) this.mainToolNamesById.set(block.id, block.name);
207441
208717
  if (block.name === CC_TODO_WRITE_TOOL_NAME && block.input) this.todoWriteInputs.set(block.id, block.input);
208718
+ if (block.name === CC_TASK_CREATE_TOOL_NAME && block.input) this.taskCreateInputs.set(block.id, block.input);
208719
+ if (block.name === CC_TASK_UPDATE_TOOL_NAME && block.input) this.taskUpdateInputs.set(block.id, block.input);
208720
+ if (block.name === CC_TASK_LIST_TOOL_NAME) this.pendingTaskListCalls.add(block.id);
207442
208721
  break;
208722
+ }
207443
208723
  }
207444
- const textAlreadyStreamed = !!messageId && this.messagesWithStreamedText.has(messageId);
207445
- const thinkingAlreadyStreamed = !!messageId && this.messagesWithStreamedThinking.has(messageId);
207446
- if (textParts.length > 0 && !textAlreadyStreamed) events.push(this.makeChunkEvent({
208724
+ if (newToolCalls.length > 0) this.pendingExternalSignal = void 0;
208725
+ const textCompletion = this.getTrailingCompletion(messageId, textParts.join(""), this.streamedTextByMessageId);
208726
+ const thinkingCompletion = this.getTrailingCompletion(messageId, reasoningParts.join(""), this.streamedThinkingByMessageId);
208727
+ if (textCompletion) events.push(this.makeChunkEvent({
207447
208728
  chunkType: "text",
207448
- content: textParts.join("")
208729
+ content: textCompletion
207449
208730
  }));
207450
- if (reasoningParts.length > 0 && !thinkingAlreadyStreamed) events.push(this.makeChunkEvent({
208731
+ if (thinkingCompletion) events.push(this.makeChunkEvent({
207451
208732
  chunkType: "reasoning",
207452
- reasoning: reasoningParts.join("")
208733
+ reasoning: thinkingCompletion
207453
208734
  }));
208735
+ if (messageId) this.clearStreamedBuffers(messageId, {
208736
+ thinking: reasoningParts.length > 0,
208737
+ text: textParts.length > 0
208738
+ });
207454
208739
  events.push(...this.emitToolChunk(newToolCalls, messageId));
207455
208740
  return events;
207456
208741
  }
@@ -207500,9 +208785,10 @@ var ClaudeCodeAdapter = class {
207500
208785
  case "thinking":
207501
208786
  if (block.thinking) reasoningParts.push(block.thinking);
207502
208787
  break;
207503
- case "tool_use":
208788
+ case "tool_use": {
208789
+ const apiName = block.name === ASK_USER_MCP_TOOL_NAME ? ASK_USER_API_NAME : block.name;
207504
208790
  newToolCalls.push({
207505
- apiName: block.name,
208791
+ apiName,
207506
208792
  arguments: JSON.stringify(block.input || {}),
207507
208793
  id: block.id,
207508
208794
  identifier: "claude-code",
@@ -207511,6 +208797,7 @@ var ClaudeCodeAdapter = class {
207511
208797
  this.pendingToolCalls.add(block.id);
207512
208798
  if (block.name === CC_TODO_WRITE_TOOL_NAME && block.input) this.todoWriteInputs.set(block.id, block.input);
207513
208799
  break;
208800
+ }
207514
208801
  }
207515
208802
  const events = [];
207516
208803
  if (textParts.length > 0) events.push(this.makeChunkEvent({
@@ -207597,6 +208884,7 @@ var ClaudeCodeAdapter = class {
207597
208884
  if (block.type !== "tool_result") continue;
207598
208885
  const toolCallId = block.tool_use_id;
207599
208886
  if (!toolCallId) continue;
208887
+ if (!subagentCtx) this.hasUnhandledUserInput = true;
207600
208888
  const resultContent = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => {
207601
208889
  if (c?.type === "tool_reference" && c.tool_name) return c.tool_name;
207602
208890
  if (c?.type === "image") return `[Image: ${c.source?.media_type || "image"}]`;
@@ -207604,7 +208892,9 @@ var ClaudeCodeAdapter = class {
207604
208892
  }).filter(Boolean).join("\n") : JSON.stringify(block.content || "");
207605
208893
  const cachedTodoArgs = this.todoWriteInputs.get(toolCallId);
207606
208894
  if (cachedTodoArgs) this.todoWriteInputs.delete(toolCallId);
207607
- const pluginState = cachedTodoArgs && !block.is_error ? synthesizeTodoWritePluginState(cachedTodoArgs) : void 0;
208895
+ const todoWritePluginState = cachedTodoArgs && !block.is_error ? synthesizeTodoWritePluginState(cachedTodoArgs) : void 0;
208896
+ const taskPluginState = subagentCtx === void 0 ? this.applyTaskToolResult(toolCallId, !!block.is_error, resultContent) : void 0;
208897
+ const pluginState = todoWritePluginState ?? taskPluginState;
207608
208898
  events.push(this.makeEvent("tool_result", {
207609
208899
  content: resultContent,
207610
208900
  isError: !!block.is_error,
@@ -207623,6 +208913,68 @@ var ClaudeCodeAdapter = class {
207623
208913
  }
207624
208914
  return events;
207625
208915
  }
208916
+ /**
208917
+ * Apply a Task* tool_result to the running {@link claudeCodeTasks}
208918
+ * accumulator and return a fresh synthesized `pluginState.todos` snapshot.
208919
+ * Returns `undefined` if the tool_result was for a non-Task tool, was an
208920
+ * error, or carried no state change (the snapshot is identical to the
208921
+ * pre-call one — but we still emit it so the UI re-syncs).
208922
+ *
208923
+ * Drain the input caches even on error to keep long sessions bounded;
208924
+ * the accumulator itself only mutates on success so a failed TaskUpdate
208925
+ * doesn't leak partial state into the rendered todo list.
208926
+ */
208927
+ applyTaskToolResult(toolCallId, isError, resultContent) {
208928
+ const cachedCreate = this.taskCreateInputs.get(toolCallId);
208929
+ if (cachedCreate) this.taskCreateInputs.delete(toolCallId);
208930
+ const cachedUpdate = this.taskUpdateInputs.get(toolCallId);
208931
+ if (cachedUpdate) this.taskUpdateInputs.delete(toolCallId);
208932
+ const wasTaskList = this.pendingTaskListCalls.has(toolCallId);
208933
+ if (wasTaskList) this.pendingTaskListCalls.delete(toolCallId);
208934
+ if (!cachedCreate && !cachedUpdate && !wasTaskList) return void 0;
208935
+ if (isError) return void 0;
208936
+ if (cachedCreate) {
208937
+ const match = TASK_CREATE_RESULT_PATTERN.exec(resultContent);
208938
+ if (match) {
208939
+ const taskId = match[1];
208940
+ this.claudeCodeTasks.set(taskId, {
208941
+ activeForm: cachedCreate.activeForm,
208942
+ description: cachedCreate.description,
208943
+ status: "pending",
208944
+ subject: cachedCreate.subject
208945
+ });
208946
+ }
208947
+ } else if (cachedUpdate) {
208948
+ if (!TASK_UPDATE_RESULT_PATTERN.test(resultContent)) return void 0;
208949
+ if (cachedUpdate.status === "deleted") this.claudeCodeTasks.delete(cachedUpdate.taskId);
208950
+ else {
208951
+ const next = this.claudeCodeTasks.get(cachedUpdate.taskId) ?? {
208952
+ status: "pending",
208953
+ subject: cachedUpdate.subject ?? `Task #${cachedUpdate.taskId}`
208954
+ };
208955
+ if (cachedUpdate.status) next.status = cachedUpdate.status;
208956
+ if (cachedUpdate.subject !== void 0) next.subject = cachedUpdate.subject;
208957
+ if (cachedUpdate.description !== void 0) next.description = cachedUpdate.description;
208958
+ if (cachedUpdate.activeForm !== void 0) next.activeForm = cachedUpdate.activeForm;
208959
+ this.claudeCodeTasks.set(cachedUpdate.taskId, next);
208960
+ }
208961
+ } else if (wasTaskList) for (const rawLine of resultContent.split("\n")) {
208962
+ const line = rawLine.trim();
208963
+ if (!line) continue;
208964
+ const m = TASK_LIST_LINE_PATTERN.exec(line);
208965
+ if (!m) continue;
208966
+ const [, taskId, status, subject] = m;
208967
+ const existing = this.claudeCodeTasks.get(taskId);
208968
+ if (existing) {
208969
+ existing.status = status;
208970
+ existing.subject = subject;
208971
+ } else this.claudeCodeTasks.set(taskId, {
208972
+ status,
208973
+ subject
208974
+ });
208975
+ }
208976
+ return synthesizeTaskPluginState(this.claudeCodeTasks);
208977
+ }
207626
208978
  handleResult(raw) {
207627
208979
  const events = [];
207628
208980
  const usage = toUsageData$1(raw.usage);
@@ -207638,6 +208990,9 @@ var ClaudeCodeAdapter = class {
207638
208990
  message: resultMessage
207639
208991
  }) : this.makeEvent("agent_runtime_end", {});
207640
208992
  this.pendingRateLimitInfo = void 0;
208993
+ this.streamedTextByMessageId.clear();
208994
+ this.streamedThinkingByMessageId.clear();
208995
+ this.pendingTaskCompletion = void 0;
207641
208996
  return [
207642
208997
  ...events,
207643
208998
  this.makeEvent("stream_end", {}),
@@ -207669,14 +209024,14 @@ var ClaudeCodeAdapter = class {
207669
209024
  if (!delta) return [];
207670
209025
  const msgId = this.currentStreamEventMessageId;
207671
209026
  if (delta.type === "text_delta" && delta.text) {
207672
- if (msgId) this.messagesWithStreamedText.add(msgId);
209027
+ if (msgId) this.streamedTextByMessageId.set(msgId, `${this.streamedTextByMessageId.get(msgId) ?? ""}${delta.text}`);
207673
209028
  return [this.makeChunkEvent({
207674
209029
  chunkType: "text",
207675
209030
  content: delta.text
207676
209031
  })];
207677
209032
  }
207678
209033
  if (delta.type === "thinking_delta" && delta.thinking) {
207679
- if (msgId) this.messagesWithStreamedThinking.add(msgId);
209034
+ if (msgId) this.streamedThinkingByMessageId.set(msgId, `${this.streamedThinkingByMessageId.get(msgId) ?? ""}${delta.thinking}`);
207680
209035
  return [this.makeChunkEvent({
207681
209036
  chunkType: "reasoning",
207682
209037
  reasoning: delta.thinking
@@ -207727,12 +209082,44 @@ var ClaudeCodeAdapter = class {
207727
209082
  }
207728
209083
  this.currentMessageId = messageId;
207729
209084
  this.stepIndex++;
209085
+ if (!this.hasUnhandledUserInput && this.activeTasks.size > 0) {
209086
+ const lastTaskKey = [...this.activeTasks.keys()].at(-1);
209087
+ const task = this.activeTasks.get(lastTaskKey);
209088
+ task.callbackCount += 1;
209089
+ this.pendingExternalSignal = {
209090
+ sequence: task.callbackCount,
209091
+ sourceToolCallId: task.toolUseId,
209092
+ sourceToolName: task.sourceToolName,
209093
+ type: "tool-stdout"
209094
+ };
209095
+ } else if (this.pendingTaskCompletion) {
209096
+ this.pendingExternalSignal = {
209097
+ sourceToolCallId: this.pendingTaskCompletion.sourceToolCallId,
209098
+ sourceToolName: this.pendingTaskCompletion.sourceToolName,
209099
+ type: "task-completion"
209100
+ };
209101
+ this.pendingTaskCompletion = void 0;
209102
+ } else this.pendingExternalSignal = void 0;
209103
+ this.hasUnhandledUserInput = false;
207730
209104
  return [this.makeEvent("stream_end", {}), this.makeEvent("stream_start", {
209105
+ externalSignal: this.pendingExternalSignal,
207731
209106
  model,
207732
209107
  newStep: true,
207733
209108
  provider: "claude-code"
207734
209109
  })];
207735
209110
  }
209111
+ getTrailingCompletion(messageId, fullContent, streamedByMessageId) {
209112
+ if (!fullContent) return;
209113
+ if (!messageId) return fullContent;
209114
+ const streamed = streamedByMessageId.get(messageId);
209115
+ if (!streamed) return fullContent;
209116
+ if (fullContent === streamed) return;
209117
+ if (fullContent.startsWith(streamed)) return fullContent.slice(streamed.length) || void 0;
209118
+ }
209119
+ clearStreamedBuffers(messageId, modes) {
209120
+ if (modes.text) this.streamedTextByMessageId.delete(messageId);
209121
+ if (modes.thinking) this.streamedThinkingByMessageId.delete(messageId);
209122
+ }
207736
209123
  makeEvent(type, data) {
207737
209124
  return {
207738
209125
  data,
@@ -208373,6 +209760,142 @@ var AgentStreamPipeline = class {
208373
209760
  }
208374
209761
  };
208375
209762
 
209763
+ //#endregion
209764
+ //#region ../../packages/heterogeneous-agents/src/spawn/cliSpawn.ts
209765
+ const WINDOWS_EXE_EXT_PATTERN = /\.exe$/i;
209766
+ const WINDOWS_NODE_EXE_PATTERN = /(?:^|[\\/])node(?:\.exe)?$/i;
209767
+ const isWindows = () => platform() === "win32";
209768
+ const isPathLikeCommand = (command) => path.win32.isAbsolute(command) || path.posix.isAbsolute(command) || /[\\/]/.test(command);
209769
+ const fileExists = async (filePath) => {
209770
+ try {
209771
+ await access(filePath);
209772
+ return true;
209773
+ } catch {
209774
+ return false;
209775
+ }
209776
+ };
209777
+ const execFileString = async (command, args) => new Promise((resolve, reject) => {
209778
+ execFile(command, args, {
209779
+ timeout: 3e3,
209780
+ windowsHide: true
209781
+ }, (error, stdout) => {
209782
+ if (error) {
209783
+ reject(error);
209784
+ return;
209785
+ }
209786
+ resolve(stdout.toString());
209787
+ });
209788
+ });
209789
+ const pickWindowsExecutable = (candidates) => candidates.find((candidate) => WINDOWS_EXE_EXT_PATTERN.test(candidate));
209790
+ const pickWindowsNodeExecutable = (candidates) => candidates.find((candidate) => WINDOWS_EXE_EXT_PATTERN.test(candidate) && WINDOWS_NODE_EXE_PATTERN.test(candidate));
209791
+ const joinShimRelativePath = (shimPath, relativePath) => path.win32.join(path.win32.dirname(shimPath), ...relativePath.replaceAll("\\", "/").split("/").filter(Boolean));
209792
+ const resolveShimPathToken = (shimPath, token) => {
209793
+ const trimmedToken = token.trim().replaceAll(/^['"]|['"]$/g, "");
209794
+ const lowerToken = trimmedToken.toLowerCase();
209795
+ if (lowerToken.startsWith("$basedir")) return joinShimRelativePath(shimPath, trimmedToken.slice(8).replace(/^[\\/]/, ""));
209796
+ if (lowerToken.startsWith("%dp0%")) return joinShimRelativePath(shimPath, trimmedToken.slice(5).replace(/^[\\/]/, ""));
209797
+ if (path.win32.isAbsolute(trimmedToken)) return trimmedToken;
209798
+ if (/[\\/]/.test(trimmedToken)) return joinShimRelativePath(shimPath, trimmedToken);
209799
+ };
209800
+ const getExistingShimPathToken = async (shimPath, token) => {
209801
+ const resolvedPath = resolveShimPathToken(shimPath, token);
209802
+ if (!resolvedPath) return;
209803
+ return await fileExists(resolvedPath) ? resolvedPath : void 0;
209804
+ };
209805
+ const resolveWindowsNodeCommand = async (shimPath) => {
209806
+ const localNodePath = path.win32.join(path.win32.dirname(shimPath), "node.exe");
209807
+ if (await fileExists(localNodePath)) return localNodePath;
209808
+ try {
209809
+ return pickWindowsNodeExecutable((await execFileString("where", ["node"])).split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
209810
+ } catch {
209811
+ return;
209812
+ }
209813
+ };
209814
+ const getNodeCommand = async (shimPath, token) => {
209815
+ const trimmedToken = token.trim().replaceAll(/^['"]|['"]$/g, "");
209816
+ if (/^node(?:\.exe)?$/i.test(trimmedToken) || /^%_prog%$/i.test(trimmedToken)) return resolveWindowsNodeCommand(shimPath);
209817
+ const resolvedPath = await getExistingShimPathToken(shimPath, trimmedToken);
209818
+ if (!resolvedPath) return;
209819
+ return WINDOWS_NODE_EXE_PATTERN.test(resolvedPath) ? resolvedPath : void 0;
209820
+ };
209821
+ const getNodeScriptTarget = async (shimPath, nodeToken, scriptToken) => {
209822
+ const command = await getNodeCommand(shimPath, nodeToken);
209823
+ if (!command) return;
209824
+ const scriptPath = await getExistingShimPathToken(shimPath, scriptToken);
209825
+ if (!scriptPath) return;
209826
+ return {
209827
+ argsPrefix: [scriptPath],
209828
+ command
209829
+ };
209830
+ };
209831
+ const inferWindowsNodeScriptFromShim = async (shimPath, source) => {
209832
+ for (const pattern of [
209833
+ /exec\s+"(\$basedir[^"]*node(?:\.exe)?)"\s+"([^"]+)"/i,
209834
+ /exec\s+(node(?:\.exe)?)\s+"([^"]+)"/i,
209835
+ /"(%dp0%[^"]*node(?:\.exe)?)"\s+"([^"]+)"/i,
209836
+ /"(%_prog%)"\s+"([^"]+)"/i,
209837
+ [/(?:^|\r?\n)\s*(node(?:\.exe)?)\s+"([^"]+)"/i, "node"]
209838
+ ]) {
209839
+ const regex = Array.isArray(pattern) ? pattern[0] : pattern;
209840
+ const match = source.match(regex);
209841
+ if (!match) continue;
209842
+ const nodeToken = Array.isArray(pattern) ? pattern[1] : match[1];
209843
+ const scriptToken = Array.isArray(pattern) ? match[2] : match[2];
209844
+ if (!nodeToken || !scriptToken) continue;
209845
+ const target = await getNodeScriptTarget(shimPath, nodeToken, scriptToken);
209846
+ if (target) return target;
209847
+ }
209848
+ };
209849
+ const inferWindowsExecutableFromShim = async (shimPath, source) => {
209850
+ const matches = [...source.matchAll(/\$basedir[\\/]([^"\s]+?\.exe)/gi), ...source.matchAll(/%dp0%\\([^"\r\n]+?\.exe)/gi)];
209851
+ for (const match of matches) {
209852
+ const relativePath = match[1]?.replaceAll("\\", "/");
209853
+ if (!relativePath || WINDOWS_NODE_EXE_PATTERN.test(relativePath)) continue;
209854
+ const command = joinShimRelativePath(shimPath, relativePath);
209855
+ if (await fileExists(command)) return { command };
209856
+ }
209857
+ };
209858
+ const inferWindowsNpmShimTarget = async (shimPath) => {
209859
+ if (WINDOWS_EXE_EXT_PATTERN.test(shimPath)) return { command: shimPath };
209860
+ if (!await fileExists(shimPath)) return;
209861
+ try {
209862
+ const source = await readFile(shimPath, "utf8");
209863
+ return await inferWindowsNodeScriptFromShim(shimPath, source) ?? await inferWindowsExecutableFromShim(shimPath, source);
209864
+ } catch {
209865
+ return;
209866
+ }
209867
+ };
209868
+ const resolveWindowsBareCommand = async (command) => {
209869
+ try {
209870
+ const candidates = (await execFileString("where", [command])).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
209871
+ const executable = pickWindowsExecutable(candidates);
209872
+ if (executable) return { command: executable };
209873
+ for (const candidate of candidates) {
209874
+ const target = await inferWindowsNpmShimTarget(candidate);
209875
+ if (target) return target;
209876
+ }
209877
+ return;
209878
+ } catch {
209879
+ return;
209880
+ }
209881
+ };
209882
+ const resolveCliSpawnPlan = async (command, args) => {
209883
+ const trimmedCommand = command.trim();
209884
+ if (!isWindows() || !trimmedCommand) return {
209885
+ args,
209886
+ command
209887
+ };
209888
+ const target = isPathLikeCommand(trimmedCommand) ? await inferWindowsNpmShimTarget(trimmedCommand) : await resolveWindowsBareCommand(trimmedCommand);
209889
+ if (!target) return {
209890
+ args,
209891
+ command
209892
+ };
209893
+ return {
209894
+ args: [...target.argsPrefix ?? [], ...args],
209895
+ command: target.command
209896
+ };
209897
+ };
209898
+
208376
209899
  //#endregion
208377
209900
  //#region ../../packages/heterogeneous-agents/src/spawn/input/normalizeImage.ts
208378
209901
  const PNG_SIG = Buffer.from([
@@ -208615,6 +210138,19 @@ const buildAgentInput = async (agentType, prompt, options = {}) => {
208615
210138
 
208616
210139
  //#endregion
208617
210140
  //#region ../../packages/heterogeneous-agents/src/spawn/spawnAgent.ts
210141
+ /**
210142
+ * Invariant Claude Code CLI flags shared by every spawn site (desktop driver,
210143
+ * `lh hetero exec`). Permission mode and `--include-partial-messages` vary by
210144
+ * caller — the desktop UI wants live deltas + user-mode bypassPermissions, the
210145
+ * sandbox CLI may run as root and skip partials — so they're composed on top
210146
+ * of this base.
210147
+ *
210148
+ * `AskUserQuestion` is disabled because CC's CLI self-injects an
210149
+ * `is_error: "Answer questions?"` tool_result in `-p` mode before the host
210150
+ * can surface the questions, so the model falls back to plain-text prompting
210151
+ * anyway. Remove this once a local MCP-backed replacement is wired to
210152
+ * LobeHub's intervention UI.
210153
+ */
208618
210154
  const CLAUDE_CODE_BASE_ARGS = [
208619
210155
  "-p",
208620
210156
  "--input-format",
@@ -208622,7 +210158,8 @@ const CLAUDE_CODE_BASE_ARGS = [
208622
210158
  "--output-format",
208623
210159
  "stream-json",
208624
210160
  "--verbose",
208625
- "--include-partial-messages"
210161
+ "--disallowedTools",
210162
+ "AskUserQuestion"
208626
210163
  ];
208627
210164
  const isRunningAsRoot = () => process.getuid?.() === 0;
208628
210165
  const CLAUDE_CODE_PERMISSION_ARGS = () => isRunningAsRoot() ? [
@@ -208636,14 +210173,15 @@ const CODEX_REQUIRED_ARGS = [
208636
210173
  "--skip-git-repo-check",
208637
210174
  "--full-auto"
208638
210175
  ];
208639
- const buildClaudeCodeArgs = (resumeSessionId, inputArgs, extraArgs) => [
210176
+ const buildClaudeCodeArgs = ({ extraArgs, includePartialMessages, inputArgs, resumeSessionId }) => [
208640
210177
  ...CLAUDE_CODE_BASE_ARGS,
210178
+ ...includePartialMessages ? ["--include-partial-messages"] : [],
208641
210179
  ...CLAUDE_CODE_PERMISSION_ARGS(),
208642
210180
  ...resumeSessionId ? ["--resume", resumeSessionId] : [],
208643
210181
  ...inputArgs,
208644
210182
  ...extraArgs
208645
210183
  ];
208646
- const buildCodexArgs = (resumeSessionId, inputArgs, extraArgs) => resumeSessionId ? [
210184
+ const buildCodexArgs = ({ extraArgs, inputArgs, resumeSessionId }) => resumeSessionId ? [
208647
210185
  "exec",
208648
210186
  "resume",
208649
210187
  ...CODEX_REQUIRED_ARGS,
@@ -208657,11 +210195,11 @@ const buildCodexArgs = (resumeSessionId, inputArgs, extraArgs) => resumeSessionI
208657
210195
  ...inputArgs,
208658
210196
  ...extraArgs
208659
210197
  ];
208660
- const buildSpawnArgs = (agentType, resumeSessionId, inputArgs, extraArgs) => {
208661
- switch (agentType) {
208662
- case "claude-code": return buildClaudeCodeArgs(resumeSessionId, inputArgs, extraArgs);
208663
- case "codex": return buildCodexArgs(resumeSessionId, inputArgs, extraArgs);
208664
- default: throw new Error(`spawnAgent: unsupported agent type "${agentType}"`);
210198
+ const buildSpawnArgs = (params) => {
210199
+ switch (params.agentType) {
210200
+ case "claude-code": return buildClaudeCodeArgs(params);
210201
+ case "codex": return buildCodexArgs(params);
210202
+ default: throw new Error(`spawnAgent: unsupported agent type "${params.agentType}"`);
208665
210203
  }
208666
210204
  };
208667
210205
  const defaultCommand = (agentType) => agentType === "codex" ? "codex" : "claude";
@@ -208698,8 +210236,17 @@ const killProcessTree = (proc, signal) => {
208698
210236
  const spawnAgent = async (options) => {
208699
210237
  const command = options.command || defaultCommand(options.agentType);
208700
210238
  const inputPlan = await buildAgentInput(options.agentType, options.prompt, options.inputOptions);
208701
- const proc = spawn(command, buildSpawnArgs(options.agentType, options.resumeSessionId, inputPlan.args, options.extraArgs ?? []), {
208702
- cwd: options.cwd || process.cwd(),
210239
+ const args = buildSpawnArgs({
210240
+ agentType: options.agentType,
210241
+ extraArgs: options.extraArgs ?? [],
210242
+ includePartialMessages: options.includePartialMessages ?? false,
210243
+ inputArgs: inputPlan.args,
210244
+ resumeSessionId: options.resumeSessionId
210245
+ });
210246
+ const cwd = options.cwd || process.cwd();
210247
+ const cliSpawnPlan = await resolveCliSpawnPlan(command, args);
210248
+ const proc = spawn(cliSpawnPlan.command, cliSpawnPlan.args, {
210249
+ cwd,
208703
210250
  detached: process.platform !== "win32",
208704
210251
  env: {
208705
210252
  ...process.env,
@@ -208711,6 +210258,13 @@ const spawnAgent = async (options) => {
208711
210258
  "pipe"
208712
210259
  ]
208713
210260
  });
210261
+ const exit = new Promise((resolve, reject) => {
210262
+ proc.on("exit", (code, signal) => resolve({
210263
+ code,
210264
+ signal
210265
+ }));
210266
+ proc.on("error", (err) => reject(err));
210267
+ });
208714
210268
  if (proc.stdin) proc.stdin.write(inputPlan.stdin, () => {
208715
210269
  proc.stdin?.end();
208716
210270
  });
@@ -208786,13 +210340,7 @@ const spawnAgent = async (options) => {
208786
210340
  }
208787
210341
  } };
208788
210342
  } },
208789
- exit: new Promise((resolve, reject) => {
208790
- proc.on("exit", (code, signal) => resolve({
208791
- code,
208792
- signal
208793
- }));
208794
- proc.on("error", (err) => reject(err));
208795
- }),
210343
+ exit,
208796
210344
  kill: (signal = "SIGINT") => killProcessTree(proc, signal),
208797
210345
  pid: proc.pid,
208798
210346
  get sessionId() {
@@ -210657,6 +212205,7 @@ function registerMigrateCommand(program) {
210657
212205
 
210658
212206
  //#endregion
210659
212207
  //#region src/commands/model.ts
212208
+ const isVisibleModel = (model) => model.visible !== false;
210660
212209
  function registerModelCommand(program) {
210661
212210
  const model = program.command("model").description("Manage AI models");
210662
212211
  model.command("list <providerId>").description("List models for a provider").option("-L, --limit <n>", "Maximum number of items", "50").option("--enabled", "Only show enabled models").option("--type <type>", "Filter by model type (chat|embedding|tts|stt|image|video|text2music|realtime)").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (providerId, options) => {
@@ -210666,7 +212215,7 @@ function registerModelCommand(program) {
210666
212215
  if (options.enabled) input.enabled = true;
210667
212216
  if (options.type) input.type = options.type;
210668
212217
  const result = await client.aiModel.getAiProviderModelList.query(input);
210669
- let items = Array.isArray(result) ? result : result.items ?? [];
212218
+ let items = (Array.isArray(result) ? result : result.items ?? []).filter(isVisibleModel);
210670
212219
  if (options.type) items = items.filter((m) => m.type === options.type);
210671
212220
  if (options.json !== void 0) {
210672
212221
  const fields = typeof options.json === "string" ? options.json : void 0;
@@ -212067,7 +213616,7 @@ function registerReviewCommands(task) {
212067
213616
  "CONFIG"
212068
213617
  ]);
212069
213618
  });
212070
- rc.command("add <id>").description("Add a review rubric").requiredOption("-n, --name <name>", "Rubric name (e.g. \"内容准确性\")").option("--type <type>", "Rubric type (default: llm-rubric)", "llm-rubric").option("-t, --threshold <n>", "Pass threshold 0-100 (converted to 0-1)").option("-d, --description <text>", "Criteria description (for llm-rubric type)").option("--value <value>", "Expected value (for contains/equals type)").option("--pattern <pattern>", "Regex pattern (for regex type)").option("-w, --weight <n>", "Weight for scoring (default: 1)").option("--recursive", "Add to all subtasks as well").action(async (id, options) => {
213619
+ rc.command("add <id>").description("Add a review rubric").requiredOption("-n, --name <name>", "Rubric name (e.g. \"Content Accuracy\")").option("--type <type>", "Rubric type (default: llm-rubric)", "llm-rubric").option("-t, --threshold <n>", "Pass threshold 0-100 (converted to 0-1)").option("-d, --description <text>", "Criteria description (for llm-rubric type)").option("--value <value>", "Expected value (for contains/equals type)").option("--pattern <pattern>", "Regex pattern (for regex type)").option("-w, --weight <n>", "Weight for scoring (default: 1)").option("--recursive", "Add to all subtasks as well").action(async (id, options) => {
212071
213620
  const client = await getTrpcClient();
212072
213621
  const buildConfig = () => {
212073
213622
  switch (options.type) {
@@ -212964,7 +214513,6 @@ function createProgram() {
212964
214513
  registerAgentCommand(program);
212965
214514
  registerAgentGroupCommand(program);
212966
214515
  registerBotCommand(program);
212967
- registerCronCommand(program);
212968
214516
  registerGenerateCommand(program);
212969
214517
  registerFileCommand(program);
212970
214518
  registerHeteroCommand(program);