openfox 2.0.113 → 2.0.115

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 (99) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/CHANGELOG.md +43 -0
  3. package/dist/agent-defaults/builder.agent.md +1 -0
  4. package/dist/agent-defaults/planner.agent.md +1 -0
  5. package/dist/{backend-P3GHHLHJ.js → backend-LVYHLGQJ.js} +2 -2
  6. package/dist/{chat-handler-PEK5JDHO.js → chat-handler-5SP265CR.js} +32 -26
  7. package/dist/{chunk-HKD4IMOE.js → chunk-27NABDXB.js} +2 -2
  8. package/dist/{chunk-FF2EKUQO.js → chunk-2FCLCUVR.js} +6 -6
  9. package/dist/{chunk-ONONXV3N.js → chunk-37QLXLML.js} +4 -4
  10. package/dist/{chunk-YB6WYVNY.js → chunk-3KBRMBTP.js} +2 -2
  11. package/dist/{chunk-YGPPFVLJ.js → chunk-3NPLERPQ.js} +17 -17
  12. package/dist/{chunk-XQR76MQ5.js → chunk-3PG65AXG.js} +18 -3
  13. package/dist/chunk-3QQAIUZI.js +957 -0
  14. package/dist/{chunk-PWXBIUEY.js → chunk-5PT4UMRR.js} +89 -1
  15. package/dist/chunk-5V3BF67X.js +7 -0
  16. package/dist/{chunk-6UPBZ2ZR.js → chunk-6KPJLELC.js} +4 -4
  17. package/dist/chunk-6RGACY4E.js +1081 -0
  18. package/dist/{chunk-BNFGDTEU.js → chunk-AESOGRFK.js} +2 -2
  19. package/dist/{chunk-YHEQTVFV.js → chunk-AZHWLLPP.js} +1 -1
  20. package/dist/{chunk-TCGWZBTS.js → chunk-BVVCRIVM.js} +4 -4
  21. package/dist/{chunk-EMI3YYLP.js → chunk-BWVO5WFK.js} +4 -4
  22. package/dist/{chunk-CP44U7BT.js → chunk-ERNWAKUB.js} +7 -7
  23. package/dist/{chunk-WWTYWLDM.js → chunk-HD6Y77RO.js} +2 -2
  24. package/dist/{chunk-4NMGX5F2.js → chunk-I7FY2LTR.js} +2 -2
  25. package/dist/{chunk-5VYSKVZT.js → chunk-JFK4GKQD.js} +321 -54
  26. package/dist/{chunk-43M7QZMX.js → chunk-JGWHRYWG.js} +4 -4
  27. package/dist/{chunk-Y64PEWKP.js → chunk-JLAR2CMV.js} +2 -2
  28. package/dist/{chunk-BYSUSSSC.js → chunk-LNNBLB6G.js} +48 -151
  29. package/dist/{chunk-V4664YKR.js → chunk-LTYL6ZGH.js} +2 -2
  30. package/dist/{chunk-R2MTWC7C.js → chunk-M6X6AIEG.js} +6 -4
  31. package/dist/{chunk-VGRVPYZ5.js → chunk-O6DU226D.js} +2 -2
  32. package/dist/{chunk-TEQEDOWI.js → chunk-OCBBAEMT.js} +288 -316
  33. package/dist/{chunk-SNLLOXCM.js → chunk-OCSTEGK7.js} +3 -3
  34. package/dist/{chunk-HGOTZWON.js → chunk-OKVHMKE2.js} +60 -1064
  35. package/dist/chunk-OSXPQSLW.js +128 -0
  36. package/dist/{chunk-O2YSYW2F.js → chunk-TFF5T3K3.js} +10 -10
  37. package/dist/{chunk-3R43CMLS.js → chunk-TGLVWAFC.js} +11 -11
  38. package/dist/{chunk-L737Y63F.js → chunk-TOKQ2UVG.js} +2 -2
  39. package/dist/{chunk-AZFZE36I.js → chunk-VWEATA7R.js} +18 -8
  40. package/dist/{chunk-BKCW3JLL.js → chunk-X25Q6RAV.js} +1 -1
  41. package/dist/{chunk-JR54MDZI.js → chunk-YX3OQBNX.js} +3 -3
  42. package/dist/{chunk-EC5T5A6O.js → chunk-ZTOG6LJD.js} +3 -3
  43. package/dist/cli/dev.js +1 -1
  44. package/dist/cli/index.js +1 -1
  45. package/dist/client-KE7EDI2D.js +16 -0
  46. package/dist/{client-pure-VFZIIK4S.js → client-pure-66Z6FWS6.js} +2 -2
  47. package/dist/command-defaults/mark-done.command.md +14 -0
  48. package/dist/compactor-SKVC2VNL.js +32 -0
  49. package/dist/{dynamic-context-F2XTQJR5.js → dynamic-context-QNB4ELLP.js} +10 -9
  50. package/dist/{events-224IO5NJ.js → events-5PLO4QJU.js} +9 -7
  51. package/dist/{folding-YTTTVS56.js → folding-CJWYFUKD.js} +7 -5
  52. package/dist/http-client-KLODRQC2.js +11 -0
  53. package/dist/{inspect-proxy-B7LGGTM7.js → inspect-proxy-K6QKEYDT.js} +4 -4
  54. package/dist/launch-EOXVWLOP.js +52 -0
  55. package/dist/{manager-4QGXI6WZ.js → manager-LZ3JQ443.js} +5 -5
  56. package/dist/{model-overrides-OY2YKUNV.js → model-overrides-5UVW3KTE.js} +4 -4
  57. package/dist/orchestrator-ED3KFPXO.js +63 -0
  58. package/dist/package.json +3 -3
  59. package/dist/{path-security-PFMZY7IA.js → path-security-B6EZC63L.js} +13 -11
  60. package/dist/{platform-QNGCKX4L.js → platform-6RPPBHKH.js} +4 -4
  61. package/dist/{processor-NYPJ5PFA.js → processor-226ZOTRI.js} +31 -25
  62. package/dist/{project-creator-AWP2JUFS.js → project-creator-VBFE35NI.js} +3 -3
  63. package/dist/{projects-C4353IQK.js → projects-AXHZOBR2.js} +5 -3
  64. package/dist/{protocol-CBT1d98i.d.ts → protocol-JdxRpPxM.d.ts} +21 -3
  65. package/dist/{protocol-LZZ7C4VQ.js → protocol-RKSWKNXI.js} +3 -3
  66. package/dist/provider/index.d.ts +4 -4
  67. package/dist/{provider-PIRPN2CC.js → provider-DL4ZWQY7.js} +8 -8
  68. package/dist/{provider-manager-GNEPR6UN.js → provider-manager-6CLRMGDE.js} +8 -8
  69. package/dist/{registry-VCKDSVKA.js → registry-5XI5NDBF.js} +6 -4
  70. package/dist/{serve-6DRHQLCG.js → serve-DARV4NJG.js} +36 -33
  71. package/dist/server/index.d.ts +13 -6
  72. package/dist/server/index.js +35 -32
  73. package/dist/server-LQJ7MBDS.js +55 -0
  74. package/dist/service-4JKTGPRG.js +25 -0
  75. package/dist/{session-overrides-RNKV35MU.js → session-overrides-AZKV6BRD.js} +8 -6
  76. package/dist/{sessions-D5CEPQGU.js → sessions-752IZIIK.js} +7 -5
  77. package/dist/{settings-BNRNX6IW.js → settings-2AHDKCD7.js} +3 -3
  78. package/dist/shared/index.d.ts +3 -3
  79. package/dist/shared/index.js +1 -1
  80. package/dist/skill-defaults/workflows/SKILL.md +24 -6
  81. package/dist/tasks-XJXHU623.js +217 -0
  82. package/dist/{tools-2B363F7L.js → tools-QYYNFMGU.js} +31 -23
  83. package/dist/{types-cVfqOj6M.d.ts → types-CIVhQMvD.d.ts} +1 -1
  84. package/dist/{types-BAxh20ZL.d.ts → types-Df7NM9b5.d.ts} +76 -1
  85. package/dist/{update-IQMRYN7J.js → update-2REP2WB6.js} +2 -2
  86. package/dist/web/assets/{index-CjWC6EQ4.css → index-BmILAZrT.css} +1 -1
  87. package/dist/web/assets/index-M7ToECa4.js +324 -0
  88. package/dist/web/index.html +2 -2
  89. package/dist/web/sw.js +1 -1
  90. package/dist/workflow-defaults/default.workflow.json +5 -3
  91. package/dist/{workspace-YUNHV4WS.js → workspace-RMZVTFJD.js} +4 -4
  92. package/package.json +3 -3
  93. package/dist/chunk-B2PHPBFU.js +0 -7
  94. package/dist/client-3LJGD6KY.js +0 -16
  95. package/dist/compactor-ZBNKANHV.js +0 -30
  96. package/dist/http-client-YC3UA5OZ.js +0 -11
  97. package/dist/orchestrator-JKBY7B3P.js +0 -57
  98. package/dist/server-HUHFJW7U.js +0 -48
  99. package/dist/web/assets/index-DBUtUSHK.js +0 -323
