tarsk 0.5.62 → 0.5.63

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 (78) hide show
  1. package/dist/index.js +1380 -785
  2. package/dist/public/assets/{account-view-D2h4wMCJ.js → account-view-DPMOlh3s.js} +1 -1
  3. package/dist/public/assets/{alert-dialog-BpJafkQ6.js → alert-dialog-tR8jdoK3.js} +1 -1
  4. package/dist/public/assets/{api-DkhEo8tW.js → api-D3kM9pkY.js} +1 -1
  5. package/dist/public/assets/{async-error-boundary-DR57nNXL.js → async-error-boundary-D2YxNX8i.js} +1 -1
  6. package/dist/public/assets/{browser-tab-B15D-jTw.js → browser-tab-DBH__fWh.js} +2 -2
  7. package/dist/public/assets/{chat-dock-context-4ZMIlxAn.js → chat-dock-context-DX0woD8r.js} +1 -1
  8. package/dist/public/assets/{commit-dialog-CQH8QaWp.js → commit-dialog-vGBCFWUk.js} +1 -1
  9. package/dist/public/assets/{commit-method-fields-DYBEHZAP.js → commit-method-fields-CENlG_Jk.js} +1 -1
  10. package/dist/public/assets/{context-menu-cv7nzOcn.js → context-menu-D6nrl6uV.js} +1 -1
  11. package/dist/public/assets/{conversation-history-view-BnhR9dL8.js → conversation-history-view-D_Umzlgd.js} +1 -1
  12. package/dist/public/assets/{create-project-dialog-DCehjzue.js → create-project-dialog-IjrZazJK.js} +1 -1
  13. package/dist/public/assets/{create-repo-dialog-CVYlw8JI.js → create-repo-dialog-LRokjKAU.js} +1 -1
  14. package/dist/public/assets/{delete-confirm-dialog-DoRgqETX.js → delete-confirm-dialog-Tv1Y9rIn.js} +1 -1
  15. package/dist/public/assets/diff-view-BzR1KSU4.js +4 -0
  16. package/dist/public/assets/explorer-tab-view-BtQAqAoR.js +2 -0
  17. package/dist/public/assets/explorer-tree-gTgMdTOW.js +1 -0
  18. package/dist/public/assets/{explorer-view-B5nGnzAV.js → explorer-view-DhR2CWcz.js} +2 -2
  19. package/dist/public/assets/{git-history-dialog-De7D-B_f.js → git-history-dialog-Do8XlsHv.js} +1 -1
  20. package/dist/public/assets/git-ops-button-BIOoGOW9.js +2 -0
  21. package/dist/public/assets/index-BdDEzAzX.js +3307 -0
  22. package/dist/public/assets/index-zilB0ZSo.css +1 -0
  23. package/dist/public/assets/{mcp-server-card-DofWLHvo.js → mcp-server-card-9_PXQg51.js} +1 -1
  24. package/dist/public/assets/{merged-pr-dialog-CLaSLBzZ.js → merged-pr-dialog-U1CBegRj.js} +1 -1
  25. package/dist/public/assets/{model-list-controls-CRnIzfdp.js → model-list-controls-DGHp3GfD.js} +1 -1
  26. package/dist/public/assets/{monaco-setup-VhLB0diG.js → monaco-setup-Xkv0hjl5.js} +1 -1
  27. package/dist/public/assets/onboarding-_okRJRpU.js +46 -0
  28. package/dist/public/assets/{project-env-vars-view-wQPOiCa_.js → project-env-vars-view-CSk-mKm9.js} +1 -1
  29. package/dist/public/assets/{project-model-aliases-view-CMEFOKJj.js → project-model-aliases-view-D6gdA6SJ.js} +1 -1
  30. package/dist/public/assets/project-settings-view-soRLv3YG.js +1 -0
  31. package/dist/public/assets/{project-tools-view-C7rQgJBp.js → project-tools-view-Bm1kgBnS.js} +1 -1
  32. package/dist/public/assets/providers-list-view-C8euNoea.js +1 -0
  33. package/dist/public/assets/pull-request-dialog-Cy-OBkM4.js +1 -0
  34. package/dist/public/assets/{pull-with-changes-dialog-BIkrgGiK.js → pull-with-changes-dialog-p3MYq6XE.js} +1 -1
  35. package/dist/public/assets/{push-before-pr-dialog-CMGZ4nuF.js → push-before-pr-dialog-DHehHLoc.js} +1 -1
  36. package/dist/public/assets/{react-vendor-DgnPMVt4.js → react-vendor-DM3Nejio.js} +1 -1
  37. package/dist/public/assets/{settings-general-view-W0AXfWxp.js → settings-general-view-B9oGXpm4.js} +1 -1
  38. package/dist/public/assets/{settings-instructions-view-Dd3mwKFL.js → settings-instructions-view-Dgiq0-Fz.js} +1 -1
  39. package/dist/public/assets/{settings-list-Cyr7e0Le.js → settings-list-CdCYY9x_.js} +1 -1
  40. package/dist/public/assets/{settings-logs-view-CGbS3tw4.js → settings-logs-view-BCAw_cxd.js} +1 -1
  41. package/dist/public/assets/settings-mcp-servers-view-Br5owe39.js +5 -0
  42. package/dist/public/assets/settings-models-view--e7dI-H6.js +1 -0
  43. package/dist/public/assets/{settings-rules-view-BnOeQFrJ.js → settings-rules-view-DD0_kkS8.js} +1 -1
  44. package/dist/public/assets/{settings-skills-view-BvvWBK7C.js → settings-skills-view-DDIvFArL.js} +1 -1
  45. package/dist/public/assets/{settings-slack-view-CnCzVPQp.js → settings-slack-view-C0yGN7-a.js} +1 -1
  46. package/dist/public/assets/{settings-slash-commands-view-Ds7RbQGD.js → settings-slash-commands-view-B-Ehi4Vm.js} +1 -1
  47. package/dist/public/assets/{settings-statistics-view-BI6827hL.js → settings-statistics-view-BFsn_ndY.js} +1 -1
  48. package/dist/public/assets/{settings-storage-view-Dp1y_kDe.js → settings-storage-view-3J7APTHD.js} +1 -1
  49. package/dist/public/assets/{settings-subagents-view-Cco1I1-K.js → settings-subagents-view-YFveKNiL.js} +1 -1
  50. package/dist/public/assets/{settings-system-prompt-view-DZKKUgRY.js → settings-system-prompt-view-BR4bBXdt.js} +1 -1
  51. package/dist/public/assets/settings-view-Cg3qC1kg.js +2 -0
  52. package/dist/public/assets/{slug-utils-DVB1L0_r.js → slug-utils-DN3CLvnc.js} +1 -1
  53. package/dist/public/assets/tasks-view-COnDzVI9.js +2 -0
  54. package/dist/public/assets/{terminal-panel-B5SzvdGO.js → terminal-panel-rUTBECZC.js} +1 -1
  55. package/dist/public/assets/{ui-components-CuWavb_S.js → ui-components-BLrzr-Yo.js} +1 -1
  56. package/dist/public/assets/{use-active-dock-panel-instance-CKEkba2q.js → use-active-dock-panel-instance-HODVGwTg.js} +1 -1
  57. package/dist/public/assets/{use-installed-ai-files-CTaDJ775.js → use-installed-ai-files-D_mfpPto.js} +1 -1
  58. package/dist/public/assets/{web-Du8iyq0K.js → web-BGcPHAdQ.js} +1 -1
  59. package/dist/public/assets/{web-CaXKZtWk.js → web-D6UAh7QL.js} +1 -1
  60. package/dist/public/assets/{web-BNUbWWwf.js → web-DMLNawO_.js} +1 -1
  61. package/dist/public/assets/{web-CHZVpVHH.js → web-D_jEgLdF.js} +1 -1
  62. package/dist/public/index.html +7 -7
  63. package/dist/recommended-models.txt +11 -11
  64. package/package.json +2 -1
  65. package/dist/public/assets/diff-view-B1xi9U57.js +0 -4
  66. package/dist/public/assets/explorer-tab-view-DssrHe9f.js +0 -2
  67. package/dist/public/assets/explorer-tree-B2SnBNoL.js +0 -1
  68. package/dist/public/assets/git-ops-button-DJYzg_I5.js +0 -2
  69. package/dist/public/assets/index-3N_2bBI0.css +0 -1
  70. package/dist/public/assets/index-cAZJlfDA.js +0 -3307
  71. package/dist/public/assets/onboarding-Dy7KG3M5.js +0 -46
  72. package/dist/public/assets/project-settings-view-BeKBKcaZ.js +0 -1
  73. package/dist/public/assets/providers-list-view-DqSw6lqh.js +0 -1
  74. package/dist/public/assets/pull-request-dialog-CX10DwW3.js +0 -1
  75. package/dist/public/assets/settings-mcp-servers-view-C3IkdzkJ.js +0 -5
  76. package/dist/public/assets/settings-models-view-DV2YF3YN.js +0 -1
  77. package/dist/public/assets/settings-view-Ckzg0maK.js +0 -2
  78. package/dist/public/assets/tasks-view-tS75bdWk.js +0 -2
package/dist/index.js CHANGED
@@ -413,6 +413,12 @@ function billablePromptTokensFromCounts(inputTokens, cacheReadTokens = 0, cacheW
413
413
  }
414
414
  return input + cachedInput;
415
415
  }