@@ -4,7 +4,7 @@ import {
4
4
  getEnabledSkillMetadata,
5
5
  isSkillEnabled,
6
6
  loadAllSkills
7
- } from "./chunk-FF2EKUQO.js";
7
+ } from "./chunk-2FCLCUVR.js";
8
8
  import {
9
9
  TurnMetrics,
10
10
  consumeStreamGenerator,
@@ -14,15 +14,15 @@ import {
14
14
  createToolCallEvent,
15
15
  createToolResultEvent,
16
16
  streamLLMPure
17
- } from "./chunk-JR54MDZI.js";
17
+ } from "./chunk-YX3OQBNX.js";
18
18
  import {
19
19
  COMPACTION_PROMPT,
20
20
  buildBasePrompt,
21
21
  computeEffectiveTools
22
- } from "./chunk-HKD4IMOE.js";
22
+ } from "./chunk-27NABDXB.js";
23
23
  import {
24
24
  startInspectProxy
25
- } from "./chunk-R2MTWC7C.js";
25
+ } from "./chunk-M6X6AIEG.js";
26
26
  import {
27
27
  checkAborted,
28
28
  createProcess,
@@ -36,7 +36,7 @@ import {
36
36
  spawnShellProcess,
37
37
  startProcessCommand,
38
38
  stopProcess
39
- } from "./chunk-EC5T5A6O.js";
39
+ } from "./chunk-ZTOG6LJD.js";
40
40
  import {
41
41
  getMaxPerSession,
42
42
  getSessionProcessCount,
@@ -52,36 +52,41 @@ import {
52
52
  getGitBranch,
53
53
  isGitRepository,
54
54
  listWorkspaces
55
- } from "./chunk-VGRVPYZ5.js";
55
+ } from "./chunk-O6DU226D.js";
56
+ import {
57
+ getGateConfig,
58
+ isTaskConflictError,
59
+ isTaskGateError
60
+ } from "./chunk-6RGACY4E.js";
56
61
  import {
57
62
  getSessionDisabledServers
58
- } from "./chunk-SNLLOXCM.js";
63
+ } from "./chunk-OCSTEGK7.js";
59
64
  import {
60
65
  PathAccessDeniedError,
61
66
  extractAbsolutePathsFromCommand,
62
67
  extractSensitivePathsFromCommand,
63
68
  registerPathConfirmation,
64
69
  requestPathAccess
65
- } from "./chunk-6UPBZ2ZR.js";
70
+ } from "./chunk-6KPJLELC.js";
66
71
  import {
67
72
  getCurrentContextWindowId,
68
73
  getCurrentWindowMessageOptions,
69
74
  getEventStore
70
- } from "./chunk-CP44U7BT.js";
75
+ } from "./chunk-ERNWAKUB.js";
71
76
  import {
72
77
  createChatDoneMessage,
73
78
  createChatMessageMessage,
74
79
  createChatMessageUpdatedMessage,
75
80
  createChatPathConfirmationMessage,
76
81
  createQueueStateMessage
77
- } from "./chunk-4NMGX5F2.js";
82
+ } from "./chunk-I7FY2LTR.js";
78
83
  import {
79
84
  AskUserInterrupt,
80
85
  askUserTool
81
86
  } from "./chunk-JURM3RPZ.js";