416
+ function calculatePromptCacheHitRate(cacheReadTokens, totalPromptTokens) {
417
+ if (!Number.isFinite(cacheReadTokens) || !Number.isFinite(totalPromptTokens) || totalPromptTokens <= 0 || cacheReadTokens <= 0) {
418
+ return 0;
419
+ }
420
+ return Math.min(1, cacheReadTokens / totalPromptTokens);
421
+ }
416
422
  function billableTotalTokens(usage) {
417
423
  if ((usage.totalTokens ?? 0) > 0) {
418
424
  return usage.totalTokens ?? 0;
@@ -2037,7 +2043,7 @@ function toBufferedLogData(data) {
2037
2043
  return toJsonSafeLogData(data);
2038
2044
  }
2039
2045
  function createLogger(event) {
2040
- function log123(level, message, data) {
2046
+ function log124(level, message, data) {
2041
2047
  const entry = {
2042
2048
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2043
2049
  level,
@@ -2053,10 +2059,10 @@ function createLogger(event) {
2053
2059
  emit(entry);
2054
2060
  }
2055
2061
  return {
2056
- debug: (message, data) => log123(LogLevel.DEBUG, message, data),
2057
- info: (message, data) => log123(LogLevel.INFO, message, data),
2058
- warn: (message, data) => log123(LogLevel.WARN, message, data),
2059
- error: (message, data) => log123(LogLevel.ERROR, message, data)
2062
+ debug: (message, data) => log124(LogLevel.DEBUG, message, data),
2063
+ info: (message, data) => log124(LogLevel.INFO, message, data),
2064
+ warn: (message, data) => log124(LogLevel.WARN, message, data),
2065
+ error: (message, data) => log124(LogLevel.ERROR, message, data)
2060
2066
  };
2061
2067
  }
2062
2068
  var LogLevel, LOG_BUFFER_CAPACITY, LOG_BUFFER_MIN_LEVEL, LOG_LEVEL_NAMES, globalLevel, transports, nextBufferId, logBuffer, logBufferListeners, LEVEL_CONSOLE;
@@ -13755,7 +13761,8 @@ async function initializeSchema(db) {
13755
13761
  setupScript TEXT,
13756
13762
  runCommand TEXT,
13757
13763
  commitMethod TEXT,
13758
- eventToken TEXT
13764
+ eventToken TEXT,
13765
+ projectIcon TEXT
13759
13766
  )
13760
13767
  `);
13761
13768
  await db.execute(`
@@ -14646,6 +14653,14 @@ async function runMigrations(db) {
14646
14653
  `ALTER TABLE projects ADD COLUMN useBuiltInBrowser INTEGER NOT NULL DEFAULT 1`
14647
14654
  );
14648
14655
  }
14656
+ const projectIconProjectsInfo = await db.execute(`PRAGMA table_info(projects)`);
14657
+ const hasProjectIcon = projectIconProjectsInfo.rows.some(
14658
+ (col) => col.name === "projectIcon"
14659
+ );
14660
+ if (!hasProjectIcon) {
14661
+ tarskDebugLog("[db] Running migration: Adding projectIcon column to projects");
14662
+ await db.execute(`ALTER TABLE projects ADD COLUMN projectIcon TEXT`);
14663
+ }
14649
14664
  const smartJsProjectsInfo = await db.execute(`PRAGMA table_info(projects)`);
14650
14665
  const hasSmartJsEnabled = smartJsProjectsInfo.rows.some(
14651
14666
  (col) => col.name === "smartJsEnabled"
@@ -18780,7 +18795,7 @@ var init_bash = __esm({
18780
18795
  var edit_default;
18781
18796
  var init_edit = __esm({
18782
18797
  "src/tools/descriptions/edit.md"() {
18783
- edit_default = "Replace exact text (unique unless replaceAll/line/occurrenceIndex). Prefer fewer large hunks; batch with replacements. Prefer mutate-in-place imports (`import { A }` \u2192 `import { A, B }`) over insert-before-anchor \u2014 insert-style retries duplicate lines and are skipped when already present. On ambiguous match, retry with occurrenceIndex/line (error lists ready values)\u2014not re-read or longer oldText. Parallelize independent files.\n";
18798
+ edit_default = "Replace exact text (unique unless replaceAll/line/occurrenceIndex). Prefer fewer large hunks; batch with replacements. Prefer mutate-in-place imports (`import { A }` \u2192 `import { A, B }`) over insert-before-anchor \u2014 insert-style retries duplicate lines and are skipped when already present. On ambiguous match, retry with occurrenceIndex/line (error lists ready values)\u2014not re-read or longer oldText. Parallelize independent files. Concurrent edits to the same path are rejected \u2014 batch those hunks in one replacements call.\n";
18784
18799
  }
18785
18800
  });
18786
18801
 
@@ -18796,7 +18811,7 @@ var init_read = __esm({
18796
18811
  var todo_default;
18797
18812
  var init_todo = __esm({
18798
18813
  "src/tools/descriptions/todo.md"() {
18799
- todo_default = "Break work into todos (~2 when planning). Actions: add/update/list/delete/learnings. Status pending\u2192working\u2192done\u2014update each item as you go; don't batch-complete or leave pending while working it. Mark verify/check todos done only after checks pass. Ralph: passes=true after checks; learnings appends progress.txt. list = full checklist; update/delete = short ack.\n";
18814
+ todo_default = "Break work into todos (~2 when planning). Actions: add (descriptions), update/delete (ids), list, learnings. Status pending\u2192working\u2192done. Mark each item done as soon as that work is finished; don't batch-complete, leave an item pending while working it, or send the final reply while any todo is pending or working. Mark verify/check todos done only after checks pass. Ralph: passes=true after checks; learnings appends progress.txt. list = full checklist; update/delete = short ack plus remaining incomplete items.\n";
18800
18815
  }
18801
18816
  });
18802
18817
 
@@ -19517,8 +19532,8 @@ async function runRipgrepMulti(args2, searchPaths, cwd) {
19517
19532
  function sortFilesByMtime(files) {
19518
19533
  return files.map((filePath) => {
19519
19534
  try {
19520
- const stat8 = statSync3(filePath);
19521
- return [filePath, stat8.mtimeMs ?? 0];
19535
+ const stat9 = statSync3(filePath);
19536
+ return [filePath, stat9.mtimeMs ?? 0];
19522
19537
  } catch {
19523
19538
  return [filePath, 0];
19524
19539
  }
@@ -19645,7 +19660,7 @@ function createRipGrepTool(cwd) {
19645
19660
  return {
19646
19661
  name: "grep",
19647
19662
  label: "Search",
19648
- description: "Search file contents with ripgrep (regex). Supports output_mode (content, files_with_matches, count), glob/type filters, context lines, multiline patterns, and pagination via head_limit/offset. Respects .gitignore. Scope with path (file or directory); use glob for extension/name filters (e.g. *.ts, **/*.tsx)\u2014not as a substitute for path.",
19663
+ description: GREP_TOOL_DESCRIPTION,
19649
19664
  parameters: ripGrepSchema,
19650
19665
  execute: async (_toolCallId, input, signal) => {
19651
19666
  if (signal?.aborted) {
@@ -19719,7 +19734,7 @@ ${output}`;
19719
19734
  details: Object.keys(details).length > 0 ? details : void 0
19720
19735
  };
19721
19736
  }
19722
- var VCS_DIRECTORIES_TO_EXCLUDE, DEFAULT_HEAD_LIMIT, ripGrepSchema, log13, PATHLESS_CONTENT_LINE2, REGEX_PARSE_ERROR_RE, REGEX_PARSE_HINT, ripGrepTool;
19737
+ var VCS_DIRECTORIES_TO_EXCLUDE, DEFAULT_HEAD_LIMIT, GREP_TOOL_DESCRIPTION, ripGrepSchema, log13, PATHLESS_CONTENT_LINE2, REGEX_PARSE_ERROR_RE, REGEX_PARSE_HINT, ripGrepTool;
19723
19738
  var init_rip_grep = __esm({
19724
19739
  "src/tools/rip-grep.ts"() {
19725
19740
  "use strict";
@@ -19732,76 +19747,34 @@ var init_rip_grep = __esm({
19732
19747
  init_log();
19733
19748
  VCS_DIRECTORIES_TO_EXCLUDE = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"];
19734
19749
  DEFAULT_HEAD_LIMIT = 250;
19750
+ GREP_TOOL_DESCRIPTION = "Search file contents with ripgrep (regex). output_mode: content (default; -A/-B/-C/context/-n apply here, -n defaults true), files_with_matches, count. Scope with path; glob for name/extension (*.ts, **/*.tsx)\u2014not a path substitute. type = rg --type. head_limit default 250 (0=unlimited); offset skips. multiline: . matches newlines. Respects .gitignore.";
19735
19751
  ripGrepSchema = Type3.Object({
19736
- pattern: Type3.String({
19737
- description: "Regex pattern to search for"
19738
- }),
19739
- path: Type3.Optional(
19740
- Type3.String({
19741
- description: "File or directory to search in (rg PATH). Defaults to current working directory."
19742
- })
19743
- ),
19752
+ pattern: Type3.String(),
19753
+ path: Type3.Optional(Type3.String()),
19744
19754
  glob: Type3.Optional(
19745
19755
  Type3.String({
19746
- description: 'Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}", "**/foo/*.ts"). Prefer path for a directory or file scope; use **/-prefixed globs when matching under a repo-relative folder.'
19756
+ description: "name/extension filter, e.g. *.ts. Prefer path for a directory."
19747
19757
  })
19748
19758
  ),
19749
19759
  output_mode: Type3.Optional(
19750
- Type3.Union(
19751
- [Type3.Literal("content"), Type3.Literal("files_with_matches"), Type3.Literal("count")],
19752
- {
19753
- description: 'Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "content".'
19754
- }
19755
- )
19756
- ),
19757
- "-B": Type3.Optional(
19758
- Type3.Number({
19759
- description: 'Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'
19760
- })
19761
- ),
19762
- "-A": Type3.Optional(
19763
- Type3.Number({
19764
- description: 'Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'
19765
- })
19766
- ),
19767
- "-C": Type3.Optional(
19768
- Type3.Number({
19769
- description: "Alias for context; requires output_mode: content."
19770
- })
19771
- ),
19772
- context: Type3.Optional(
19773
- Type3.Number({
19774
- description: 'Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'
19775
- })
19776
- ),
19777
- "-n": Type3.Optional(
19778
- Type3.Boolean({
19779
- description: 'Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'
19780
- })
19781
- ),
19782
- "-i": Type3.Optional(
19783
- Type3.Boolean({
19784
- description: "Case-insensitive search (rg -i)"
19785
- })
19786
- ),
19787
- type: Type3.Optional(
19788
- Type3.String({
19789
- description: "File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types."
19790
- })
19791
- ),
19792
- head_limit: Type3.Optional(
19793
- Type3.Number({
19794
- description: 'Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes. Defaults to 250 when unspecified. Pass 0 for unlimited.'
19795
- })
19796
- ),
19797
- offset: Type3.Optional(
19798
- Type3.Number({
19799
- description: 'Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.'
19800
- })
19760
+ Type3.Union([
19761
+ Type3.Literal("content"),
19762
+ Type3.Literal("files_with_matches"),
19763
+ Type3.Literal("count")
19764
+ ])
19801
19765
  ),
19766
+ "-B": Type3.Optional(Type3.Number()),
19767
+ "-A": Type3.Optional(Type3.Number()),
19768
+ "-C": Type3.Optional(Type3.Number()),
19769
+ context: Type3.Optional(Type3.Number()),
19770
+ "-n": Type3.Optional(Type3.Boolean()),
19771
+ "-i": Type3.Optional(Type3.Boolean()),
19772
+ type: Type3.Optional(Type3.String()),
19773
+ head_limit: Type3.Optional(Type3.Number()),
19774
+ offset: Type3.Optional(Type3.Number()),
19802
19775
  multiline: Type3.Optional(
19803
19776
  Type3.Boolean({
19804
- description: "Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false."
19777
+ description: ". matches newlines"
19805
19778
  })
19806
19779
  )
19807
19780
  });
@@ -20968,8 +20941,275 @@ var init_bash3 = __esm({
20968
20941
  }
20969
20942
  });
20970
20943
 
20944
+ // ../node_modules/.bun/diff@8.0.4/node_modules/diff/libesm/diff/base.js
20945
+ var Diff;
20946
+ var init_base = __esm({
20947
+ "../node_modules/.bun/diff@8.0.4/node_modules/diff/libesm/diff/base.js"() {
20948
+ Diff = class {
20949
+ diff(oldStr, newStr, options = {}) {
20950
+ let callback;
20951
+ if (typeof options === "function") {
20952
+ callback = options;
20953
+ options = {};
20954
+ } else if ("callback" in options) {
20955
+ callback = options.callback;
20956
+ }
20957
+ const oldString = this.castInput(oldStr, options);
20958
+ const newString = this.castInput(newStr, options);
20959
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
20960
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
20961
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
20962
+ }
20963
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
20964
+ var _a;
20965
+ const done = (value) => {
20966
+ value = this.postProcess(value, options);
20967
+ if (callback) {
20968
+ setTimeout(function() {
20969
+ callback(value);
20970
+ }, 0);
20971
+ return void 0;
20972
+ } else {
20973
+ return value;
20974
+ }
20975
+ };
20976
+ const newLen = newTokens.length, oldLen = oldTokens.length;
20977
+ let editLength = 1;
20978
+ let maxEditLength = newLen + oldLen;
20979
+ if (options.maxEditLength != null) {
20980
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
20981
+ }
20982
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
20983
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
20984
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
20985
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
20986
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
20987
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
20988
+ }
20989
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
20990
+ const execEditLength = () => {
20991
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
20992
+ let basePath;
20993
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
20994
+ if (removePath) {
20995
+ bestPath[diagonalPath - 1] = void 0;
20996
+ }
20997
+ let canAdd = false;
20998
+ if (addPath) {
20999
+ const addPathNewPos = addPath.oldPos - diagonalPath;
21000
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
21001
+ }
21002
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
21003
+ if (!canAdd && !canRemove) {
21004
+ bestPath[diagonalPath] = void 0;
21005
+ continue;
21006
+ }
21007
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
21008
+ basePath = this.addToPath(addPath, true, false, 0, options);
21009
+ } else {
21010
+ basePath = this.addToPath(removePath, false, true, 1, options);
21011
+ }
21012
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
21013
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
21014
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
21015
+ } else {
21016
+ bestPath[diagonalPath] = basePath;
21017
+ if (basePath.oldPos + 1 >= oldLen) {
21018
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
21019
+ }
21020
+ if (newPos + 1 >= newLen) {
21021
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
21022
+ }
21023
+ }
21024
+ }
21025
+ editLength++;
21026
+ };
21027
+ if (callback) {
21028
+ (function exec() {
21029
+ setTimeout(function() {
21030
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
21031
+ return callback(void 0);
21032
+ }
21033
+ if (!execEditLength()) {
21034
+ exec();
21035
+ }
21036
+ }, 0);
21037
+ })();
21038
+ } else {
21039
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
21040
+ const ret = execEditLength();
21041
+ if (ret) {
21042
+ return ret;
21043
+ }
21044
+ }
21045
+ }
21046
+ }
21047
+ addToPath(path7, added, removed, oldPosInc, options) {
21048
+ const last = path7.lastComponent;
21049
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
21050
+ return {
21051
+ oldPos: path7.oldPos + oldPosInc,
21052
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
21053
+ };
21054
+ } else {
21055
+ return {
21056
+ oldPos: path7.oldPos + oldPosInc,
21057
+ lastComponent: { count: 1, added, removed, previousComponent: last }
21058
+ };
21059
+ }
21060
+ }
21061
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
21062
+ const newLen = newTokens.length, oldLen = oldTokens.length;
21063
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
21064
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
21065
+ newPos++;
21066
+ oldPos++;
21067
+ commonCount++;
21068
+ if (options.oneChangePerToken) {
21069
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
21070
+ }
21071
+ }
21072
+ if (commonCount && !options.oneChangePerToken) {
21073
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
21074
+ }
21075
+ basePath.oldPos = oldPos;
21076
+ return newPos;
21077
+ }
21078
+ equals(left, right, options) {
21079
+ if (options.comparator) {
21080
+ return options.comparator(left, right);
21081
+ } else {
21082
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
21083
+ }
21084
+ }
21085
+ removeEmpty(array) {
21086
+ const ret = [];
21087
+ for (let i = 0; i < array.length; i++) {
21088
+ if (array[i]) {
21089
+ ret.push(array[i]);
21090
+ }
21091
+ }
21092
+ return ret;
21093
+ }
21094
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
21095
+ castInput(value, options) {
21096
+ return value;
21097
+ }
21098
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
21099
+ tokenize(value, options) {
21100
+ return Array.from(value);
21101
+ }
21102
+ join(chars) {
21103
+ return chars.join("");
21104
+ }
21105
+ postProcess(changeObjects, options) {
21106
+ return changeObjects;
21107
+ }
21108
+ get useLongestToken() {
21109
+ return false;
21110
+ }
21111
+ buildValues(lastComponent, newTokens, oldTokens) {
21112
+ const components = [];
21113
+ let nextComponent;
21114
+ while (lastComponent) {
21115
+ components.push(lastComponent);
21116
+ nextComponent = lastComponent.previousComponent;
21117
+ delete lastComponent.previousComponent;
21118
+ lastComponent = nextComponent;
21119
+ }
21120
+ components.reverse();
21121
+ const componentLen = components.length;
21122
+ let componentPos = 0, newPos = 0, oldPos = 0;
21123
+ for (; componentPos < componentLen; componentPos++) {
21124
+ const component = components[componentPos];
21125
+ if (!component.removed) {
21126
+ if (!component.added && this.useLongestToken) {
21127
+ let value = newTokens.slice(newPos, newPos + component.count);
21128
+ value = value.map(function(value2, i) {
21129
+ const oldValue = oldTokens[oldPos + i];
21130
+ return oldValue.length > value2.length ? oldValue : value2;
21131
+ });
21132
+ component.value = this.join(value);
21133
+ } else {
21134
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
21135
+ }
21136
+ newPos += component.count;
21137
+ if (!component.added) {
21138
+ oldPos += component.count;
21139
+ }
21140
+ } else {
21141
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
21142
+ oldPos += component.count;
21143
+ }
21144
+ }
21145
+ return components;
21146
+ }
21147
+ };
21148
+ }
21149
+ });
21150
+
21151
+ // ../node_modules/.bun/diff@8.0.4/node_modules/diff/libesm/diff/line.js
21152
+ function diffLines(oldStr, newStr, options) {
21153
+ return lineDiff.diff(oldStr, newStr, options);
21154
+ }
21155
+ function tokenize(value, options) {
21156
+ if (options.stripTrailingCr) {
21157
+ value = value.replace(/\r\n/g, "\n");
21158
+ }
21159
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
21160
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
21161
+ linesAndNewlines.pop();
21162
+ }
21163
+ for (let i = 0; i < linesAndNewlines.length; i++) {
21164
+ const line = linesAndNewlines[i];
21165
+ if (i % 2 && !options.newlineIsToken) {
21166
+ retLines[retLines.length - 1] += line;
21167
+ } else {
21168
+ retLines.push(line);
21169
+ }
21170
+ }
21171
+ return retLines;
21172
+ }
21173
+ var LineDiff, lineDiff;
21174
+ var init_line = __esm({
21175
+ "../node_modules/.bun/diff@8.0.4/node_modules/diff/libesm/diff/line.js"() {
21176
+ init_base();
21177
+ LineDiff = class extends Diff {
21178
+ constructor() {
21179
+ super(...arguments);
21180
+ this.tokenize = tokenize;
21181
+ }
21182
+ equals(left, right, options) {
21183
+ if (options.ignoreWhitespace) {
21184
+ if (!options.newlineIsToken || !left.includes("\n")) {
21185
+ left = left.trim();
21186
+ }
21187
+ if (!options.newlineIsToken || !right.includes("\n")) {
21188
+ right = right.trim();
21189
+ }
21190
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
21191
+ if (left.endsWith("\n")) {
21192
+ left = left.slice(0, -1);
21193
+ }
21194
+ if (right.endsWith("\n")) {
21195
+ right = right.slice(0, -1);
21196
+ }
21197
+ }
21198
+ return super.equals(left, right, options);
21199
+ }
21200
+ };
21201
+ lineDiff = new LineDiff();
21202
+ }
21203
+ });
21204
+
21205
+ // ../node_modules/.bun/diff@8.0.4/node_modules/diff/libesm/index.js
21206
+ var init_libesm = __esm({
21207
+ "../node_modules/.bun/diff@8.0.4/node_modules/diff/libesm/index.js"() {
21208
+ init_line();
21209
+ }
21210
+ });
21211
+
20971
21212
  // src/tools/edit-diff.ts
20972
- import * as Diff from "diff";
20973
21213
  function detectLineEnding(content) {
20974
21214
  const crlfIdx = content.indexOf("\r\n");
20975
21215
  const lfIdx = content.indexOf("\n");
@@ -21244,7 +21484,7 @@ function stripBom(content) {
21244
21484
  return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
21245
21485
  }
21246
21486
  function generateDiffString(oldContent, newContent, contextLines = 4) {
21247
- const parts = Diff.diffLines(oldContent, newContent);
21487
+ const parts = diffLines(oldContent, newContent);
21248
21488
  const output = [];
21249
21489
  const oldLines = oldContent.split("\n");
21250
21490
  const newLines = newContent.split("\n");
@@ -21314,6 +21554,7 @@ var EDIT_MISS_COMMON_TOKENS;
21314
21554
  var init_edit_diff = __esm({
21315
21555
  "src/tools/edit-diff.ts"() {
21316
21556
  "use strict";
21557
+ init_libesm();
21317
21558
  EDIT_MISS_COMMON_TOKENS = /* @__PURE__ */ new Set([
21318
21559
  "name",
21319
21560
  "type",
@@ -21490,6 +21731,33 @@ var init_edit_after_proof = __esm({
21490
21731
  }
21491
21732
  });
21492
21733
 
21734
+ // src/tools/edit-path-lock.ts
21735
+ import { resolve as resolvePath2 } from "node:path";
21736
+ function editPathLockKey(absolutePath) {
21737
+ return resolvePath2(absolutePath);
21738
+ }
21739
+ function tryAcquireEditPathLock(absolutePath) {
21740
+ const key = editPathLockKey(absolutePath);
21741
+ if (heldPaths.has(key)) {
21742
+ return false;
21743
+ }
21744
+ heldPaths.add(key);
21745
+ return true;
21746
+ }
21747
+ function releaseEditPathLock(absolutePath) {
21748
+ heldPaths.delete(editPathLockKey(absolutePath));
21749
+ }
21750
+ function formatSameFileEditInProgressError(path7) {
21751
+ return `Another edit is already in progress for ${path7}. Do not issue parallel edit calls on the same file \u2014 batch hunks in one call with replacements: [{ oldText, newText }, \u2026]. Parallelize only independent files.`;
21752
+ }
21753
+ var heldPaths;
21754
+ var init_edit_path_lock = __esm({
21755
+ "src/tools/edit-path-lock.ts"() {
21756
+ "use strict";
21757
+ heldPaths = /* @__PURE__ */ new Set();
21758
+ }
21759
+ });
21760
+
21493
21761
  // src/tools/edit-occurrences.ts
21494
21762
  function lineNumberAtIndex(content, index) {
21495
21763
  if (index <= 0) {
@@ -21822,115 +22090,122 @@ function createEditTool(cwd, options) {
21822
22090
  const absolutePath = resolveToCwd(path7, cwd);
21823
22091
  validatePathWithinCwd(absolutePath, cwd);
21824
22092
  const hunks = resolveReplacements(input);
21825
- return withAbortSignal(signal, async (isAborted) => {
21826
- try {
21827
- await ops.access(absolutePath);
21828
- } catch (error) {
21829
- rethrowIfFileNotFound(error, cwd, absolutePath, path7);
21830
- }
21831
- if (isAborted()) return { content: [], details: void 0 };
21832
- const buffer = await ops.readFile(absolutePath);
21833
- const rawContent = buffer.toString("utf-8");
21834
- if (isAborted()) return { content: [], details: void 0 };
21835
- const { bom, text: content } = stripBom(rawContent);
21836
- const originalEnding = detectLineEnding(content);
21837
- const baseContent = normalizeToLF(content);
21838
- let newContent = baseContent;
21839
- let appliedCount = 0;
21840
- let skippedAlreadyApplied = 0;
21841
- for (const hunk of hunks) {
21842
- if (isEditAlreadyApplied(newContent, hunk.oldText, hunk.newText)) {
21843
- skippedAlreadyApplied++;
21844
- continue;
22093
+ if (!tryAcquireEditPathLock(absolutePath)) {
22094
+ throw new Error(formatSameFileEditInProgressError(path7));
22095
+ }
22096
+ try {
22097
+ return await withAbortSignal(signal, async (isAborted) => {
22098
+ try {
22099
+ await ops.access(absolutePath);
22100
+ } catch (error) {
22101
+ rethrowIfFileNotFound(error, cwd, absolutePath, path7);
21845
22102
  }
21846
- const contentBeforeHunk = newContent;
21847
- newContent = applyReplacement(
21848
- newContent,
21849
- path7,
21850
- hunk.oldText,
21851
- hunk.newText,
21852
- hunk.replaceAll,
21853
- { line: hunk.line, occurrenceIndex: hunk.occurrenceIndex }
21854
- );
21855
- appliedCount++;
21856
- if (hunk.newText.includes(hunk.oldText)) {
21857
- const dupLine = findConsecutiveDuplicateInsertedLine(
22103
+ if (isAborted()) return { content: [], details: void 0 };
22104
+ const buffer = await ops.readFile(absolutePath);
22105
+ const rawContent = buffer.toString("utf-8");
22106
+ if (isAborted()) return { content: [], details: void 0 };
22107
+ const { bom, text: content } = stripBom(rawContent);
22108
+ const originalEnding = detectLineEnding(content);
22109
+ const baseContent = normalizeToLF(content);
22110
+ let newContent = baseContent;
22111
+ let appliedCount = 0;
22112
+ let skippedAlreadyApplied = 0;
22113
+ for (const hunk of hunks) {
22114
+ if (isEditAlreadyApplied(newContent, hunk.oldText, hunk.newText)) {
22115
+ skippedAlreadyApplied++;
22116
+ continue;
22117
+ }
22118
+ const contentBeforeHunk = newContent;
22119
+ newContent = applyReplacement(
21858
22120
  newContent,
22121
+ path7,
21859
22122
  hunk.oldText,
21860
22123
  hunk.newText,
21861
- contentBeforeHunk
22124
+ hunk.replaceAll,
22125
+ { line: hunk.line, occurrenceIndex: hunk.occurrenceIndex }
21862
22126
  );
21863
- if (dupLine) {
21864
- if (shouldTreatIntegrityDupAsAlreadyApplied(
21865
- contentBeforeHunk,
22127
+ appliedCount++;
22128
+ if (hunk.newText.includes(hunk.oldText)) {
22129
+ const dupLine = findConsecutiveDuplicateInsertedLine(
22130
+ newContent,
21866
22131
  hunk.oldText,
21867
- hunk.newText
21868
- )) {
21869
- newContent = contentBeforeHunk;
21870
- appliedCount--;
21871
- skippedAlreadyApplied++;
21872
- continue;
22132
+ hunk.newText,
22133
+ contentBeforeHunk
22134
+ );
22135
+ if (dupLine) {
22136
+ if (shouldTreatIntegrityDupAsAlreadyApplied(
22137
+ contentBeforeHunk,
22138
+ hunk.oldText,
22139
+ hunk.newText
22140
+ )) {
22141
+ newContent = contentBeforeHunk;
22142
+ appliedCount--;
22143
+ skippedAlreadyApplied++;
22144
+ continue;
22145
+ }
22146
+ throw new Error(formatEditIntegrityDuplicateError(path7, dupLine));
21873
22147
  }
21874
- throw new Error(formatEditIntegrityDuplicateError(path7, dupLine));
21875
22148
  }
21876
- }
21877
- if (isAborted()) return { content: [], details: void 0 };
21878
- }
21879
- if (baseContent === newContent) {
21880
- if (skippedAlreadyApplied > 0 && appliedCount === 0) {
21881
- const proof2 = appendAfterProof(
21882
- path7,
21883
- newContent,
21884
- hunks.map((hunk) => hunk.newText),
21885
- `already applied (skipped): ${path7}`
21886
- );
21887
- return {
21888
- content: [
21889
- {
21890
- type: "text",
21891
- text: proof2.text
22149
+ if (isAborted()) return { content: [], details: void 0 };
22150
+ }
22151
+ if (baseContent === newContent) {
22152
+ if (skippedAlreadyApplied > 0 && appliedCount === 0) {
22153
+ const proof2 = appendAfterProof(
22154
+ path7,
22155
+ newContent,
22156
+ hunks.map((hunk) => hunk.newText),
22157
+ `already applied (skipped): ${path7}`
22158
+ );
22159
+ return {
22160
+ content: [
22161
+ {
22162
+ type: "text",
22163
+ text: proof2.text
22164
+ }
22165
+ ],
22166
+ details: {
22167
+ diff: "",
22168
+ replacementCount: 0,
22169
+ afterProof: proof2.afterProof,
22170
+ proved: proof2.proved
21892
22171
  }
21893
- ],
21894
- details: {
21895
- diff: "",
21896
- replacementCount: 0,
21897
- afterProof: proof2.afterProof,
21898
- proved: proof2.proved
21899
- }
21900
- };
22172
+ };
22173
+ }
22174
+ throw new Error(
22175
+ `No changes made to ${path7}. The replacement produced identical content.`
22176
+ );
21901
22177
  }
21902
- throw new Error(
21903
- `No changes made to ${path7}. The replacement produced identical content.`
22178
+ const finalContent = bom + restoreLineEndings(newContent, originalEnding);
22179
+ await ops.writeFile(absolutePath, finalContent);
22180
+ if (isAborted()) return { content: [], details: void 0 };
22181
+ rememberSessionMutatedPath(threadId, path7);
22182
+ const diffResult = generateDiffString(baseContent, newContent);
22183
+ const hunkLabel = hunks.length === 1 ? "text" : `${hunks.length} hunks`;
22184
+ const proof = appendAfterProof(
22185
+ path7,
22186
+ newContent,
22187
+ hunks.map((hunk) => hunk.newText),
22188
+ `Successfully replaced ${hunkLabel} in ${path7}.`
21904
22189
  );
21905
- }
21906
- const finalContent = bom + restoreLineEndings(newContent, originalEnding);
21907
- await ops.writeFile(absolutePath, finalContent);
21908
- if (isAborted()) return { content: [], details: void 0 };
21909
- rememberSessionMutatedPath(threadId, path7);
21910
- const diffResult = generateDiffString(baseContent, newContent);
21911
- const hunkLabel = hunks.length === 1 ? "text" : `${hunks.length} hunks`;
21912
- const proof = appendAfterProof(
21913
- path7,
21914
- newContent,
21915
- hunks.map((hunk) => hunk.newText),
21916
- `Successfully replaced ${hunkLabel} in ${path7}.`
21917
- );
21918
- return {
21919
- content: [
21920
- {
21921
- type: "text",
21922
- text: proof.text
22190
+ return {
22191
+ content: [
22192
+ {
22193
+ type: "text",
22194
+ text: proof.text
22195
+ }
22196
+ ],
22197
+ details: {
22198
+ diff: diffResult.diff,
22199
+ firstChangedLine: diffResult.firstChangedLine,
22200
+ replacementCount: hunks.length,
22201
+ afterProof: proof.afterProof,
22202
+ proved: proof.proved
21923
22203
  }
21924
- ],
21925
- details: {
21926
- diff: diffResult.diff,
21927
- firstChangedLine: diffResult.firstChangedLine,
21928
- replacementCount: hunks.length,
21929
- afterProof: proof.afterProof,
21930
- proved: proof.proved
21931
- }
21932
- };
21933
- });
22204
+ };
22205
+ });
22206
+ } finally {
22207
+ releaseEditPathLock(absolutePath);
22208
+ }
21934
22209
  }
21935
22210
  };
21936
22211
  }
@@ -21961,65 +22236,48 @@ var init_edit2 = __esm({
21961
22236
  init_edit_diff();
21962
22237
  init_edit_already_applied();
21963
22238
  init_edit_after_proof();
22239
+ init_edit_path_lock();
21964
22240
  init_edit_occurrences();
21965
22241
  init_load_tool_description();
21966
22242
  init_path_utils();
21967
22243
  init_tool_helpers();
21968
22244
  replacementSchema = Type5.Object({
21969
- oldText: Type5.String({
21970
- description: "Exact text to find and replace (must match exactly; unique unless replaceAll is true)"
21971
- }),
21972
- newText: Type5.String({ description: "Replacement text" }),
21973
- replaceAll: Type5.Optional(
21974
- Type5.Boolean({
21975
- description: "When true, replace every occurrence of oldText in the file."
21976
- })
21977
- ),
22245
+ oldText: Type5.String(),
22246
+ newText: Type5.String(),
22247
+ replaceAll: Type5.Optional(Type5.Boolean()),
21978
22248
  line: Type5.Optional(
21979
22249
  Type5.Integer({
21980
- description: "1-based source line where the occurrence starts (from ambiguous-edit error). Use instead of widening oldText.",
22250
+ description: "1-based start line",
21981
22251
  minimum: 1
21982
22252
  })
21983
22253
  ),
21984
22254
  occurrenceIndex: Type5.Optional(
21985
22255
  Type5.Integer({
21986
- description: "1-based occurrence index when oldText matches multiple times (first match is 1).",
22256
+ description: "1-based; first match is 1",
21987
22257
  minimum: 1
21988
22258
  })
21989
22259
  )
21990
22260
  });
21991
22261
  editSchema = Type5.Object({
21992
- path: Type5.String({ description: "Path to the file to edit (relative or absolute)" }),
21993
- oldText: Type5.Optional(
21994
- Type5.String({
21995
- description: "Exact text to find and replace (must match exactly). Required unless replacements is set."
21996
- })
21997
- ),
21998
- newText: Type5.Optional(
21999
- Type5.String({
22000
- description: "New text to replace oldText with. Required unless replacements is set."
22001
- })
22002
- ),
22003
- replaceAll: Type5.Optional(
22004
- Type5.Boolean({
22005
- description: "When true with oldText/newText, replace every occurrence. Per-hunk replaceAll in replacements overrides this."
22006
- })
22007
- ),
22262
+ path: Type5.String(),
22263
+ oldText: Type5.Optional(Type5.String()),
22264
+ newText: Type5.Optional(Type5.String()),
22265
+ replaceAll: Type5.Optional(Type5.Boolean()),
22008
22266
  replacements: Type5.Optional(
22009
22267
  Type5.Array(replacementSchema, {
22010
22268
  minItems: 1,
22011
- description: "Multiple hunks to apply in one call (applied in order). Prefer this over serial single-hunk edits on the same file."
22269
+ description: "batch hunks, applied in order"
22012
22270
  })
22013
22271
  ),
22014
22272
  line: Type5.Optional(
22015
22273
  Type5.Integer({
22016
- description: "Default line target for single oldText/newText edits when oldText is ambiguous.",
22274
+ description: "1-based start line",
22017
22275
  minimum: 1
22018
22276
  })
22019
22277
  ),
22020
22278
  occurrenceIndex: Type5.Optional(
22021
22279
  Type5.Integer({
22022
- description: "Default occurrenceIndex for single oldText/newText edits when ambiguous.",
22280
+ description: "1-based; first match is 1",
22023
22281
  minimum: 1
22024
22282
  })
22025
22283
  )
@@ -22052,8 +22310,8 @@ function createLsTool(cwd, options) {
22052
22310
  if (!await ops.exists(dirPath)) {
22053
22311
  throw new Error(`Path not found: ${dirPath}`);
22054
22312
  }
22055
- const stat8 = await ops.stat(dirPath);
22056
- if (!stat8.isDirectory()) {
22313
+ const stat9 = await ops.stat(dirPath);
22314
+ if (!stat9.isDirectory()) {
22057
22315
  throw new Error(`Not a directory: ${dirPath}`);
22058
22316
  }
22059
22317
  let entries;
@@ -23416,6 +23674,20 @@ function formatDeletedTodosAck(items) {
23416
23674
  const parts = items.map((item) => `"${truncateTodoDescription(item.description)}"`);
23417
23675
  return `Deleted ${items.length} todos: ${parts.join("; ")}`;
23418
23676
  }
23677
+ function formatRemainingTodosSuffix(todos) {
23678
+ const remaining = todos.filter((todo) => todo.status !== "done").map((todo) => ({
23679
+ id: todo.id,
23680
+ description: todo.description,
23681
+ status: todo.status
23682
+ }));
23683
+ if (remaining.length === 0) {
23684
+ return "All todos complete.";
23685
+ }
23686
+ return `Remaining: ${JSON.stringify(remaining)}`;
23687
+ }
23688
+ function formatTodoMutationResult(ack, todos) {
23689
+ return `${ack} ${formatRemainingTodosSuffix(todos)}`;
23690
+ }
23419
23691
  async function fetchTodosByIds(db, threadId, ids) {
23420
23692
  const rows = /* @__PURE__ */ new Map();
23421
23693
  for (const id of ids) {
@@ -23455,10 +23727,10 @@ function createTodoTool(threadId, threadPath) {
23455
23727
  content: [
23456
23728
  {
23457
23729
  type: "text",
23458
- text: JSON.stringify({ todos: todosAfterAdd })
23730
+ text: JSON.stringify({ todos: todosAfterAdd, note: ADD_TODOS_NOTE })
23459
23731
  }
23460
23732
  ],
23461
- details: void 0
23733
+ details: { todos: todosAfterAdd }
23462
23734
  };
23463
23735
  }
23464
23736
  case "update": {
@@ -23510,14 +23782,18 @@ function createTodoTool(threadId, threadPath) {
23510
23782
  status: status ?? existing.status
23511
23783
  });
23512
23784
  }
23785
+ const todosAfterUpdate = await fetchAllTodos(db, threadId);
23513
23786
  return {
23514
23787
  content: [
23515
23788
  {
23516
23789
  type: "text",
23517
- text: formatUpdatedTodosAck(updatedItems)
23790
+ text: formatTodoMutationResult(
23791
+ formatUpdatedTodosAck(updatedItems),
23792
+ todosAfterUpdate
23793
+ )
23518
23794
  }
23519
23795
  ],
23520
- details: void 0
23796
+ details: { todos: todosAfterUpdate }
23521
23797
  };
23522
23798
  }
23523
23799
  case "list": {
@@ -23529,7 +23805,7 @@ function createTodoTool(threadId, threadPath) {
23529
23805
  text: JSON.stringify({ todos })
23530
23806
  }
23531
23807
  ],
23532
- details: void 0
23808
+ details: { todos }
23533
23809
  };
23534
23810
  }
23535
23811
  case "delete": {
@@ -23547,14 +23823,18 @@ function createTodoTool(threadId, threadPath) {
23547
23823
  args: [id, threadId]
23548
23824
  });
23549
23825
  }
23826
+ const todosAfterDelete = await fetchAllTodos(db, threadId);
23550
23827
  return {
23551
23828
  content: [
23552
23829
  {
23553
23830
  type: "text",
23554
- text: formatDeletedTodosAck(deletedItems)
23831
+ text: formatTodoMutationResult(
23832
+ formatDeletedTodosAck(deletedItems),
23833
+ todosAfterDelete
23834
+ )
23555
23835
  }
23556
23836
  ],
23557
- details: void 0
23837
+ details: { todos: todosAfterDelete }
23558
23838
  };
23559
23839
  }
23560
23840
  case "learnings": {
@@ -23605,7 +23885,7 @@ function createTodoTool(threadId, threadPath) {
23605
23885
  }
23606
23886
  };
23607
23887
  }
23608
- var todoSchema, todoTool;
23888
+ var todoSchema, ADD_TODOS_NOTE, todoTool;
23609
23889
  var init_todo2 = __esm({
23610
23890
  "src/tools/todo.ts"() {
23611
23891
  "use strict";
@@ -23614,49 +23894,39 @@ var init_todo2 = __esm({
23614
23894
  init_ralph_progress();
23615
23895
  init_load_tool_description();
23616
23896
  todoSchema = Type13.Object({
23617
- action: Type13.Union(
23618
- [
23619
- Type13.Literal("add"),
23620
- Type13.Literal("update"),
23621
- Type13.Literal("list"),
23622
- Type13.Literal("delete"),
23623
- Type13.Literal("learnings")
23624
- ],
23625
- {
23626
- description: "Operation to perform: 'add' creates new items, 'update' changes status, 'list' returns all items, 'delete' removes items, 'learnings' appends text to progress.txt for cross-iteration memory."
23627
- }
23628
- ),
23897
+ action: Type13.Union([
23898
+ Type13.Literal("add"),
23899
+ Type13.Literal("update"),
23900
+ Type13.Literal("list"),
23901
+ Type13.Literal("delete"),
23902
+ Type13.Literal("learnings")
23903
+ ]),
23629
23904
  descriptions: Type13.Optional(
23630
23905
  Type13.Array(Type13.String(), {
23631
- description: "Todo item descriptions. Required for 'add'. Provide one or more descriptions to create multiple items at once."
23906
+ description: "required for add"
23632
23907
  })
23633
23908
  ),
23634
23909
  ids: Type13.Optional(
23635
23910
  Type13.Array(Type13.String(), {
23636
- description: "Todo item IDs. Required for 'update' and 'delete'. Provide one or more IDs to update or delete multiple items at once."
23911
+ description: "required for update/delete"
23637
23912
  })
23638
23913
  ),
23639
23914
  status: Type13.Optional(
23640
- Type13.Union([Type13.Literal("pending"), Type13.Literal("working"), Type13.Literal("done")], {
23641
- description: "New status for 'update'. Applied to all specified IDs. 'pending' = not started, 'working' = in progress, 'done' = complete."
23642
- })
23643
- ),
23644
- assignedTo: Type13.Optional(
23645
- Type13.String({
23646
- description: "Who items are assigned to. Defaults to 'agent'. Applied to all specified items."
23647
- })
23915
+ Type13.Union([Type13.Literal("pending"), Type13.Literal("working"), Type13.Literal("done")])
23648
23916
  ),
23917
+ assignedTo: Type13.Optional(Type13.String()),
23649
23918
  passes: Type13.Optional(
23650
23919
  Type13.Boolean({
23651
- description: "Whether the story's quality checks passed. Used in Ralph mode to mark stories as verified. Set to true when checks pass after implementation."
23920
+ description: "Ralph: true after checks"
23652
23921
  })
23653
23922
  ),
23654
23923
  learnings: Type13.Optional(
23655
23924
  Type13.String({
23656
- description: "Text to append to progress.txt. Required for 'learnings' action. Records patterns, gotchas, and context discovered during implementation."
23925
+ description: "required for learnings; appends progress.txt"
23657
23926
  })
23658
23927
  )
23659
23928
  });
23929
+ ADD_TODOS_NOTE = "Mark each item done as you finish it. Do not end the turn with pending todos.";
23660
23930
  todoTool = createTodoTool("");
23661
23931
  }
23662
23932
  });
@@ -24425,24 +24695,29 @@ var init_skills_manager = __esm({
24425
24695
  );
24426
24696
  }
24427
24697
  /**
24428
- * Load all available skills for a given thread
24429
- * Project skills override global ones with the same name
24698
+ * Load all available skills for a thread.
24699
+ *
24700
+ * Project settings install skills in the project root, while a thread may
24701
+ * use a separate checkout (for example, a git-backed thread). Load both
24702
+ * locations so a project-installed skill is available in every thread.
24703
+ * Thread-local skills override project skills, which override global ones.
24430
24704
  */
24431
- async loadSkills(threadPath) {
24705
+ async loadSkills(threadPath, projectPath = threadPath) {
24432
24706
  const skills = /* @__PURE__ */ new Map();
24433
24707
  const globalDir = getGlobalSkillsDir();
24434
24708
  if (existsSync12(globalDir)) {
24435
24709
  const globalSkills = await this.loadSkillsFromDir(globalDir, "global");
24436
- for (const skill of globalSkills) {
24437
- skills.set(skill.name, skill);
24438
- }
24710
+ for (const skill of globalSkills) skills.set(skill.name, skill);
24439
24711
  }
24440
- const projectDir = getProjectSkillsDir(threadPath);
24712
+ const projectDir = getProjectSkillsDir(projectPath);
24441
24713
  if (existsSync12(projectDir)) {
24442
24714
  const projectSkills = await this.loadSkillsFromDir(projectDir, "project");
24443
- for (const skill of projectSkills) {
24444
- skills.set(skill.name, skill);
24445
- }
24715
+ for (const skill of projectSkills) skills.set(skill.name, skill);
24716
+ }
24717
+ const threadDir = getProjectSkillsDir(threadPath);
24718
+ if (threadDir !== projectDir && existsSync12(threadDir)) {
24719
+ const threadSkills = await this.loadSkillsFromDir(threadDir, "project");
24720
+ for (const skill of threadSkills) skills.set(skill.name, skill);
24446
24721
  }
24447
24722
  return Array.from(skills.values()).sort((a, b) => a.name.localeCompare(b.name));
24448
24723
  }
@@ -27019,7 +27294,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
27019
27294
  );
27020
27295
  });
27021
27296
  }
27022
- function runGhPrCreateOnce(gitRoot, branchName, title, description) {
27297
+ function runGhPrCreateOnce(gitRoot, branchName, title, description, baseBranch) {
27023
27298
  return new Promise((resolve9, reject) => {
27024
27299
  const args2 = [
27025
27300
  "pr",
@@ -27029,7 +27304,8 @@ function runGhPrCreateOnce(gitRoot, branchName, title, description) {
27029
27304
  "--title",
27030
27305
  title ?? "",
27031
27306
  "--body",
27032
- description ?? ""
27307
+ description ?? "",
27308
+ ...baseBranch ? ["--base", baseBranch] : []
27033
27309
  ];
27034
27310
  log28.info("Executing gh pr create", { args: args2 });
27035
27311
  const proc = spawnProcess("gh", args2, { cwd: gitRoot });
@@ -27068,10 +27344,10 @@ function runGhPrCreateOnce(gitRoot, branchName, title, description) {
27068
27344
  });
27069
27345
  });
27070
27346
  }
27071
- async function createPullRequest(gitRoot, branchName, title, description) {
27347
+ async function createPullRequest(gitRoot, branchName, title, description, baseBranch) {
27072
27348
  for (let attempt = 0; ; attempt++) {
27073
27349
  try {
27074
- return await runGhPrCreateOnce(gitRoot, branchName, title, description);
27350
+ return await runGhPrCreateOnce(gitRoot, branchName, title, description, baseBranch);
27075
27351
  } catch (error) {
27076
27352
  const message = error instanceof Error ? error.message : String(error);
27077
27353
  const waitMs = PR_CREATE_RACE_RETRY_DELAYS_MS[attempt];
@@ -27212,10 +27488,6 @@ async function resolveCompactModel(model, provider, metadataManager) {
27212
27488
  if (resolved) return { ...resolved, providerName: providerCandidate };
27213
27489
  }
27214
27490
  }
27215
- for (const [slug, defaultModel] of Object.entries(DEFAULT_COMPACT_MODELS)) {
27216
- const resolved = await resolveModelAndKey(slug, defaultModel, metadataManager);
27217
- if (resolved) return { ...resolved, providerName: slug };
27218
- }
27219
27491
  return null;
27220
27492
  }
27221
27493
  function normalizeCompactModelId(model, provider, fallbackModelId) {
@@ -27343,7 +27615,7 @@ async function compactConversationUnlocked(params, dependencies) {
27343
27615
  summary
27344
27616
  };
27345
27617
  }
27346
- var COMPACT_SUMMARY_PROMPT, DEFAULT_COMPACT_MODELS, CompactError;
27618
+ var COMPACT_SUMMARY_PROMPT, CompactError;
27347
27619
  var init_chat_compact_service = __esm({
27348
27620
  "src/features/chat/chat-compact.service.ts"() {
27349
27621
  "use strict";
@@ -27354,13 +27626,6 @@ var init_chat_compact_service = __esm({
27354
27626
  init_conversations_database();
27355
27627
  init_git_utils();
27356
27628
  COMPACT_SUMMARY_PROMPT = `Summarize this coding conversation for continuation. Preserve: project goals, technical decisions, files changed, current state, and outstanding tasks. Omit: debugging noise, failed attempts, and verbose tool output.`;
27357
- DEFAULT_COMPACT_MODELS = {
27358
- anthropic: "claude-3-5-sonnet-20241022",
27359
- openai: "gpt-4o-mini",
27360
- openrouter: "anthropic/claude-3.5-sonnet",
27361
- groq: "llama-3.3-70b-versatile",
27362
- deepseek: "deepseek-chat"
27363
- };
27364
27629
  CompactError = class extends Error {
27365
27630
  code;
27366
27631
  constructor(code, message) {
@@ -28003,10 +28268,107 @@ var init_rules_manager = __esm({
28003
28268
  }
28004
28269
  });
28005
28270
 
28271
+ // src/tools/compact-json-schema.ts
28272
+ function isPlainObject(value) {
28273
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28274
+ }
28275
+ function isNullTypeSchema(value) {
28276
+ return isPlainObject(value) && value.type === "null" && !("const" in value) && !("enum" in value);
28277
+ }
28278
+ function isBareStringSchema(value) {
28279
+ return isPlainObject(value) && value.type === "string" && !("const" in value) && !("enum" in value) && !("anyOf" in value) && !("oneOf" in value);
28280
+ }
28281
+ function readStringConst(value) {
28282
+ if (!isPlainObject(value) || typeof value.const !== "string") {
28283
+ return void 0;
28284
+ }
28285
+ if (value.type !== void 0 && value.type !== "string") {
28286
+ return void 0;
28287
+ }
28288
+ return value.const;
28289
+ }
28290
+ function compactLiteralAnyOf(anyOf) {
28291
+ if (anyOf.length === 0) {
28292
+ return null;
28293
+ }
28294
+ const stringConsts = [];
28295
+ let hasNull = false;
28296
+ let hasBareString = false;
28297
+ for (const item of anyOf) {
28298
+ if (isNullTypeSchema(item)) {
28299
+ hasNull = true;
28300
+ continue;
28301
+ }
28302
+ const literal = readStringConst(item);
28303
+ if (literal !== void 0) {
28304
+ stringConsts.push(literal);
28305
+ continue;
28306
+ }
28307
+ if (isBareStringSchema(item)) {
28308
+ hasBareString = true;
28309
+ continue;
28310
+ }
28311
+ return null;
28312
+ }
28313
+ if (stringConsts.length > 0 && !hasBareString) {
28314
+ return {
28315
+ type: hasNull ? ["string", "null"] : "string",
28316
+ enum: hasNull ? [...stringConsts, null] : stringConsts
28317
+ };
28318
+ }
28319
+ if (hasBareString && hasNull && stringConsts.length === 0) {
28320
+ return { type: ["string", "null"] };
28321
+ }
28322
+ return null;
28323
+ }
28324
+ function compactJsonSchemaForModel(schema) {
28325
+ if (Array.isArray(schema)) {
28326
+ return schema.map((item) => compactJsonSchemaForModel(item));
28327
+ }
28328
+ if (!isPlainObject(schema)) {
28329
+ return schema;
28330
+ }
28331
+ const compactedAnyOf = Array.isArray(schema.anyOf) ? compactLiteralAnyOf(schema.anyOf) : null;
28332
+ const result = {};
28333
+ for (const [key, value] of Object.entries(schema)) {
28334
+ if (compactedAnyOf && key === "anyOf") {
28335
+ continue;
28336
+ }
28337
+ result[key] = compactJsonSchemaForModel(value);
28338
+ }
28339
+ if (compactedAnyOf) {
28340
+ result.type = compactedAnyOf.type;
28341
+ if (compactedAnyOf.enum) {
28342
+ result.enum = compactedAnyOf.enum;
28343
+ }
28344
+ }
28345
+ return result;
28346
+ }
28347
+ function compactToolSchemasInPayload(payload) {
28348
+ if (!isPlainObject(payload)) {
28349
+ return payload;
28350
+ }
28351
+ let next;
28352
+ if (Array.isArray(payload.tools)) {
28353
+ next = next ?? { ...payload };
28354
+ next.tools = payload.tools.map((tool) => compactJsonSchemaForModel(tool));
28355
+ }
28356
+ if (isPlainObject(payload.toolConfig)) {
28357
+ next = next ?? { ...payload };
28358
+ next.toolConfig = compactJsonSchemaForModel(payload.toolConfig);
28359
+ }
28360
+ return next ?? payload;
28361
+ }
28362
+ var init_compact_json_schema = __esm({
28363
+ "src/tools/compact-json-schema.ts"() {
28364
+ "use strict";
28365
+ }
28366
+ });
28367
+
28006
28368
  // src/tools/tool-search.ts
28007
28369
  import { Type as Type14 } from "@sinclair/typebox";
28008
28370
  function formatToolSchema(tool) {
28009
- const schema = JSON.stringify(tool.parameters, null, 2);
28371
+ const schema = JSON.stringify(compactJsonSchemaForModel(tool.parameters), null, 2);
28010
28372
  return `<function>{"name": "${tool.name}", "description": ${JSON.stringify(tool.description)}, "parameters": ${schema}}</function>`;
28011
28373
  }
28012
28374
  function parseQuery(query) {
@@ -28114,6 +28476,7 @@ var toolSearchSchema;
28114
28476
  var init_tool_search = __esm({
28115
28477
  "src/tools/tool-search.ts"() {
28116
28478
  "use strict";
28479
+ init_compact_json_schema();
28117
28480
  toolSearchSchema = Type14.Object({
28118
28481
  query: Type14.String({
28119
28482
  description: 'Search query to find tools. Use "select:name1,name2" to fetch exact tools by name, or keywords to search by description.'
@@ -28651,8 +29014,8 @@ async function insertProject(db, project) {
28651
29014
  try {
28652
29015
  await db.execute(
28653
29016
  `
28654
- INSERT INTO projects (id, name, gitUrl, path, createdAt, openWith, commands, setupScript, validationScript, runCommand, commitMethod, planPrompt, testPrompt, reviewPrompt, customSystemPrompt, useCustomSystemPrompt, allowAllCommands, allowedCommandPatterns, allowedFetchDomains, modelAliases, defaultChatMode, eventToken, disabledTools, smartJsEnabled, useBuiltInBrowser)
28655
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
29017
+ INSERT INTO projects (id, name, gitUrl, path, createdAt, openWith, commands, setupScript, validationScript, runCommand, commitMethod, planPrompt, testPrompt, reviewPrompt, customSystemPrompt, useCustomSystemPrompt, allowAllCommands, allowedCommandPatterns, allowedFetchDomains, modelAliases, defaultChatMode, eventToken, disabledTools, smartJsEnabled, useBuiltInBrowser, projectIcon)
29018
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
28656
29019
  `,
28657
29020
  [
28658
29021
  project.id,
@@ -28679,7 +29042,8 @@ async function insertProject(db, project) {
28679
29042
  project.eventToken ?? null,
28680
29043
  project.disabledTools?.length ? JSON.stringify(project.disabledTools) : null,
28681
29044
  project.smartJsEnabled ? 1 : 0,
28682
- project.useBuiltInBrowser === false ? 0 : 1
29045
+ project.useBuiltInBrowser === false ? 0 : 1,
29046
+ project.projectIcon ?? null
28683
29047
  ]
28684
29048
  );
28685
29049
  } catch (error) {
@@ -28787,6 +29151,10 @@ async function updateProject(db, id, updates) {
28787
29151
  fields.push("useBuiltInBrowser = ?");
28788
29152
  values.push(updates.useBuiltInBrowser ? 1 : 0);
28789
29153
  }
29154
+ if (updates.projectIcon !== void 0) {
29155
+ fields.push("projectIcon = ?");
29156
+ values.push(updates.projectIcon ?? null);
29157
+ }
28790
29158
  if (fields.length === 0) {
28791
29159
  return;
28792
29160
  }
@@ -28917,6 +29285,7 @@ function deserializeProjectWithThreads(row, threadIds) {
28917
29285
  disabledTools: row.disabledTools ? JSON.parse(row.disabledTools) : void 0,
28918
29286
  smartJsEnabled: Boolean(row.smartJsEnabled),
28919
29287
  useBuiltInBrowser: row.useBuiltInBrowser === null ? true : Boolean(row.useBuiltInBrowser),
29288
+ projectIcon: row.projectIcon ?? void 0,
28920
29289
  tasksModifiedCount: row.tasksModifiedCount ?? 0
28921
29290
  };
28922
29291
  }
@@ -29639,9 +30008,9 @@ function extractToolResultText(result) {
29639
30008
  }
29640
30009
  return content.filter((block) => block.type === "text").map((block) => block.text ?? "").join("");
29641
30010
  }
29642
- function logToolCallFailed(log123, event, pending2) {
30011
+ function logToolCallFailed(log124, event, pending2) {
29643
30012
  const errorMessage2 = extractToolResultText(event.result).trim() || "Unknown tool error";
29644
- log123.warn("Tool call failed", {
30013
+ log124.warn("Tool call failed", {
29645
30014
  toolName: event.toolName,
29646
30015
  toolCallId: event.toolCallId,
29647
30016
  arguments: serializeArgumentsForLog(pending2?.args),
@@ -29649,7 +30018,7 @@ function logToolCallFailed(log123, event, pending2) {
29649
30018
  });
29650
30019
  }
29651
30020
  function createAgentEventLogger(event) {
29652
- const log123 = createLogger(event);
30021
+ const log124 = createLogger(event);
29653
30022
  const pendingToolCalls = /* @__PURE__ */ new Map();
29654
30023
  let sequence = 0;
29655
30024
  function logPiEvent(piEvent) {
@@ -29665,15 +30034,15 @@ function createAgentEventLogger(event) {
29665
30034
  const pending2 = pendingToolCalls.get(piEvent.toolCallId);
29666
30035
  pendingToolCalls.delete(piEvent.toolCallId);
29667
30036
  if (piEvent.isError) {
29668
- logToolCallFailed(log123, piEvent, pending2);
30037
+ logToolCallFailed(log124, piEvent, pending2);
29669
30038
  }
29670
30039
  }
29671
- log123.debug(`Agent event ${piEvent.type}`, {
30040
+ log124.debug(`Agent event ${piEvent.type}`, {
29672
30041
  sequence: sequence++,
29673
30042
  detail: formatPiAgentEventDetail(piEvent)
29674
30043
  });
29675
30044
  }
29676
- return { log: log123, logPiEvent };
30045
+ return { log: log124, logPiEvent };
29677
30046
  }
29678
30047
  var PI_AGENT_EVENT_DETAIL_MAX_CHARS;
29679
30048
  var init_agent_event_log = __esm({
@@ -29838,6 +30207,9 @@ var init_agent_event_transformer = __esm({
29838
30207
  if (isObservationRef(resultDetails?.observation)) {
29839
30208
  toolResultBlock.observation = resultDetails.observation;
29840
30209
  }
30210
+ if (event.toolName === "todo" && Array.isArray(resultDetails?.todos)) {
30211
+ toolResultBlock.todos = resultDetails.todos;
30212
+ }
29841
30213
  if (event.toolName === "generate_image" || event.toolName === "find_images" || event.toolName === "browser") {
29842
30214
  toolResultBlock.content = stripGeneratedImageText(resultContent);
29843
30215
  const details = event.result?.details;
@@ -30005,6 +30377,9 @@ async function executeSubagent(options) {
30005
30377
  tools,
30006
30378
  streamOptions: HARNESS_STREAM_OPTIONS
30007
30379
  });
30380
+ harness.on("before_provider_payload", (event) => ({
30381
+ payload: compactToolSchemasInPayload(event.payload)
30382
+ }));
30008
30383
  const eventQueue = new EventQueue();
30009
30384
  let done = false;
30010
30385
  let aborted = false;
@@ -30098,6 +30473,7 @@ var init_agent_subagent_executor = __esm({
30098
30473
  "use strict";
30099
30474
  init_dist();
30100
30475
  init_agent_executor();
30476
+ init_compact_json_schema();
30101
30477
  init_agent_event_transformer();
30102
30478
  init_agent_event_log();
30103
30479
  init_agent_error_utils();
@@ -30363,21 +30739,18 @@ var init_agent_tool = __esm({
30363
30739
  import {
30364
30740
  estimateContextTokens as estimateSessionContextTokens
30365
30741
  } from "@earendil-works/pi-agent-core";
30366
- function serializeToolForEstimate(tool) {
30367
- return JSON.stringify({
30742
+ function toModelToolDefinition(tool) {
30743
+ return {
30368
30744
  name: tool.name,
30369
30745
  description: tool.description,
30370
- parameters: tool.parameters
30371
- });
30746
+ parameters: compactJsonSchemaForModel(tool.parameters ?? {})
30747
+ };
30748
+ }
30749
+ function serializeToolForEstimate(tool) {
30750
+ return JSON.stringify(toModelToolDefinition(tool));
30372
30751
  }
30373
30752
  function serializeToolsForEstimate(tools) {
30374
- return JSON.stringify(
30375
- tools.map((tool) => ({
30376
- name: tool.name,
30377
- description: tool.description,
30378
- parameters: tool.parameters
30379
- }))
30380
- );
30753
+ return JSON.stringify(tools.map((item) => toModelToolDefinition(item)));
30381
30754
  }
30382
30755
  function formatToolDescription(description) {
30383
30756
  if (typeof description === "string") {
@@ -30393,7 +30766,7 @@ function buildToolDefinitionsBreakdown(tools) {
30393
30766
  name: tool.name,
30394
30767
  tokens: estimateTokenCount(serializeToolForEstimate(tool)),
30395
30768
  description: formatToolDescription(tool.description),
30396
- parameters: tool.parameters ?? {}
30769
+ parameters: compactJsonSchemaForModel(tool.parameters ?? {})
30397
30770
  }));
30398
30771
  toolDetails.sort((left, right) => right.tokens - left.tokens);
30399
30772
  const totalTokens = toolDetails.reduce((sum, tool) => sum + tool.tokens, 0);
@@ -30499,14 +30872,14 @@ async function loadToolsForDefinitionEstimate(thread, threadId, messages, metada
30499
30872
  const latestMessage = messages[messages.length - 1];
30500
30873
  const planMode = latestMessage.request.planMode === true;
30501
30874
  const ralphMode = latestMessage.request.ralphMode === true;
30875
+ const db = await getDatabase();
30876
+ const project = await getProject(db, thread.projectId);
30502
30877
  const skillManager2 = new SkillManager();
30503
- const allSkills = await skillManager2.loadSkills(threadPath);
30878
+ const allSkills = await skillManager2.loadSkills(threadPath, project?.path);
30504
30879
  const bundledSkills = await skillManager2.loadBundledSkills();
30505
30880
  const availableSkills = getAvailableSkills(allSkills, thread, bundledSkills);
30506
30881
  const agentsManager = new AgentsManager();
30507
30882
  const agents = await agentsManager.loadAgents(threadPath);
30508
- const db = await getDatabase();
30509
- const project = await getProject(db, thread.projectId);
30510
30883
  const { tools, deferredToolNames } = await createCodingTools(threadPath, {
30511
30884
  skills: availableSkills,
30512
30885
  threadId,
@@ -30614,17 +30987,17 @@ async function computeContextBreakdown(threadId, conversationId, conversationMan
30614
30987
  const latestMessage = messages[messages.length - 1];
30615
30988
  const planMode = latestMessage.request.planMode === true;
30616
30989
  const ralphMode = latestMessage.request.ralphMode === true;
30990
+ const db = await getDatabase();
30991
+ const project = await getProject(db, thread.projectId);
30617
30992
  const skillManager2 = new SkillManager();
30618
- const allSkills = await skillManager2.loadSkills(threadPath);
30993
+ const allSkills = await skillManager2.loadSkills(threadPath, project?.path);
30619
30994
  const bundledSkills = await skillManager2.loadBundledSkills();
30620
30995
  const availableSkills = getAvailableSkills(allSkills, thread, bundledSkills);
30621
30996
  const agentsManager = new AgentsManager();
30622
30997
  const agents = await agentsManager.loadAgents(threadPath);
30623
- const db = await getDatabase();
30624
30998
  const sessionRepo = new LibsqlSessionRepo(db);
30625
30999
  const sessionMetadata = await sessionRepo.findByConversation(threadId, conversationId);
30626
31000
  const session = sessionMetadata ? await sessionRepo.open(sessionMetadata) : void 0;
30627
- const project = await getProject(db, thread.projectId);
30628
31001
  const { tools, deferredToolNames } = await createCodingTools(threadPath, {
30629
31002
  skills: availableSkills,
30630
31003
  threadId,
@@ -30727,6 +31100,7 @@ var init_context_breakdown = __esm({
30727
31100
  init_agents_manager();
30728
31101
  init_models_manager();
30729
31102
  init_agent_session_repo();
31103
+ init_compact_json_schema();
30730
31104
  }
30731
31105
  });
30732
31106
 
@@ -31853,7 +32227,9 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
31853
32227
  }
31854
32228
  let smartAliasModelSettings;
31855
32229
  const skillManager2 = new SkillManager();
31856
- const allSkills = await skillManager2.loadSkills(threadPath);
32230
+ const skillDb = await getDatabase();
32231
+ const skillProject = await getProject(skillDb, thread.projectId);
32232
+ const allSkills = await skillManager2.loadSkills(threadPath, skillProject?.path);
31857
32233
  const bundledSkills = await skillManager2.loadBundledSkills();
31858
32234
  const availableSkills = getAvailableSkills(allSkills, thread, bundledSkills);
31859
32235
  const activatedSkills = await activateSkills(allSkills, userContent, thread, bundledSkills);
@@ -32705,7 +33081,7 @@ function createTasksTool(projectId, threadId, executionDeps) {
32705
33081
  return {
32706
33082
  name: "tasks",
32707
33083
  label: "tasks",
32708
- description: `Tasks: add, schedule (one-off/recurring), list (returns task id, title and status; filter by query/status/tags), get (full details of a single task), tags (project tag vocabulary with counts), update, implement (this chat), runNow (new thread), delete. Store intent + optional skills; load skills only when the task runs. When looking up a task by ticket id or phrase, call list with query to find its id, filter by status/tags to triage the queue, or call get for a task's full details instead of listing everything.`,
33084
+ description: TASKS_TOOL_DESCRIPTION,
32709
33085
  parameters: tasksSchema,
32710
33086
  execute: async (_toolCallId, {
32711
33087
  action,
@@ -32985,7 +33361,7 @@ function createTasksTool(projectId, threadId, executionDeps) {
32985
33361
  }
32986
33362
  };
32987
33363
  }
32988
- var VALID_STATUSES, tasksSchema, tasksTool;
33364
+ var VALID_STATUSES, TASKS_TOOL_DESCRIPTION, tasksSchema, tasksTool;
32989
33365
  var init_tasks = __esm({
32990
33366
  "src/tools/tasks.ts"() {
32991
33367
  "use strict";
@@ -32995,101 +33371,72 @@ var init_tasks = __esm({
32995
33371
  init_task_execution();
32996
33372
  init_task_trigger_validation();
32997
33373
  VALID_STATUSES = ["Ready", "Plan", "Build", "Test", "Review", "Done"];
33374
+ TASKS_TOOL_DESCRIPTION = "Project tasks. add/schedule: title+description required; optional tags, skillRef, trigger. list: id/title/status, hides Done; filter query/status/tags. get: full task. tags: vocabulary+counts. update any field (null clears trigger/skill). implement this chat; runNow new thread (scheduled/event only). delete. Target by id or unique reference. Skills load only at run.";
32998
33375
  tasksSchema = Type16.Object({
32999
- action: Type16.Union(
33000
- [
33001
- Type16.Literal("add"),
33002
- Type16.Literal("schedule"),
33003
- Type16.Literal("list"),
33004
- Type16.Literal("get"),
33005
- Type16.Literal("tags"),
33006
- Type16.Literal("update"),
33007
- Type16.Literal("implement"),
33008
- Type16.Literal("runNow"),
33009
- Type16.Literal("delete")
33010
- ],
33011
- {
33012
- description: "Action: add, schedule, list, get, tags, update, implement, runNow, or delete."
33013
- }
33014
- ),
33015
- description: Type16.Optional(
33016
- Type16.String({
33017
- description: "Task description or future-run prompt. Required for 'add'/'schedule'; optional for 'update'."
33018
- })
33019
- ),
33020
- title: Type16.Optional(
33021
- Type16.String({
33022
- description: "Task title. Required for 'add'/'schedule'; optional for 'update'."
33023
- })
33024
- ),
33376
+ action: Type16.Union([
33377
+ Type16.Literal("add"),
33378
+ Type16.Literal("schedule"),
33379
+ Type16.Literal("list"),
33380
+ Type16.Literal("get"),
33381
+ Type16.Literal("tags"),
33382
+ Type16.Literal("update"),
33383
+ Type16.Literal("implement"),
33384
+ Type16.Literal("runNow"),
33385
+ Type16.Literal("delete")
33386
+ ]),
33387
+ description: Type16.Optional(Type16.String()),
33388
+ title: Type16.Optional(Type16.String()),
33025
33389
  query: Type16.Optional(
33026
33390
  Type16.String({
33027
- description: "For 'list', case-insensitive title/description substring. Prefer this to find a task; combine with 'status'/'tags'. Omit to list all non-Done tasks."
33391
+ description: "list: title/description substring"
33028
33392
  })
33029
33393
  ),
33030
33394
  status: Type16.Optional(
33031
- Type16.Union(
33032
- [
33033
- Type16.Literal("Ready"),
33034
- Type16.Literal("Plan"),
33035
- Type16.Literal("Build"),
33036
- Type16.Literal("Test"),
33037
- Type16.Literal("Review"),
33038
- Type16.Literal("Done")
33039
- ],
33040
- {
33041
- description: "For 'list', filter by status; for 'update', set status. Done tasks are excluded from lists."
33042
- }
33043
- )
33044
- ),
33045
- id: Type16.Optional(
33046
- Type16.String({
33047
- description: "Task ID. Required for 'update'/'get'/'implement'/'runNow'/'delete' unless 'reference' is given."
33048
- })
33395
+ Type16.Union([
33396
+ Type16.Literal("Ready"),
33397
+ Type16.Literal("Plan"),
33398
+ Type16.Literal("Build"),
33399
+ Type16.Literal("Test"),
33400
+ Type16.Literal("Review"),
33401
+ Type16.Literal("Done")
33402
+ ])
33049
33403
  ),
33404
+ id: Type16.Optional(Type16.String()),
33050
33405
  reference: Type16.Optional(
33051
33406
  Type16.String({
33052
- description: "Task name or ticket, e.g. 'oc-2', resolved case-insensitively from title/description. Must match one task. Use 'list'/'query' to browse."
33407
+ description: "unique title/ticket, e.g. oc-2"
33053
33408
  })
33054
33409
  ),
33055
33410
  tags: Type16.Optional(
33056
33411
  Type16.Array(Type16.String(), {
33057
- description: "For 'add'/'schedule'/'update', set tags; for 'list', require all tags. Unknown tags are created automatically. Max 10 tags, 40 chars each."
33412
+ description: "set on write; AND-filter on list; auto-create; max 10 tags, 40 chars"
33058
33413
  })
33059
33414
  ),
33060
33415
  triggerType: Type16.Optional(
33061
33416
  Type16.Union([Type16.Literal("schedule"), Type16.Literal("event"), Type16.Null()], {
33062
- description: "`schedule` for one-shot, `event` for webhook, or `null` to clear."
33417
+ description: "null clears"
33063
33418
  })
33064
33419
  ),
33065
33420
  scheduledAt: Type16.Optional(
33066
33421
  Type16.Union([Type16.String(), Type16.Null()], {
33067
- description: "ISO timestamp for a one-shot schedule. Required for 'schedule' unless `scheduleCron` is set."
33422
+ description: "ISO one-shot"
33068
33423
  })
33069
33424
  ),
33070
33425
  scheduleCron: Type16.Optional(
33071
33426
  Type16.Union([Type16.String(), Type16.Null()], {
33072
- description: "Cron expression for recurring schedules."
33073
- })
33074
- ),
33075
- timezone: Type16.Optional(
33076
- Type16.Union([Type16.String(), Type16.Null()], {
33077
- description: "IANA timezone for schedules."
33078
- })
33079
- ),
33080
- enabled: Type16.Optional(
33081
- Type16.Boolean({
33082
- description: "Whether the automated trigger is active."
33427
+ description: "cron"
33083
33428
  })
33084
33429
  ),
33430
+ timezone: Type16.Optional(Type16.Union([Type16.String(), Type16.Null()])),
33431
+ enabled: Type16.Optional(Type16.Boolean()),
33085
33432
  skillRef: Type16.Optional(
33086
33433
  Type16.Union([Type16.String(), Type16.Null()], {
33087
- description: "Skill to activate: `//skill-name` for bundled or `/skill-name` for project/global."
33434
+ description: "//bundled or /project-or-global"
33088
33435
  })
33089
33436
  ),
33090
33437
  eventKey: Type16.Optional(
33091
33438
  Type16.Union([Type16.String(), Type16.Null()], {
33092
- description: "Webhook event key. Required when `triggerType` is `event`."
33439
+ description: "required for event"
33093
33440
  })
33094
33441
  )
33095
33442
  });
@@ -35933,7 +36280,7 @@ function getProxyCatalogForThread(threadId) {
35933
36280
  (tool) => buildSmartJsToolCatalogEntry({
35934
36281
  name: tool.name,
35935
36282
  description: tool.description,
35936
- parameters: tool.parameters
36283
+ parameters: compactJsonSchemaForModel(tool.parameters)
35937
36284
  })
35938
36285
  ).sort((left, right) => left.name.localeCompare(right.name));
35939
36286
  }
@@ -36291,6 +36638,7 @@ var init_execute_browser_js = __esm({
36291
36638
  "use strict";
36292
36639
  init_dist();
36293
36640
  init_tarsk_debug();
36641
+ init_compact_json_schema();
36294
36642
  init_try_path_from_error();
36295
36643
  init_try_path_from_error();
36296
36644
  runJsPatchSchema = Type20.Object({
@@ -37990,7 +38338,7 @@ async function createProxyToolCatalog(cwd, options) {
37990
38338
  buildSmartJsToolCatalogEntry({
37991
38339
  name: tool.name,
37992
38340
  description: tool.description,
37993
- parameters: tool.parameters
38341
+ parameters: compactJsonSchemaForModel(tool.parameters)
37994
38342
  })
37995
38343
  );
37996
38344
  }
@@ -38078,6 +38426,7 @@ var init_tools = __esm({
38078
38426
  init_tarsk_debug();
38079
38427
  init_tool_registry();
38080
38428
  init_skills_allowed_tools();
38429
+ init_compact_json_schema();
38081
38430
  init_wrap_tool_input_repair();
38082
38431
  init_skill_tool();
38083
38432
  init_skill_reference_tool();
@@ -38974,7 +39323,7 @@ function estimateMicrocompactFixedOverhead(systemPrompt, tools) {
38974
39323
  tools.map((tool) => ({
38975
39324
  name: tool.name,
38976
39325
  description: tool.description,
38977
- parameters: tool.parameters
39326
+ parameters: compactJsonSchemaForModel(tool.parameters)
38978
39327
  }))
38979
39328
  );
38980
39329
  return estimatePaddedTokens(`${systemPrompt}
@@ -39041,6 +39390,20 @@ async function microcompactMessages(messages, options = {}) {
39041
39390
  const agentTurnCount = countAssistantTurns(messages);
39042
39391
  const proactiveTrigger = agentTurnCount >= proactiveTurnThreshold || tokensBefore >= proactivePayloadTokens;
39043
39392
  const nearWindowCeiling = tokensBefore >= effectiveWindow - triggerMargin;
39393
+ const cacheProtectedMessageCount = options.frozenToolCallIds ? messages.filter(
39394
+ (message) => isToolResultMessage2(message) && options.frozenToolCallIds?.has(message.toolCallId) === true
39395
+ ).length : 0;
39396
+ const cacheProtectedTokens = options.frozenToolCallIds ? messages.reduce(
39397
+ (sum, message) => isToolResultMessage2(message) && options.frozenToolCallIds?.has(message.toolCallId) === true ? sum + estimateMessageTokens(message) : sum,
39398
+ 0
39399
+ ) : 0;
39400
+ if (cacheProtectedMessageCount > 0) {
39401
+ log62.debug("Preserving cache-protected prompt prefix", {
39402
+ cacheProtectedMessageCount,
39403
+ cacheProtectedTokens,
39404
+ tokensBefore
39405
+ });
39406
+ }
39044
39407
  if (!nearWindowCeiling && !proactiveTrigger) {
39045
39408
  return {
39046
39409
  messages,
@@ -39138,7 +39501,12 @@ async function microcompactMessages(messages, options = {}) {
39138
39501
  applied: clearedCount > 0
39139
39502
  };
39140
39503
  if (stats.applied) {
39141
- log62.info("Applied micro-compaction", stats);
39504
+ log62.info("Applied micro-compaction", {
39505
+ ...stats,
39506
+ cacheProtectedMessageCount,
39507
+ cacheProtectedTokens,
39508
+ cacheRewriteAvoided: cacheProtectedMessageCount > 0
39509
+ });
39142
39510
  }
39143
39511
  return { messages: next, stats };
39144
39512
  }
@@ -39247,55 +39615,48 @@ var init_microcompact2 = __esm({
39247
39615
  init_observations_config();
39248
39616
  init_microcompact_config();
39249
39617
  init_microcompact_prefix_freeze();
39618
+ init_compact_json_schema();
39250
39619
  log62 = createLogger("microcompact");
39251
39620
  OBSERVATION_ID_IN_TEXT = /obs_[a-f0-9]{64}/;
39252
39621
  TODO_DUPLICATE_OMITTED_PREFIX = "[Duplicate todo result omitted; identical to toolCallId=";
39253
39622
  }
39254
39623
  });
39255
39624
 
39256
- // src/agent/agent-harness.adapters.ts
39257
- function createHarnessContextHandler(transform) {
39258
- return async function handleContext(event) {
39259
- const messages = await transform(event.messages.slice());
39260
- return { messages };
39261
- };
39262
- }
39263
- function adaptHarnessToolResult(event) {
39264
- return {
39265
- toolCall: {
39266
- type: "toolCall",
39267
- id: event.toolCallId,
39268
- name: event.toolName,
39269
- arguments: event.input
39270
- },
39271
- result: {
39272
- content: event.content,
39273
- details: event.details,
39274
- usage: event.usage
39275
- },
39276
- isError: event.isError
39277
- };
39625
+ // src/agent/prompt-cache-fingerprint.ts
39626
+ import { createHash as createHash7 } from "node:crypto";
39627
+ function readNumber(value) {
39628
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
39278
39629
  }
39279
- function createHarnessToolResultHandler(consumers) {
39280
- return async function handleToolResult(event) {
39281
- const adaptedEvent = adaptHarnessToolResult(event);
39282
- for (const consumer of consumers) {
39283
- const result = await consumer(adaptedEvent);
39284
- if (result) {
39285
- return result;
39286
- }
39630
+ function findUsage(value, depth = 0) {
39631
+ if (depth > 4 || !value || typeof value !== "object") {
39632
+ return void 0;
39633
+ }
39634
+ if (Array.isArray(value)) {
39635
+ for (const item of value) {
39636
+ const result = findUsage(item, depth + 1);
39637
+ if (result) return result;
39287
39638
  }
39288
39639
  return void 0;
39289
- };
39290
- }
39291
- var init_agent_harness_adapters = __esm({
39292
- "src/agent/agent-harness.adapters.ts"() {
39293
- "use strict";
39294
39640
  }
39295
- });
39296
-
39297
- // src/agent/prompt-cache-fingerprint.ts
39298
- import { createHash as createHash7 } from "node:crypto";
39641
+ const record = value;
39642
+ if ("input" in record || "inputTokens" in record || "cacheRead" in record || "cacheReadTokens" in record || "cacheWrite" in record || "cacheWriteTokens" in record) {
39643
+ return record;
39644
+ }
39645
+ for (const nested of Object.values(record)) {
39646
+ const result = findUsage(nested, depth + 1);
39647
+ if (result) return result;
39648
+ }
39649
+ return void 0;
39650
+ }
39651
+ function usageTotalsFromEvent(event) {
39652
+ const usage = findUsage(event);
39653
+ if (!usage) return void 0;
39654
+ const inputTokens = readNumber(usage.inputTokens ?? usage.input);
39655
+ const cacheReadTokens = readNumber(usage.cacheReadTokens ?? usage.cacheRead);
39656
+ const cacheWriteTokens = readNumber(usage.cacheWriteTokens ?? usage.cacheWrite);
39657
+ if (inputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) return void 0;
39658
+ return { inputTokens, cacheReadTokens, cacheWriteTokens };
39659
+ }
39299
39660
  function canonicalize(value) {
39300
39661
  if (Array.isArray(value)) {
39301
39662
  return value.map((item) => canonicalize(item));
@@ -39335,6 +39696,52 @@ function extractPayloadSections(payload) {
39335
39696
  function hashMessages(messages) {
39336
39697
  return Array.isArray(messages) ? messages.map((message) => hash(message)) : [];
39337
39698
  }
39699
+ function isOpenAIPromptCacheProvider(provider) {
39700
+ return typeof provider === "string" && provider.includes("openai");
39701
+ }
39702
+ function extractPromptCacheKey(payload) {
39703
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
39704
+ return void 0;
39705
+ }
39706
+ const key = payload.prompt_cache_key;
39707
+ return typeof key === "string" && key.length > 0 ? key : void 0;
39708
+ }
39709
+ function createStablePromptCacheKey(model, payload, provider) {
39710
+ const sections = extractPayloadSections(payload);
39711
+ const digest = hash({
39712
+ provider: provider ?? "",
39713
+ model,
39714
+ system: sections.system,
39715
+ tools: sections.tools
39716
+ });
39717
+ const key = `tarsk-${digest}`;
39718
+ return key.length <= OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH ? key : key.slice(0, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH);
39719
+ }
39720
+ function applyStablePromptCacheKey(payload, model, provider) {
39721
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
39722
+ return payload;
39723
+ }
39724
+ const record = payload;
39725
+ if (!("prompt_cache_key" in record) && !isOpenAIPromptCacheProvider(provider)) {
39726
+ return payload;
39727
+ }
39728
+ return {
39729
+ ...record,
39730
+ prompt_cache_key: createStablePromptCacheKey(model, payload, provider)
39731
+ };
39732
+ }
39733
+ function promptCacheTurnMetrics(usage, firstTurn) {
39734
+ const billablePromptTokens2 = billablePromptTokensFromCounts(
39735
+ usage.inputTokens,
39736
+ usage.cacheReadTokens,
39737
+ usage.cacheWriteTokens
39738
+ );
39739
+ return {
39740
+ billablePromptTokens: billablePromptTokens2,
39741
+ cacheHitRate: calculatePromptCacheHitRate(usage.cacheReadTokens, billablePromptTokens2),
39742
+ firstTurnMiss: firstTurn && usage.cacheReadTokens === 0
39743
+ };
39744
+ }
39338
39745
  function comparePromptCacheFingerprints(previous, next) {
39339
39746
  return prefixDiffFromHashes({
39340
39747
  toolsChanged: previous.tools !== next.tools,
@@ -39368,10 +39775,71 @@ function createPromptCacheFingerprint(payload) {
39368
39775
  messageHashes: hashMessages(sections.messages)
39369
39776
  };
39370
39777
  }
39778
+ function recordPromptCacheUsage(sessionId, event) {
39779
+ const usage = usageTotalsFromEvent(event);
39780
+ if (!usage) return;
39781
+ const totals = usageBySession.get(sessionId) ?? {
39782
+ calls: 0,
39783
+ inputTokens: 0,
39784
+ cacheReadTokens: 0,
39785
+ cacheWriteTokens: 0
39786
+ };
39787
+ totals.calls += 1;
39788
+ totals.inputTokens += usage.inputTokens;
39789
+ totals.cacheReadTokens += usage.cacheReadTokens;
39790
+ totals.cacheWriteTokens += usage.cacheWriteTokens;
39791
+ usageBySession.set(sessionId, totals);
39792
+ const last = lastFingerprintBySession.get(sessionId);
39793
+ const metrics = promptCacheTurnMetrics(usage, last?.firstTurn === true);
39794
+ if (last?.firstTurn) {
39795
+ lastFingerprintBySession.set(sessionId, { ...last, firstTurn: false });
39796
+ }
39797
+ log63.debug("Prompt cache usage", {
39798
+ sessionId,
39799
+ ...usage,
39800
+ ...metrics,
39801
+ promptCacheKey: last?.promptCacheKey,
39802
+ system: last?.fingerprint.system,
39803
+ tools: last?.fingerprint.tools,
39804
+ messages: last?.fingerprint.messages,
39805
+ payloadBytes: last?.fingerprint.payloadBytes,
39806
+ messageCount: last?.fingerprint.messageHashes.length
39807
+ });
39808
+ }
39809
+ function logPromptCacheDiagnostics(sessionId) {
39810
+ const usage = usageBySession.get(sessionId);
39811
+ const last = lastFingerprintBySession.get(sessionId);
39812
+ if (!usage && !last) return void 0;
39813
+ const totals = usage ?? { calls: 0, inputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
39814
+ const fingerprint = last?.fingerprint;
39815
+ const billablePromptTokens2 = billablePromptTokensFromCounts(
39816
+ totals.inputTokens,
39817
+ totals.cacheReadTokens,
39818
+ totals.cacheWriteTokens
39819
+ );
39820
+ const diagnostics = {
39821
+ sessionId,
39822
+ calls: totals.calls,
39823
+ inputTokens: totals.inputTokens,
39824
+ cacheReadTokens: totals.cacheReadTokens,
39825
+ cacheWriteTokens: totals.cacheWriteTokens,
39826
+ billablePromptTokens: billablePromptTokens2,
39827
+ cacheHitRate: calculatePromptCacheHitRate(totals.cacheReadTokens, billablePromptTokens2),
39828
+ promptCacheKey: last?.promptCacheKey,
39829
+ finalPayloadBytes: fingerprint?.payloadBytes ?? 0,
39830
+ finalMessageCount: fingerprint?.messageHashes.length ?? 0
39831
+ };
39832
+ log63.info("Prompt cache session diagnostics", diagnostics);
39833
+ usageBySession.delete(sessionId);
39834
+ lastFingerprintBySession.delete(sessionId);
39835
+ return diagnostics;
39836
+ }
39371
39837
  function recordPromptCacheFingerprint(sessionId, provider, model, payload) {
39372
39838
  const fingerprint = createPromptCacheFingerprint(payload);
39373
39839
  const key = `${provider}:${model}:${sessionId}`;
39374
39840
  const previous = previousBySession.get(key);
39841
+ const promptCacheKey = extractPromptCacheKey(payload);
39842
+ const firstTurn = previous === void 0;
39375
39843
  previousBySession.delete(key);
39376
39844
  previousBySession.set(key, fingerprint);
39377
39845
  if (previousBySession.size > MAX_TRACKED_SESSIONS2) {
@@ -39380,11 +39848,14 @@ function recordPromptCacheFingerprint(sessionId, provider, model, payload) {
39380
39848
  previousBySession.delete(oldest);
39381
39849
  }
39382
39850
  }
39851
+ lastFingerprintBySession.set(sessionId, { fingerprint, promptCacheKey, firstTurn });
39383
39852
  const prefix = previous ? comparePromptCacheFingerprints(previous, fingerprint) : void 0;
39384
39853
  log63.debug("Prompt cache fingerprint", {
39385
39854
  sessionId,
39386
39855
  provider,
39387
39856
  model,
39857
+ promptCacheKey,
39858
+ firstTurn,
39388
39859
  full: fingerprint.full,
39389
39860
  system: fingerprint.system,
39390
39861
  tools: fingerprint.tools,
@@ -39403,7 +39874,7 @@ function recordPromptCacheFingerprint(sessionId, provider, model, payload) {
39403
39874
  });
39404
39875
  return fingerprint;
39405
39876
  }
39406
- var log63, MAX_TRACKED_SESSIONS2, previousBySession;
39877
+ var log63, MAX_TRACKED_SESSIONS2, previousBySession, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH, usageBySession, lastFingerprintBySession;
39407
39878
  var init_prompt_cache_fingerprint = __esm({
39408
39879
  "src/agent/prompt-cache-fingerprint.ts"() {
39409
39880
  "use strict";
@@ -39411,6 +39882,50 @@ var init_prompt_cache_fingerprint = __esm({
39411
39882
  log63 = createLogger("agent.prompt-cache");
39412
39883
  MAX_TRACKED_SESSIONS2 = 500;
39413
39884
  previousBySession = /* @__PURE__ */ new Map();
39885
+ OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
39886
+ usageBySession = /* @__PURE__ */ new Map();
39887
+ lastFingerprintBySession = /* @__PURE__ */ new Map();
39888
+ }
39889
+ });
39890
+
39891
+ // src/agent/agent-harness.adapters.ts
39892
+ function createHarnessContextHandler(transform) {
39893
+ return async function handleContext(event) {
39894
+ const messages = await transform(event.messages.slice());
39895
+ return { messages };
39896
+ };
39897
+ }
39898
+ function adaptHarnessToolResult(event) {
39899
+ return {
39900
+ toolCall: {
39901
+ type: "toolCall",
39902
+ id: event.toolCallId,
39903
+ name: event.toolName,
39904
+ arguments: event.input
39905
+ },
39906
+ result: {
39907
+ content: event.content,
39908
+ details: event.details,
39909
+ usage: event.usage
39910
+ },
39911
+ isError: event.isError
39912
+ };
39913
+ }
39914
+ function createHarnessToolResultHandler(consumers) {
39915
+ return async function handleToolResult(event) {
39916
+ const adaptedEvent = adaptHarnessToolResult(event);
39917
+ for (const consumer of consumers) {
39918
+ const result = await consumer(adaptedEvent);
39919
+ if (result) {
39920
+ return result;
39921
+ }
39922
+ }
39923
+ return void 0;
39924
+ };
39925
+ }
39926
+ var init_agent_harness_adapters = __esm({
39927
+ "src/agent/agent-harness.adapters.ts"() {
39928
+ "use strict";
39414
39929
  }
39415
39930
  });
39416
39931
 
@@ -39605,6 +40120,7 @@ var init_agent_executor = __esm({
39605
40120
  init_load_coding_tools();
39606
40121
  init_tool_registry();
39607
40122
  init_agent_tool();
40123
+ init_compact_json_schema();
39608
40124
  init_tools();
39609
40125
  init_database();
39610
40126
  init_paths();
@@ -39621,13 +40137,13 @@ var init_agent_executor = __esm({
39621
40137
  init_user_message_prompt();
39622
40138
  init_observations_config();
39623
40139
  init_microcompact2();
40140
+ init_prompt_cache_fingerprint();
39624
40141
  init_microcompact_config();
39625
40142
  init_agent_harness_adapters();
39626
40143
  init_agent_harness_runtime();
39627
40144
  init_agent_session_repo();
39628
40145
  init_agent_session_lock();
39629
40146
  init_conversations_database();
39630
- init_prompt_cache_fingerprint();
39631
40147
  init_conversation_prompt_snapshot();
39632
40148
  log64 = createLogger("agent.executor");
39633
40149
  toolCallFailureLog = createLogger("agent.executor.toolcall.failure");
@@ -39945,13 +40461,18 @@ var init_agent_executor = __esm({
39945
40461
  );
39946
40462
  }
39947
40463
  harness.on("before_provider_payload", (event) => {
40464
+ const payload = applyStablePromptCacheKey(
40465
+ compactToolSchemasInPayload(event.payload),
40466
+ event.model.id,
40467
+ event.model.provider
40468
+ );
39948
40469
  recordPromptCacheFingerprint(
39949
40470
  sessionMetadata.id,
39950
40471
  event.model.provider,
39951
40472
  event.model.id,
39952
- event.payload
40473
+ payload
39953
40474
  );
39954
- return void 0;
40475
+ return { payload };
39955
40476
  });
39956
40477
  harness.on("tool_result", async (event) => {
39957
40478
  const observationOverride = await observationHandler(event);
@@ -40005,6 +40526,9 @@ var init_agent_executor = __esm({
40005
40526
  if (!isPiAgentEvent(event)) {
40006
40527
  return;
40007
40528
  }
40529
+ if (event.type === "message_end") {
40530
+ recordPromptCacheUsage(sessionMetadata.id, event);
40531
+ }
40008
40532
  logPiEvent(event);
40009
40533
  eventQueue.handlePiEvent(event);
40010
40534
  });
@@ -40091,6 +40615,7 @@ ${finalPrompt}`;
40091
40615
  if (assistantMessage.stopReason === "error") {
40092
40616
  throw new Error(assistantMessage.errorMessage ?? "Agent prompt failed");
40093
40617
  }
40618
+ logPromptCacheDiagnostics(sessionMetadata.id);
40094
40619
  log64.info("Agent prompt completed successfully");
40095
40620
  done = true;
40096
40621
  eventQueue.notify();
@@ -44174,6 +44699,7 @@ async function handleListProjects(c, projectManager, threadManager, db) {
44174
44699
  disabledTools: project.disabledTools ?? [],
44175
44700
  smartJsEnabled: project.smartJsEnabled === true,
44176
44701
  useBuiltInBrowser: project.useBuiltInBrowser !== false,
44702
+ projectIcon: project.projectIcon,
44177
44703
  tasksModifiedCount: project.tasksModifiedCount ?? 0,
44178
44704
  threads: threads.map((thread) => {
44179
44705
  const cached = statusCache.get(thread.id);
@@ -44536,7 +45062,8 @@ async function handleUpdateProject(c, projectManager, metadataManager) {
44536
45062
  lastSelectedModelProvider,
44537
45063
  disabledTools,
44538
45064
  smartJsEnabled,
44539
- useBuiltInBrowser
45065
+ useBuiltInBrowser,
45066
+ projectIcon
44540
45067
  } = body;
44541
45068
  const resolved = await requireProject(c, projectManager);
44542
45069
  if (!resolved.ok) {
@@ -44646,6 +45173,17 @@ async function handleUpdateProject(c, projectManager, metadataManager) {
44646
45173
  if (useBuiltInBrowser !== void 0) {
44647
45174
  await projectManager.updateBuiltInBrowserSetting(projectId, useBuiltInBrowser === true);
44648
45175
  }
45176
+ if (projectIcon !== void 0) {
45177
+ if (projectIcon !== null && typeof projectIcon !== "string") {
45178
+ return errorResponse(
45179
+ c,
45180
+ ErrorCodes.INVALID_REQUEST,
45181
+ "Project icon must be a PNG data URL or null",
45182
+ 400
45183
+ );
45184
+ }
45185
+ await projectManager.updateProjectIcon(projectId, projectIcon);
45186
+ }
44649
45187
  if (name !== void 0) {
44650
45188
  if (!name?.trim()) {
44651
45189
  return errorResponse(c, ErrorCodes.INVALID_REQUEST, "Project name is required", 400);
@@ -45888,7 +46426,7 @@ var init_plugin_export_ops = __esm({
45888
46426
  // src/features/plugin-export/plugin-import.ops.ts
45889
46427
  import AdmZip2 from "adm-zip";
45890
46428
  import { lookup } from "dns/promises";
45891
- import { access as access4, mkdtemp, mkdir as mkdir4, rename as rename2, rm as rm2, writeFile as writeFile5 } from "fs/promises";
46429
+ import { access as access4, cp, mkdtemp, mkdir as mkdir4, readdir as readdir8, rename as rename2, rm as rm2, stat as stat4, writeFile as writeFile5 } from "fs/promises";
45892
46430
  import { isIP } from "net";
45893
46431
  import { dirname as dirname11, join as join30, normalize as normalize3, relative as relative4, sep as sep3 } from "path";
45894
46432
  function safeArchivePath(path7) {
@@ -46011,7 +46549,39 @@ async function previewPluginImport(projectPath, archive) {
46011
46549
  }))
46012
46550
  };
46013
46551
  }
46014
- async function installPluginImport(projectPath, archive, selectedSkills, selectedMcpServers, replace) {
46552
+ async function sweepStalePluginStagingDirs(projectPath, minimumAgeMs = STALE_STAGING_MIN_AGE_MS) {
46553
+ const entries = await readdir8(projectPath, { withFileTypes: true }).catch(() => []);
46554
+ const now = Date.now();
46555
+ await Promise.all(
46556
+ entries.filter(
46557
+ (entry) => entry.isDirectory() && entry.name.startsWith(STAGING_PREFIX) && entry.name.length > STAGING_PREFIX.length
46558
+ ).map(async (entry) => {
46559
+ const dirPath = join30(projectPath, entry.name);
46560
+ try {
46561
+ const info = await stat4(dirPath);
46562
+ const age = Math.max(0, now - info.mtimeMs);
46563
+ if (age < minimumAgeMs) return;
46564
+ await rm2(dirPath, { recursive: true, force: true });
46565
+ log77.warn("Removed stale plugin import staging directory", { path: dirPath });
46566
+ } catch (error) {
46567
+ log77.warn("Failed to remove stale plugin import staging directory", {
46568
+ path: dirPath,
46569
+ error: error instanceof Error ? error.message : String(error)
46570
+ });
46571
+ }
46572
+ })
46573
+ );
46574
+ }
46575
+ async function moveStagedSkill(stagedPath, target) {
46576
+ try {
46577
+ await rename2(stagedPath, target);
46578
+ } catch (error) {
46579
+ if (error.code !== "EXDEV") throw error;
46580
+ await cp(stagedPath, target, { recursive: true });
46581
+ await rm2(stagedPath, { recursive: true, force: true });
46582
+ }
46583
+ }
46584
+ async function installPluginImport(projectPath, archive, selectedSkills, selectedMcpServers, replace, threadPaths = []) {
46015
46585
  const preview = await previewPluginImport(projectPath, archive);
46016
46586
  const skillNames = new Set(preview.skills.map(({ name }) => name));
46017
46587
  const mcpNames = new Set(preview.mcpServers.map(({ name }) => name));
@@ -46019,8 +46589,9 @@ async function installPluginImport(projectPath, archive, selectedSkills, selecte
46019
46589
  throw new Error("Selected skill is not present in the plugin archive");
46020
46590
  if (selectedMcpServers.some((name) => !mcpNames.has(name)))
46021
46591
  throw new Error("Selected MCP server is not present in the plugin archive");
46592
+ await sweepStalePluginStagingDirs(projectPath);
46022
46593
  const zip = new AdmZip2(archive);
46023
- const stagingRoot = await mkdtemp(join30(projectPath, ".plugin-import-"));
46594
+ const stagingRoot = await mkdtemp(join30(projectPath, STAGING_PREFIX));
46024
46595
  try {
46025
46596
  const stagedSkills = [];
46026
46597
  for (const skill of preview.skills.filter(({ name }) => selectedSkills.includes(name))) {
@@ -46051,29 +46622,51 @@ async function installPluginImport(projectPath, archive, selectedSkills, selecte
46051
46622
  for (const skill of stagedSkills) {
46052
46623
  const target = join30(agentsRoot, "skills", skill.name);
46053
46624
  if (skill.conflict) await rm2(target, { recursive: true, force: true });
46054
- await rename2(skill.path, target);
46625
+ await moveStagedSkill(skill.path, target);
46626
+ for (const threadPath of new Set(threadPaths)) {
46627
+ if (threadPath === projectPath) continue;
46628
+ const threadTarget = join30(threadPath, ".agents", "skills", skill.name);
46629
+ let threadExists = true;
46630
+ try {
46631
+ await access4(threadTarget);
46632
+ } catch {
46633
+ threadExists = false;
46634
+ }
46635
+ if (threadExists && !replace) continue;
46636
+ if (threadExists) await rm2(threadTarget, { recursive: true, force: true });
46637
+ await mkdir4(dirname11(threadTarget), { recursive: true });
46638
+ await cp(target, threadTarget, { recursive: true });
46639
+ }
46055
46640
  }
46056
46641
  if (selectedMcpServers.length) {
46057
46642
  const configPath = join30(agentsRoot, "mcp.json");
46058
46643
  const stagedConfigPath = join30(stagingRoot, "mcp.json");
46059
46644
  await writeFile5(stagedConfigPath, JSON.stringify({ mcpServers: current }, null, 2) + "\n");
46060
- await rm2(configPath, { force: true });
46061
46645
  await rename2(stagedConfigPath, configPath);
46062
46646
  }
46063
46647
  } finally {
46064
- await rm2(stagingRoot, { recursive: true, force: true });
46648
+ await rm2(stagingRoot, { recursive: true, force: true }).catch((error) => {
46649
+ log77.warn("Failed to remove plugin import staging directory", {
46650
+ path: stagingRoot,
46651
+ error: error instanceof Error ? error.message : String(error)
46652
+ });
46653
+ });
46065
46654
  }
46066
46655
  }
46067
- var MAX_ARCHIVE_BYTES, MAX_FILES, MAX_FILE_BYTES, MAX_UNCOMPRESSED_BYTES, PLUGIN_FETCH_TIMEOUT_MS;
46656
+ var log77, MAX_ARCHIVE_BYTES, MAX_FILES, MAX_FILE_BYTES, MAX_UNCOMPRESSED_BYTES, PLUGIN_FETCH_TIMEOUT_MS, STAGING_PREFIX, STALE_STAGING_MIN_AGE_MS;
46068
46657
  var init_plugin_import_ops = __esm({
46069
46658
  "src/features/plugin-export/plugin-import.ops.ts"() {
46070
46659
  "use strict";
46660
+ init_dist();
46071
46661
  init_mcp_config();
46662
+ log77 = createLogger("features.plugin-export.plugin-import");
46072
46663
  MAX_ARCHIVE_BYTES = 25 * 1024 * 1024;
46073
46664
  MAX_FILES = 500;
46074
46665
  MAX_FILE_BYTES = 2 * 1024 * 1024;
46075
46666
  MAX_UNCOMPRESSED_BYTES = 100 * 1024 * 1024;
46076
46667
  PLUGIN_FETCH_TIMEOUT_MS = 15e3;
46668
+ STAGING_PREFIX = ".plugin-import-";
46669
+ STALE_STAGING_MIN_AGE_MS = 5e3;
46077
46670
  }
46078
46671
  });
46079
46672
 
@@ -46116,7 +46709,7 @@ async function readAuthToken() {
46116
46709
  const session = await getState(db, KEY_SESSION);
46117
46710
  return typeof session?.token === "string" && session.token.length > 0 ? session.token : null;
46118
46711
  } catch (error) {
46119
- log77.error("[plugin] Failed to read auth session:", error);
46712
+ log78.error("[plugin] Failed to read auth session:", error);
46120
46713
  return null;
46121
46714
  }
46122
46715
  }
@@ -46135,7 +46728,7 @@ async function handlePluginExport(c, projectManager) {
46135
46728
  manifest
46136
46729
  });
46137
46730
  const filename = `${normalizePluginName(manifest.name)}.zip`;
46138
- log77.info(`[plugin] Exporting ${filename}`);
46731
+ log78.info(`[plugin] Exporting ${filename}`);
46139
46732
  const encoded = encodeURIComponent(filename);
46140
46733
  return new Response(new Uint8Array(zipBuffer), {
46141
46734
  status: 200,
@@ -46147,7 +46740,7 @@ async function handlePluginExport(c, projectManager) {
46147
46740
  });
46148
46741
  } catch (error) {
46149
46742
  const message = error instanceof Error ? error.message : String(error);
46150
- log77.error("[plugin] Failed to export plugin:", error);
46743
+ log78.error("[plugin] Failed to export plugin:", error);
46151
46744
  return errorResponse(c, ErrorCodes.INTERNAL_ERROR, `Failed to export plugin: ${message}`, 500);
46152
46745
  }
46153
46746
  }
@@ -46165,7 +46758,7 @@ async function handlePluginImportPreview(c, projectManager) {
46165
46758
  return errorResponse(c, ErrorCodes.INVALID_REQUEST, message, 400);
46166
46759
  }
46167
46760
  }
46168
- async function handlePluginImport(c, projectManager) {
46761
+ async function handlePluginImport(c, projectManager, threadManager) {
46169
46762
  try {
46170
46763
  const resolved = await requireProject(c, projectManager);
46171
46764
  if (!resolved.ok) return resolved.response;
@@ -46173,12 +46766,14 @@ async function handlePluginImport(c, projectManager) {
46173
46766
  if (typeof body.url !== "string")
46174
46767
  return errorResponse(c, ErrorCodes.INVALID_REQUEST, "url is required", 400);
46175
46768
  const archive = await fetchPluginArchive(body.url);
46769
+ const threads = await threadManager.listThreads(resolved.project.id);
46176
46770
  await installPluginImport(
46177
46771
  resolved.project.path,
46178
46772
  archive,
46179
46773
  Array.isArray(body.skills) ? body.skills.filter((item) => typeof item === "string") : [],
46180
46774
  Array.isArray(body.mcpServers) ? body.mcpServers.filter((item) => typeof item === "string") : [],
46181
- body.replace === true
46775
+ body.replace === true,
46776
+ threads.map((thread) => thread.path)
46182
46777
  );
46183
46778
  return c.json({ success: true });
46184
46779
  } catch (error) {
@@ -46206,7 +46801,7 @@ async function handlePluginUpload(c, projectManager) {
46206
46801
  });
46207
46802
  const filename = `${normalizePluginName(manifest.name)}.zip`;
46208
46803
  const result = await uploadPluginZip(zipBuffer, token);
46209
- log77.info(
46804
+ log78.info(
46210
46805
  `[plugin] Uploaded ${filename}: valid=${result.valid}, errors=${result.errors.length}, warnings=${result.warnings.length}`
46211
46806
  );
46212
46807
  return c.json({
@@ -46218,16 +46813,16 @@ async function handlePluginUpload(c, projectManager) {
46218
46813
  });
46219
46814
  } catch (error) {
46220
46815
  if (error instanceof PluginUploadError) {
46221
- log77.error(`[plugin] Upload failed (${error.status}):`, error.message);
46816
+ log78.error(`[plugin] Upload failed (${error.status}):`, error.message);
46222
46817
  const status = error.status >= 400 && error.status < 500 ? error.status : 502;
46223
46818
  return errorResponse(c, ErrorCodes.INTERNAL_ERROR, error.message, status);
46224
46819
  }
46225
46820
  const message = error instanceof Error ? error.message : String(error);
46226
- log77.error("[plugin] Failed to upload plugin:", error);
46821
+ log78.error("[plugin] Failed to upload plugin:", error);
46227
46822
  return errorResponse(c, ErrorCodes.INTERNAL_ERROR, `Failed to upload plugin: ${message}`, 500);
46228
46823
  }
46229
46824
  }
46230
- var log77, KEY_SESSION;
46825
+ var log78, KEY_SESSION;
46231
46826
  var init_plugin_export_routes = __esm({
46232
46827
  "src/features/plugin-export/plugin-export.routes.ts"() {
46233
46828
  "use strict";
@@ -46239,7 +46834,7 @@ var init_plugin_export_routes = __esm({
46239
46834
  init_database_state();
46240
46835
  init_plugin_export_ops();
46241
46836
  init_plugin_import_ops();
46242
- log77 = createLogger("features.plugin-export.route");
46837
+ log78 = createLogger("features.plugin-export.route");
46243
46838
  KEY_SESSION = "AuthSession";
46244
46839
  }
46245
46840
  });
@@ -46641,7 +47236,7 @@ function createProjectRoutes(projectManager, threadManager, metadataManager, upg
46641
47236
  return handlePluginImportPreview(c, projectManager);
46642
47237
  });
46643
47238
  router.post("/:id/plugin-import", async (c) => {
46644
- return handlePluginImport(c, projectManager);
47239
+ return handlePluginImport(c, projectManager, threadManager);
46645
47240
  });
46646
47241
  router.post("/:id/plugin-upload", async (c) => {
46647
47242
  return handlePluginUpload(c, projectManager);
@@ -47659,7 +48254,7 @@ var init_scaffold_templates = __esm({
47659
48254
  // src/features/scaffold/scaffold.runner.ts
47660
48255
  import { existsSync as existsSync21 } from "fs";
47661
48256
  import { join as join31 } from "path";
47662
- import { mkdir as mkdir5, readdir as readdir8, stat as stat4, rename as rename3, rm as rm3, writeFile as writeFile6 } from "fs/promises";
48257
+ import { mkdir as mkdir5, readdir as readdir9, stat as stat5, rename as rename3, rm as rm3, writeFile as writeFile6 } from "fs/promises";
47663
48258
  import { createInterface } from "readline";
47664
48259
  function registerScaffoldHandler(handlerName, callback) {
47665
48260
  HANDLERS.set(handlerName, callback);
@@ -47927,13 +48522,13 @@ async function* createScaffoldedProjectStreaming(options) {
47927
48522
  const projectName2 = getProjectName(options.projectName);
47928
48523
  const projectSubDir = join31(projectPath, projectName2);
47929
48524
  try {
47930
- const subDirStat = await stat4(projectSubDir);
48525
+ const subDirStat = await stat5(projectSubDir);
47931
48526
  if (subDirStat.isDirectory()) {
47932
48527
  yield {
47933
48528
  type: "progress",
47934
48529
  message: `Moving project files from ${projectName2}/ to thread folder...`
47935
48530
  };
47936
- const items = await readdir8(projectSubDir);
48531
+ const items = await readdir9(projectSubDir);
47937
48532
  for (const item of items) {
47938
48533
  const oldPath = join31(projectSubDir, item);
47939
48534
  const newPath = join31(projectPath, item);
@@ -47995,7 +48590,7 @@ async function* createScaffoldedProjectStreaming(options) {
47995
48590
  function getScaffoldTemplates() {
47996
48591
  return loadCatalog();
47997
48592
  }
47998
- var log78, HANDLERS;
48593
+ var log79, HANDLERS;
47999
48594
  var init_scaffold_runner = __esm({
48000
48595
  "src/features/scaffold/scaffold.runner.ts"() {
48001
48596
  "use strict";
@@ -48003,7 +48598,7 @@ var init_scaffold_runner = __esm({
48003
48598
  init_shell();
48004
48599
  init_dist();
48005
48600
  init_scaffold_templates();
48006
- log78 = createLogger("features.scaffold.scaffold.runner");
48601
+ log79 = createLogger("features.scaffold.scaffold.runner");
48007
48602
  HANDLERS = /* @__PURE__ */ new Map();
48008
48603
  registerScaffoldHandler("capacitor-plugin", capacitorPluginHandler);
48009
48604
  }
@@ -48019,9 +48614,9 @@ var init_random_words = __esm({
48019
48614
 
48020
48615
  // src/features/projects/projects.creator.ts
48021
48616
  import { randomUUID as randomUUID14 } from "crypto";
48022
- import { basename as basename4, join as join32, relative as relative5, resolve as resolvePath2 } from "path";
48617
+ import { basename as basename4, join as join32, relative as relative5, resolve as resolvePath3 } from "path";
48023
48618
  import { realpathSync as realpathSync2 } from "fs";
48024
- import { access as access5, mkdir as mkdir6, rm as rm4, stat as stat5, readdir as readdir9 } from "fs/promises";
48619
+ import { access as access5, mkdir as mkdir6, rm as rm4, stat as stat6, readdir as readdir10 } from "fs/promises";
48025
48620
  async function loadUtils() {
48026
48621
  if (utilsLoaded) return;
48027
48622
  utilsLoaded = true;
@@ -48032,7 +48627,7 @@ async function loadUtils() {
48032
48627
  }
48033
48628
  }
48034
48629
  function normalizeFolderPath(folderPath) {
48035
- const resolved = resolvePath2(folderPath);
48630
+ const resolved = resolvePath3(folderPath);
48036
48631
  try {
48037
48632
  return realpathSync2(resolved);
48038
48633
  } catch {
@@ -48445,7 +49040,7 @@ var init_projects_creator = __esm({
48445
49040
  }
48446
49041
  async folderExists(folderPath) {
48447
49042
  try {
48448
- await stat5(folderPath);
49043
+ await stat6(folderPath);
48449
49044
  return true;
48450
49045
  } catch {
48451
49046
  return false;
@@ -48484,7 +49079,7 @@ var init_projects_creator = __esm({
48484
49079
  async *createProjectFromPath(folderPath) {
48485
49080
  const normalizedPath = normalizeFolderPath(folderPath);
48486
49081
  try {
48487
- const stats = await stat5(normalizedPath);
49082
+ const stats = await stat6(normalizedPath);
48488
49083
  if (!stats.isDirectory()) {
48489
49084
  yield { type: "error", message: `Selected path is not a directory: ${normalizedPath}` };
48490
49085
  return;
@@ -48660,11 +49255,11 @@ var init_projects_creator = __esm({
48660
49255
  } catch {
48661
49256
  }
48662
49257
  try {
48663
- const entries = await readdir9(currentPath);
49258
+ const entries = await readdir10(currentPath);
48664
49259
  for (const entry of entries) {
48665
49260
  const entryPath = join32(currentPath, entry);
48666
49261
  try {
48667
- const stats = await stat5(entryPath);
49262
+ const stats = await stat6(entryPath);
48668
49263
  if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
48669
49264
  await this.findAllPackageJsonFiles(rootPath, entryPath, setupScripts);
48670
49265
  }
@@ -48768,7 +49363,7 @@ var init_terminal_session_manager = __esm({
48768
49363
  import { join as join33 } from "path";
48769
49364
  import { realpathSync as realpathSync4 } from "fs";
48770
49365
  import { rm as rm5 } from "fs/promises";
48771
- var log79, ProjectManagerImpl;
49366
+ var log80, ProjectManagerImpl;
48772
49367
  var init_projects_manager = __esm({
48773
49368
  "src/features/projects/projects.manager.ts"() {
48774
49369
  "use strict";
@@ -48779,7 +49374,7 @@ var init_projects_manager = __esm({
48779
49374
  init_projects_open_with();
48780
49375
  init_projects_creator();
48781
49376
  init_terminal_session_manager();
48782
- log79 = createLogger("projects.manager");
49377
+ log80 = createLogger("projects.manager");
48783
49378
  ProjectManagerImpl = class {
48784
49379
  rootFolder;
48785
49380
  metadataManager;
@@ -49255,6 +49850,15 @@ ___CWD___%s
49255
49850
  project.useBuiltInBrowser = useBuiltInBrowser;
49256
49851
  await this.metadataManager.saveProjects(projects);
49257
49852
  }
49853
+ async updateProjectIcon(projectId, projectIcon) {
49854
+ const projects = await this.metadataManager.loadProjects();
49855
+ const project = projects.find((p) => p.id === projectId);
49856
+ if (!project) {
49857
+ throw new Error(`Project not found: ${projectId}`);
49858
+ }
49859
+ project.projectIcon = projectIcon ?? void 0;
49860
+ await this.metadataManager.saveProjects(projects);
49861
+ }
49258
49862
  /**
49259
49863
  * Starts running a dev server process for a project
49260
49864
  * @param projectId - The project ID
@@ -49262,7 +49866,7 @@ ___CWD___%s
49262
49866
  */
49263
49867
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
49264
49868
  async *startRunningProcess(projectId, threadId) {
49265
- log79.info("Starting running process for project", { projectId, threadId });
49869
+ log80.info("Starting running process for project", { projectId, threadId });
49266
49870
  const project = await this.getProject(projectId);
49267
49871
  if (!project) {
49268
49872
  throw new Error(`Project not found: ${projectId}`);
@@ -49278,7 +49882,7 @@ ___CWD___%s
49278
49882
  const threads = await this.metadataManager.loadThreads();
49279
49883
  const thread = threads.find((t) => t.id === targetThreadId);
49280
49884
  if (!thread) {
49281
- log79.warn("Thread not found for project while starting process", {
49885
+ log80.warn("Thread not found for project while starting process", {
49282
49886
  projectId,
49283
49887
  targetThreadId,
49284
49888
  availableThreadIds: project.threads
@@ -49287,7 +49891,7 @@ ___CWD___%s
49287
49891
  `Thread not found for project: ${projectId} with threadId: ${targetThreadId}`
49288
49892
  );
49289
49893
  }
49290
- log79.info("Launching project run command", {
49894
+ log80.info("Launching project run command", {
49291
49895
  projectId,
49292
49896
  threadId: targetThreadId,
49293
49897
  command: project.runCommand,
@@ -49300,7 +49904,7 @@ ___CWD___%s
49300
49904
  targetThreadId
49301
49905
  );
49302
49906
  } catch (error) {
49303
- log79.error("Failed to start running process", { projectId, threadId: targetThreadId, error });
49907
+ log80.error("Failed to start running process", { projectId, threadId: targetThreadId, error });
49304
49908
  throw error;
49305
49909
  }
49306
49910
  }
@@ -49704,12 +50308,12 @@ async function getOpenRouterCredits(apiKey) {
49704
50308
  return { balance: null, status: "unknown", reason: String(error) };
49705
50309
  }
49706
50310
  }
49707
- var log80;
50311
+ var log81;
49708
50312
  var init_model_info_openrouter = __esm({
49709
50313
  "src/features/models/model-info-openrouter.ts"() {
49710
50314
  "use strict";
49711
50315
  init_dist();
49712
- log80 = createLogger("features.models.model-info-openrouter");
50316
+ log81 = createLogger("features.models.model-info-openrouter");
49713
50317
  }
49714
50318
  });
49715
50319
 
@@ -49736,12 +50340,12 @@ async function getAIHubMixCredits(apiKey) {
49736
50340
  return { balance: null, status: "unknown", reason: String(error) };
49737
50341
  }
49738
50342
  }
49739
- var log81;
50343
+ var log82;
49740
50344
  var init_model_info_aihubmix = __esm({
49741
50345
  "src/features/models/model-info-aihubmix.ts"() {
49742
50346
  "use strict";
49743
50347
  init_dist();
49744
- log81 = createLogger("features.models.model-info-aihubmix");
50348
+ log82 = createLogger("features.models.model-info-aihubmix");
49745
50349
  }
49746
50350
  });
49747
50351
 
@@ -49891,31 +50495,31 @@ function openUrlWithSystemBrowser(url) {
49891
50495
  async function openExternalUrlInSystemBrowser(url) {
49892
50496
  const utils = getElectrobunUtils();
49893
50497
  if (utils) {
49894
- log82.debug("Trying Utils.openExternal", { url });
50498
+ log83.debug("Trying Utils.openExternal", { url });
49895
50499
  try {
49896
50500
  const opened = utils.openExternal(url);
49897
50501
  if (opened) {
49898
- log82.info("Opened external URL via Utils.openExternal", { url });
50502
+ log83.info("Opened external URL via Utils.openExternal", { url });
49899
50503
  return;
49900
50504
  }
49901
- log82.warn("Utils.openExternal returned false, falling back to system command", { url });
50505
+ log83.warn("Utils.openExternal returned false, falling back to system command", { url });
49902
50506
  } catch (error) {
49903
- log82.warn("Utils.openExternal threw, falling back to system command", { url, error });
50507
+ log83.warn("Utils.openExternal threw, falling back to system command", { url, error });
49904
50508
  }
49905
50509
  } else {
49906
- log82.debug("Electrobun Utils unavailable, using system command", { url });
50510
+ log83.debug("Electrobun Utils unavailable, using system command", { url });
49907
50511
  }
49908
50512
  await openUrlWithSystemBrowser(url);
49909
- log82.info("Opened external URL via system command", { url, platform: process.platform });
50513
+ log83.info("Opened external URL via system command", { url, platform: process.platform });
49910
50514
  }
49911
- var log82;
50515
+ var log83;
49912
50516
  var init_open_external_url = __esm({
49913
50517
  "src/core/open-external-url.ts"() {
49914
50518
  "use strict";
49915
50519
  init_dist();
49916
50520
  init_electrobun_utils();
49917
50521
  init_utils();
49918
- log82 = createLogger("providers.open-external");
50522
+ log83 = createLogger("providers.open-external");
49919
50523
  }
49920
50524
  });
49921
50525
 
@@ -49934,18 +50538,18 @@ async function openExternalUrl(c) {
49934
50538
  await openExternalUrlInSystemBrowser(url);
49935
50539
  return c.json({ success: true });
49936
50540
  } catch (error) {
49937
- log83.error("Failed to open external URL", error);
50541
+ log84.error("Failed to open external URL", error);
49938
50542
  const message = error instanceof Error ? error.message : "Failed to open URL";
49939
50543
  return c.json({ error: message }, 500);
49940
50544
  }
49941
50545
  }
49942
- var log83;
50546
+ var log84;
49943
50547
  var init_providers_open_external_route = __esm({
49944
50548
  "src/features/providers/providers-open-external.route.ts"() {
49945
50549
  "use strict";
49946
50550
  init_dist();
49947
50551
  init_open_external_url();
49948
- log83 = createLogger("providers.open-external");
50552
+ log84 = createLogger("providers.open-external");
49949
50553
  }
49950
50554
  });
49951
50555
 
@@ -50234,7 +50838,7 @@ async function checkVp(c) {
50234
50838
  const installed = result.status === 0;
50235
50839
  return c.json({ installed });
50236
50840
  } catch (error) {
50237
- log84.error("Failed to check vp installation:", error);
50841
+ log85.error("Failed to check vp installation:", error);
50238
50842
  return c.json({ installed: false });
50239
50843
  }
50240
50844
  }
@@ -50262,14 +50866,14 @@ async function installVp(c) {
50262
50866
  );
50263
50867
  }
50264
50868
  }
50265
- var log84;
50869
+ var log85;
50266
50870
  var init_scaffold_vp_route = __esm({
50267
50871
  "src/features/scaffold/scaffold-vp.route.ts"() {
50268
50872
  "use strict";
50269
50873
  init_utils();
50270
50874
  init_shell();
50271
50875
  init_dist();
50272
- log84 = createLogger("features.scaffold.scaffold-vp");
50876
+ log85 = createLogger("features.scaffold.scaffold-vp");
50273
50877
  }
50274
50878
  });
50275
50879
 
@@ -50293,7 +50897,7 @@ var init_scaffold_routes = __esm({
50293
50897
  });
50294
50898
 
50295
50899
  // src/features/slash-commands/slash-commands.manager.ts
50296
- import { readdir as readdir10, readFile as readFile15, mkdir as mkdir7, writeFile as writeFile7, unlink as unlink4 } from "fs/promises";
50900
+ import { readdir as readdir11, readFile as readFile15, mkdir as mkdir7, writeFile as writeFile7, unlink as unlink4 } from "fs/promises";
50297
50901
  import { join as join35, basename as basename5, extname as extname5, relative as relative6 } from "path";
50298
50902
  import { existsSync as existsSync22 } from "fs";
50299
50903
  import { homedir as homedir9 } from "os";
@@ -50330,13 +50934,13 @@ function getGlobalCommandsDir() {
50330
50934
  function getProjectCommandsDir(threadPath) {
50331
50935
  return join35(threadPath, ".agents", "commands");
50332
50936
  }
50333
- var log85, SlashCommandManager;
50937
+ var log86, SlashCommandManager;
50334
50938
  var init_slash_commands_manager = __esm({
50335
50939
  "src/features/slash-commands/slash-commands.manager.ts"() {
50336
50940
  "use strict";
50337
50941
  init_dist();
50338
50942
  init_frontmatter();
50339
- log85 = createLogger("features.slash-commands.slash-commands.manager");
50943
+ log86 = createLogger("features.slash-commands.slash-commands.manager");
50340
50944
  SlashCommandManager = class _SlashCommandManager {
50341
50945
  /**
50342
50946
  * Built-in commands that cannot be overridden
@@ -50434,7 +51038,7 @@ var init_slash_commands_manager = __esm({
50434
51038
  async loadCommandsFromDir(dir, threadPath, scope) {
50435
51039
  const commands = [];
50436
51040
  try {
50437
- const files = await readdir10(dir);
51041
+ const files = await readdir11(dir);
50438
51042
  for (const file of files) {
50439
51043
  if (!file.endsWith(".md")) continue;
50440
51044
  const filePath = join35(dir, file);
@@ -50452,7 +51056,7 @@ var init_slash_commands_manager = __esm({
50452
51056
  });
50453
51057
  }
50454
51058
  } catch (error) {
50455
- log85.error(`Failed to load commands from ${dir}:`, error);
51059
+ log86.error(`Failed to load commands from ${dir}:`, error);
50456
51060
  }
50457
51061
  return commands;
50458
51062
  }
@@ -50580,7 +51184,9 @@ async function getSlashCommands(c, threadManager) {
50580
51184
  );
50581
51185
  }
50582
51186
  const commands = await slashCommandManager.loadCommands(thread.path);
50583
- const skills = await skillManager.loadSkills(thread.path);
51187
+ const db = await getDatabase();
51188
+ const project = await getProject(db, thread.projectId);
51189
+ const skills = await skillManager.loadSkills(thread.path, project?.path);
50584
51190
  const bundledSkills = await skillManager.loadBundledSkills();
50585
51191
  const response = {
50586
51192
  commands,
@@ -50589,7 +51195,7 @@ async function getSlashCommands(c, threadManager) {
50589
51195
  };
50590
51196
  return c.json(response);
50591
51197
  } catch (error) {
50592
- log86.error("Failed to load slash commands:", error);
51198
+ log87.error("Failed to load slash commands:", error);
50593
51199
  return c.json(
50594
51200
  {
50595
51201
  error: {
@@ -50603,14 +51209,16 @@ async function getSlashCommands(c, threadManager) {
50603
51209
  );
50604
51210
  }
50605
51211
  }
50606
- var log86, slashCommandManager, skillManager;
51212
+ var log87, slashCommandManager, skillManager;
50607
51213
  var init_slash_commands_get_route = __esm({
50608
51214
  "src/features/slash-commands/slash-commands-get.route.ts"() {
50609
51215
  "use strict";
50610
51216
  init_slash_commands_manager();
50611
51217
  init_skills_manager();
50612
51218
  init_dist();
50613
- log86 = createLogger("features.slash-commands.slash-commands-get");
51219
+ init_database();
51220
+ init_projects_database();
51221
+ log87 = createLogger("features.slash-commands.slash-commands-get");
50614
51222
  slashCommandManager = new SlashCommandManager();
50615
51223
  skillManager = new SkillManager();
50616
51224
  }
@@ -50683,7 +51291,7 @@ async function createSlashCommand(c, threadManager) {
50683
51291
  );
50684
51292
  return c.json(command, 201);
50685
51293
  } catch (error) {
50686
- log87.error("Failed to create slash command:", error);
51294
+ log88.error("Failed to create slash command:", error);
50687
51295
  return c.json(
50688
51296
  {
50689
51297
  error: {
@@ -50697,13 +51305,13 @@ async function createSlashCommand(c, threadManager) {
50697
51305
  );
50698
51306
  }
50699
51307
  }
50700
- var log87, slashCommandManager2;
51308
+ var log88, slashCommandManager2;
50701
51309
  var init_slash_commands_post_route = __esm({
50702
51310
  "src/features/slash-commands/slash-commands-post.route.ts"() {
50703
51311
  "use strict";
50704
51312
  init_slash_commands_manager();
50705
51313
  init_dist();
50706
- log87 = createLogger("features.slash-commands.slash-commands-post");
51314
+ log88 = createLogger("features.slash-commands.slash-commands-post");
50707
51315
  slashCommandManager2 = new SlashCommandManager();
50708
51316
  }
50709
51317
  });
@@ -50728,7 +51336,7 @@ async function updateSlashCommand(c) {
50728
51336
  await slashCommandManager3.updateCommand(filePath, content, metadata ?? {});
50729
51337
  return c.json({ success: true });
50730
51338
  } catch (error) {
50731
- log88.error("Failed to update slash command:", error);
51339
+ log89.error("Failed to update slash command:", error);
50732
51340
  return c.json(
50733
51341
  {
50734
51342
  error: {
@@ -50742,13 +51350,13 @@ async function updateSlashCommand(c) {
50742
51350
  );
50743
51351
  }
50744
51352
  }
50745
- var log88, slashCommandManager3;
51353
+ var log89, slashCommandManager3;
50746
51354
  var init_slash_commands_put_route = __esm({
50747
51355
  "src/features/slash-commands/slash-commands-put.route.ts"() {
50748
51356
  "use strict";
50749
51357
  init_slash_commands_manager();
50750
51358
  init_dist();
50751
- log88 = createLogger("features.slash-commands.slash-commands-put");
51359
+ log89 = createLogger("features.slash-commands.slash-commands-put");
50752
51360
  slashCommandManager3 = new SlashCommandManager();
50753
51361
  }
50754
51362
  });
@@ -50773,7 +51381,7 @@ async function deleteSlashCommand(c) {
50773
51381
  await slashCommandManager4.deleteCommand(filePath);
50774
51382
  return c.json({ success: true });
50775
51383
  } catch (error) {
50776
- log89.error("Failed to delete slash command:", error);
51384
+ log90.error("Failed to delete slash command:", error);
50777
51385
  return c.json(
50778
51386
  {
50779
51387
  error: {
@@ -50787,13 +51395,13 @@ async function deleteSlashCommand(c) {
50787
51395
  );
50788
51396
  }
50789
51397
  }
50790
- var log89, slashCommandManager4;
51398
+ var log90, slashCommandManager4;
50791
51399
  var init_slash_commands_delete_route = __esm({
50792
51400
  "src/features/slash-commands/slash-commands-delete.route.ts"() {
50793
51401
  "use strict";
50794
51402
  init_slash_commands_manager();
50795
51403
  init_dist();
50796
- log89 = createLogger("features.slash-commands.slash-commands-delete");
51404
+ log90 = createLogger("features.slash-commands.slash-commands-delete");
50797
51405
  slashCommandManager4 = new SlashCommandManager();
50798
51406
  }
50799
51407
  });
@@ -51015,7 +51623,7 @@ Describe your rules here as markdown which will be used when Tarsk sees is promp
51015
51623
  201
51016
51624
  );
51017
51625
  } catch (error) {
51018
- log90.error("Failed to create rule:", error);
51626
+ log91.error("Failed to create rule:", error);
51019
51627
  return c.json(
51020
51628
  {
51021
51629
  error: {
@@ -51029,12 +51637,12 @@ Describe your rules here as markdown which will be used when Tarsk sees is promp
51029
51637
  );
51030
51638
  }
51031
51639
  }
51032
- var log90;
51640
+ var log91;
51033
51641
  var init_rules_post_route = __esm({
51034
51642
  "src/features/rules/rules-post.route.ts"() {
51035
51643
  "use strict";
51036
51644
  init_dist();
51037
- log90 = createLogger("features.rules.rules-post");
51645
+ log91 = createLogger("features.rules.rules-post");
51038
51646
  }
51039
51647
  });
51040
51648
 
@@ -51183,7 +51791,7 @@ var init_thread_folder_name = __esm({
51183
51791
  import { randomUUID as randomUUID15 } from "crypto";
51184
51792
  import { join as join36 } from "path";
51185
51793
  import { execSync as execSync3 } from "child_process";
51186
- import { rm as rm6, stat as stat6, mkdir as mkdir8 } from "fs/promises";
51794
+ import { rm as rm6, stat as stat7, mkdir as mkdir8 } from "fs/promises";
51187
51795
  async function* removePathWithKeepalive(path7, threadId) {
51188
51796
  const removePromise = rm6(path7, { recursive: true, force: true });
51189
51797
  while (true) {
@@ -51199,7 +51807,7 @@ async function* removePathWithKeepalive(path7, threadId) {
51199
51807
  yield { type: "progress", message: `Still removing ${path7}...`, threadId };
51200
51808
  }
51201
51809
  }
51202
- var log91, DELETE_REMOVE_HEARTBEAT_MS, ThreadManagerImpl;
51810
+ var log92, DELETE_REMOVE_HEARTBEAT_MS, ThreadManagerImpl;
51203
51811
  var init_threads_manager = __esm({
51204
51812
  "src/features/threads/threads.manager.ts"() {
51205
51813
  "use strict";
@@ -51210,7 +51818,7 @@ var init_threads_manager = __esm({
51210
51818
  init_database();
51211
51819
  init_threads_database();
51212
51820
  init_thread_folder_name();
51213
- log91 = createLogger("features.threads.threads.manager");
51821
+ log92 = createLogger("features.threads.threads.manager");
51214
51822
  DELETE_REMOVE_HEARTBEAT_MS = 1e4;
51215
51823
  ThreadManagerImpl = class {
51216
51824
  metadataManager;
@@ -51270,7 +51878,7 @@ var init_threads_manager = __esm({
51270
51878
  return;
51271
51879
  }
51272
51880
  try {
51273
- await stat6(project.path);
51881
+ await stat7(project.path);
51274
51882
  } catch {
51275
51883
  await mkdir8(project.path, { recursive: true });
51276
51884
  }
@@ -51318,7 +51926,7 @@ var init_threads_manager = __esm({
51318
51926
  };
51319
51927
  try {
51320
51928
  try {
51321
- await stat6(project.path);
51929
+ await stat7(project.path);
51322
51930
  } catch {
51323
51931
  await mkdir8(project.path, { recursive: true });
51324
51932
  }
@@ -51556,7 +52164,7 @@ var init_threads_manager = __esm({
51556
52164
  const db = await getDatabase();
51557
52165
  await deleteThread(db, threadId);
51558
52166
  } catch (error) {
51559
- log91.error(
52167
+ log92.error(
51560
52168
  `Failed to mark thread as deleted in database: ${error instanceof Error ? error.message : String(error)}`
51561
52169
  );
51562
52170
  }
@@ -51933,7 +52541,7 @@ var init_project_scripts_database = __esm({
51933
52541
  });
51934
52542
 
51935
52543
  // src/core/project-inspector.ts
51936
- import { readFile as readFile16, readdir as readdir11 } from "fs/promises";
52544
+ import { readFile as readFile16, readdir as readdir12 } from "fs/promises";
51937
52545
  import { existsSync as existsSync23 } from "fs";
51938
52546
  import { join as join37, basename as basename7, relative as relative7 } from "path";
51939
52547
  import { glob as glob2 } from "glob";
@@ -52135,7 +52743,7 @@ async function resolveNxWorkspaces(projectPath) {
52135
52743
  if (workspaces.length > 0) return workspaces;
52136
52744
  const appsDir = join37(projectPath, "apps");
52137
52745
  if (existsSync23(appsDir)) {
52138
- const entries = await readdir11(appsDir, { withFileTypes: true });
52746
+ const entries = await readdir12(appsDir, { withFileTypes: true });
52139
52747
  for (const entry of entries) {
52140
52748
  if (entry.isDirectory() && !entry.name.startsWith(".")) {
52141
52749
  const folder = join37("apps", entry.name);
@@ -52171,7 +52779,7 @@ async function resolveFolderWorkspaces(projectPath) {
52171
52779
  }
52172
52780
  async function detectFolderBasedWorkspaces(projectPath) {
52173
52781
  try {
52174
- const entries = await readdir11(projectPath, { withFileTypes: true });
52782
+ const entries = await readdir12(projectPath, { withFileTypes: true });
52175
52783
  const results = [];
52176
52784
  for (const entry of entries) {
52177
52785
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "build") {
@@ -52276,7 +52884,7 @@ async function handleSelectThread(c, threadManager) {
52276
52884
  }
52277
52885
  await threadManager.selectThread(threadId);
52278
52886
  inspectProject(thread.path, thread.projectId).catch((err) => {
52279
- log92.error(`Failed to inspect project scripts for thread ${threadId}:`, err);
52887
+ log93.error(`Failed to inspect project scripts for thread ${threadId}:`, err);
52280
52888
  });
52281
52889
  return successResponse(c, { success: true, message: "Thread selected successfully", threadId });
52282
52890
  } catch (error) {
@@ -52289,7 +52897,7 @@ async function handleSelectThread(c, threadManager) {
52289
52897
  );
52290
52898
  }
52291
52899
  }
52292
- var log92;
52900
+ var log93;
52293
52901
  var init_threads_select_route = __esm({
52294
52902
  "src/features/threads/threads-select.route.ts"() {
52295
52903
  "use strict";
@@ -52297,7 +52905,7 @@ var init_threads_select_route = __esm({
52297
52905
  init_error_responses();
52298
52906
  init_project_inspector();
52299
52907
  init_dist();
52300
- log92 = createLogger("features.threads.threads-select");
52908
+ log93 = createLogger("features.threads.threads-select");
52301
52909
  }
52302
52910
  });
52303
52911
 
@@ -52472,7 +53080,7 @@ var init_threads_files_route = __esm({
52472
53080
  });
52473
53081
 
52474
53082
  // src/features/threads/threads-explorer.route.ts
52475
- import { readdir as readdir12, readFile as readFile17, rename as rename4, writeFile as writeFile8, mkdir as mkdir9, access as access6, open as open2, stat as stat7, rm as rm7 } from "fs/promises";
53083
+ import { readdir as readdir13, readFile as readFile17, rename as rename4, writeFile as writeFile8, mkdir as mkdir9, access as access6, open as open2, stat as stat8, rm as rm7 } from "fs/promises";
52476
53084
  import { isAbsolute as isAbsolute6, join as join38, resolve as resolve5, basename as basename8 } from "path";
52477
53085
  import { spawn as spawn2 } from "child_process";
52478
53086
  import openFilePath from "open";
@@ -52529,7 +53137,7 @@ async function openExportPath(outputPath) {
52529
53137
  const openResult = await Utils2.openPath(outputPath);
52530
53138
  opened = openResult !== false;
52531
53139
  } catch (error) {
52532
- log93.warn("Failed to open explorer export with electrobun", { outputPath, error });
53140
+ log94.warn("Failed to open explorer export with electrobun", { outputPath, error });
52533
53141
  opened = false;
52534
53142
  }
52535
53143
  }
@@ -52545,7 +53153,7 @@ async function openExportPath(outputPath) {
52545
53153
  }
52546
53154
  }
52547
53155
  async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath) {
52548
- const dirents = await readdir12(currentPath, { withFileTypes: true });
53156
+ const dirents = await readdir13(currentPath, { withFileTypes: true });
52549
53157
  const isRoot = relativePath.length === 0;
52550
53158
  const entries = dirents.filter((d) => !(isRoot && d.name === ".git") && d.name !== "node_modules").map((d) => {
52551
53159
  const entryPath = relativePath ? join38(relativePath, d.name) : d.name;
@@ -52589,7 +53197,7 @@ async function handleListThreadExplorerDir(c, threadManager) {
52589
53197
  }
52590
53198
  let dirents;
52591
53199
  try {
52592
- dirents = await readdir12(absDir, { withFileTypes: true });
53200
+ dirents = await readdir13(absDir, { withFileTypes: true });
52593
53201
  } catch {
52594
53202
  return c.json({ error: { code: "NOT_FOUND", message: "Directory not found" } }, 404);
52595
53203
  }
@@ -52863,7 +53471,7 @@ function resolveExplorerMediaAbsolutePath(threadPath, filePath) {
52863
53471
  return validateFilePath(threadPath, filePath);
52864
53472
  }
52865
53473
  async function buildExplorerMediaResponse(absPath, contentType, rangeHeader) {
52866
- const { size: fileSize } = await stat7(absPath);
53474
+ const { size: fileSize } = await stat8(absPath);
52867
53475
  if (rangeHeader?.startsWith("bytes=")) {
52868
53476
  const [startStr, endStr] = rangeHeader.slice(6).split("-", 2);
52869
53477
  const start = Number.parseInt(startStr, 10);
@@ -52937,7 +53545,7 @@ async function handleGetExplorerMedia(c, threadManager) {
52937
53545
  }
52938
53546
  const absPath = resolveExplorerMediaAbsolutePath(thread.path, filePath);
52939
53547
  if (!absPath) {
52940
- log93.warn("[explorer-media] Invalid media path", {
53548
+ log94.warn("[explorer-media] Invalid media path", {
52941
53549
  threadId,
52942
53550
  filePath,
52943
53551
  threadPath: thread.path
@@ -52947,7 +53555,7 @@ async function handleGetExplorerMedia(c, threadManager) {
52947
53555
  try {
52948
53556
  await access6(absPath);
52949
53557
  } catch {
52950
- log93.warn("[explorer-media] Media file not found", {
53558
+ log94.warn("[explorer-media] Media file not found", {
52951
53559
  threadId,
52952
53560
  filePath,
52953
53561
  absPath
@@ -52958,7 +53566,7 @@ async function handleGetExplorerMedia(c, threadManager) {
52958
53566
  const contentType = MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
52959
53567
  const rangeHeader = c.req.header("range");
52960
53568
  const response = await buildExplorerMediaResponse(absPath, contentType, rangeHeader);
52961
- log93.info("[explorer-media] Served media file", {
53569
+ log94.info("[explorer-media] Served media file", {
52962
53570
  threadId,
52963
53571
  filePath,
52964
53572
  absPath,
@@ -53003,7 +53611,7 @@ async function handleUploadExplorerFile(c, threadManager) {
53003
53611
  absParent = validated;
53004
53612
  }
53005
53613
  try {
53006
- const parentStat = await stat7(absParent);
53614
+ const parentStat = await stat8(absParent);
53007
53615
  if (!parentStat.isDirectory()) {
53008
53616
  return c.json({ error: { code: "NOT_FOUND", message: "Directory not found" } }, 404);
53009
53617
  }
@@ -53031,7 +53639,7 @@ async function handleUploadExplorerFile(c, threadManager) {
53031
53639
  throw error;
53032
53640
  }
53033
53641
  const relPath = dirPath ? `${dirPath}/${name}` : name;
53034
- log93.info("[explorer-upload] Uploaded dropped file", {
53642
+ log94.info("[explorer-upload] Uploaded dropped file", {
53035
53643
  threadId,
53036
53644
  dirPath,
53037
53645
  name,
@@ -53094,7 +53702,7 @@ async function handleCreateExplorerFolder(c, threadManager) {
53094
53702
  );
53095
53703
  }
53096
53704
  }
53097
- var log93, Utils2, utilsLoaded2, MEDIA_MIME_TYPES, EXPLORER_MEDIA_RESPONSE_HEADERS;
53705
+ var log94, Utils2, utilsLoaded2, MEDIA_MIME_TYPES, EXPLORER_MEDIA_RESPONSE_HEADERS;
53098
53706
  var init_threads_explorer_route = __esm({
53099
53707
  "src/features/threads/threads-explorer.route.ts"() {
53100
53708
  "use strict";
@@ -53103,7 +53711,7 @@ var init_threads_explorer_route = __esm({
53103
53711
  init_error_responses();
53104
53712
  init_ai_files_ops();
53105
53713
  init_paths();
53106
- log93 = createLogger("threads.explorer");
53714
+ log94 = createLogger("threads.explorer");
53107
53715
  Utils2 = null;
53108
53716
  utilsLoaded2 = false;
53109
53717
  MEDIA_MIME_TYPES = EXPLORER_MEDIA_MIME_TYPES;
@@ -53182,7 +53790,7 @@ async function handleOpenThread(c, threadManager) {
53182
53790
  422
53183
53791
  );
53184
53792
  }
53185
- log94.info("Opening thread in external program", {
53793
+ log95.info("Opening thread in external program", {
53186
53794
  threadId,
53187
53795
  program,
53188
53796
  threadPath
@@ -53191,10 +53799,10 @@ async function handleOpenThread(c, threadManager) {
53191
53799
  await open3(threadPath, {
53192
53800
  app: { name: appNames }
53193
53801
  });
53194
- log94.info("[POST /api/threads/:id/open] Successfully opened thread in %s", program);
53802
+ log95.info("[POST /api/threads/:id/open] Successfully opened thread in %s", program);
53195
53803
  } catch (openError) {
53196
53804
  const message = openError instanceof Error ? openError.message : String(openError);
53197
- log94.error("[POST /api/threads/:id/open] Failed to open thread:", message);
53805
+ log95.error("[POST /api/threads/:id/open] Failed to open thread:", message);
53198
53806
  return errorResponse(
53199
53807
  c,
53200
53808
  ErrorCodes.OPEN_PROGRAM_ERROR,
@@ -53214,7 +53822,7 @@ async function handleOpenThread(c, threadManager) {
53214
53822
  );
53215
53823
  }
53216
53824
  }
53217
- var log94;
53825
+ var log95;
53218
53826
  var init_threads_open_route = __esm({
53219
53827
  "src/features/threads/threads-open.route.ts"() {
53220
53828
  "use strict";
@@ -53222,7 +53830,7 @@ var init_threads_open_route = __esm({
53222
53830
  init_projects_open_with();
53223
53831
  init_route_helpers();
53224
53832
  init_error_responses();
53225
- log94 = createLogger("features.threads.threads-open");
53833
+ log95 = createLogger("features.threads.threads-open");
53226
53834
  }
53227
53835
  });
53228
53836
 
@@ -53396,7 +54004,7 @@ async function listGithubFolderFiles(repoUrl, srcPath, options = {}) {
53396
54004
  const refParam = ref ?? "HEAD";
53397
54005
  const tree = await fetchGithubTree(owner, repo, refParam, token);
53398
54006
  if (tree.truncated) {
53399
- log95.warn(
54007
+ log96.warn(
53400
54008
  "Warning: the repository tree was truncated by GitHub. Large repos may be incomplete."
53401
54009
  );
53402
54010
  }
@@ -53421,12 +54029,12 @@ async function fetchGithubFolderFile(repoUrl, srcPath, relativePath, options = {
53421
54029
  content = content.replace(/Claude/gi, "Tarsk");
53422
54030
  return content;
53423
54031
  }
53424
- var log95;
54032
+ var log96;
53425
54033
  var init_github_folder = __esm({
53426
54034
  "src/features/git/github-folder.ts"() {
53427
54035
  "use strict";
53428
54036
  init_dist();
53429
- log95 = createLogger("features.git.github-folder");
54037
+ log96 = createLogger("features.git.github-folder");
53430
54038
  }
53431
54039
  });
53432
54040
 
@@ -53437,7 +54045,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
53437
54045
  const { ref, excludeRelativePrefixes = [] } = options;
53438
54046
  const { files, ref: refParam } = await listGithubFolderFiles(repoUrl, srcPath, options);
53439
54047
  const { owner, repo } = parseGithubRepoUrl(repoUrl);
53440
- log96.info(`Downloading ${files.length} file(s) from ${owner}/${repo}/${srcPath} \u2192 ${destPath}`);
54048
+ log97.info(`Downloading ${files.length} file(s) from ${owner}/${repo}/${srcPath} \u2192 ${destPath}`);
53441
54049
  await Promise.all(
53442
54050
  files.map(async (relativePath) => {
53443
54051
  const localPath = join39(destPath, relativePath);
@@ -53448,18 +54056,18 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
53448
54056
  excludeRelativePrefixes
53449
54057
  });
53450
54058
  await writeFile9(localPath, content, "utf8");
53451
- log96.info(` \u2713 ${relativePath}`);
54059
+ log97.info(` \u2713 ${relativePath}`);
53452
54060
  })
53453
54061
  );
53454
- log96.info("Done.");
54062
+ log97.info("Done.");
53455
54063
  }
53456
- var log96;
54064
+ var log97;
53457
54065
  var init_git_download_folder = __esm({
53458
54066
  "src/features/git/git-download-folder.ts"() {
53459
54067
  "use strict";
53460
54068
  init_dist();
53461
54069
  init_github_folder();
53462
- log96 = createLogger("features.git.git-download-folder");
54070
+ log97 = createLogger("features.git.git-download-folder");
53463
54071
  }
53464
54072
  });
53465
54073
 
@@ -53753,7 +54361,7 @@ var init_skills_sources = __esm({
53753
54361
  });
53754
54362
 
53755
54363
  // src/features/skills/skills.catalog-cache.ts
53756
- import { cp, mkdtemp as mkdtemp2, readdir as readdir13, readFile as readFile18, rm as rm9 } from "fs/promises";
54364
+ import { cp as cp2, mkdtemp as mkdtemp2, readdir as readdir14, readFile as readFile18, rm as rm9 } from "fs/promises";
53757
54365
  import { join as join40, relative as relative8, sep as sep4 } from "path";
53758
54366
  import { tmpdir as tmpdir4 } from "os";
53759
54367
  function getBundledSkillSourceById(sourceId) {
@@ -53774,7 +54382,7 @@ function normalizeSkillContent(content) {
53774
54382
  return content.replace(/Claude/gi, "Tarsk");
53775
54383
  }
53776
54384
  async function listFilesRecursive(dir, rootDir = dir) {
53777
- const entries = await readdir13(dir, { withFileTypes: true });
54385
+ const entries = await readdir14(dir, { withFileTypes: true });
53778
54386
  const files = [];
53779
54387
  for (const entry of entries) {
53780
54388
  const fullPath = join40(dir, entry.name);
@@ -53912,7 +54520,7 @@ async function readCachedCatalogSkillFile(sourceId, skillId, relativePath) {
53912
54520
  }
53913
54521
  async function copyCachedCatalogSkillToDirectory(sourceId, skillId, destPath) {
53914
54522
  const skillDir = await getSkillDirEnsured(sourceId, skillId);
53915
- await cp(skillDir, destPath, { recursive: true });
54523
+ await cp2(skillDir, destPath, { recursive: true });
53916
54524
  }
53917
54525
  var inFlightRefreshes;
53918
54526
  var init_skills_catalog_cache = __esm({
@@ -55310,50 +55918,50 @@ var init_git_commit_route = __esm({
55310
55918
  async function gitPushHandler(c, metadataManager) {
55311
55919
  try {
55312
55920
  const threadId = c.req.param("threadId");
55313
- log97.info("Starting push", { threadId });
55921
+ log98.info("Starting push", { threadId });
55314
55922
  const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
55315
55923
  if (!thread) {
55316
- log97.warn("Thread not found", { threadId });
55924
+ log98.warn("Thread not found", { threadId });
55317
55925
  return c.json({ error: "Thread not found" }, 404);
55318
55926
  }
55319
- log97.info("Thread found", { threadId, title: thread.title });
55927
+ log98.info("Thread found", { threadId, title: thread.title });
55320
55928
  const repoPath = thread.path;
55321
55929
  if (!repoPath) {
55322
- log97.warn("Thread path not found", { threadId });
55930
+ log98.warn("Thread path not found", { threadId });
55323
55931
  return c.json({ error: "Thread path not found" }, 404);
55324
55932
  }
55325
55933
  const absolutePath = resolveThreadPath(repoPath);
55326
- log97.info("Resolved thread path", { absolutePath });
55934
+ log98.info("Resolved thread path", { absolutePath });
55327
55935
  let gitRoot;
55328
55936
  try {
55329
55937
  gitRoot = await getGitRoot(absolutePath);
55330
- log97.info("Resolved git root", { gitRoot });
55938
+ log98.info("Resolved git root", { gitRoot });
55331
55939
  } catch {
55332
- log97.warn("Path is not a git repository", { absolutePath });
55940
+ log98.warn("Path is not a git repository", { absolutePath });
55333
55941
  return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
55334
55942
  }
55335
55943
  const currentBranch = await getCurrentBranch(gitRoot);
55336
- log97.info("Resolved current branch", { currentBranch });
55337
- log97.info("Pushing branch to origin", { currentBranch });
55944
+ log98.info("Resolved current branch", { currentBranch });
55945
+ log98.info("Pushing branch to origin", { currentBranch });
55338
55946
  await pushToOrigin(gitRoot, currentBranch);
55339
- log97.info("Successfully pushed branch to origin", { currentBranch });
55947
+ log98.info("Successfully pushed branch to origin", { currentBranch });
55340
55948
  return c.json({ success: true });
55341
55949
  } catch (error) {
55342
55950
  const message = error instanceof Error ? error.message : "Failed to push changes";
55343
- log97.error("Push failed", { message });
55344
- log97.error("Push failed stack", {
55951
+ log98.error("Push failed", { message });
55952
+ log98.error("Push failed stack", {
55345
55953
  stack: error instanceof Error ? error.stack : "No stack available"
55346
55954
  });
55347
55955
  return c.json({ error: message }, 500);
55348
55956
  }
55349
55957
  }
55350
- var log97;
55958
+ var log98;
55351
55959
  var init_git_push_route = __esm({
55352
55960
  "src/features/git/git-push.route.ts"() {
55353
55961
  "use strict";
55354
55962
  init_logger2();
55355
55963
  init_git_utils();
55356
- log97 = createLogger("git.push");
55964
+ log98 = createLogger("git.push");
55357
55965
  }
55358
55966
  });
55359
55967
 
@@ -55407,16 +56015,16 @@ var init_git_fetch_route = __esm({
55407
56015
  async function gitGeneratePrInfoHandler(c, metadataManager) {
55408
56016
  try {
55409
56017
  const threadId = c.req.param("threadId");
55410
- log98.info("Starting PR info generation", { threadId });
56018
+ log99.info("Starting PR info generation", { threadId });
55411
56019
  if (!threadId) {
55412
56020
  return c.json({ error: "Thread ID is required" }, 400);
55413
56021
  }
55414
56022
  const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
55415
56023
  if (!thread) {
55416
- log98.warn("Thread not found", { threadId });
56024
+ log99.warn("Thread not found", { threadId });
55417
56025
  return c.json({ error: "Thread not found" }, 404);
55418
56026
  }
55419
- log98.info("Thread found", {
56027
+ log99.info("Thread found", {
55420
56028
  threadId,
55421
56029
  title: thread.title,
55422
56030
  model: thread.model,
@@ -55424,13 +56032,13 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55424
56032
  });
55425
56033
  const repoPath = thread.path;
55426
56034
  if (!repoPath) {
55427
- log98.warn("Thread path not found", { threadId });
56035
+ log99.warn("Thread path not found", { threadId });
55428
56036
  return c.json({ error: "Thread path not found" }, 404);
55429
56037
  }
55430
56038
  const body = await c.req.json().catch(() => ({}));
55431
56039
  const provider = body.provider ?? thread.modelProvider;
55432
56040
  if (!provider) {
55433
- log98.warn("Provider not found in request or thread metadata", { threadId });
56041
+ log99.warn("Provider not found in request or thread metadata", { threadId });
55434
56042
  return c.json(
55435
56043
  {
55436
56044
  title: "Update",
@@ -55441,30 +56049,30 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55441
56049
  );
55442
56050
  }
55443
56051
  const model = body.model ?? thread.model;
55444
- log98.info("Using provider and model", { provider, model });
56052
+ log99.info("Using provider and model", { provider, model });
55445
56053
  const absolutePath = resolveThreadPath(repoPath);
55446
- log98.info("Resolved thread path", { absolutePath });
56054
+ log99.info("Resolved thread path", { absolutePath });
55447
56055
  let gitRoot;
55448
56056
  try {
55449
56057
  gitRoot = await getGitRoot(absolutePath);
55450
- log98.info("Resolved git root", { gitRoot });
56058
+ log99.info("Resolved git root", { gitRoot });
55451
56059
  } catch {
55452
- log98.warn("Path is not a git repository", { absolutePath });
56060
+ log99.warn("Path is not a git repository", { absolutePath });
55453
56061
  return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
55454
56062
  }
55455
56063
  const currentBranch = await getCurrentBranch(gitRoot);
55456
- log98.info("Resolved current branch", { currentBranch });
55457
- log98.info("Collecting uncommitted git diff", { gitRoot });
56064
+ log99.info("Resolved current branch", { currentBranch });
56065
+ log99.info("Collecting uncommitted git diff", { gitRoot });
55458
56066
  const uncommittedDiff = await new Promise((resolveDiff, reject) => {
55459
56067
  const runPlainDiff = () => {
55460
- log98.info("No HEAD found, using plain git diff", { gitRoot });
56068
+ log99.info("No HEAD found, using plain git diff", { gitRoot });
55461
56069
  const proc2 = spawnProcess("git", ["diff"], { cwd: gitRoot });
55462
56070
  let out2 = "";
55463
56071
  proc2.stdout?.on("data", (d) => {
55464
56072
  out2 += d.toString();
55465
56073
  });
55466
56074
  proc2.on("close", () => {
55467
- log98.info("Collected plain diff", { length: out2.length });
56075
+ log99.info("Collected plain diff", { length: out2.length });
55468
56076
  resolveDiff(out2);
55469
56077
  });
55470
56078
  proc2.on("error", reject);
@@ -55480,46 +56088,46 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55480
56088
  });
55481
56089
  proc.on("close", (code) => {
55482
56090
  if (code === 0) {
55483
- log98.info("Collected HEAD diff", { length: out.length });
56091
+ log99.info("Collected HEAD diff", { length: out.length });
55484
56092
  resolveDiff(out);
55485
56093
  } else {
55486
56094
  const noHead = /HEAD|revision|unknown revision/i.test(err);
55487
56095
  if (noHead) runPlainDiff();
55488
56096
  else {
55489
- log98.error("Failed to get git diff", { error: err });
56097
+ log99.error("Failed to get git diff", { error: err });
55490
56098
  reject(new Error(err || "Failed to get git diff"));
55491
56099
  }
55492
56100
  }
55493
56101
  });
55494
56102
  proc.on("error", reject);
55495
56103
  });
55496
- log98.info("Finding base branch", { gitRoot });
56104
+ log99.info("Finding base branch", { gitRoot });
55497
56105
  const baseBranch = await getDefaultBranch(gitRoot);
55498
- log98.info("Resolved default branch result", { baseBranch });
56106
+ log99.info("Resolved default branch result", { baseBranch });
55499
56107
  const effectiveBaseBranch = baseBranch ?? await findDefaultBranch(gitRoot);
55500
- log98.info("Resolved effective base branch", { effectiveBaseBranch });
56108
+ log99.info("Resolved effective base branch", { effectiveBaseBranch });
55501
56109
  let commitLog = "";
55502
56110
  if (effectiveBaseBranch && currentBranch !== effectiveBaseBranch) {
55503
- log98.info("Collecting commit log", { effectiveBaseBranch, currentBranch });
56111
+ log99.info("Collecting commit log", { effectiveBaseBranch, currentBranch });
55504
56112
  commitLog = await getCommitLog(gitRoot, effectiveBaseBranch, currentBranch);
55505
- log98.info("Collected commit log", { length: commitLog.length });
56113
+ log99.info("Collected commit log", { length: commitLog.length });
55506
56114
  } else {
55507
- log98.info("No commit log needed", { effectiveBaseBranch, currentBranch });
56115
+ log99.info("No commit log needed", { effectiveBaseBranch, currentBranch });
55508
56116
  }
55509
56117
  let detailedCommits = "";
55510
56118
  if (effectiveBaseBranch && currentBranch !== effectiveBaseBranch && commitLog) {
55511
- log98.info("Collecting detailed commits", { effectiveBaseBranch, currentBranch });
56119
+ log99.info("Collecting detailed commits", { effectiveBaseBranch, currentBranch });
55512
56120
  detailedCommits = await getDetailedCommits(gitRoot, effectiveBaseBranch, currentBranch);
55513
- log98.info("Collected detailed commits", { length: detailedCommits.length });
56121
+ log99.info("Collected detailed commits", { length: detailedCommits.length });
55514
56122
  }
55515
56123
  let branchDiff = "";
55516
56124
  if (effectiveBaseBranch && currentBranch !== effectiveBaseBranch) {
55517
- log98.info("Collecting PR branch diff", { effectiveBaseBranch });
56125
+ log99.info("Collecting PR branch diff", { effectiveBaseBranch });
55518
56126
  branchDiff = await getPrBranchDiff(gitRoot, effectiveBaseBranch);
55519
- log98.info("Collected PR branch diff", { length: branchDiff.length });
56127
+ log99.info("Collected PR branch diff", { length: branchDiff.length });
55520
56128
  }
55521
56129
  if (!uncommittedDiff.trim() && !commitLog.trim() && !branchDiff.trim()) {
55522
- log98.warn("No changes found for PR generation", {
56130
+ log99.warn("No changes found for PR generation", {
55523
56131
  uncommittedDiffLength: uncommittedDiff.length,
55524
56132
  commitLogLength: commitLog.length,
55525
56133
  branchDiffLength: branchDiff.length
@@ -55531,7 +56139,7 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55531
56139
  400
55532
56140
  );
55533
56141
  }
55534
- log98.info("Calling AI to generate PR info", { provider, model });
56142
+ log99.info("Calling AI to generate PR info", { provider, model });
55535
56143
  const prInfo = await generatePRInfoWithAI(
55536
56144
  commitLog,
55537
56145
  detailedCommits,
@@ -55543,7 +56151,7 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55543
56151
  model,
55544
56152
  provider
55545
56153
  );
55546
- log98.info("Generated PR info", {
56154
+ log99.info("Generated PR info", {
55547
56155
  title: prInfo.title,
55548
56156
  descriptionLength: prInfo.description.length
55549
56157
  });
@@ -55559,14 +56167,14 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55559
56167
  metadataManager
55560
56168
  );
55561
56169
  if (newBranch !== branchBeforeRename) {
55562
- log98.info("Renamed branch for PR", { oldBranch: branchBeforeRename, newBranch });
56170
+ log99.info("Renamed branch for PR", { oldBranch: branchBeforeRename, newBranch });
55563
56171
  }
55564
56172
  } catch (branchError) {
55565
- log98.warn("Failed to rename branch during PR info generation", {
56173
+ log99.warn("Failed to rename branch during PR info generation", {
55566
56174
  error: branchError instanceof Error ? branchError.message : String(branchError)
55567
56175
  });
55568
56176
  }
55569
- log98.info("PR generation completed successfully", {
56177
+ log99.info("PR generation completed successfully", {
55570
56178
  threadId,
55571
56179
  currentBranch: newBranch,
55572
56180
  baseBranch: effectiveBaseBranch
@@ -55578,10 +56186,10 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55578
56186
  currentBranch: newBranch
55579
56187
  });
55580
56188
  } catch (error) {
55581
- log98.error("Error during PR generation", {
56189
+ log99.error("Error during PR generation", {
55582
56190
  message: error instanceof Error ? error.message : String(error)
55583
56191
  });
55584
- log98.error("PR generation stack", {
56192
+ log99.error("PR generation stack", {
55585
56193
  stack: error instanceof Error ? error.stack : "No stack available"
55586
56194
  });
55587
56195
  return c.json({
@@ -55593,14 +56201,14 @@ async function gitGeneratePrInfoHandler(c, metadataManager) {
55593
56201
  });
55594
56202
  }
55595
56203
  }
55596
- var log98;
56204
+ var log99;
55597
56205
  var init_git_generate_pr_info_route = __esm({
55598
56206
  "src/features/git/git-generate-pr-info.route.ts"() {
55599
56207
  "use strict";
55600
56208
  init_utils();
55601
56209
  init_git_utils();
55602
56210
  init_logger2();
55603
- log98 = createLogger("git.generate-pr-info");
56211
+ log99 = createLogger("git.generate-pr-info");
55604
56212
  }
55605
56213
  });
55606
56214
 
@@ -55635,36 +56243,36 @@ async function gitCreatePrHandler(c, metadataManager) {
55635
56243
  try {
55636
56244
  const threadId = c.req.param("threadId");
55637
56245
  const body = await c.req.json();
55638
- const { title, description } = body;
55639
- log99.info("Starting PR creation", { threadId });
55640
- log99.info("Received PR title", { title });
55641
- log99.info("Received PR description", { descriptionLength: description?.length ?? 0 });
56246
+ const { title, description, baseBranch } = body;
56247
+ log100.info("Starting PR creation", { threadId });
56248
+ log100.info("Received PR title", { title });
56249
+ log100.info("Received PR description", { descriptionLength: description?.length ?? 0 });
55642
56250
  const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
55643
56251
  if (!threadId) {
55644
56252
  return c.json({ error: "Thread ID is required" }, 400);
55645
56253
  }
55646
56254
  if (!thread) {
55647
- log99.warn("Thread not found", { threadId });
56255
+ log100.warn("Thread not found", { threadId });
55648
56256
  return c.json({ error: "Thread not found" }, 404);
55649
56257
  }
55650
- log99.info("Thread found", { threadId, title: thread.title });
56258
+ log100.info("Thread found", { threadId, title: thread.title });
55651
56259
  const repoPath = thread.path;
55652
56260
  if (!repoPath) {
55653
- log99.warn("Thread path not found", { threadId });
56261
+ log100.warn("Thread path not found", { threadId });
55654
56262
  return c.json({ error: "Thread path not found" }, 404);
55655
56263
  }
55656
56264
  const absolutePath = resolveThreadPath(repoPath);
55657
- log99.info("Resolved thread path", { absolutePath });
56265
+ log100.info("Resolved thread path", { absolutePath });
55658
56266
  let gitRoot;
55659
56267
  try {
55660
56268
  gitRoot = await getGitRoot(absolutePath);
55661
- log99.info("Resolved git root", { gitRoot });
56269
+ log100.info("Resolved git root", { gitRoot });
55662
56270
  } catch {
55663
- log99.warn("Path is not a git repository", { absolutePath });
56271
+ log100.warn("Path is not a git repository", { absolutePath });
55664
56272
  return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
55665
56273
  }
55666
56274
  const currentBranch = await getCurrentBranch(gitRoot);
55667
- log99.info("Resolved current branch", { currentBranch });
56275
+ log100.info("Resolved current branch", { currentBranch });
55668
56276
  const trimmedTitle = title?.trim();
55669
56277
  const prTitle = trimmedTitle ? trimmedTitle : currentBranch;
55670
56278
  const prDescription = description ?? "";
@@ -55676,33 +56284,33 @@ async function gitCreatePrHandler(c, metadataManager) {
55676
56284
  prDescription,
55677
56285
  metadataManager
55678
56286
  ) : await pushBranchForPr(gitRoot, currentBranch);
55679
- log99.info("Resolved branch name for PR", { branchName });
55680
- log99.info("Creating pull request with GitHub CLI", { branchName });
55681
- const prUrl = await createPullRequest(gitRoot, branchName, prTitle, prDescription);
55682
- log99.info("Pull request created successfully", { prUrl });
56287
+ log100.info("Resolved branch name for PR", { branchName });
56288
+ log100.info("Creating pull request with GitHub CLI", { branchName });
56289
+ const prUrl = baseBranch ? await createPullRequest(gitRoot, branchName, prTitle, prDescription, baseBranch) : await createPullRequest(gitRoot, branchName, prTitle, prDescription);
56290
+ log100.info("Pull request created successfully", { prUrl });
55683
56291
  const normalizedPrTitle = normalizeThreadTitle(prTitle);
55684
56292
  if (normalizedPrTitle !== thread.title) {
55685
56293
  const threadUpdates = { title: normalizedPrTitle };
55686
56294
  await metadataManager.updateThreadFields(threadId, threadUpdates);
55687
- log99.info("Updated thread title to PR title", { normalizedPrTitle });
56295
+ log100.info("Updated thread title to PR title", { normalizedPrTitle });
55688
56296
  }
55689
56297
  return c.json({ success: true, prUrl, title: normalizedPrTitle, branchName });
55690
56298
  } catch (error) {
55691
56299
  const message = error instanceof Error ? error.message : "Failed to create PR";
55692
- log99.error("Pull request creation failed", { message });
55693
- log99.error("Pull request creation stack", {
56300
+ log100.error("Pull request creation failed", { message });
56301
+ log100.error("Pull request creation stack", {
55694
56302
  stack: error instanceof Error ? error.stack : "No stack available"
55695
56303
  });
55696
56304
  return c.json({ error: message }, 500);
55697
56305
  }
55698
56306
  }
55699
- var log99;
56307
+ var log100;
55700
56308
  var init_git_create_pr_route = __esm({
55701
56309
  "src/features/git/git-create-pr.route.ts"() {
55702
56310
  "use strict";
55703
56311
  init_logger2();
55704
56312
  init_git_utils();
55705
- log99 = createLogger("git.create-pr");
56313
+ log100 = createLogger("git.create-pr");
55706
56314
  }
55707
56315
  });
55708
56316
 
@@ -55735,7 +56343,7 @@ async function gitGithubStatusHandler(c, metadataManager) {
55735
56343
  status = await getGitSyncStatus(gitRoot, currentBranch);
55736
56344
  }
55737
56345
  } catch (error) {
55738
- log100.warn(
56346
+ log101.warn(
55739
56347
  `Failed to check git sync status: ${error instanceof Error ? error.message : String(error)}`
55740
56348
  );
55741
56349
  status = "Up to date";
@@ -55753,14 +56361,14 @@ async function gitGithubStatusHandler(c, metadataManager) {
55753
56361
  return c.json({ error: message }, 500);
55754
56362
  }
55755
56363
  }
55756
- var log100;
56364
+ var log101;
55757
56365
  var init_git_github_status_route = __esm({
55758
56366
  "src/features/git/git-github-status.route.ts"() {
55759
56367
  "use strict";
55760
56368
  init_dist();
55761
56369
  init_git_utils();
55762
56370
  init_git_route_context();
55763
- log100 = createLogger("features.git.git-github-status");
56371
+ log101 = createLogger("features.git.git-github-status");
55764
56372
  }
55765
56373
  });
55766
56374
 
@@ -55786,12 +56394,12 @@ function createServerStepTracker(threadId) {
55786
56394
  return await fn();
55787
56395
  } finally {
55788
56396
  const ms = Date.now() - start;
55789
- log101.info(`[gitops-timing] unified-status thread=${threadId} step=${name} ${ms}ms`);
56397
+ log102.info(`[gitops-timing] unified-status thread=${threadId} step=${name} ${ms}ms`);
55790
56398
  }
55791
56399
  }
55792
56400
  function record(name, start, extra) {
55793
56401
  const ms = Date.now() - start;
55794
- log101.info(
56402
+ log102.info(
55795
56403
  `[gitops-timing] unified-status thread=${threadId} step=${name} ${ms}ms${extra ? ` ${extra}` : ""}`
55796
56404
  );
55797
56405
  }
@@ -55903,7 +56511,7 @@ function runUnifiedGitStatusRefresh(db, threadId, fresh) {
55903
56511
  }
55904
56512
  await refreshTracker.step("db-cache-write", () => setGitStatusCache(db, threadId, result));
55905
56513
  } catch (error) {
55906
- log101.error("Failed to refresh unified git status in background:", error);
56514
+ log102.error("Failed to refresh unified git status in background:", error);
55907
56515
  } finally {
55908
56516
  stopLagMonitor();
55909
56517
  }
@@ -55991,7 +56599,7 @@ async function gitUnifiedStatusHandler(c, _metadataManager, db) {
55991
56599
  return c.json({ error: message }, 500);
55992
56600
  }
55993
56601
  }
55994
- var log101;
56602
+ var log102;
55995
56603
  var init_git_unified_status_route = __esm({
55996
56604
  "src/features/git/git-unified-status.route.ts"() {
55997
56605
  "use strict";
@@ -56002,7 +56610,7 @@ var init_git_unified_status_route = __esm({
56002
56610
  init_threads_database();
56003
56611
  init_git_utils();
56004
56612
  init_git_subprocess_timing();
56005
- log101 = createLogger("features.git.git-unified-status");
56613
+ log102 = createLogger("features.git.git-unified-status");
56006
56614
  }
56007
56615
  });
56008
56616
 
@@ -56056,19 +56664,19 @@ async function gitPrStatusHandler(c, metadataManager) {
56056
56664
  const prStatus = await getPullRequestStatus(resolved.context.gitRoot);
56057
56665
  return c.json({ success: true, ...prStatus });
56058
56666
  } catch (error) {
56059
- log102.error("Failed to check PR status:", error);
56667
+ log103.error("Failed to check PR status:", error);
56060
56668
  const message = error instanceof Error ? error.message : "Failed to check PR status";
56061
56669
  return c.json({ error: message }, 500);
56062
56670
  }
56063
56671
  }
56064
- var log102;
56672
+ var log103;
56065
56673
  var init_git_pr_status_route = __esm({
56066
56674
  "src/features/git/git-pr-status.route.ts"() {
56067
56675
  "use strict";
56068
56676
  init_git_utils();
56069
56677
  init_git_route_context();
56070
56678
  init_dist();
56071
- log102 = createLogger("features.git.git-pr-status");
56679
+ log103 = createLogger("features.git.git-pr-status");
56072
56680
  }
56073
56681
  });
56074
56682
 
@@ -56128,21 +56736,21 @@ import { existsSync as existsSync27 } from "fs";
56128
56736
  async function gitSyncBranchHandler(c, metadataManager) {
56129
56737
  try {
56130
56738
  const threadId = c.req.param("threadId");
56131
- log103.info("Starting branch sync", { threadId });
56739
+ log104.info("Starting branch sync", { threadId });
56132
56740
  const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
56133
56741
  if (!thread) {
56134
- log103.warn("Thread not found", { threadId });
56742
+ log104.warn("Thread not found", { threadId });
56135
56743
  return c.json({ error: "Thread not found" }, 404);
56136
56744
  }
56137
56745
  const repoPath = thread.path;
56138
56746
  if (!repoPath) {
56139
- log103.warn("Thread path not found", { threadId });
56747
+ log104.warn("Thread path not found", { threadId });
56140
56748
  return c.json({ error: "Thread path not found" }, 404);
56141
56749
  }
56142
56750
  const absolutePath = resolveThreadPath(repoPath);
56143
- log103.info("Resolved thread path", { absolutePath });
56751
+ log104.info("Resolved thread path", { absolutePath });
56144
56752
  if (!existsSync27(absolutePath)) {
56145
- log103.warn("Thread repo path does not exist", { absolutePath });
56753
+ log104.warn("Thread repo path does not exist", { absolutePath });
56146
56754
  return c.json(
56147
56755
  {
56148
56756
  error: `Thread repo path does not exist: ${absolutePath}`
@@ -56153,10 +56761,10 @@ async function gitSyncBranchHandler(c, metadataManager) {
56153
56761
  let gitRoot;
56154
56762
  try {
56155
56763
  gitRoot = await getGitRoot(absolutePath);
56156
- log103.info("Resolved git root", { gitRoot });
56764
+ log104.info("Resolved git root", { gitRoot });
56157
56765
  } catch (e) {
56158
56766
  const msg = e instanceof Error ? e.message : String(e);
56159
- log103.warn("Path is not a git repository", { message: msg });
56767
+ log104.warn("Path is not a git repository", { message: msg });
56160
56768
  return c.json(
56161
56769
  {
56162
56770
  error: `Path is not a git repository: ${msg}`
@@ -56166,9 +56774,9 @@ async function gitSyncBranchHandler(c, metadataManager) {
56166
56774
  }
56167
56775
  const currentBranch = await getCurrentBranch(gitRoot);
56168
56776
  const defaultBranch = await findDefaultBranch(gitRoot);
56169
- log103.info("Resolved branches for sync", { currentBranch, defaultBranch });
56777
+ log104.info("Resolved branches for sync", { currentBranch, defaultBranch });
56170
56778
  if (!defaultBranch) {
56171
- log103.warn("Could not determine default branch", { gitRoot });
56779
+ log104.warn("Could not determine default branch", { gitRoot });
56172
56780
  return c.json(
56173
56781
  {
56174
56782
  error: "Could not determine default branch"
@@ -56178,12 +56786,12 @@ async function gitSyncBranchHandler(c, metadataManager) {
56178
56786
  }
56179
56787
  const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
56180
56788
  return new Promise((resolve9) => {
56181
- log103.info("Checking branch status", { gitRoot, currentBranch });
56789
+ log104.info("Checking branch status", { gitRoot, currentBranch });
56182
56790
  const statusProc = spawnProcess2("git", ["status", "-sb"], { cwd: gitRoot });
56183
56791
  let statusOutput = "";
56184
56792
  statusProc.on("close", (statusCode) => {
56185
56793
  if (statusCode !== 0) {
56186
- log103.warn("Failed to get git status", { statusCode });
56794
+ log104.warn("Failed to get git status", { statusCode });
56187
56795
  resolve9(
56188
56796
  c.json(
56189
56797
  {
@@ -56196,28 +56804,28 @@ async function gitSyncBranchHandler(c, metadataManager) {
56196
56804
  return;
56197
56805
  }
56198
56806
  statusOutput = statusOutput.trim();
56199
- log103.debug("Git status output", { statusOutput });
56807
+ log104.debug("Git status output", { statusOutput });
56200
56808
  const isDiverged = statusOutput.includes("ahead") && statusOutput.includes("behind");
56201
56809
  if (isDiverged) {
56202
- log103.info("Branch is diverged, pushing commits first", { currentBranch });
56810
+ log104.info("Branch is diverged, pushing commits first", { currentBranch });
56203
56811
  const pushProc = spawnProcess2("git", ["push", "origin", currentBranch], { cwd: gitRoot });
56204
56812
  let pushOut = "";
56205
56813
  let pushErr = "";
56206
56814
  if (pushProc.stdout) {
56207
56815
  pushProc.stdout.on("data", (d) => {
56208
56816
  pushOut += d.toString();
56209
- log103.debug("Push stdout", { output: d.toString().trim() });
56817
+ log104.debug("Push stdout", { output: d.toString().trim() });
56210
56818
  });
56211
56819
  }
56212
56820
  if (pushProc.stderr) {
56213
56821
  pushProc.stderr.on("data", (d) => {
56214
56822
  pushErr += d.toString();
56215
- log103.debug("Push stderr", { output: d.toString().trim() });
56823
+ log104.debug("Push stderr", { output: d.toString().trim() });
56216
56824
  });
56217
56825
  }
56218
56826
  pushProc.on("close", (pushCode) => {
56219
56827
  if (pushCode !== 0) {
56220
- log103.warn("Failed to push commits before sync", { pushCode });
56828
+ log104.warn("Failed to push commits before sync", { pushCode });
56221
56829
  resolve9(
56222
56830
  c.json(
56223
56831
  {
@@ -56229,11 +56837,11 @@ async function gitSyncBranchHandler(c, metadataManager) {
56229
56837
  );
56230
56838
  return;
56231
56839
  }
56232
- log103.info("Pushed commits successfully before sync", { currentBranch });
56840
+ log104.info("Pushed commits successfully before sync", { currentBranch });
56233
56841
  performSync();
56234
56842
  });
56235
56843
  pushProc.on("error", (error) => {
56236
- log103.error("Push process error", {
56844
+ log104.error("Push process error", {
56237
56845
  message: error instanceof Error ? error.message : String(error)
56238
56846
  });
56239
56847
  resolve9(
@@ -56246,7 +56854,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
56246
56854
  );
56247
56855
  });
56248
56856
  } else {
56249
- log103.info("Branch is not diverged, proceeding with sync", {
56857
+ log104.info("Branch is not diverged, proceeding with sync", {
56250
56858
  currentBranch,
56251
56859
  defaultBranch
56252
56860
  });
@@ -56264,7 +56872,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
56264
56872
  });
56265
56873
  }
56266
56874
  statusProc.on("error", (error) => {
56267
- log103.error("Status process error", {
56875
+ log104.error("Status process error", {
56268
56876
  message: error instanceof Error ? error.message : String(error)
56269
56877
  });
56270
56878
  resolve9(
@@ -56277,11 +56885,11 @@ async function gitSyncBranchHandler(c, metadataManager) {
56277
56885
  );
56278
56886
  });
56279
56887
  function performSync() {
56280
- log103.info("Configuring git pull strategy to rebase", { gitRoot });
56888
+ log104.info("Configuring git pull strategy to rebase", { gitRoot });
56281
56889
  const configProc = spawnProcess2("git", ["config", "pull.rebase", "true"], { cwd: gitRoot });
56282
56890
  configProc.on("close", (configCode) => {
56283
56891
  if (configCode !== 0) {
56284
- log103.warn("Failed to configure git pull strategy", { configCode });
56892
+ log104.warn("Failed to configure git pull strategy", { configCode });
56285
56893
  resolve9(
56286
56894
  c.json(
56287
56895
  {
@@ -56293,8 +56901,8 @@ async function gitSyncBranchHandler(c, metadataManager) {
56293
56901
  );
56294
56902
  return;
56295
56903
  }
56296
- log103.info("Configured git pull strategy", { gitRoot });
56297
- log103.info("Starting git pull rebase", { defaultBranch });
56904
+ log104.info("Configured git pull strategy", { gitRoot });
56905
+ log104.info("Starting git pull rebase", { defaultBranch });
56298
56906
  const pullProc = spawnProcess2("git", ["pull", "--rebase", "origin", defaultBranch], {
56299
56907
  cwd: gitRoot
56300
56908
  });
@@ -56303,19 +56911,19 @@ async function gitSyncBranchHandler(c, metadataManager) {
56303
56911
  if (pullProc.stdout) {
56304
56912
  pullProc.stdout.on("data", (d) => {
56305
56913
  out += d.toString();
56306
- log103.debug("Pull stdout", { output: d.toString().trim() });
56914
+ log104.debug("Pull stdout", { output: d.toString().trim() });
56307
56915
  });
56308
56916
  }
56309
56917
  if (pullProc.stderr) {
56310
56918
  pullProc.stderr.on("data", (d) => {
56311
56919
  err += d.toString();
56312
- log103.debug("Pull stderr", { output: d.toString().trim() });
56920
+ log104.debug("Pull stderr", { output: d.toString().trim() });
56313
56921
  });
56314
56922
  }
56315
56923
  pullProc.on("close", (code) => {
56316
56924
  if (code === 0) {
56317
56925
  const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
56318
- log103.info("Branch sync completed successfully", {
56926
+ log104.info("Branch sync completed successfully", {
56319
56927
  message,
56320
56928
  branch: currentBranch,
56321
56929
  defaultBranch
@@ -56329,8 +56937,8 @@ async function gitSyncBranchHandler(c, metadataManager) {
56329
56937
  })
56330
56938
  );
56331
56939
  } else {
56332
- log103.warn("Failed to sync branch", { code, defaultBranch });
56333
- log103.warn("Branch sync error output", { output: err || out });
56940
+ log104.warn("Failed to sync branch", { code, defaultBranch });
56941
+ log104.warn("Branch sync error output", { output: err || out });
56334
56942
  resolve9(
56335
56943
  c.json(
56336
56944
  {
@@ -56343,7 +56951,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
56343
56951
  }
56344
56952
  });
56345
56953
  pullProc.on("error", (error) => {
56346
- log103.error("Branch sync process error", {
56954
+ log104.error("Branch sync process error", {
56347
56955
  message: error instanceof Error ? error.message : String(error)
56348
56956
  });
56349
56957
  resolve9(
@@ -56357,7 +56965,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
56357
56965
  });
56358
56966
  });
56359
56967
  configProc.on("error", (error) => {
56360
- log103.error("Git config process error", {
56968
+ log104.error("Git config process error", {
56361
56969
  message: error instanceof Error ? error.message : String(error)
56362
56970
  });
56363
56971
  resolve9(
@@ -56373,17 +56981,17 @@ async function gitSyncBranchHandler(c, metadataManager) {
56373
56981
  });
56374
56982
  } catch (error) {
56375
56983
  const message = error instanceof Error ? error.message : "Failed to sync branch";
56376
- log103.error("Unexpected branch sync error", { message });
56984
+ log104.error("Unexpected branch sync error", { message });
56377
56985
  return c.json({ error: message }, 500);
56378
56986
  }
56379
56987
  }
56380
- var log103;
56988
+ var log104;
56381
56989
  var init_git_sync_branch_route = __esm({
56382
56990
  "src/features/git/git-sync-branch.route.ts"() {
56383
56991
  "use strict";
56384
56992
  init_logger2();
56385
56993
  init_git_utils();
56386
- log103 = createLogger("git.sync-branch");
56994
+ log104 = createLogger("git.sync-branch");
56387
56995
  }
56388
56996
  });
56389
56997
 
@@ -56689,7 +57297,7 @@ async function gitCheckpointHandler(c, metadataManager) {
56689
57297
  }
56690
57298
  const repoPath = thread.path;
56691
57299
  if (!repoPath) {
56692
- log104.debug("Checkpoint skipped: thread has no path", { threadId });
57300
+ log105.debug("Checkpoint skipped: thread has no path", { threadId });
56693
57301
  return c.json({ success: false, reason: "no_path" });
56694
57302
  }
56695
57303
  const absolutePath = resolveThreadPath(repoPath);
@@ -56697,36 +57305,36 @@ async function gitCheckpointHandler(c, metadataManager) {
56697
57305
  try {
56698
57306
  gitRoot = await getGitRoot(absolutePath);
56699
57307
  } catch {
56700
- log104.debug("Checkpoint skipped: not a git repository", { threadId, absolutePath });
57308
+ log105.debug("Checkpoint skipped: not a git repository", { threadId, absolutePath });
56701
57309
  return c.json({ success: false, reason: "not_a_git_repo" });
56702
57310
  }
56703
57311
  const commitCount = await getCommitCount(gitRoot);
56704
57312
  if (commitCount === 0) {
56705
- log104.debug("Checkpoint skipped: repository has no commits", { threadId, gitRoot });
57313
+ log105.debug("Checkpoint skipped: repository has no commits", { threadId, gitRoot });
56706
57314
  return c.json({ success: true, stashed: false, reason: "no_commits" });
56707
57315
  }
56708
57316
  const label = `tarsk-checkpoint:${messageId}`;
56709
57317
  const stashed = await stashWithLabel(gitRoot, label);
56710
57318
  if (stashed) {
56711
- log104.info("Checkpoint created", { threadId, messageId, gitRoot });
57319
+ log105.info("Checkpoint created", { threadId, messageId, gitRoot });
56712
57320
  } else {
56713
- log104.info("Checkpoint recorded with clean working tree", { threadId, messageId, gitRoot });
57321
+ log105.info("Checkpoint recorded with clean working tree", { threadId, messageId, gitRoot });
56714
57322
  }
56715
57323
  return c.json({ success: true, stashed, checkpointRef: label });
56716
57324
  } catch (error) {
56717
57325
  const message = error instanceof Error ? error.message : "Failed to create checkpoint";
56718
- log104.error("Checkpoint creation failed", { threadId: c.req.param("threadId"), error: message });
57326
+ log105.error("Checkpoint creation failed", { threadId: c.req.param("threadId"), error: message });
56719
57327
  return c.json({ error: message }, 500);
56720
57328
  }
56721
57329
  }
56722
- var log104;
57330
+ var log105;
56723
57331
  var init_git_checkpoint_route = __esm({
56724
57332
  "src/features/git/git-checkpoint.route.ts"() {
56725
57333
  "use strict";
56726
57334
  init_dist();
56727
57335
  init_git_utils();
56728
57336
  init_utils();
56729
- log104 = createLogger("git.checkpoint");
57337
+ log105 = createLogger("git.checkpoint");
56730
57338
  }
56731
57339
  });
56732
57340
 
@@ -56822,15 +57430,15 @@ async function gitCheckpointRestoreHandler(c, metadataManager, conversationManag
56822
57430
  const match = stashes.find((s) => s.label === checkpointRef);
56823
57431
  if (!match) {
56824
57432
  if (checkpointRef.startsWith(TARSK_CHECKPOINT_PREFIX)) {
56825
- log105.info("Restoring clean-tree checkpoint", { threadId, checkpointRef, gitRoot });
57433
+ log106.info("Restoring clean-tree checkpoint", { threadId, checkpointRef, gitRoot });
56826
57434
  await discardWorkingChanges(gitRoot);
56827
57435
  await truncateConversationFromCheckpoint(conversationManager, threadId, checkpointRef);
56828
57436
  return c.json({ success: true });
56829
57437
  }
56830
- log105.warn("Checkpoint not found in stash list", { threadId, checkpointRef, gitRoot });
57438
+ log106.warn("Checkpoint not found in stash list", { threadId, checkpointRef, gitRoot });
56831
57439
  return c.json({ error: "Checkpoint not found in stash list" }, 404);
56832
57440
  }
56833
- log105.info("Restoring stash checkpoint", {
57441
+ log106.info("Restoring stash checkpoint", {
56834
57442
  threadId,
56835
57443
  checkpointRef,
56836
57444
  gitRoot,
@@ -56842,26 +57450,26 @@ async function gitCheckpointRestoreHandler(c, metadataManager, conversationManag
56842
57450
  return c.json({ success: true });
56843
57451
  } catch (error) {
56844
57452
  const message = error instanceof Error ? error.message : "Failed to restore checkpoint";
56845
- log105.error("Checkpoint restore failed", { threadId: c.req.param("threadId"), error: message });
57453
+ log106.error("Checkpoint restore failed", { threadId: c.req.param("threadId"), error: message });
56846
57454
  return c.json({ error: message }, 500);
56847
57455
  }
56848
57456
  }
56849
57457
  async function truncateConversationFromCheckpoint(conversationManager, threadId, checkpointRef) {
56850
57458
  const deletedCount = await conversationManager.truncateFromCheckpoint(threadId, checkpointRef);
56851
- log105.info("Removed conversation messages after checkpoint restore", {
57459
+ log106.info("Removed conversation messages after checkpoint restore", {
56852
57460
  threadId,
56853
57461
  checkpointRef,
56854
57462
  deletedCount
56855
57463
  });
56856
57464
  }
56857
- var log105, TARSK_CHECKPOINT_PREFIX;
57465
+ var log106, TARSK_CHECKPOINT_PREFIX;
56858
57466
  var init_git_checkpoint_restore_route = __esm({
56859
57467
  "src/features/git/git-checkpoint-restore.route.ts"() {
56860
57468
  "use strict";
56861
57469
  init_dist();
56862
57470
  init_git_utils();
56863
57471
  init_utils();
56864
- log105 = createLogger("git.checkpoint-restore");
57472
+ log106 = createLogger("git.checkpoint-restore");
56865
57473
  TARSK_CHECKPOINT_PREFIX = "tarsk-checkpoint:";
56866
57474
  }
56867
57475
  });
@@ -57146,7 +57754,7 @@ function createUpdateRoutes(updater) {
57146
57754
  supported: true
57147
57755
  });
57148
57756
  } catch (error) {
57149
- log106.error("Failed to check for update", error);
57757
+ log107.error("Failed to check for update", error);
57150
57758
  return c.json({ updateAvailable: false, supported: true, error: "Check failed" });
57151
57759
  }
57152
57760
  });
@@ -57163,18 +57771,18 @@ function createUpdateRoutes(updater) {
57163
57771
  }
57164
57772
  return c.json({ success: false, message: "Update downloaded but not ready to apply" });
57165
57773
  } catch (error) {
57166
- log106.error("Failed to apply update", error);
57774
+ log107.error("Failed to apply update", error);
57167
57775
  return c.json({ error: "Failed to apply update" }, 500);
57168
57776
  }
57169
57777
  });
57170
57778
  return app;
57171
57779
  }
57172
- var log106;
57780
+ var log107;
57173
57781
  var init_updates_routes = __esm({
57174
57782
  "src/features/updates/updates.routes.ts"() {
57175
57783
  "use strict";
57176
57784
  init_dist();
57177
- log106 = createLogger("updates.routes");
57785
+ log107 = createLogger("updates.routes");
57178
57786
  }
57179
57787
  });
57180
57788
 
@@ -57350,12 +57958,12 @@ async function performMCPOAuthFlow(options) {
57350
57958
  onRedirect: async (authorizationUrl) => {
57351
57959
  const url = authorizationUrl.toString();
57352
57960
  onAuthorizationUrl?.(url);
57353
- log107.info("Opening MCP OAuth authorization URL", { serverName });
57961
+ log108.info("Opening MCP OAuth authorization URL", { serverName });
57354
57962
  await openExternalUrlInSystemBrowser(url);
57355
57963
  }
57356
57964
  });
57357
57965
  if (!oauthConfig?.clientId) {
57358
- log107.debug("No pre-registered OAuth clientId; will attempt DCR or CIMD", {
57966
+ log108.debug("No pre-registered OAuth clientId; will attempt DCR or CIMD", {
57359
57967
  serverName
57360
57968
  });
57361
57969
  }
@@ -57479,7 +58087,7 @@ async function performMCPOAuthFlow(options) {
57479
58087
  try {
57480
58088
  const result2 = await sdkAuth(provider, { serverUrl });
57481
58089
  if (result2 !== "REDIRECT") {
57482
- log107.warn("Unexpected MCP OAuth auth result before redirect", {
58090
+ log108.warn("Unexpected MCP OAuth auth result before redirect", {
57483
58091
  serverName,
57484
58092
  result: result2
57485
58093
  });
@@ -57512,7 +58120,7 @@ async function performMCPOAuthFlow(options) {
57512
58120
  if (result !== "AUTHORIZED") {
57513
58121
  throw new Error(`Unexpected MCP OAuth result: ${result}`);
57514
58122
  }
57515
- log107.info("MCP OAuth authorization succeeded", { serverName });
58123
+ log108.info("MCP OAuth authorization succeeded", { serverName });
57516
58124
  return {
57517
58125
  authorized: true,
57518
58126
  authorizationUrl: provider.authorizationUrl
@@ -57521,7 +58129,7 @@ async function performMCPOAuthFlow(options) {
57521
58129
  cleanup();
57522
58130
  }
57523
58131
  }
57524
- var log107, AUTH_TIMEOUT_MS;
58132
+ var log108, AUTH_TIMEOUT_MS;
57525
58133
  var init_mcp_oauth_flow = __esm({
57526
58134
  "src/features/mcp/mcp.oauth-flow.ts"() {
57527
58135
  "use strict";
@@ -57529,7 +58137,7 @@ var init_mcp_oauth_flow = __esm({
57529
58137
  init_open_external_url();
57530
58138
  init_mcp_oauth_provider();
57531
58139
  init_mcp_oauth_port();
57532
- log107 = createLogger("mcp.oauth.flow");
58140
+ log108 = createLogger("mcp.oauth.flow");
57533
58141
  AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
57534
58142
  }
57535
58143
  });
@@ -57699,7 +58307,7 @@ function createMCPRoutes() {
57699
58307
  });
57700
58308
  } catch (error) {
57701
58309
  const message = error instanceof Error ? error.message : String(error);
57702
- log108.warn("MCP OAuth authorize failed", { projectPath, serverName, error: message });
58310
+ log109.warn("MCP OAuth authorize failed", { projectPath, serverName, error: message });
57703
58311
  return c.json({ error: { code: "OAUTH_ERROR", message } }, 500);
57704
58312
  }
57705
58313
  });
@@ -57784,12 +58392,12 @@ function createMCPRoutes() {
57784
58392
  const limit = c.req.query("limit") ?? "10";
57785
58393
  const url = `https://registry.modelcontextprotocol.io/v0/servers?search=${encodeURIComponent(q)}&limit=${encodeURIComponent(limit)}`;
57786
58394
  try {
57787
- log108.debug("Fetching MCP registry search results", { url, query: q, limit });
58395
+ log109.debug("Fetching MCP registry search results", { url, query: q, limit });
57788
58396
  const response = await fetch(url, {
57789
58397
  headers: { Accept: "application/json" }
57790
58398
  });
57791
58399
  if (!response.ok) {
57792
- log108.warn("MCP registry returned non-OK response", {
58400
+ log109.warn("MCP registry returned non-OK response", {
57793
58401
  status: response.status,
57794
58402
  statusText: response.statusText,
57795
58403
  url
@@ -57800,7 +58408,7 @@ function createMCPRoutes() {
57800
58408
  );
57801
58409
  }
57802
58410
  const data = await response.json();
57803
- log108.debug("Received MCP registry search response", {
58411
+ log109.debug("Received MCP registry search response", {
57804
58412
  url,
57805
58413
  resultCount: data.servers?.length,
57806
58414
  data
@@ -57808,7 +58416,7 @@ function createMCPRoutes() {
57808
58416
  return c.json(data);
57809
58417
  } catch (error) {
57810
58418
  const message = error instanceof Error ? error.message : String(error);
57811
- log108.error("Failed to fetch MCP registry search results", {
58419
+ log109.error("Failed to fetch MCP registry search results", {
57812
58420
  url,
57813
58421
  query: q,
57814
58422
  limit,
@@ -57819,7 +58427,7 @@ function createMCPRoutes() {
57819
58427
  });
57820
58428
  return router;
57821
58429
  }
57822
- var MCP_CONFIG_PATHS2, log108;
58430
+ var MCP_CONFIG_PATHS2, log109;
57823
58431
  var init_mcp_routes = __esm({
57824
58432
  "src/features/mcp/mcp.routes.ts"() {
57825
58433
  "use strict";
@@ -57830,7 +58438,7 @@ var init_mcp_routes = __esm({
57830
58438
  init_mcp_oauth_store();
57831
58439
  init_mcp_tools();
57832
58440
  MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
57833
- log108 = createLogger("mcp.routes");
58441
+ log109 = createLogger("mcp.routes");
57834
58442
  }
57835
58443
  });
57836
58444
 
@@ -57935,7 +58543,7 @@ var init_skills_popular = __esm({
57935
58543
  });
57936
58544
 
57937
58545
  // src/features/skills/skills.catalog.ts
57938
- import { readdir as readdir14, readFile as readFile20 } from "fs/promises";
58546
+ import { readdir as readdir15, readFile as readFile20 } from "fs/promises";
57939
58547
  import { join as join43 } from "path";
57940
58548
  function getSkillSources() {
57941
58549
  const sources = skills_sources_default;
@@ -57949,7 +58557,7 @@ async function getAllSkillSources() {
57949
58557
  return [...bundled, ...uniqueCustom].sort((a, b) => a.title.localeCompare(b.title));
57950
58558
  }
57951
58559
  async function scanDownloadedSkillCatalog(tmpDir, parsed, sourceId) {
57952
- const entries = await readdir14(tmpDir, { withFileTypes: true });
58560
+ const entries = await readdir15(tmpDir, { withFileTypes: true });
57953
58561
  const catalogItems = [];
57954
58562
  for (const entry of entries) {
57955
58563
  if (!entry.isDirectory()) {
@@ -58392,12 +59000,6 @@ async function resolvePreferredTaskTitleModel(metadataManager, options) {
58392
59000
  return resolveModelAndKey(thread.modelProvider, thread.model, metadataManager);
58393
59001
  }
58394
59002
  }
58395
- for (const [slug, defaultModel] of Object.entries(DEFAULT_MODELS)) {
58396
- const resolved = await resolveModelAndKey(slug, defaultModel, metadataManager);
58397
- if (resolved) {
58398
- return resolved;
58399
- }
58400
- }
58401
59003
  return null;
58402
59004
  }
58403
59005
  async function generateProjectTaskTitle(description, metadataManager, options) {
@@ -58425,11 +59027,11 @@ Todo description: ${trimmed}`;
58425
59027
  return title || fallbackTitle;
58426
59028
  } catch (error) {
58427
59029
  const details = error instanceof Error ? error.stack ?? error.message : String(error);
58428
- log109.error("[project-tasks.title] Failed to generate task title:", details);
59030
+ log110.error("[project-tasks.title] Failed to generate task title:", details);
58429
59031
  return fallbackTitle;
58430
59032
  }
58431
59033
  }
58432
- var log109, DEFAULT_MODELS;
59034
+ var log110;
58433
59035
  var init_project_tasks_title = __esm({
58434
59036
  "src/features/project-tasks/project-tasks.title.ts"() {
58435
59037
  "use strict";
@@ -58438,14 +59040,7 @@ var init_project_tasks_title = __esm({
58438
59040
  init_database();
58439
59041
  init_threads_database();
58440
59042
  init_dist();
58441
- log109 = createLogger("features.project-tasks.project-tasks.title");
58442
- DEFAULT_MODELS = {
58443
- anthropic: "claude-3-5-sonnet-20241022",
58444
- openai: "gpt-4o-mini",
58445
- openrouter: "anthropic/claude-3.5-sonnet",
58446
- groq: "llama-3.3-70b-versatile",
58447
- deepseek: "deepseek-chat"
58448
- };
59043
+ log110 = createLogger("features.project-tasks.project-tasks.title");
58449
59044
  }
58450
59045
  });
58451
59046
 
@@ -58711,7 +59306,7 @@ function removeFileIfPresent(filePath) {
58711
59306
  fs4.unlinkSync(filePath);
58712
59307
  }
58713
59308
  } catch (error) {
58714
- log110.warn(`Failed to remove ${filePath}:`, error);
59309
+ log111.warn(`Failed to remove ${filePath}:`, error);
58715
59310
  }
58716
59311
  }
58717
59312
  async function deleteAccount(c) {
@@ -58724,22 +59319,22 @@ async function deleteAccount(c) {
58724
59319
  clearLogBuffer();
58725
59320
  clearFileLog();
58726
59321
  removeFileIfPresent(getFileLoggingPath());
58727
- log110.info("Account deleted: database and logs removed");
59322
+ log111.info("Account deleted: database and logs removed");
58728
59323
  return c.json({ success: true });
58729
59324
  } catch (error) {
58730
59325
  const message = error instanceof Error ? error.message : "Failed to delete account";
58731
- log110.error("Failed to delete account:", error);
59326
+ log111.error("Failed to delete account:", error);
58732
59327
  return c.json({ error: message }, 500);
58733
59328
  }
58734
59329
  }
58735
- var log110, DB_SIDECAR_SUFFIXES;
59330
+ var log111, DB_SIDECAR_SUFFIXES;
58736
59331
  var init_account_delete_route = __esm({
58737
59332
  "src/features/account/account-delete.route.ts"() {
58738
59333
  "use strict";
58739
59334
  init_dist();
58740
59335
  init_database();
58741
59336
  init_file_log_transport();
58742
- log110 = createLogger("features.account.account-delete");
59337
+ log111 = createLogger("features.account.account-delete");
58743
59338
  DB_SIDECAR_SUFFIXES = ["", "-wal", "-shm"];
58744
59339
  }
58745
59340
  });
@@ -59176,13 +59771,13 @@ async function runChannelPrompt(params) {
59176
59771
  })
59177
59772
  });
59178
59773
  } catch (error) {
59179
- log111.error("Failed to reach chat API from Control Channel", { error, threadId });
59774
+ log112.error("Failed to reach chat API from Control Channel", { error, threadId });
59180
59775
  return { reply: "", error: formatChatConnectionError(error) };
59181
59776
  }
59182
59777
  if (response.status === 409) {
59183
59778
  const resumeThreadId = await readResumeThreadId(response);
59184
59779
  if (resumeThreadId) {
59185
- log111.warn("Chat POST conflict; resuming accepted turn via subscribe", {
59780
+ log112.warn("Chat POST conflict; resuming accepted turn via subscribe", {
59186
59781
  threadId: resumeThreadId,
59187
59782
  idempotencyKey
59188
59783
  });
@@ -59224,7 +59819,7 @@ async function runChannelPrompt(params) {
59224
59819
  });
59225
59820
  }
59226
59821
  const interruptError = initial.error;
59227
- log111.warn("Chat stream interrupted before complete; starting recovery", {
59822
+ log112.warn("Chat stream interrupted before complete; starting recovery", {
59228
59823
  threadId,
59229
59824
  interruptedByError: initial.interruptedByError,
59230
59825
  error: interruptError
@@ -59245,7 +59840,7 @@ async function runChannelPrompt(params) {
59245
59840
  needsUiInput: handlerCtx.needsUiInput
59246
59841
  });
59247
59842
  }
59248
- log111.error("Chat stream recovery exhausted for Control Channel", {
59843
+ log112.error("Chat stream recovery exhausted for Control Channel", {
59249
59844
  error: interruptError ?? recovered.error,
59250
59845
  threadId
59251
59846
  });
@@ -59309,7 +59904,7 @@ async function recoverChatStreamViaSubscribe(params) {
59309
59904
  response = await fetch(subscribeUrl);
59310
59905
  } catch (error) {
59311
59906
  lastError = error;
59312
- log111.warn("Chat stream subscribe recovery attempt failed", {
59907
+ log112.warn("Chat stream subscribe recovery attempt failed", {
59313
59908
  threadId,
59314
59909
  attempt: attempt + 1,
59315
59910
  error
@@ -59317,7 +59912,7 @@ async function recoverChatStreamViaSubscribe(params) {
59317
59912
  return { ok: false, done: false };
59318
59913
  }
59319
59914
  if (response.status === 404) {
59320
- log111.info("Chat stream recovery: turn finished before subscribe", {
59915
+ log112.info("Chat stream recovery: turn finished before subscribe", {
59321
59916
  threadId,
59322
59917
  attempt: attempt + 1
59323
59918
  });
@@ -59325,7 +59920,7 @@ async function recoverChatStreamViaSubscribe(params) {
59325
59920
  }
59326
59921
  if (!response.ok) {
59327
59922
  lastError = new Error(`Subscribe failed (${response.status})`);
59328
- log111.warn("Chat stream subscribe recovery attempt returned error", {
59923
+ log112.warn("Chat stream subscribe recovery attempt returned error", {
59329
59924
  threadId,
59330
59925
  attempt: attempt + 1,
59331
59926
  status: response.status
@@ -59339,14 +59934,14 @@ async function recoverChatStreamViaSubscribe(params) {
59339
59934
  streamEvents.length = 0;
59340
59935
  const consumed = await consumeNdjsonChatStream(response.body, handleEvent);
59341
59936
  if (consumed.sawComplete) {
59342
- log111.info("Chat stream recovery succeeded via subscribe", {
59937
+ log112.info("Chat stream recovery succeeded via subscribe", {
59343
59938
  threadId,
59344
59939
  attempt: attempt + 1
59345
59940
  });
59346
59941
  return { ok: true, done: true };
59347
59942
  }
59348
59943
  lastError = consumed.error ?? new Error("Subscribe stream ended without complete");
59349
- log111.warn("Chat stream subscribe recovery ended before complete", {
59944
+ log112.warn("Chat stream subscribe recovery ended before complete", {
59350
59945
  threadId,
59351
59946
  attempt: attempt + 1,
59352
59947
  error: lastError
@@ -59456,7 +60051,7 @@ async function pollBufferedGateEvents(params) {
59456
60051
  try {
59457
60052
  response = await fetch(`${baseUrl}/api/chat/buffer/${encodeURIComponent(threadId)}`);
59458
60053
  } catch (error) {
59459
- log111.warn("Buffered gate poll failed", { threadId, error });
60054
+ log112.warn("Buffered gate poll failed", { threadId, error });
59460
60055
  await sleep2(pollIntervalMs);
59461
60056
  continue;
59462
60057
  }
@@ -59473,7 +60068,7 @@ async function pollBufferedGateEvents(params) {
59473
60068
  handleEvent
59474
60069
  });
59475
60070
  if (gateCount > 0) {
59476
- log111.info("Buffered gate poll: new gates", {
60071
+ log112.info("Buffered gate poll: new gates", {
59477
60072
  threadId,
59478
60073
  gateCount,
59479
60074
  scanned: newEvents.length,
@@ -59485,7 +60080,7 @@ async function pollBufferedGateEvents(params) {
59485
60080
  await promptPendingAskUserFromApi({ baseUrl, handleEvent });
59486
60081
  if (body.processing === false) {
59487
60082
  appendSummaryEventsFromBuffer(handlerCtx.streamEvents, events);
59488
- log111.info("Buffered gate poll: turn finished", { threadId, totalEvents: events.length });
60083
+ log112.info("Buffered gate poll: turn finished", { threadId, totalEvents: events.length });
59489
60084
  return true;
59490
60085
  }
59491
60086
  await sleep2(pollIntervalMs);
@@ -59504,7 +60099,7 @@ async function watchChannelTurnUntilComplete(params) {
59504
60099
  needsUiInput: false
59505
60100
  };
59506
60101
  const handleEvent = createChannelStreamHandler({ onNeedsUi }, handlerCtx);
59507
- log111.info("Background Control Channel recovery: polling for gates", { threadId });
60102
+ log112.info("Background Control Channel recovery: polling for gates", { threadId });
59508
60103
  const bufferFinished = await pollBufferedGateEvents({
59509
60104
  baseUrl,
59510
60105
  threadId,
@@ -59512,14 +60107,14 @@ async function watchChannelTurnUntilComplete(params) {
59512
60107
  handlerCtx
59513
60108
  });
59514
60109
  if (bufferFinished) {
59515
- log111.info("Background Control Channel recovery: turn finished", { threadId });
60110
+ log112.info("Background Control Channel recovery: turn finished", { threadId });
59516
60111
  return finalizeChannelPromptResult({
59517
60112
  streamEvents,
59518
60113
  streamError: handlerCtx.streamError,
59519
60114
  needsUiInput: handlerCtx.needsUiInput
59520
60115
  });
59521
60116
  }
59522
- log111.error("Background Control Channel recovery exhausted", { threadId });
60117
+ log112.error("Background Control Channel recovery exhausted", { threadId });
59523
60118
  const partial = assistantTextFromChatStreamEvents(streamEvents).trim();
59524
60119
  return {
59525
60120
  reply: partial,
@@ -59679,17 +60274,17 @@ async function stopChannelThread(baseUrl, threadId) {
59679
60274
  });
59680
60275
  return response.ok;
59681
60276
  } catch (error) {
59682
- log111.warn("Failed to stop thread from Control Channel", { error, threadId });
60277
+ log112.warn("Failed to stop thread from Control Channel", { error, threadId });
59683
60278
  return false;
59684
60279
  }
59685
60280
  }
59686
- var log111, CHAT_STREAM_INITIAL_RECOVERY_BACKOFF_MS, CHAT_STREAM_EXTENDED_RETRY_MS, CHAT_STREAM_RECOVERY_MAX_MS, CHAT_BUFFER_GATE_POLL_MS;
60281
+ var log112, CHAT_STREAM_INITIAL_RECOVERY_BACKOFF_MS, CHAT_STREAM_EXTENDED_RETRY_MS, CHAT_STREAM_RECOVERY_MAX_MS, CHAT_BUFFER_GATE_POLL_MS;
59687
60282
  var init_slack_chat = __esm({
59688
60283
  "src/features/slack/slack.chat.ts"() {
59689
60284
  "use strict";
59690
60285
  init_dist();
59691
60286
  init_conversations_content();
59692
- log111 = createLogger("slack.chat");
60287
+ log112 = createLogger("slack.chat");
59693
60288
  CHAT_STREAM_INITIAL_RECOVERY_BACKOFF_MS = [1e3, 2e3, 4e3];
59694
60289
  CHAT_STREAM_EXTENDED_RETRY_MS = 3e4;
59695
60290
  CHAT_STREAM_RECOVERY_MAX_MS = 30 * 60 * 1e3;
@@ -59814,7 +60409,7 @@ async function fetchSlackChannelHistoryMessages(params) {
59814
60409
  });
59815
60410
  return history.map((message) => toCatchUpMessage(channelId, message)).filter((message) => message !== null).sort((a, b) => compareSlackTs(a.ts, b.ts));
59816
60411
  } catch (error) {
59817
- log112.warn("conversations.history failed during Slack catch-up", {
60412
+ log113.warn("conversations.history failed during Slack catch-up", {
59818
60413
  teamId,
59819
60414
  channelId,
59820
60415
  error
@@ -59848,7 +60443,7 @@ async function fetchSlackThreadRawReplies(params) {
59848
60443
  });
59849
60444
  byThread.set(map.slackThreadTs, replies);
59850
60445
  } catch (error) {
59851
- log112.warn("conversations.replies failed during Slack catch-up", {
60446
+ log113.warn("conversations.replies failed during Slack catch-up", {
59852
60447
  teamId,
59853
60448
  channelId,
59854
60449
  slackThreadTs: map.slackThreadTs,
@@ -59878,7 +60473,7 @@ async function fetchSlackThreadRawRepliesForRoot(params) {
59878
60473
  }
59879
60474
  });
59880
60475
  } catch (error) {
59881
- log112.warn("conversations.replies failed during Slack catch-up lookup", {
60476
+ log113.warn("conversations.replies failed during Slack catch-up lookup", {
59882
60477
  channelId,
59883
60478
  threadTs,
59884
60479
  error
@@ -59980,7 +60575,7 @@ async function catchUpSlackMissedMessages(deps) {
59980
60575
  await deps.ingest(message, deps.teamId);
59981
60576
  messagesIngested += 1;
59982
60577
  } catch (error) {
59983
- log112.error("Failed to ingest Slack catch-up history message", {
60578
+ log113.error("Failed to ingest Slack catch-up history message", {
59984
60579
  teamId: deps.teamId,
59985
60580
  channelId,
59986
60581
  ts: message.ts,
@@ -60014,7 +60609,7 @@ async function catchUpSlackMissedMessages(deps) {
60014
60609
  await deps.ingest(message, deps.teamId);
60015
60610
  messagesIngested += 1;
60016
60611
  } catch (error) {
60017
- log112.error("Failed to ingest Slack catch-up reply message", {
60612
+ log113.error("Failed to ingest Slack catch-up reply message", {
60018
60613
  teamId: deps.teamId,
60019
60614
  channelId,
60020
60615
  ts: message.ts,
@@ -60031,13 +60626,13 @@ async function catchUpSlackMissedMessages(deps) {
60031
60626
  }
60032
60627
  return { channelsScanned: bindings.length, messagesIngested };
60033
60628
  }
60034
- var log112, SLACK_CATCH_UP_MAX_LOOKBACK_MS, MAX_PAGES_PER_FETCH, PAGE_SIZE;
60629
+ var log113, SLACK_CATCH_UP_MAX_LOOKBACK_MS, MAX_PAGES_PER_FETCH, PAGE_SIZE;
60035
60630
  var init_slack_catch_up = __esm({
60036
60631
  "src/features/slack/slack.catch-up.ts"() {
60037
60632
  "use strict";
60038
60633
  init_dist();
60039
60634
  init_slack_chat();
60040
- log112 = createLogger("slack.catch-up");
60635
+ log113 = createLogger("slack.catch-up");
60041
60636
  SLACK_CATCH_UP_MAX_LOOKBACK_MS = 60 * 60 * 1e3;
60042
60637
  MAX_PAGES_PER_FETCH = 5;
60043
60638
  PAGE_SIZE = 200;
@@ -60374,7 +60969,7 @@ async function getEncryptedSecret(key) {
60374
60969
  }
60375
60970
  return stored;
60376
60971
  } catch (error) {
60377
- log113.error("Failed to decrypt Slack secret", { key, error });
60972
+ log114.error("Failed to decrypt Slack secret", { key, error });
60378
60973
  return null;
60379
60974
  }
60380
60975
  }
@@ -60693,7 +61288,7 @@ async function clearSlackHandledPrompts() {
60693
61288
  const db = await getDatabase();
60694
61289
  await deleteState(db, HANDLED_PROMPTS_KEY);
60695
61290
  }
60696
- var log113, BOT_TOKEN_KEY, APP_TOKEN_KEY, BINDINGS_KEY, THREAD_MAPS_KEY, CATCH_UP_CURSORS_KEY, HANDLED_PROMPTS_KEY, HANDLED_PROMPT_RETENTION_MS, handledPromptClaimChain;
61291
+ var log114, BOT_TOKEN_KEY, APP_TOKEN_KEY, BINDINGS_KEY, THREAD_MAPS_KEY, CATCH_UP_CURSORS_KEY, HANDLED_PROMPTS_KEY, HANDLED_PROMPT_RETENTION_MS, handledPromptClaimChain;
60697
61292
  var init_slack_state = __esm({
60698
61293
  "src/features/slack/slack.state.ts"() {
60699
61294
  "use strict";
@@ -60701,7 +61296,7 @@ var init_slack_state = __esm({
60701
61296
  init_database();
60702
61297
  init_database_state();
60703
61298
  init_database_encryption();
60704
- log113 = createLogger("slack.state");
61299
+ log114 = createLogger("slack.state");
60705
61300
  BOT_TOKEN_KEY = "SlackBotToken";
60706
61301
  APP_TOKEN_KEY = "SlackAppToken";
60707
61302
  BINDINGS_KEY = "SlackChannelBindings";
@@ -60889,7 +61484,7 @@ function resolveMappedSlackMode(map, defaultTarskThreadId) {
60889
61484
  function slackMessageDedupeKey(teamId, channelId, messageTs) {
60890
61485
  return `${teamId}:${channelId}:${messageTs}`;
60891
61486
  }
60892
- var log114, MESSAGE_DEDUPE_TTL_MS, SlackBridge;
61487
+ var log115, MESSAGE_DEDUPE_TTL_MS, SlackBridge;
60893
61488
  var init_slack_bridge = __esm({
60894
61489
  "src/features/slack/slack.bridge.ts"() {
60895
61490
  "use strict";
@@ -60904,7 +61499,7 @@ var init_slack_bridge = __esm({
60904
61499
  init_slack_skills();
60905
61500
  init_slack_state();
60906
61501
  init_slack_tasks();
60907
- log114 = createLogger("slack.bridge");
61502
+ log115 = createLogger("slack.bridge");
60908
61503
  MESSAGE_DEDUPE_TTL_MS = 5 * 60 * 1e3;
60909
61504
  SlackBridge = class {
60910
61505
  deps;
@@ -60963,7 +61558,7 @@ var init_slack_bridge = __esm({
60963
61558
  const botToken = await getSlackBotToken();
60964
61559
  const appToken = await getSlackAppToken();
60965
61560
  if (!botToken || !appToken) {
60966
- log114.info("Slack bridge not started (missing bot or app token)");
61561
+ log115.info("Slack bridge not started (missing bot or app token)");
60967
61562
  return;
60968
61563
  }
60969
61564
  const web = new WebClient(botToken);
@@ -60984,20 +61579,20 @@ var init_slack_bridge = __esm({
60984
61579
  });
60985
61580
  this.socket = socket;
60986
61581
  socket.on("connected", () => {
60987
- log114.info("Slack Socket Mode connected", {
61582
+ log115.info("Slack Socket Mode connected", {
60988
61583
  botUserId: this.botUserId,
60989
61584
  teamId: this.teamId
60990
61585
  });
60991
61586
  this.requestCatchUp();
60992
61587
  });
60993
61588
  socket.on("disconnected", () => {
60994
- log114.warn("Slack Socket Mode disconnected");
61589
+ log115.warn("Slack Socket Mode disconnected");
60995
61590
  });
60996
61591
  socket.on("reconnecting", () => {
60997
- log114.warn("Slack Socket Mode reconnecting");
61592
+ log115.warn("Slack Socket Mode reconnecting");
60998
61593
  });
60999
61594
  socket.on("error", (error) => {
61000
- log114.error("Slack Socket Mode error", { error });
61595
+ log115.error("Slack Socket Mode error", { error });
61001
61596
  });
61002
61597
  socket.on(
61003
61598
  "slack_event",
@@ -61006,7 +61601,7 @@ var init_slack_bridge = __esm({
61006
61601
  try {
61007
61602
  await ack();
61008
61603
  } catch (error) {
61009
- log114.warn("Failed to ack Slack Socket Mode envelope", { type, error });
61604
+ log115.warn("Failed to ack Slack Socket Mode envelope", { type, error });
61010
61605
  }
61011
61606
  if (!body || !type) {
61012
61607
  return;
@@ -61014,10 +61609,10 @@ var init_slack_bridge = __esm({
61014
61609
  if (type === "events_api") {
61015
61610
  const event = body.event;
61016
61611
  if (!event) {
61017
- log114.debug("events_api envelope missing event payload", { bodyKeys: Object.keys(body) });
61612
+ log115.debug("events_api envelope missing event payload", { bodyKeys: Object.keys(body) });
61018
61613
  return;
61019
61614
  }
61020
- log114.info("Slack events_api received", {
61615
+ log115.info("Slack events_api received", {
61021
61616
  eventType: event.type,
61022
61617
  channel: event.channel,
61023
61618
  user: event.user,
@@ -61025,21 +61620,21 @@ var init_slack_bridge = __esm({
61025
61620
  threadTs: event.thread_ts
61026
61621
  });
61027
61622
  void this.handleEvent(event, body).catch((error) => {
61028
- log114.error("Unhandled Slack event error", { error, eventType: event.type });
61623
+ log115.error("Unhandled Slack event error", { error, eventType: event.type });
61029
61624
  });
61030
61625
  return;
61031
61626
  }
61032
61627
  if (type === "interactive") {
61033
- log114.info("Slack interactive received", { interactiveType: body.type });
61628
+ log115.info("Slack interactive received", { interactiveType: body.type });
61034
61629
  void this.handleInteractive(body).catch((error) => {
61035
- log114.error("Unhandled Slack interactive error", { error });
61630
+ log115.error("Unhandled Slack interactive error", { error });
61036
61631
  });
61037
61632
  }
61038
61633
  }
61039
61634
  );
61040
61635
  await socket.start();
61041
61636
  this.running = true;
61042
- log114.info("Slack bridge started", {
61637
+ log115.info("Slack bridge started", {
61043
61638
  botUsername: this.botUsername,
61044
61639
  teamName: this.teamName
61045
61640
  });
@@ -61062,7 +61657,7 @@ var init_slack_bridge = __esm({
61062
61657
  try {
61063
61658
  await socket.disconnect();
61064
61659
  } catch (error) {
61065
- log114.warn("Error disconnecting Slack Socket Mode", { error });
61660
+ log115.warn("Error disconnecting Slack Socket Mode", { error });
61066
61661
  }
61067
61662
  }
61068
61663
  }
@@ -61077,7 +61672,7 @@ var init_slack_bridge = __esm({
61077
61672
  }
61078
61673
  this.catchUpRunning = true;
61079
61674
  void this.runCatchUp().catch((error) => {
61080
- log114.error("Slack reconnect catch-up failed", { error });
61675
+ log115.error("Slack reconnect catch-up failed", { error });
61081
61676
  }).finally(() => {
61082
61677
  this.catchUpRunning = false;
61083
61678
  if (this.catchUpQueued && this.socket) {
@@ -61092,7 +61687,7 @@ var init_slack_bridge = __esm({
61092
61687
  if (!web || !teamId) {
61093
61688
  return;
61094
61689
  }
61095
- log114.info("Slack reconnect catch-up started", { teamId });
61690
+ log115.info("Slack reconnect catch-up started", { teamId });
61096
61691
  const result = await catchUpSlackMissedMessages({
61097
61692
  web,
61098
61693
  teamId,
@@ -61107,7 +61702,7 @@ var init_slack_bridge = __esm({
61107
61702
  }
61108
61703
  });
61109
61704
  if (result.channelsScanned > 0 || result.messagesIngested > 0) {
61110
- log114.info("Slack reconnect catch-up complete", result);
61705
+ log115.info("Slack reconnect catch-up complete", result);
61111
61706
  }
61112
61707
  }
61113
61708
  noteLiveMessageSeen(teamId, channelId, messageTs) {
@@ -61116,7 +61711,7 @@ var init_slack_bridge = __esm({
61116
61711
  slackChannelId: channelId,
61117
61712
  seenTs: messageTs
61118
61713
  }).catch((error) => {
61119
- log114.debug("Failed to advance Slack catch-up cursor", {
61714
+ log115.debug("Failed to advance Slack catch-up cursor", {
61120
61715
  teamId,
61121
61716
  channelId,
61122
61717
  messageTs,
@@ -61220,7 +61815,7 @@ var init_slack_bridge = __esm({
61220
61815
  }
61221
61816
  const binding = await getSlackBinding(teamId, channelId);
61222
61817
  if (!binding) {
61223
- log114.info("Ignoring @mention in unbound channel", { teamId, channelId });
61818
+ log115.info("Ignoring @mention in unbound channel", { teamId, channelId });
61224
61819
  await this.postEphemeralOrThread(
61225
61820
  channelId,
61226
61821
  userId,
@@ -61229,7 +61824,7 @@ var init_slack_bridge = __esm({
61229
61824
  );
61230
61825
  return;
61231
61826
  }
61232
- log114.info("Dispatching Slack @mention as Channel Prompt", {
61827
+ log115.info("Dispatching Slack @mention as Channel Prompt", {
61233
61828
  teamId,
61234
61829
  channelId,
61235
61830
  projectId: binding.projectId,
@@ -61258,7 +61853,7 @@ var init_slack_bridge = __esm({
61258
61853
  currentConversationId: conversationId2
61259
61854
  });
61260
61855
  } catch (error) {
61261
- log114.warn("Failed to point channel default thread at new Slack conversation", {
61856
+ log115.warn("Failed to point channel default thread at new Slack conversation", {
61262
61857
  error,
61263
61858
  tarskThreadId: defaultTarget.tarskThreadId,
61264
61859
  conversationId: conversationId2
@@ -61702,7 +62297,7 @@ var init_slack_bridge = __esm({
61702
62297
  let conversationId = params.conversationId;
61703
62298
  let slackMode = params.slackMode;
61704
62299
  if (createTarskThread && !explicitBranchCommand && !claimChannelDefault) {
61705
- log114.error(
62300
+ log115.error(
61706
62301
  "dispatchChannelPrompt: createTarskThread requires explicitBranchCommand or claimChannelDefault",
61707
62302
  {
61708
62303
  channelId,
@@ -61716,11 +62311,11 @@ var init_slack_bridge = __esm({
61716
62311
  try {
61717
62312
  claimed = await claimSlackPromptHandled(idempotencyKey);
61718
62313
  } catch (error) {
61719
- log114.error("Failed to claim Slack prompt idempotency key", { idempotencyKey, error });
62314
+ log115.error("Failed to claim Slack prompt idempotency key", { idempotencyKey, error });
61720
62315
  return;
61721
62316
  }
61722
62317
  if (!claimed) {
61723
- log114.info("Skipping already-handled Slack prompt", { idempotencyKey });
62318
+ log115.info("Skipping already-handled Slack prompt", { idempotencyKey });
61724
62319
  return;
61725
62320
  }
61726
62321
  if (ackReaction && (content.trim().length > 0 || attachments.length > 0)) {
@@ -61807,7 +62402,7 @@ var init_slack_bridge = __esm({
61807
62402
  currentConversationId: conversationId
61808
62403
  });
61809
62404
  } catch (error) {
61810
- log114.warn("Failed to set conversation id on new Slack Tarsk thread", {
62405
+ log115.warn("Failed to set conversation id on new Slack Tarsk thread", {
61811
62406
  error,
61812
62407
  tarskThreadId,
61813
62408
  conversationId
@@ -61824,7 +62419,7 @@ var init_slack_bridge = __esm({
61824
62419
  try {
61825
62420
  await this.deps.threadManager.deleteThread(tarskThreadId);
61826
62421
  } catch (error) {
61827
- log114.warn("Failed to delete duplicate channel default clone", { error, tarskThreadId });
62422
+ log115.warn("Failed to delete duplicate channel default clone", { error, tarskThreadId });
61828
62423
  }
61829
62424
  tarskThreadId = claim.existing.defaultTarskThreadId;
61830
62425
  conversationId = randomUUID17();
@@ -61844,7 +62439,7 @@ var init_slack_bridge = __esm({
61844
62439
  currentConversationId: conversationId
61845
62440
  });
61846
62441
  } catch (error) {
61847
- log114.warn("Failed to point winning channel default at Slack conversation", {
62442
+ log115.warn("Failed to point winning channel default at Slack conversation", {
61848
62443
  error,
61849
62444
  tarskThreadId,
61850
62445
  conversationId
@@ -61887,7 +62482,7 @@ var init_slack_bridge = __esm({
61887
62482
  currentConversationId: conversationId
61888
62483
  });
61889
62484
  } catch (error) {
61890
- log114.warn("Failed to sync Tarsk currentConversationId for Slack prompt", {
62485
+ log115.warn("Failed to sync Tarsk currentConversationId for Slack prompt", {
61891
62486
  error,
61892
62487
  tarskThreadId,
61893
62488
  conversationId
@@ -62126,7 +62721,7 @@ _Warning: ${result.error}_` : result.reply || "Done (no text response).";
62126
62721
  mimeType
62127
62722
  });
62128
62723
  } catch (error) {
62129
- log114.warn("Failed to download Slack file", { fileId: file.id, error });
62724
+ log115.warn("Failed to download Slack file", { fileId: file.id, error });
62130
62725
  }
62131
62726
  }
62132
62727
  return attachments;
@@ -62143,7 +62738,7 @@ _Warning: ${result.error}_` : result.reply || "Done (no text response).";
62143
62738
  currentConversationId: existingConversationId
62144
62739
  });
62145
62740
  } catch (error) {
62146
- log114.warn("Failed to sync Tarsk currentConversationId for Slack follow-up", {
62741
+ log115.warn("Failed to sync Tarsk currentConversationId for Slack follow-up", {
62147
62742
  error,
62148
62743
  tarskThreadId,
62149
62744
  existingConversationId
@@ -62178,7 +62773,7 @@ _Warning: ${result.error}_` : result.reply || "Done (no text response).";
62178
62773
  if (code === "already_reacted" || code === "too_many_emoji") {
62179
62774
  return;
62180
62775
  }
62181
- log114.warn("Failed to add Slack reaction", { error, channelId, messageTs, emoji });
62776
+ log115.warn("Failed to add Slack reaction", { error, channelId, messageTs, emoji });
62182
62777
  }
62183
62778
  }
62184
62779
  async postInThread(channelId, threadTs, text, blocks) {
@@ -62195,7 +62790,7 @@ _Warning: ${result.error}_` : result.reply || "Done (no text response).";
62195
62790
  });
62196
62791
  return typeof result.ts === "string" ? result.ts : null;
62197
62792
  } catch (error) {
62198
- log114.error("Failed to post Slack message", { error, channelId });
62793
+ log115.error("Failed to post Slack message", { error, channelId });
62199
62794
  return null;
62200
62795
  }
62201
62796
  }
@@ -62206,7 +62801,7 @@ _Warning: ${result.error}_` : result.reply || "Done (no text response).";
62206
62801
  try {
62207
62802
  await this.web.chat.update({ channel: channelId, ts, text });
62208
62803
  } catch (error) {
62209
- log114.warn("Failed to update Slack message; posting new one", { error });
62804
+ log115.warn("Failed to update Slack message; posting new one", { error });
62210
62805
  }
62211
62806
  }
62212
62807
  async postEphemeralOrThread(channelId, userId, threadTs, text, blocks) {
@@ -62314,7 +62909,7 @@ _Warning: ${result.error}_` : result.reply || "Done (no text response).";
62314
62909
  provider: selected.provider
62315
62910
  });
62316
62911
  } catch (error) {
62317
- log114.error("Failed to switch Slack thread model", { error, selected, tarskThreadId });
62912
+ log115.error("Failed to switch Slack thread model", { error, selected, tarskThreadId });
62318
62913
  await this.respondInteractive(responseUrl, "Failed to switch model. Try again from Tarsk.");
62319
62914
  return;
62320
62915
  }
@@ -62533,7 +63128,7 @@ async function startSlackBridge(deps) {
62533
63128
  try {
62534
63129
  await bridge.start();
62535
63130
  } catch (error) {
62536
- log115.error("Slack bridge failed to start", {
63131
+ log116.error("Slack bridge failed to start", {
62537
63132
  bridgeRunning: bridge.isRunning(),
62538
63133
  error
62539
63134
  });
@@ -62553,13 +63148,13 @@ async function stopSlackBridge() {
62553
63148
  await bridge.stop();
62554
63149
  bridge = null;
62555
63150
  }
62556
- var log115, bridge, lastDeps;
63151
+ var log116, bridge, lastDeps;
62557
63152
  var init_slack_service = __esm({
62558
63153
  "src/features/slack/slack.service.ts"() {
62559
63154
  "use strict";
62560
63155
  init_dist();
62561
63156
  init_slack_bridge();
62562
- log115 = createLogger("slack.service");
63157
+ log116 = createLogger("slack.service");
62563
63158
  bridge = null;
62564
63159
  lastDeps = null;
62565
63160
  }
@@ -62992,7 +63587,7 @@ async function generateImage(c, metadataManager, conversationManager, threadMana
62992
63587
  // imageMode
62993
63588
  );
62994
63589
  } catch (err) {
62995
- log116.error("[image] Failed to persist request to conversation history:", err);
63590
+ log117.error("[image] Failed to persist request to conversation history:", err);
62996
63591
  }
62997
63592
  }
62998
63593
  const isOpenRouter = provider.toLowerCase() === "openrouter";
@@ -63018,18 +63613,18 @@ async function generateImage(c, metadataManager, conversationManager, threadMana
63018
63613
  imageUrl
63019
63614
  );
63020
63615
  } catch (err) {
63021
- log116.error("[image] Failed to persist response to conversation history:", err);
63616
+ log117.error("[image] Failed to persist response to conversation history:", err);
63022
63617
  }
63023
63618
  }
63024
63619
  return c.json({ imageUrl, imageData, text });
63025
63620
  } catch (error) {
63026
63621
  const message = error instanceof Error ? error.message : "Unknown error";
63027
- log116.error(`[image] Error:`, error);
63622
+ log117.error(`[image] Error:`, error);
63028
63623
  return c.json({ error: message }, 500);
63029
63624
  }
63030
63625
  }
63031
63626
  async function callOpenRouter(apiKey, model, prompt, options) {
63032
- log116.info(`[image] Generating via OpenRouter chat completions \u2014 model: ${model}`);
63627
+ log117.info(`[image] Generating via OpenRouter chat completions \u2014 model: ${model}`);
63033
63628
  const result = await generateWithOpenRouter(apiKey, model, prompt, {
63034
63629
  aspectRatio: options.aspectRatio,
63035
63630
  imageSize: options.imageSize
@@ -63042,7 +63637,7 @@ async function callStandard(apiKey, baseUrl, rawModel, provider, prompt) {
63042
63637
  }
63043
63638
  const model = normalizeProviderModelId(provider, rawModel);
63044
63639
  const apiUrl = `${baseUrl.replace(/\/$/, "")}/images/generations`;
63045
- log116.info(`[image] Generating via ${provider} at ${apiUrl} \u2014 model: ${model}`);
63640
+ log117.info(`[image] Generating via ${provider} at ${apiUrl} \u2014 model: ${model}`);
63046
63641
  const response = await fetch(apiUrl, {
63047
63642
  method: "POST",
63048
63643
  headers: {
@@ -63099,7 +63694,7 @@ async function callStandard(apiKey, baseUrl, rawModel, provider, prompt) {
63099
63694
  }
63100
63695
  return { imageUrl: imageResult.url, imageData: imageResult.b64_json };
63101
63696
  }
63102
- var log116;
63697
+ var log117;
63103
63698
  var init_image_generate_route = __esm({
63104
63699
  "src/features/image/image-generate.route.ts"() {
63105
63700
  "use strict";
@@ -63107,7 +63702,7 @@ var init_image_generate_route = __esm({
63107
63702
  init_provider_resolver();
63108
63703
  init_agent_model_resolver();
63109
63704
  init_openrouter_image_client();
63110
- log116 = createLogger("features.image.image-generate");
63705
+ log117 = createLogger("features.image.image-generate");
63111
63706
  }
63112
63707
  });
63113
63708
 
@@ -63169,17 +63764,17 @@ async function clipboardWriteImage(c) {
63169
63764
  Utils3.clipboardWriteImage(resolved.data);
63170
63765
  return c.json({ success: true });
63171
63766
  } catch (error) {
63172
- log117.error("[image] Failed to write image to clipboard:", error);
63767
+ log118.error("[image] Failed to write image to clipboard:", error);
63173
63768
  return c.json({ error: "Failed to copy image to clipboard" }, 500);
63174
63769
  }
63175
63770
  }
63176
- var log117, Utils3, utilsLoaded3;
63771
+ var log118, Utils3, utilsLoaded3;
63177
63772
  var init_image_clipboard_route = __esm({
63178
63773
  "src/features/image/image-clipboard.route.ts"() {
63179
63774
  "use strict";
63180
63775
  init_dist();
63181
63776
  init_image_bytes();
63182
- log117 = createLogger("features.image.image-clipboard");
63777
+ log118 = createLogger("features.image.image-clipboard");
63183
63778
  Utils3 = null;
63184
63779
  utilsLoaded3 = false;
63185
63780
  }
@@ -63216,17 +63811,17 @@ async function saveImage(c) {
63216
63811
  await Bun.write(savePath, resolved.data);
63217
63812
  return c.json({ success: true, path: savePath });
63218
63813
  } catch (error) {
63219
- log118.error("[image] Failed to save image:", error);
63814
+ log119.error("[image] Failed to save image:", error);
63220
63815
  return c.json({ error: "Failed to save image" }, 500);
63221
63816
  }
63222
63817
  }
63223
- var log118, Utils4, utilsLoaded4;
63818
+ var log119, Utils4, utilsLoaded4;
63224
63819
  var init_image_save_route = __esm({
63225
63820
  "src/features/image/image-save.route.ts"() {
63226
63821
  "use strict";
63227
63822
  init_dist();
63228
63823
  init_image_bytes();
63229
- log118 = createLogger("features.image.image-save");
63824
+ log119 = createLogger("features.image.image-save");
63230
63825
  Utils4 = null;
63231
63826
  utilsLoaded4 = false;
63232
63827
  }
@@ -63871,18 +64466,18 @@ function createLogsRoutes() {
63871
64466
  router.delete("/", (c) => {
63872
64467
  clearLogBuffer();
63873
64468
  clearFileLog();
63874
- log119.info("Log buffer cleared by client request");
64469
+ log120.info("Log buffer cleared by client request");
63875
64470
  return c.json({ ok: true });
63876
64471
  });
63877
64472
  return router;
63878
64473
  }
63879
- var log119, LOG_FEED_PING_INTERVAL_MS;
64474
+ var log120, LOG_FEED_PING_INTERVAL_MS;
63880
64475
  var init_logs_routes = __esm({
63881
64476
  "src/features/logs/logs.routes.ts"() {
63882
64477
  "use strict";
63883
64478
  init_dist();
63884
64479
  init_file_log_transport();
63885
- log119 = createLogger("logs.api");
64480
+ log120 = createLogger("logs.api");
63886
64481
  LOG_FEED_PING_INTERVAL_MS = 25e3;
63887
64482
  }
63888
64483
  });
@@ -64007,13 +64602,13 @@ async function runScheduledTask(taskId, deps, task) {
64007
64602
  })
64008
64603
  });
64009
64604
  } catch (error) {
64010
- log120.error("Failed to run scheduled task", {
64605
+ log121.error("Failed to run scheduled task", {
64011
64606
  error,
64012
64607
  taskId
64013
64608
  });
64014
64609
  }
64015
64610
  }
64016
- var log120, POLL_INTERVAL_MS;
64611
+ var log121, POLL_INTERVAL_MS;
64017
64612
  var init_task_scheduler = __esm({
64018
64613
  "src/features/project-tasks/task-scheduler.ts"() {
64019
64614
  "use strict";
@@ -64023,7 +64618,7 @@ var init_task_scheduler = __esm({
64023
64618
  init_project_tasks_database();
64024
64619
  init_database();
64025
64620
  init_task_trigger_validation();
64026
- log120 = createLogger("task.scheduler");
64621
+ log121 = createLogger("task.scheduler");
64027
64622
  POLL_INTERVAL_MS = 3e4;
64028
64623
  }
64029
64624
  });
@@ -64163,7 +64758,7 @@ var init_inbound_events_routes = __esm({
64163
64758
  async function openScreenRecordingSettings() {
64164
64759
  if (process.platform !== "darwin") {
64165
64760
  const message = "Screen recording settings are only available on macOS.";
64166
- log121.warn(`[ScreenRecordingSettings] ${message}`);
64761
+ log122.warn(`[ScreenRecordingSettings] ${message}`);
64167
64762
  return {
64168
64763
  success: false,
64169
64764
  message,
@@ -64171,8 +64766,8 @@ async function openScreenRecordingSettings() {
64171
64766
  };
64172
64767
  }
64173
64768
  const settingsUrl = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
64174
- log121.info(`[ScreenRecordingSettings] Opening System Settings to Screen Recording pane...`);
64175
- log121.info(`[ScreenRecordingSettings] URL: ${settingsUrl}`);
64769
+ log122.info(`[ScreenRecordingSettings] Opening System Settings to Screen Recording pane...`);
64770
+ log122.info(`[ScreenRecordingSettings] URL: ${settingsUrl}`);
64176
64771
  try {
64177
64772
  const { shell, args: args2 } = getShellConfig();
64178
64773
  const result = spawnSyncProcess(shell, [...args2, `open "${settingsUrl}"`], {
@@ -64181,17 +64776,17 @@ async function openScreenRecordingSettings() {
64181
64776
  timeout: 5e3
64182
64777
  });
64183
64778
  if (result.status === 0) {
64184
- log121.info(`[ScreenRecordingSettings] Successfully opened System Settings`);
64779
+ log122.info(`[ScreenRecordingSettings] Successfully opened System Settings`);
64185
64780
  return {
64186
64781
  success: true,
64187
64782
  message: "System Settings opened to Screen & Audio Recording pane."
64188
64783
  };
64189
64784
  } else {
64190
64785
  const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "Unknown error";
64191
- log121.error(
64786
+ log122.error(
64192
64787
  `[ScreenRecordingSettings] Failed to open System Settings. Exit code: ${result.status}`
64193
64788
  );
64194
- log121.error(`[ScreenRecordingSettings] stderr: ${stderr}`);
64789
+ log122.error(`[ScreenRecordingSettings] stderr: ${stderr}`);
64195
64790
  return {
64196
64791
  success: false,
64197
64792
  message: "Failed to open System Settings. Please open it manually: System Settings > Privacy & Security > Screen & Audio Recording.",
@@ -64200,7 +64795,7 @@ async function openScreenRecordingSettings() {
64200
64795
  }
64201
64796
  } catch (error) {
64202
64797
  const errorMessage2 = error instanceof Error ? error.message : String(error);
64203
- log121.error(`[ScreenRecordingSettings] Exception while opening System Settings:`, error);
64798
+ log122.error(`[ScreenRecordingSettings] Exception while opening System Settings:`, error);
64204
64799
  return {
64205
64800
  success: false,
64206
64801
  message: "Failed to open System Settings. Please open it manually: System Settings > Privacy & Security > Screen & Audio Recording.",
@@ -64208,14 +64803,14 @@ async function openScreenRecordingSettings() {
64208
64803
  };
64209
64804
  }
64210
64805
  }
64211
- var log121;
64806
+ var log122;
64212
64807
  var init_screen_recording_settings_opener = __esm({
64213
64808
  "src/features/system-permissions/screen-recording-settings-opener.ts"() {
64214
64809
  "use strict";
64215
64810
  init_utils();
64216
64811
  init_shell();
64217
64812
  init_dist();
64218
- log121 = createLogger("features.system-permissions.screen-recording-settings-opener");
64813
+ log122 = createLogger("features.system-permissions.screen-recording-settings-opener");
64219
64814
  }
64220
64815
  });
64221
64816
 
@@ -64383,7 +64978,7 @@ async function startTarskServerInternal(options) {
64383
64978
  const fullUrl = `${requestUrl.protocol}//${hostHeader}${reqPath}`;
64384
64979
  const isThreadMessages = reqPath.startsWith("/api/threads/") && reqPath.endsWith("/messages");
64385
64980
  if (!fullUrl.includes("processing") && !reqPath.startsWith("/api/logs") && !isThreadMessages) {
64386
- log122.info(`${method} ${fullUrl}`);
64981
+ log123.info(`${method} ${fullUrl}`);
64387
64982
  }
64388
64983
  }
64389
64984
  return next();
@@ -64619,7 +65214,7 @@ function writePortFile(port, url) {
64619
65214
  fs5.writeFileSync(portFilePath, `${JSON.stringify({ port, url }, null, 2)}
64620
65215
  `, "utf8");
64621
65216
  } catch (error) {
64622
- log122.warn("Failed to write port.json", { error });
65217
+ log123.warn("Failed to write port.json", { error });
64623
65218
  }
64624
65219
  }
64625
65220
  async function listenForTarskServer(options) {
@@ -64657,7 +65252,7 @@ async function listenForTarskServer(options) {
64657
65252
  port += 1;
64658
65253
  }
64659
65254
  }
64660
- var log122, __filename, __dirname, startPromise;
65255
+ var log123, __filename, __dirname, startPromise;
64661
65256
  var init_server = __esm({
64662
65257
  "src/server.ts"() {
64663
65258
  "use strict";
@@ -64712,7 +65307,7 @@ var init_server = __esm({
64712
65307
  init_server_bind_mode_store();
64713
65308
  init_server_bind_mode();
64714
65309
  init_cli_process_routes();
64715
- log122 = createLogger("server");
65310
+ log123 = createLogger("server");
64716
65311
  __filename = fileURLToPath4(import.meta.url);
64717
65312
  __dirname = path5.dirname(__filename);
64718
65313
  startPromise = null;