82
87
  import {
83
88
  resolveLLMClientForAgent
84
- } from "./chunk-Y64PEWKP.js";
89
+ } from "./chunk-JLAR2CMV.js";
85
90
  import {
86
91
  buildContextMessagesFromEventHistory,
87
92
  foldContextState,
@@ -91,13 +96,13 @@ import {
91
96
  handleToolResult,
92
97
  reorderToolMessages,
93
98
  stripOrphanedToolCalls
94
- } from "./chunk-YB6WYVNY.js";
99
+ } from "./chunk-3KBRMBTP.js";
95
100
  import {
96
101
  findAgentById,
97
102
  getRuntimeConfig,
98
103
  getSubAgents,
99
104
  loadAllAgentsDefault
100
- } from "./chunk-BYSUSSSC.js";
105
+ } from "./chunk-LNNBLB6G.js";
101
106
  import {
102
107
  modelSupportsVision
103
108
  } from "./chunk-V4IE7HJY.js";
@@ -111,11 +116,11 @@ import {
111
116
  formatPdfErrorMessage,
112
117
  isPdfBuffer,
113
118
  processPdfContent
114
- } from "./chunk-L737Y63F.js";
119
+ } from "./chunk-TOKQ2UVG.js";
115
120
  import {
116
121
  SETTINGS_KEYS,
117
122
  getSetting
118
- } from "./chunk-WWTYWLDM.js";
123
+ } from "./chunk-HD6Y77RO.js";
119
124
  import {
120
125
  logger
121
126
  } from "./chunk-K44MW7JJ.js";
@@ -129,7 +134,7 @@ import {
129
134
 
130
135
  // src/server/tools/read.ts
131
136
  import { readFile as readFile2, stat, readdir } from "fs/promises";
132
- import { extname, join } from "path";
137
+ import { extname, join, relative as relative2 } from "path";
133
138
 
134
139
  // src/server/tools/tool-helpers.ts
135
140
  import { randomUUID } from "crypto";
@@ -139,7 +144,7 @@ async function requestUserConfirmation(context, toolLabel, desc) {
139
144
  if (context.dangerLevel === "dangerous") return Promise.resolve(true);
140
145
  if (toolLabel === "workspace" || toolLabel === "command") {
141
146
  try {
142
- const { getSetting: getSetting2, SETTINGS_KEYS: SETTINGS_KEYS2 } = await import("./settings-BNRNX6IW.js");
147
+ const { getSetting: getSetting2, SETTINGS_KEYS: SETTINGS_KEYS2 } = await import("./settings-2AHDKCD7.js");
143
148
  if (getSetting2(SETTINGS_KEYS2.CONFIRM_ON_WORKSPACE_ACTIONS) !== "true") {
144
149
  return Promise.resolve(true);
145
150
  }
@@ -268,7 +273,7 @@ function createTool(name, definition, handler2) {
268
273
  // src/server/tools/file-tracker.ts
269
274
  import { createHash } from "crypto";
270
275
  import { readFile } from "fs/promises";
271
- import { resolve as resolve2 } from "path";
276
+ import { relative, resolve as resolve2 } from "path";
272
277
  var FileNotReadError = class extends Error {
273
278
  constructor(path) {
274
279
  super(`File "${path}" must be read before writing. Use read_file first.`);
@@ -296,26 +301,35 @@ async function computeFileHash(filePath) {
296
301
  throw error;
297
302
  }
298
303
  }
299
- async function validateFileForWrite(filePath, readFiles) {
304
+ async function validateFileForWrite(filePath, readFiles, baseDir) {
300
305
  const normalizedPath = resolve2(filePath);
301
306
  const currentHash = await computeFileHash(normalizedPath);
302
307
  if (currentHash === null) {
303
308
  return { valid: true };
304
309
  }
305
310
  const readEntry = readFiles[normalizedPath];
306
- if (!readEntry) {
307
- return {
308
- valid: false,
309
- error: new FileNotReadError(filePath)
310
- };
311
+ if (readEntry) {
312
+ if (readEntry.hash !== currentHash) {
313
+ return {
314
+ valid: false,
315
+ error: new FileChangedExternallyError(filePath)
316
+ };
317
+ }
318
+ return { valid: true };
311
319
  }
312
- if (readEntry.hash !== currentHash) {
313
- return {
314
- valid: false,
315
- error: new FileChangedExternallyError(filePath)
316
- };
320
+ if (baseDir) {
321
+ const relPath = relative(baseDir, normalizedPath);
322
+ if (!relPath.startsWith("..")) {
323
+ const relMatches = Object.values(readFiles).filter((entry) => entry.relPath === relPath);
324
+ if (relMatches.some((entry) => entry.hash === currentHash)) {
325
+ return { valid: true };
326
+ }
327
+ }
317
328
  }
318
- return { valid: true };
329
+ return {
330
+ valid: false,
331
+ error: new FileNotReadError(filePath)
332
+ };
319
333
  }
320
334
  function isNodeError(error) {
321
335
  return error instanceof Error && "code" in error;
@@ -604,7 +618,12 @@ var readFileTool = createTool(
604
618
  const dataUrl = `data:${mimeType};base64,${base64Data}`;
605
619
  const contentHash3 = await computeFileHash(fullPath);
606
620
  if (contentHash3) {
607
- context.sessionManager.recordFileRead(context.sessionId, fullPath, contentHash3);
621
+ context.sessionManager.recordFileRead(
622
+ context.sessionId,
623
+ fullPath,
624
+ contentHash3,
625
+ relative2(context.workdir, fullPath)
626
+ );
608
627
  }
609
628
  return helpers.success(`[Image: ${args.path} (${mimeType}, ${rawBuffer.length} bytes)]`, false, {
610
629
  metadata: {
@@ -622,7 +641,12 @@ var readFileTool = createTool(
622
641
  const { output: output2, truncated: truncated2, isScanned } = processPdfContent(text, OUTPUT_LIMITS.read_file.maxBytes);
623
642
  const contentHash3 = await computeFileHash(fullPath);
624
643
  if (contentHash3) {
625
- context.sessionManager.recordFileRead(context.sessionId, fullPath, contentHash3);
644
+ context.sessionManager.recordFileRead(
645
+ context.sessionId,
646
+ fullPath,
647
+ contentHash3,
648
+ relative2(context.workdir, fullPath)
649
+ );
626
650
  }
627
651
  if (isScanned) {
628
652
  return helpers.success(
@@ -673,7 +697,12 @@ var readFileTool = createTool(
673
697
  }
674
698
  const contentHash2 = await computeFileHash(fullPath);
675
699
  if (contentHash2) {
676
- context.sessionManager.recordFileRead(context.sessionId, fullPath, contentHash2);
700
+ context.sessionManager.recordFileRead(
701
+ context.sessionId,
702
+ fullPath,
703
+ contentHash2,
704
+ relative2(context.workdir, fullPath)
705
+ );
677
706
  }
678
707
  return helpers.success(output, truncated, {
679
708
  metadata: {
@@ -690,7 +719,7 @@ var readFileTool = createTool(
690
719
 
691
720
  // src/server/tools/write.ts
692
721
  import { writeFile, mkdir } from "fs/promises";
693
- import { dirname } from "path";
722
+ import { dirname, relative as relative3 } from "path";
694
723
 
695
724
  // src/server/tools/diagnostics.ts
696
725
  function formatDiagnosticsForLLM(diagnostics) {
@@ -757,7 +786,7 @@ var writeFileTool = createTool(
757
786
  const fullPath = helpers.resolvePath(args.path);
758
787
  await helpers.checkPathAccess([fullPath]);
759
788
  const readFiles = context.sessionManager.getReadFiles(context.sessionId);
760
- const validation = await validateFileForWrite(fullPath, readFiles);
789
+ const validation = await validateFileForWrite(fullPath, readFiles, context.workdir);
761
790
  if (!validation.valid) {
762
791
  return helpers.error(validation.error?.message ?? "File validation failed");
763
792
  }
@@ -777,7 +806,7 @@ var writeFileTool = createTool(
777
806
  }
778
807
  const newHash = await computeFileHash(fullPath);
779
808
  if (newHash) {
780
- context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash);
809
+ context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash, relative3(context.workdir, fullPath));
781
810
  }
782
811
  return helpers.success(output, false, {
783
812
  ...diagnostics.length > 0 && { diagnostics },
@@ -788,6 +817,7 @@ var writeFileTool = createTool(
788
817
 
789
818
  // src/server/tools/edit.ts
790
819
  import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
820
+ import { relative as relative4 } from "path";
791
821
 
792
822
  // src/server/tools/edit-context.ts
793
823
  var CONTEXT_LINES = 4;
@@ -953,7 +983,7 @@ var editFileTool = createTool(
953
983
  return withFileLock(fullPath, async () => {
954
984
  const replaceAll = args.replace_all ?? false;
955
985
  const readFiles = context.sessionManager.getReadFiles(context.sessionId);
956
- const validation = await validateFileForWrite(fullPath, readFiles);
986
+ const validation = await validateFileForWrite(fullPath, readFiles, context.workdir);
957
987
  if (!validation.valid) {
958
988
  return helpers.error(validation.error?.message ?? "File validation failed");
959
989
  }
@@ -1037,7 +1067,7 @@ Make sure whitespace and indentation match exactly.`
1037
1067
  }
1038
1068
  const newHash = await computeFileHash(fullPath);
1039
1069
  if (newHash) {
1040
- context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash);
1070
+ context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash, relative4(context.workdir, fullPath));
1041
1071
  }
1042
1072
  return helpers.success(output, false, {
1043
1073
  ...diagnostics.length > 0 && { diagnostics },
@@ -1517,10 +1547,10 @@ var INTERRUPTED_ERROR = "Tool execution was interrupted by user";
1517
1547
  function extractSubAgentPrompt(args) {
1518
1548
  return args["prompt"] || args["query"] || args["task"] || "";
1519
1549
  }
1520
- async function transformSubAgentAliases(toolCalls, toolRegistry) {
1550
+ async function transformSubAgentAliases(toolCalls, toolRegistry, projectDir) {
1521
1551
  const hasCallSubAgent = toolRegistry.tools.some((t) => t.name === "call_sub_agent");
1522
1552
  if (!hasCallSubAgent) return;
1523
- const agents = await loadAllAgentsDefault();
1553
+ const agents = await loadAllAgentsDefault(projectDir);
1524
1554
  for (const tc of toolCalls) {
1525
1555
  const agentDef = findAgentById(tc.name, agents);
1526
1556
  if (!agentDef?.metadata.subagent) continue;
@@ -1545,7 +1575,7 @@ async function executeTools(assistantMsgId, toolCalls, ctx, append) {
1545
1575
  if (ctx.signal?.aborted) {
1546
1576
  throw new Error("Aborted");
1547
1577
  }
1548
- await transformSubAgentAliases(toolCalls, ctx.toolRegistry);
1578
+ await transformSubAgentAliases(toolCalls, ctx.toolRegistry, ctx.sessionManager.getProjectWorkdir(ctx.sessionId));
1549
1579
  for (const toolCall of toolCalls) {
1550
1580
  append(createToolCallEvent(assistantMsgId, toolCall));
1551
1581
  }
@@ -1779,7 +1809,7 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
1779
1809
  const session2 = sessionManager.requireSession(sessionId);
1780
1810
  const runtimeConfig2 = getRuntimeConfig();
1781
1811
  const configDir2 = getGlobalConfigDir(runtimeConfig2.mode ?? "production");
1782
- const skills2 = await getEnabledSkillMetadata(configDir2, sessionManager.getEffectiveWorkdir(sessionId));
1812
+ const skills2 = await getEnabledSkillMetadata(configDir2, sessionManager.getProjectWorkdir(sessionId));
1783
1813
  const { content: instructionContent2 } = await getAllInstructions(session2.workdir, session2.projectId);
1784
1814
  const toolRegistry2 = config.getToolRegistry();
1785
1815
  const assembledRequest2 = await config.assembleRequest({
@@ -1834,7 +1864,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
1834
1864
  }
1835
1865
  const runtimeConfig = getRuntimeConfig();
1836
1866
  const configDir = getGlobalConfigDir(runtimeConfig.mode ?? "production");
1837
- const skills = await getEnabledSkillMetadata(configDir, sessionManager.getEffectiveWorkdir(sessionId));
1867
+ const skills = await getEnabledSkillMetadata(configDir, sessionManager.getProjectWorkdir(sessionId));
1838
1868
  if (signal?.aborted) throw new Error("Aborted");
1839
1869
  const assembledRequest = await config.assembleRequest({
1840
1870
  workdir: session.workdir,
@@ -1861,7 +1891,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
1861
1891
  const requestedMaxTokens = modelSettings.maxTokens ?? 16384;
1862
1892
  modelSettings = { ...modelSettings, maxTokens: Math.min(requestedMaxTokens, availableForOutput) };
1863
1893
  }
1864
- const allAgents = await loadAllAgentsDefault();
1894
+ const allAgents = await loadAllAgentsDefault(sessionManager.getProjectWorkdir(sessionId));
1865
1895
  const subAgentAliases = new Set(getSubAgents(allAgents).map((a) => a.metadata.id));
1866
1896
  const streamGen = streamLLMPure({
1867
1897
  messageId: assistantMsgId,
@@ -1935,7 +1965,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
1935
1965
  );
1936
1966
  if (!compacting) {
1937
1967
  const contextState2 = sessionManager.getContextState(sessionId);
1938
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-ZBNKANHV.js");
1968
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-SKVC2VNL.js");
1939
1969
  if (shouldCompact(
1940
1970
  contextState2.currentTokens,
1941
1971
  contextState2.maxTokens,
@@ -2653,8 +2683,8 @@ function buildSubAgentResult(returnValueContent, returnValueResult) {
2653
2683
  ...returnValueResult !== null && returnValueResult !== void 0 ? { result: returnValueResult } : {}
2654
2684
  };
2655
2685
  }
2656
- async function resolveAgentDef(agentId) {
2657
- const allAgents = await loadAllAgentsDefault();
2686
+ async function resolveAgentDef(agentId, projectDir) {
2687
+ const allAgents = await loadAllAgentsDefault(projectDir);
2658
2688
  const def = findAgentById(agentId, allAgents);
2659
2689
  if (!def) throw new Error(`Unknown sub-agent type: ${agentId}`);
2660
2690
  if (!def.metadata.subagent) throw new Error(`Agent '${agentId}' is not a sub-agent`);
@@ -2705,7 +2735,7 @@ async function executeSubAgent(options) {
2705
2735
  signal,
2706
2736
  onMessage
2707
2737
  } = options;
2708
- const agentDef = await resolveAgentDef(subAgentType);
2738
+ const agentDef = await resolveAgentDef(subAgentType, sessionManager.getProjectWorkdir(sessionId));
2709
2739
  const eventStore = getEventStore();
2710
2740
  const subAgentId = crypto.randomUUID();
2711
2741
  const session = sessionManager.requireSession(sessionId);
@@ -2809,7 +2839,7 @@ async function executeSubAgent(options) {
2809
2839
  const { content: instructionContent } = await getAllInstructions(effectiveWorkdir, session.projectId);
2810
2840
  const config = getRuntimeConfig();
2811
2841
  const configDir = getGlobalConfigDir(config.mode ?? "production");
2812
- const skills = await getEnabledSkillMetadata(configDir, effectiveWorkdir);
2842
+ const skills = await getEnabledSkillMetadata(configDir, session.workdir);
2813
2843
  const hasRunCommand = agentDef.metadata.allowedTools?.includes("run_command") ?? false;
2814
2844
  const gitignoreSection = hasRunCommand ? await loadGitIgnoreRules(effectiveWorkdir) : "";
2815
2845
  const systemPrompt = buildBasePrompt(effectiveWorkdir, void 0, skills.length > 0 ? skills : void 0, llmClient.getModel()) + "\n\n" + agentDef.prompt + (gitignoreSection ? "\n\n" + gitignoreSection : "") + RETURN_VALUE_INSTRUCTION;
@@ -2923,7 +2953,7 @@ var callSubAgentTool = {
2923
2953
  };
2924
2954
  }
2925
2955
  try {
2926
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-2B363F7L.js");
2956
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QYYNFMGU.js");
2927
2957
  const toolRegistry = getToolRegistryForAgent2(agentDef);
2928
2958
  const turnMetrics = new TurnMetrics();
2929
2959
  const result = await executeSubAgent({
@@ -2979,7 +3009,7 @@ var handler = async (args, context, helpers) => {
2979
3009
  }
2980
3010
  const config = getRuntimeConfig();
2981
3011
  const configDir = getGlobalConfigDir(config.mode ?? "production");
2982
- const allSkills = await loadAllSkills(configDir, context.workdir);
3012
+ const allSkills = await loadAllSkills(configDir, context.sessionManager.getProjectWorkdir(context.sessionId));
2983
3013
  const skill = findSkillById(skillId, allSkills);
2984
3014
  if (!skill) {
2985
3015
  const available = allSkills.map((s) => s.metadata.id).join(", ");
@@ -3945,7 +3975,7 @@ var mcpConfigTool = createTool(
3945
3975
  mcpNotifyChanged?.(sessionId);
3946
3976
  }
3947
3977
  async function rebuildTools() {
3948
- const { setMcpTools: setMcpTools2 } = await import("./tools-2B363F7L.js");
3978
+ const { setMcpTools: setMcpTools2 } = await import("./tools-QYYNFMGU.js");
3949
3979
  const mcpTools = createMcpTools(mcpManagerForTools);
3950
3980
  setMcpTools2(mcpTools);
3951
3981
  }
@@ -4359,6 +4389,241 @@ Actions:
4359
4389
  }
4360
4390
  );
4361
4391
 
4392
+ // src/server/tools/project-tasks.ts
4393
+ var tasksService = null;
4394
+ function setTasksService(service) {
4395
+ tasksService = service;
4396
+ }
4397
+ function getTasksService() {
4398
+ if (!tasksService) {
4399
+ throw new Error("Project tasks service not initialized");
4400
+ }
4401
+ return tasksService;
4402
+ }
4403
+ var VALID_ACTIONS = [
4404
+ "list",
4405
+ "get",
4406
+ "create",
4407
+ "edit",
4408
+ "move",
4409
+ "set_gate_value",
4410
+ "set_gates",
4411
+ "duplicate",
4412
+ "delete",
4413
+ "reorder"
4414
+ ];
4415
+ var projectTasksTool = createTool(
4416
+ "project_tasks",
4417
+ {
4418
+ type: "function",
4419
+ function: {
4420
+ name: "project_tasks",
4421
+ description: "Work with the project task board \u2014 the kanban of tasks owned by this project. Provides full parity with the human UI: list, get, create, edit, move, fill gate values, set gate configuration, duplicate, delete, and reorder.\n\nRules:\n- Moving a task to in_progress binds it to YOUR current session (you never create a new session for it).\n- Entering 'done' is blocked by unmet column gates (definition of done). When blocked, the error lists the missing fields; set them with action=set_gate_value (filling proof/evidence as part of your work), then retry the move.\n- All transitions are serialized server-side. If another actor changed the task, you get a CONFLICT error: re-list and retry.\n\nActions:\n- list: list all tasks (with gate values, status, queue position, bound session, audit trail)\n- get: fetch a single task (taskId)\n- create: create a task in To Do (prompt must contain text or an attachment)\n- edit: update prompt/attachments/agent/model (taskId + any fields)\n- move: change state \u2014 to: 'todo' | 'in_progress' | 'done' (optional reason for reverts)\n- set_gate_value: fill a gate field (taskId, gateId, value)\n- set_gates: replace the project gate configuration (gates: array of {id, name, description, required, variant})\n- duplicate: copy a task into To Do (taskId)\n- delete: permanently delete a task (taskId). Sessions stay untouched.\n- reorder: move a task to an index within its column (taskId, status, index)",
4422
+ parameters: {
4423
+ type: "object",
4424
+ properties: {
4425
+ action: { type: "string", enum: VALID_ACTIONS, description: "The action to perform" },
4426
+ taskId: {
4427
+ type: "string",
4428
+ description: "Target task id (get, edit, move, set_gate_value, duplicate, delete, reorder)"
4429
+ },
4430
+ prompt: { type: "string", description: "The prompt/instruction executed when the task launches" },
4431
+ attachments: { type: "array", description: "Optional attachments (same shape as chat attachments)" },
4432
+ agentId: { type: "string", description: "Selected agent id" },
4433
+ providerId: { type: "string", description: "Provider id used when a session is spawned" },
4434
+ model: { type: "string", description: "Model label used when a session is spawned" },
4435
+ to: { type: "string", enum: ["todo", "in_progress", "done"], description: "Destination column for move" },
4436
+ reason: { type: "string", description: "Optional short reason (recorded in the audit trail) for reverts" },
4437
+ gateId: { type: "string", description: "Gate field id for set_gate_value" },
4438
+ value: { type: "string", description: "Proof/evidence value for set_gate_value" },
4439
+ gates: {
4440
+ type: "array",
4441
+ description: "Full replacement gate config: [{id, name, description, required, variant}]"
4442
+ },
4443
+ status: { type: "string", enum: ["todo", "in_progress", "done"], description: "Column for reorder" },
4444
+ index: { type: "number", description: "Zero-based target index within the column (reorder)" },
4445
+ expectedVersion: {
4446
+ type: "number",
4447
+ description: "Optimistic concurrency guard \u2014 pass the task version you saw; stale writes fail with CONFLICT"
4448
+ }
4449
+ },
4450
+ required: ["action"]
4451
+ }
4452
+ }
4453
+ },
4454
+ async (args, context, helpers) => {
4455
+ const action = args.action;
4456
+ const permError = validateActionWithPermission(action, VALID_ACTIONS, "project_tasks", context.permittedActions);
4457
+ if (permError) return permError;
4458
+ const session = context.sessionManager.getSession(context.sessionId);
4459
+ if (!session) return helpers.error("Session not found");
4460
+ const projectId = session.projectId;
4461
+ const svc = getTasksService();
4462
+ const actor = { actor: "agent", actorName: "agent" };
4463
+ try {
4464
+ switch (action) {
4465
+ case "list": {
4466
+ const tasks = svc.list(projectId);
4467
+ const gates = getGateConfig(projectId);
4468
+ return helpers.success(JSON.stringify({ gates, tasks: tasks.map((t) => taskForAgent(t)) }, null, 2));
4469
+ }
4470
+ case "get": {
4471
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=get');
4472
+ const task = svc.get(projectId, args.taskId);
4473
+ if (!task) return helpers.error(`Task not found: ${args.taskId}`);
4474
+ return helpers.success(JSON.stringify(taskForAgent(task), null, 2));
4475
+ }
4476
+ case "create": {
4477
+ const task = svc.create(
4478
+ projectId,
4479
+ {
4480
+ prompt: args.prompt ?? "",
4481
+ ...args.attachments ? { attachments: sanitizeAttachments(args.attachments) } : {},
4482
+ ...args.agentId ? { agentId: args.agentId } : {},
4483
+ ...args.providerId ? { providerId: args.providerId } : {},
4484
+ ...args.model ? { model: args.model } : {}
4485
+ },
4486
+ actor
4487
+ );
4488
+ return helpers.success(JSON.stringify(taskForAgent(task), null, 2));
4489
+ }
4490
+ case "edit": {
4491
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=edit');
4492
+ const result = await svc.update(
4493
+ projectId,
4494
+ args.taskId,
4495
+ {
4496
+ ...args.prompt !== void 0 ? { prompt: args.prompt } : {},
4497
+ ...args.attachments ? { attachments: sanitizeAttachments(args.attachments) } : {},
4498
+ ...args.agentId !== void 0 ? { agentId: args.agentId } : {},
4499
+ ...args.providerId !== void 0 ? { providerId: args.providerId } : {},
4500
+ ...args.model !== void 0 ? { model: args.model } : {}
4501
+ },
4502
+ actor,
4503
+ args.expectedVersion
4504
+ );
4505
+ return helpers.success(JSON.stringify(taskForAgent(result.task), null, 2));
4506
+ }
4507
+ case "move": {
4508
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=move');
4509
+ if (!args.to)
4510
+ return helpers.error('Parameter "to" is required for action=move ("todo" | "in_progress" | "done")');
4511
+ const result = await svc.move(projectId, args.taskId, args.to, {
4512
+ actor: "agent",
4513
+ actorName: "agent",
4514
+ sessionId: context.sessionId,
4515
+ ...args.reason ? { reason: args.reason } : {},
4516
+ ...args.expectedVersion ? { expectedVersion: args.expectedVersion } : {}
4517
+ });
4518
+ return helpers.success(JSON.stringify(taskForAgent(result.task), null, 2));
4519
+ }
4520
+ case "set_gate_value": {
4521
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=set_gate_value');
4522
+ if (!args.gateId) return helpers.error('Parameter "gateId" is required for action=set_gate_value');
4523
+ if (args.value === void 0) return helpers.error('Parameter "value" is required for action=set_gate_value');
4524
+ const result = await svc.setGateValue(
4525
+ projectId,
4526
+ args.taskId,
4527
+ args.gateId,
4528
+ args.value,
4529
+ actor,
4530
+ context.sessionId,
4531
+ args.expectedVersion
4532
+ );
4533
+ return helpers.success(JSON.stringify(taskForAgent(result.task), null, 2));
4534
+ }
4535
+ case "set_gates": {
4536
+ if (!Array.isArray(args.gates)) {
4537
+ return helpers.error('Parameter "gates" (array) is required for action=set_gates');
4538
+ }
4539
+ const gates = sanitizeGates(args.gates);
4540
+ svc.setGateConfig(projectId, gates, actor);
4541
+ return helpers.success(JSON.stringify({ gates }, null, 2));
4542
+ }
4543
+ case "duplicate": {
4544
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=duplicate');
4545
+ const task = svc.duplicate(projectId, args.taskId, actor);
4546
+ return helpers.success(JSON.stringify(taskForAgent(task), null, 2));
4547
+ }
4548
+ case "delete": {
4549
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=delete');
4550
+ const task = svc.get(projectId, args.taskId);
4551
+ if (!task) return helpers.error(`Task not found: ${args.taskId}`);
4552
+ await svc.remove(projectId, args.taskId, actor);
4553
+ return helpers.success(
4554
+ JSON.stringify(
4555
+ { message: `Deleted: ${task.prompt.split("\n")[0]}`, taskId: task.id, prompt: task.prompt },
4556
+ null,
4557
+ 2
4558
+ )
4559
+ );
4560
+ }
4561
+ case "reorder": {
4562
+ if (!args.taskId) return helpers.error('Parameter "taskId" is required for action=reorder');
4563
+ if (!args.status || args.index === void 0) {
4564
+ return helpers.error('Parameters "status" and "index" are required for action=reorder');
4565
+ }
4566
+ const task = svc.reorder(projectId, args.taskId, args.status, args.index);
4567
+ return helpers.success(JSON.stringify(taskForAgent(task), null, 2));
4568
+ }
4569
+ default:
4570
+ return helpers.error(`Unknown action: ${String(action)}`);
4571
+ }
4572
+ } catch (error) {
4573
+ if (isTaskGateError(error)) {
4574
+ const missing = error.missing.map((m) => `'${m.name}' (${m.gateId}): ${m.description}`).join("; ");
4575
+ return helpers.error(
4576
+ `Move blocked by column gates. Missing required gate fields: ${missing}. Fill them with action=set_gate_value (taskId, gateId, value=<acceptable proof>) and then call move again. You set these values as part of your work \u2014 this is the intended loop, not a dead end.`
4577
+ );
4578
+ }
4579
+ if (isTaskConflictError(error)) {
4580
+ return helpers.error(
4581
+ `Task changed, refresh and retry. Another actor modified this task since your last read. Re-list and retry with the latest updatedAt.`
4582
+ );
4583
+ }
4584
+ throw error;
4585
+ }
4586
+ }
4587
+ );
4588
+ function taskForAgent(task) {
4589
+ return {
4590
+ id: task.id,
4591
+ prompt: task.prompt,
4592
+ status: task.status,
4593
+ ...task.runState ? { runState: task.runState } : {},
4594
+ ...task.queuePosition ? { queuePosition: task.queuePosition } : {},
4595
+ ...task.activeSessionId ? { boundSession: task.activeSessionId } : {},
4596
+ ...task.model ? { model: task.model } : {},
4597
+ version: task.version,
4598
+ attachments: task.attachments.length,
4599
+ gateValues: task.gateValues.map((v) => ({ [v.gateId]: v.value, actor: v.actor, timestamp: v.timestamp })),
4600
+ auditTrail: task.auditTrail.map((a) => ({
4601
+ action: a.action,
4602
+ actor: a.actor,
4603
+ detail: a.detail,
4604
+ timestamp: a.timestamp
4605
+ })),
4606
+ updatedAt: task.updatedAt
4607
+ };
4608
+ }
4609
+ function sanitizeAttachments(raw) {
4610
+ return raw.filter(
4611
+ (a) => typeof a === "object" && a !== null && typeof a.id === "string" && typeof a.filename === "string"
4612
+ );
4613
+ }
4614
+ function sanitizeGates(raw) {
4615
+ return raw.map((g, i) => {
4616
+ const gate = g;
4617
+ return {
4618
+ id: typeof gate.id === "string" && gate.id ? gate.id : `gate_${i}`,
4619
+ name: typeof gate.name === "string" && gate.name ? gate.name : `Gate ${i + 1}`,
4620
+ description: typeof gate.description === "string" ? gate.description : "",
4621
+ required: gate.required !== false,
4622
+ variant: gate.variant === "ready" ? "ready" : "done"
4623
+ };
4624
+ });
4625
+ }
4626
+
4362
4627
  // src/server/tools/index.ts
4363
4628
  var _builtInTools;
4364
4629
  function getBuiltInTools() {
@@ -4379,7 +4644,8 @@ function getBuiltInTools() {
4379
4644
  stepDoneTool,
4380
4645
  backgroundProcessTool,
4381
4646
  mcpConfigTool,
4382
- workspaceTool
4647
+ workspaceTool,
4648
+ projectTasksTool
4383
4649
  ];
4384
4650
  }
4385
4651
  return _builtInTools;
@@ -4628,6 +4894,7 @@ export {
4628
4894
  setMcpConfigMode,
4629
4895
  setMcpConfigPath,
4630
4896
  setNotifyMcpServersChanged,
4897
+ setTasksService,
4631
4898
  getBuiltInToolNames,
4632
4899
  parseToolPermissions,
4633
4900
  getToolPermissions,
@@ -4638,4 +4905,4 @@ export {
4638
4905
  getToolRegistryForAgent,
4639
4906
  createToolRegistry
4640
4907
  };
4641
- //# sourceMappingURL=chunk-5VYSKVZT.js.map
4908
+ //# sourceMappingURL=chunk-JFK4GKQD.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-V4IE7HJY.js";
4
4
  import {
5
5
  getBackendCapabilities
6
- } from "./chunk-YHEQTVFV.js";
6
+ } from "./chunk-AZHWLLPP.js";
7
7
  import {
8
8
  ensureVersionPrefix
9
9
  } from "./chunk-HNCM3D7Y.js";
@@ -13,11 +13,11 @@ import {
13
13
  getThinking,
14
14
  mapFinishReason,
15
15
  parseToolArguments
16
- } from "./chunk-L737Y63F.js";
16
+ } from "./chunk-TOKQ2UVG.js";
17
17
  import {
18
18
  LLMError,
19
19
  OpenAIHttpClient
20
- } from "./chunk-V4664YKR.js";
20
+ } from "./chunk-LTYL6ZGH.js";
21
21
  import {
22
22
  logger
23
23
  } from "./chunk-K44MW7JJ.js";
@@ -310,4 +310,4 @@ function createLLMClient(config, initialBackend = "unknown") {
310
310
  export {
311
311
  createLLMClient
312
312
  };
313
- //# sourceMappingURL=chunk-43M7QZMX.js.map
313
+ //# sourceMappingURL=chunk-JGWHRYWG.js.map