openfox 1.6.14 → 1.6.16

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 (39) hide show
  1. package/dist/{auto-compaction-C4PMBB24.js → auto-compaction-2RKC6DC2.js} +5 -5
  2. package/dist/{chat-handler-US3QOOE4.js → chat-handler-IY2XVOO6.js} +26 -56
  3. package/dist/{chunk-7HLRI6JM.js → chunk-2KP34IDL.js} +161 -204
  4. package/dist/{chunk-N25QUEPO.js → chunk-4OI4VPZ6.js} +434 -812
  5. package/dist/{chunk-XFXOSPYH.js → chunk-55N6FAAZ.js} +1 -1
  6. package/dist/{chunk-5GE3QJSZ.js → chunk-6L3X7T4K.js} +82 -160
  7. package/dist/{chunk-QDEKU5RL.js → chunk-F54ZJN4X.js} +38 -2
  8. package/dist/{chunk-UL4JYQKK.js → chunk-IN5EP4ZB.js} +2 -2
  9. package/dist/{chunk-OOIRCXAY.js → chunk-KOUMYBYM.js} +53 -112
  10. package/dist/{chunk-3JU6H6A4.js → chunk-NN65D5SI.js} +426 -448
  11. package/dist/{chunk-3XZ23PXM.js → chunk-OVLFEBRR.js} +76 -92
  12. package/dist/chunk-SN7OBEVL.js +44 -0
  13. package/dist/{chunk-PCOG5FQD.js → chunk-T5PBG2PE.js} +6 -6
  14. package/dist/{chunk-H22VHHQ4.js → chunk-U54QVKL2.js} +4 -4
  15. package/dist/{chunk-J2DHVXRX.js → chunk-ZDNXCVW4.js} +2 -2
  16. package/dist/cli/dev.js +1 -1
  17. package/dist/cli/index.js +1 -1
  18. package/dist/{config-DEM6ASQM.js → config-67AX6CNS.js} +5 -5
  19. package/dist/{events-VDAC7E7K.js → events-2ETDOE5B.js} +3 -3
  20. package/dist/{folding-NDFQCISZ.js → folding-M7FMUBOL.js} +6 -4
  21. package/dist/{orchestrator-KZI7B5IS.js → orchestrator-U5VCLBYQ.js} +6 -6
  22. package/dist/package.json +13 -3
  23. package/dist/{processor-KBSF2HFD.js → processor-VUEJM73B.js} +24 -49
  24. package/dist/{provider-K3PHZO27.js → provider-DKGBQHUS.js} +7 -7
  25. package/dist/{serve-DERN4AB6.js → serve-GOLQMQFA.js} +12 -12
  26. package/dist/server/index.d.ts +2 -0
  27. package/dist/server/index.js +10 -10
  28. package/dist/{tools-PLVK22CF.js → tools-55YICCVH.js} +5 -5
  29. package/dist/{vision-fallback-NOC3YYIB.js → vision-fallback-ADYRFFD4.js} +2 -2
  30. package/dist/web/assets/index-CZBXRYpK.js +150 -0
  31. package/dist/web/assets/{index-MCWDS5UQ.css → index-D4V7Gtvt.css} +1 -1
  32. package/dist/web/index.html +2 -2
  33. package/dist/web/sw.js +1 -1
  34. package/package.json +13 -3
  35. package/dist/web/assets/index-Ccy2Csbl.js +0 -150
  36. /package/dist/{command-defaults → server/commands/defaults}/commit-push.command.md +0 -0
  37. /package/dist/{command-defaults → server/commands/defaults}/init.command.md +0 -0
  38. /package/dist/{command-defaults → server/commands/defaults}/test-ui.command.md +0 -0
  39. /package/dist/{skill-defaults → server/skills/defaults}/browser.skill.md +0 -0
@@ -13,12 +13,12 @@ import {
13
13
  } from "./chunk-574HZVLE.js";
14
14
  import {
15
15
  streamWithSegments
16
- } from "./chunk-XFXOSPYH.js";
16
+ } from "./chunk-55N6FAAZ.js";
17
17
  import {
18
18
  getContextMessages,
19
19
  getCurrentContextWindowId,
20
20
  getEventStore
21
- } from "./chunk-5GE3QJSZ.js";
21
+ } from "./chunk-6L3X7T4K.js";
22
22
  import {
23
23
  createChatDoneMessage,
24
24
  createChatMessageMessage,
@@ -34,6 +34,7 @@ import {
34
34
  } from "./chunk-22CTURMH.js";
35
35
  import {
36
36
  SETTINGS_KEYS,
37
+ deleteSetting,
37
38
  getSetting,
38
39
  setSetting
39
40
  } from "./chunk-7IOZFJBW.js";
@@ -375,6 +376,31 @@ You may use available tools to read files and verify changes if needed.`;
375
376
  var FORMAT_CORRECTION_PROMPT = `IMPORTANT: You MUST use the JSON function calling API. Do NOT output XML tags like <tool_call>, <function=>, or <parameter=>. Your previous attempt was stopped because you used the wrong format. Use the proper tool_calls format.`;
376
377
 
377
378
  // src/server/chat/request-context.ts
379
+ function minimalMessagesToRequestContextMessages(messages, source = "history") {
380
+ return messages.map((message) => minimalMessageToRequestContextMessage(message, source));
381
+ }
382
+ function spreadMessageProps(message) {
383
+ return {
384
+ ...message.toolCalls ? { toolCalls: message.toolCalls } : {},
385
+ ...message.toolCallId ? { toolCallId: message.toolCallId } : {},
386
+ ...message.attachments ? { attachments: message.attachments } : {}
387
+ };
388
+ }
389
+ function minimalMessageToRequestContextMessage(message, source = "history") {
390
+ return {
391
+ role: message.role,
392
+ content: message.content,
393
+ source,
394
+ ...spreadMessageProps(message)
395
+ };
396
+ }
397
+ function messageToMinimal(message) {
398
+ return {
399
+ role: message.role,
400
+ content: message.content,
401
+ ...spreadMessageProps(message)
402
+ };
403
+ }
378
404
  function getTriggerUserMessage(messages) {
379
405
  const stripRuntimeReminders = (content) => {
380
406
  return content.replace(/\n*<system-reminder>[\s\S]*<\/system-reminder>\s*/gi, "").trim();
@@ -396,12 +422,8 @@ function createPromptContext(input) {
396
422
  injectedFiles: input.injectedFiles,
397
423
  userMessage: input.userMessage ?? getTriggerUserMessage(input.messages),
398
424
  messages: input.messages.map((message) => ({
399
- role: message.role,
400
- content: message.content,
401
- source: message.source,
402
- ...message.toolCalls ? { toolCalls: message.toolCalls } : {},
403
- ...message.toolCallId ? { toolCallId: message.toolCallId } : {},
404
- ...message.attachments ? { attachments: message.attachments } : {}
425
+ ...messageToMinimal(message),
426
+ source: message.source
405
427
  })),
406
428
  tools: input.requestTools.map((tool) => ({
407
429
  name: tool.function.name,
@@ -419,19 +441,25 @@ function createAssemblyResult(input) {
419
441
  const messagesForLLM = input.messages.filter((m) => m.source !== "runtime");
420
442
  return {
421
443
  systemPrompt: input.systemPrompt,
422
- messages: messagesForLLM.map((message) => ({
423
- role: message.role,
424
- content: message.content,
425
- ...message.toolCalls ? { toolCalls: message.toolCalls } : {},
426
- ...message.toolCallId ? { toolCallId: message.toolCallId } : {},
427
- ...message.attachments ? { attachments: message.attachments } : {}
428
- })),
444
+ messages: messagesForLLM.map((message) => messageToMinimal(message)),
429
445
  promptContext: createPromptContext({
430
446
  ...input,
431
447
  userMessage: triggerUserMessage
432
448
  })
433
449
  };
434
450
  }
451
+ function buildAssemblyInput(systemPrompt, baseInput) {
452
+ return createAssemblyResult({
453
+ systemPrompt,
454
+ messages: baseInput.messages,
455
+ injectedFiles: baseInput.injectedFiles,
456
+ requestTools: baseInput.requestTools ?? baseInput.promptTools,
457
+ toolChoice: baseInput.toolChoice ?? "auto",
458
+ disableThinking: baseInput.disableThinking ?? false,
459
+ ...baseInput.customInstructions ? { customInstructions: baseInput.customInstructions } : {},
460
+ ...baseInput.skills && baseInput.skills.length > 0 ? { skills: baseInput.skills } : {}
461
+ });
462
+ }
435
463
  function assembleAgentRequest(input) {
436
464
  const { agentDef, subAgentDefs, ...baseInput } = input;
437
465
  if (agentDef.metadata.subagent) {
@@ -440,14 +468,7 @@ function assembleAgentRequest(input) {
440
468
  agentDef,
441
469
  baseInput.skills
442
470
  );
443
- return createAssemblyResult({
444
- systemPrompt: systemPrompt2,
445
- messages: baseInput.messages,
446
- injectedFiles: baseInput.injectedFiles,
447
- requestTools: baseInput.requestTools ?? baseInput.promptTools,
448
- toolChoice: baseInput.toolChoice ?? "auto",
449
- disableThinking: baseInput.disableThinking ?? false
450
- });
471
+ return buildAssemblyInput(systemPrompt2, baseInput);
451
472
  }
452
473
  const systemPrompt = buildTopLevelSystemPrompt(
453
474
  baseInput.workdir,
@@ -455,14 +476,25 @@ function assembleAgentRequest(input) {
455
476
  baseInput.skills,
456
477
  subAgentDefs
457
478
  );
458
- return createAssemblyResult({
459
- systemPrompt,
460
- messages: baseInput.messages,
461
- injectedFiles: baseInput.injectedFiles,
462
- requestTools: baseInput.requestTools ?? baseInput.promptTools,
463
- toolChoice: baseInput.toolChoice ?? "auto",
464
- disableThinking: baseInput.disableThinking ?? false
465
- });
479
+ return buildAssemblyInput(systemPrompt, baseInput);
480
+ }
481
+
482
+ // src/server/chat/stream-utils.ts
483
+ function buildStreamRequestObject(params) {
484
+ const { messages, tools, toolChoice, disableThinking, signal, onVisionFallbackStart, onVisionFallbackDone } = params;
485
+ const streamRequest = {
486
+ messages,
487
+ ...tools && { tools },
488
+ ...toolChoice && { toolChoice },
489
+ disableThinking: disableThinking ?? false,
490
+ ...signal && { signal }
491
+ };
492
+ if (onVisionFallbackStart) streamRequest.onVisionFallbackStart = onVisionFallbackStart;
493
+ if (onVisionFallbackDone) streamRequest.onVisionFallbackDone = onVisionFallbackDone;
494
+ return streamRequest;
495
+ }
496
+ function buildStreamRequest(client, options) {
497
+ return streamWithSegments(client, buildStreamRequestObject(options));
466
498
  }
467
499
 
468
500
  // src/server/chat/stats.ts
@@ -483,19 +515,29 @@ function computeAggregatedStats(input) {
483
515
  }
484
516
 
485
517
  // src/server/chat/stream-pure.ts
518
+ function createEmptyStreamResult(aborted, xmlFormatError) {
519
+ return {
520
+ content: "",
521
+ toolCalls: [],
522
+ segments: [],
523
+ usage: { promptTokens: 0, completionTokens: 0 },
524
+ timing: { ttft: 0, completionTime: 0, tps: 0, prefillTps: 0 },
525
+ aborted,
526
+ xmlFormatError
527
+ };
528
+ }
486
529
  async function* streamLLMPure(options) {
487
530
  const { messageId, systemPrompt, llmClient, messages, tools, toolChoice, signal, disableThinking, onVisionFallbackStart, onVisionFallbackDone } = options;
488
531
  const llmMessages = [{ role: "system", content: systemPrompt }, ...messages];
489
- const streamRequest = {
532
+ const stream = buildStreamRequest(llmClient, {
490
533
  messages: llmMessages,
491
- ...tools && { tools },
492
- ...tools && { toolChoice: toolChoice ?? "auto" },
534
+ tools,
535
+ toolChoice,
493
536
  disableThinking: disableThinking ?? false,
494
- ...signal && { signal }
495
- };
496
- if (onVisionFallbackStart) streamRequest.onVisionFallbackStart = onVisionFallbackStart;
497
- if (onVisionFallbackDone) streamRequest.onVisionFallbackDone = onVisionFallbackDone;
498
- const stream = streamWithSegments(llmClient, streamRequest);
537
+ signal,
538
+ onVisionFallbackStart,
539
+ onVisionFallbackDone
540
+ });
499
541
  const seenToolIndices = /* @__PURE__ */ new Set();
500
542
  const toolNames = /* @__PURE__ */ new Map();
501
543
  const toolIds = /* @__PURE__ */ new Map();
@@ -594,15 +636,7 @@ async function* streamLLMPure(options) {
594
636
  }
595
637
  }
596
638
  if (!result) {
597
- return {
598
- content: "",
599
- toolCalls: [],
600
- segments: [],
601
- usage: { promptTokens: 0, completionTokens: 0 },
602
- timing: { ttft: 0, completionTime: 0, tps: 0, prefillTps: 0 },
603
- aborted,
604
- xmlFormatError
605
- };
639
+ return createEmptyStreamResult(aborted, xmlFormatError);
606
640
  }
607
641
  const baseResult = {
608
642
  content: result.content,
@@ -768,15 +802,7 @@ async function consumeStreamWithToolLoop(options) {
768
802
  let iterations = 0;
769
803
  for (; ; ) {
770
804
  if (signal?.aborted) {
771
- return {
772
- content: "",
773
- toolCalls: [],
774
- segments: [],
775
- usage: { promptTokens: 0, completionTokens: 0 },
776
- timing: { ttft: 0, completionTime: 0, tps: 0, prefillTps: 0 },
777
- aborted: true,
778
- xmlFormatError: false
779
- };
805
+ return createEmptyStreamResult(true, false);
780
806
  }
781
807
  if (++iterations > MAX_TOOL_LOOP_ITERATIONS) {
782
808
  throw new Error("Max tool loop iterations exceeded during compaction");
@@ -844,7 +870,7 @@ async function consumeStreamWithToolLoop(options) {
844
870
  }
845
871
  async function executeToolBatchWithContext(assistantMsgId, toolCalls, ctx, onEvent) {
846
872
  const toolMessages = [];
847
- let criteriaChanged = false;
873
+ const criteriaChanged = false;
848
874
  for (const toolCall of toolCalls) {
849
875
  if (ctx.signal?.aborted) {
850
876
  throw new Error("Aborted");
@@ -884,7 +910,7 @@ Error: ${toolResult.error}` : `Error: ${toolResult.error}`;
884
910
  }
885
911
 
886
912
  // src/server/agents/registry.ts
887
- import { readdir, readFile as readFile2, writeFile, copyFile, mkdir, access as access2, unlink } from "fs/promises";
913
+ import { readdir, readFile as readFile2, writeFile, mkdir, access as access2, unlink } from "fs/promises";
888
914
  import { join as join2, dirname as dirname2 } from "path";
889
915
  import { constants as constants2 } from "fs";
890
916
  import { fileURLToPath } from "url";
@@ -904,37 +930,6 @@ async function pathExists(path) {
904
930
  return false;
905
931
  }
906
932
  }
907
- async function ensureDefaultAgents(configDir) {
908
- const agentsDir = getAgentsDir(configDir);
909
- if (!await pathExists(agentsDir)) {
910
- await mkdir(agentsDir, { recursive: true });
911
- }
912
- let defaultFiles;
913
- let sourceDir;
914
- try {
915
- defaultFiles = (await readdir(DEFAULTS_DIR)).filter((f) => f.endsWith(AGENT_EXTENSION));
916
- sourceDir = DEFAULTS_DIR;
917
- } catch {
918
- try {
919
- defaultFiles = (await readdir(DEFAULTS_DIR_ALT)).filter((f) => f.endsWith(AGENT_EXTENSION));
920
- sourceDir = DEFAULTS_DIR_ALT;
921
- } catch {
922
- logger.warn("No bundled agent defaults found", { dir: DEFAULTS_DIR });
923
- return;
924
- }
925
- }
926
- for (const file of defaultFiles) {
927
- const targetPath = join2(agentsDir, file);
928
- if (!await pathExists(targetPath)) {
929
- try {
930
- await copyFile(join2(sourceDir, file), targetPath);
931
- logger.info("Installed default agent", { file });
932
- } catch (err) {
933
- logger.error("Failed to copy default agent", { file, error: err instanceof Error ? err.message : String(err) });
934
- }
935
- }
936
- }
937
- }
938
933
  function parseAgentFile(raw, filename) {
939
934
  const { data, content } = matter(raw);
940
935
  const meta = data;
@@ -977,13 +972,21 @@ async function loadAgentsFromDir(dir) {
977
972
  }
978
973
  return agents;
979
974
  }
975
+ async function loadDefaultAgents() {
976
+ const agents = await loadAgentsFromDir(DEFAULTS_DIR);
977
+ if (agents.length > 0) return agents;
978
+ return loadAgentsFromDir(DEFAULTS_DIR_ALT);
979
+ }
980
+ async function loadUserAgents(configDir) {
981
+ return loadAgentsFromDir(getAgentsDir(configDir));
982
+ }
980
983
  async function loadAllAgents(configDir) {
981
- const [builtinAgents, userAgents] = await Promise.all([
982
- loadBuiltinAgents(),
983
- loadAgentsFromDir(getAgentsDir(configDir))
984
+ const [defaultAgents, userAgents] = await Promise.all([
985
+ loadDefaultAgents(),
986
+ loadUserAgents(configDir)
984
987
  ]);
985
988
  const agentMap = /* @__PURE__ */ new Map();
986
- for (const agent of builtinAgents) {
989
+ for (const agent of defaultAgents) {
987
990
  agentMap.set(agent.metadata.id, agent);
988
991
  }
989
992
  for (const agent of userAgents) {
@@ -996,14 +999,9 @@ async function loadAllAgentsDefault() {
996
999
  const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? "production");
997
1000
  return await loadAllAgents(configDir);
998
1001
  } catch {
999
- return loadBuiltinAgents();
1002
+ return loadDefaultAgents();
1000
1003
  }
1001
1004
  }
1002
- async function loadBuiltinAgents() {
1003
- const agents = await loadAgentsFromDir(DEFAULTS_DIR);
1004
- if (agents.length > 0) return agents;
1005
- return loadAgentsFromDir(DEFAULTS_DIR_ALT);
1006
- }
1007
1005
  async function getDefaultAgentIds() {
1008
1006
  for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {
1009
1007
  try {
@@ -1014,48 +1012,20 @@ async function getDefaultAgentIds() {
1014
1012
  }
1015
1013
  return [];
1016
1014
  }
1017
- async function restoreDefaultAgent(configDir, agentId) {
1018
- const defaultIds = await getDefaultAgentIds();
1019
- if (!defaultIds.includes(agentId)) return false;
1020
- const userPath = join2(getAgentsDir(configDir), `${agentId}${AGENT_EXTENSION}`);
1021
- try {
1022
- await unlink(userPath);
1023
- } catch {
1024
- }
1025
- return true;
1015
+ async function getDefaultAgentContent(agentId) {
1016
+ const defaults = await loadDefaultAgents();
1017
+ return defaults.find((a) => a.metadata.id === agentId) ?? null;
1026
1018
  }
1027
- async function getModifiedDefaultAgentIds(configDir) {
1019
+ async function isDefaultAgent(agentId) {
1028
1020
  const defaultIds = await getDefaultAgentIds();
1029
- const modified = [];
1030
- for (const id of defaultIds) {
1031
- const filename = `${id}${AGENT_EXTENSION}`;
1032
- const userPath = join2(getAgentsDir(configDir), filename);
1033
- let bundledContent = null;
1034
- for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {
1035
- try {
1036
- bundledContent = await readFile2(join2(dir, filename), "utf-8");
1037
- break;
1038
- } catch {
1039
- }
1040
- }
1041
- if (!bundledContent) continue;
1042
- try {
1043
- const userContent = await readFile2(userPath, "utf-8");
1044
- if (userContent !== bundledContent) {
1045
- modified.push(id);
1046
- }
1047
- } catch {
1048
- }
1049
- }
1050
- return modified;
1021
+ return defaultIds.includes(agentId);
1051
1022
  }
1052
- async function restoreAllDefaultAgents(configDir) {
1053
- const ids = await getDefaultAgentIds();
1054
- let count = 0;
1055
- for (const id of ids) {
1056
- if (await restoreDefaultAgent(configDir, id)) count++;
1057
- }
1058
- return count;
1023
+ function getAgentFilePaths(agentsDir, agentId) {
1024
+ const hyphenated = agentId.replace(/_/g, "-");
1025
+ return [
1026
+ join2(agentsDir, `${agentId}${AGENT_EXTENSION}`),
1027
+ join2(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)
1028
+ ];
1059
1029
  }
1060
1030
  function findAgentById(agentId, agents) {
1061
1031
  return agents.find((a) => a.metadata.id === agentId);
@@ -1065,11 +1035,7 @@ function getSubAgents(agents) {
1065
1035
  }
1066
1036
  async function agentExists(configDir, agentId) {
1067
1037
  const agentsDir = getAgentsDir(configDir);
1068
- const hyphenated = agentId.replace(/_/g, "-");
1069
- const paths = [
1070
- join2(agentsDir, `${agentId}${AGENT_EXTENSION}`),
1071
- join2(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)
1072
- ];
1038
+ const paths = getAgentFilePaths(agentsDir, agentId);
1073
1039
  for (const filePath of paths) {
1074
1040
  if (await pathExists(filePath)) {
1075
1041
  return true;
@@ -1087,21 +1053,21 @@ async function saveAgent(configDir, agent) {
1087
1053
  await writeFile(filePath, content, "utf-8");
1088
1054
  }
1089
1055
  async function deleteAgent(configDir, agentId) {
1056
+ const isDefault = await isDefaultAgent(agentId);
1057
+ if (isDefault) {
1058
+ return { success: false, reason: "Cannot delete built-in defaults" };
1059
+ }
1090
1060
  const agentsDir = getAgentsDir(configDir);
1091
- const hyphenated = agentId.replace(/_/g, "-");
1092
- const paths = [
1093
- join2(agentsDir, `${agentId}${AGENT_EXTENSION}`),
1094
- join2(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)
1095
- ];
1061
+ const paths = getAgentFilePaths(agentsDir, agentId);
1096
1062
  for (const filePath of paths) {
1097
1063
  try {
1098
1064
  await unlink(filePath);
1099
- return true;
1065
+ return { success: true };
1100
1066
  } catch {
1101
1067
  continue;
1102
1068
  }
1103
1069
  }
1104
- return false;
1070
+ return { success: false };
1105
1071
  }
1106
1072
 
1107
1073
  // src/server/tools/read.ts
@@ -1461,6 +1427,39 @@ function cancelPathConfirmationsForSession(sessionId, reason) {
1461
1427
  }
1462
1428
 
1463
1429
  // src/server/tools/tool-helpers.ts
1430
+ function validateAction(action, allowed, startTime) {
1431
+ if (!action || !allowed.includes(action)) {
1432
+ return {
1433
+ success: false,
1434
+ error: `Invalid action: ${action}. Must be one of: ${allowed.join(", ")}`,
1435
+ durationMs: Date.now() - startTime,
1436
+ truncated: false
1437
+ };
1438
+ }
1439
+ return void 0;
1440
+ }
1441
+ function checkActionPermission(action, permittedActions, startTime) {
1442
+ if (action && permittedActions && !permittedActions.includes(action)) {
1443
+ return {
1444
+ success: false,
1445
+ error: `Action '${action}' not allowed. Available: ${permittedActions.join(", ")}`,
1446
+ durationMs: Date.now() - startTime,
1447
+ truncated: false
1448
+ };
1449
+ }
1450
+ return void 0;
1451
+ }
1452
+ function requireSession(sessionManager, sessionId) {
1453
+ return sessionManager.requireSession(sessionId);
1454
+ }
1455
+ function validateActionWithPermission(action, allowedActions, toolName, permittedActions, startTime) {
1456
+ const actionError = validateAction(action, allowedActions, startTime ?? Date.now());
1457
+ if (actionError) return actionError;
1458
+ const permittedToolActions = permittedActions?.[toolName];
1459
+ const permissionError = checkActionPermission(action, permittedToolActions, startTime ?? Date.now());
1460
+ if (permissionError) return permissionError;
1461
+ return void 0;
1462
+ }
1464
1463
  function createTool(name, definition, handler2) {
1465
1464
  return {
1466
1465
  name,
@@ -2021,11 +2020,26 @@ Make sure whitespace and indentation match exactly.`
2021
2020
  );
2022
2021
 
2023
2022
  // src/server/tools/shell.ts
2024
- import { spawn as spawn2 } from "child_process";
2023
+ import "child_process";
2025
2024
  import { resolve as resolve4, isAbsolute as isAbsolute2 } from "path";
2026
2025
 
2027
- // src/server/utils/process-tree.ts
2026
+ // src/server/utils/shell.ts
2028
2027
  import { spawn } from "child_process";
2028
+ function checkAborted(signal) {
2029
+ return !!signal?.aborted;
2030
+ }
2031
+ function spawnShellProcess(command, cwd, signal, detached = false) {
2032
+ const shell = getPlatformShell();
2033
+ return spawn(shell.command, [...shell.args, command], {
2034
+ cwd,
2035
+ env: { ...process.env, FORCE_COLOR: "0" },
2036
+ stdio: ["ignore", "pipe", "pipe"],
2037
+ ...detached ? { detached: true } : {}
2038
+ });
2039
+ }
2040
+
2041
+ // src/server/utils/process-tree.ts
2042
+ import { spawn as spawn2 } from "child_process";
2029
2043
  import { setTimeout as sleep } from "timers/promises";
2030
2044
  var SIGKILL_TIMEOUT_MS = 200;
2031
2045
  async function terminateProcessTree(proc, options) {
@@ -2035,7 +2049,7 @@ async function terminateProcessTree(proc, options) {
2035
2049
  }
2036
2050
  if (process.platform === "win32") {
2037
2051
  await new Promise((resolve6) => {
2038
- const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], {
2052
+ const killer = spawn2("taskkill", ["/pid", String(pid), "/f", "/t"], {
2039
2053
  stdio: "ignore",
2040
2054
  windowsHide: true
2041
2055
  });
@@ -2139,18 +2153,11 @@ ${result.stderr}`;
2139
2153
  );
2140
2154
  function executeCommand(command, cwd, timeout, signal, onProgress) {
2141
2155
  return new Promise((resolve6, reject) => {
2142
- if (signal?.aborted) {
2156
+ if (checkAborted(signal)) {
2143
2157
  reject(new Error("Command aborted before execution"));
2144
2158
  return;
2145
2159
  }
2146
- const shell = getPlatformShell();
2147
- const proc = spawn2(shell.command, [...shell.args, command], {
2148
- cwd,
2149
- env: { ...process.env, FORCE_COLOR: "0" },
2150
- stdio: ["ignore", "pipe", "pipe"],
2151
- detached: true
2152
- // Create new process group so we can kill all children
2153
- });
2160
+ const proc = spawnShellProcess(command, cwd, signal, true);
2154
2161
  let stdout = "";
2155
2162
  let stderr = "";
2156
2163
  let killed = false;
@@ -2168,12 +2175,12 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
2168
2175
  }
2169
2176
  };
2170
2177
  signal?.addEventListener("abort", onAbort);
2171
- proc.stdout.on("data", (data) => {
2178
+ proc.stdout?.on("data", (data) => {
2172
2179
  const chunk = data.toString();
2173
2180
  stdout += chunk;
2174
2181
  onProgress?.(`[stdout] ${chunk}`);
2175
2182
  });
2176
- proc.stderr.on("data", (data) => {
2183
+ proc.stderr?.on("data", (data) => {
2177
2184
  const chunk = data.toString();
2178
2185
  stderr += chunk;
2179
2186
  onProgress?.(`[stderr] ${chunk}`);
@@ -2216,10 +2223,27 @@ function formatCriteriaList(criteria) {
2216
2223
  if (criteria.length === 0) return "No criteria defined.";
2217
2224
  return criteria.map((c, i) => `${i + 1}. [${c.id}] ${c.description}`).join("\n");
2218
2225
  }
2219
- var criterionTool = {
2220
- name: "criterion",
2221
- permittedActions: ["get", "add", "update", "remove", "complete", "pass", "fail"],
2222
- definition: {
2226
+ function completeCriterion(session, context, id, statusType, reason) {
2227
+ const criterion = session.criteria.find((c) => c.id === id);
2228
+ if (!criterion) {
2229
+ return { success: false, output: `Criterion "${id}" not found` };
2230
+ }
2231
+ const status = statusType === "passed" ? { type: "passed", verifiedAt: (/* @__PURE__ */ new Date()).toISOString(), ...reason && { reason } } : { type: "failed", failedAt: (/* @__PURE__ */ new Date()).toISOString(), reason };
2232
+ context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
2233
+ context.sessionManager.addCriterionAttempt(context.sessionId, id, {
2234
+ attemptNumber: criterion.attempts.length + 1,
2235
+ status: statusType,
2236
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2237
+ ...reason ? { details: reason } : {}
2238
+ });
2239
+ return {
2240
+ success: true,
2241
+ output: statusType === "passed" ? `Criterion "${id}" verified as PASSED.${reason ? ` Verification: ${reason}` : ""}` : `Criterion "${id}" marked as FAILED. Reason: ${reason}`
2242
+ };
2243
+ }
2244
+ var criterionTool = createTool(
2245
+ "criterion",
2246
+ {
2223
2247
  type: "function",
2224
2248
  function: {
2225
2249
  name: "criterion",
@@ -2249,267 +2273,90 @@ var criterionTool = {
2249
2273
  }
2250
2274
  }
2251
2275
  },
2252
- async execute(args, context) {
2253
- const startTime = Date.now();
2254
- try {
2255
- const action = args["action"];
2256
- const id = args["id"];
2257
- const description = args["description"];
2258
- const reason = args["reason"];
2259
- if (!action || !["get", "add", "update", "remove", "complete", "pass", "fail"].includes(action)) {
2260
- return {
2261
- success: false,
2262
- error: `Invalid action: ${action}. Must be one of: get, add, update, remove, complete, pass, fail`,
2263
- durationMs: Date.now() - startTime,
2264
- truncated: false
2265
- };
2266
- }
2267
- const permittedActions = context.permittedActions?.["criterion"];
2268
- if (permittedActions && !permittedActions.includes(action)) {
2269
- return {
2270
- success: false,
2271
- error: `Action '${action}' not allowed. Available: ${permittedActions.join(", ")}`,
2272
- durationMs: Date.now() - startTime,
2273
- truncated: false
2274
- };
2275
- }
2276
- const session = context.sessionManager.requireSession(context.sessionId);
2277
- if (action === "get") {
2278
- const criteria = session.criteria;
2279
- return {
2280
- success: true,
2281
- output: criteria.length === 0 ? "No criteria defined yet." : JSON.stringify(criteria.map((c) => ({
2282
- id: c.id,
2283
- description: c.description
2284
- })), null, 2),
2285
- durationMs: Date.now() - startTime,
2286
- truncated: false
2287
- };
2288
- }
2289
- if (action === "add") {
2290
- if (!description) {
2291
- return {
2292
- success: false,
2293
- error: "Missing required field: description",
2294
- durationMs: Date.now() - startTime,
2295
- truncated: false
2296
- };
2297
- }
2298
- const criterion = {
2299
- id: id || "",
2300
- description,
2301
- status: { type: "pending" },
2302
- attempts: []
2303
- };
2304
- const result = context.sessionManager.addCriterion(context.sessionId, criterion);
2305
- if ("error" in result) {
2306
- return { success: false, error: result.error, durationMs: Date.now() - startTime, truncated: false };
2307
- }
2308
- return {
2309
- success: true,
2310
- output: `Added criterion "${result.actualId}". Current criteria:
2311
- ${formatCriteriaList(result.criteria)}`,
2312
- durationMs: Date.now() - startTime,
2313
- truncated: false
2314
- };
2315
- }
2316
- if (action === "update") {
2317
- if (!id) {
2318
- return {
2319
- success: false,
2320
- error: "Missing required field: id",
2321
- durationMs: Date.now() - startTime,
2322
- truncated: false
2323
- };
2324
- }
2325
- if (!description) {
2326
- return {
2327
- success: false,
2328
- error: "Missing required field: description",
2329
- durationMs: Date.now() - startTime,
2330
- truncated: false
2331
- };
2332
- }
2333
- if (!session.criteria.find((c) => c.id === id)) {
2334
- return {
2335
- success: false,
2336
- error: `Criterion "${id}" not found`,
2337
- durationMs: Date.now() - startTime,
2338
- truncated: false
2339
- };
2340
- }
2341
- const criteria = context.sessionManager.updateCriterionFull(context.sessionId, id, { description });
2342
- return {
2343
- success: true,
2344
- output: `Updated criterion "${id}". Current criteria:
2345
- ${formatCriteriaList(criteria)}`,
2346
- durationMs: Date.now() - startTime,
2347
- truncated: false
2348
- };
2349
- }
2350
- if (action === "remove") {
2351
- if (!id) {
2352
- return {
2353
- success: false,
2354
- error: "Missing required field: id",
2355
- durationMs: Date.now() - startTime,
2356
- truncated: false
2357
- };
2358
- }
2359
- if (!session.criteria.find((c) => c.id === id)) {
2360
- return {
2361
- success: false,
2362
- error: `Criterion "${id}" not found`,
2363
- durationMs: Date.now() - startTime,
2364
- truncated: false
2365
- };
2366
- }
2367
- const criteria = context.sessionManager.removeCriterion(context.sessionId, id);
2368
- return {
2369
- success: true,
2370
- output: criteria.length === 0 ? `Removed criterion "${id}". No criteria remaining.` : `Removed criterion "${id}". Current criteria:
2371
- ${formatCriteriaList(criteria)}`,
2372
- durationMs: Date.now() - startTime,
2373
- truncated: false
2374
- };
2375
- }
2376
- if (action === "complete") {
2377
- if (!id) {
2378
- return {
2379
- success: false,
2380
- error: "Missing required field: id",
2381
- durationMs: Date.now() - startTime,
2382
- truncated: false
2383
- };
2384
- }
2385
- const criterion = session.criteria.find((c) => c.id === id);
2386
- if (!criterion) {
2387
- return {
2388
- success: false,
2389
- error: `Criterion "${id}" not found. Available: ${session.criteria.map((c) => c.id).join(", ")}`,
2390
- durationMs: Date.now() - startTime,
2391
- truncated: false
2392
- };
2393
- }
2394
- const status = {
2395
- type: "completed",
2396
- completedAt: (/* @__PURE__ */ new Date()).toISOString(),
2397
- ...reason ? { reason } : {}
2398
- };
2399
- context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
2400
- return {
2401
- success: true,
2402
- output: `Criterion "${id}" marked as completed.${reason ? ` Reason: ${reason}` : ""}`,
2403
- durationMs: Date.now() - startTime,
2404
- truncated: false
2405
- };
2406
- }
2407
- if (action === "pass") {
2408
- if (!id) {
2409
- return {
2410
- success: false,
2411
- error: "Missing required field: id",
2412
- durationMs: Date.now() - startTime,
2413
- truncated: false
2414
- };
2415
- }
2416
- const criterion = session.criteria.find((c) => c.id === id);
2417
- if (!criterion) {
2418
- return {
2419
- success: false,
2420
- error: `Criterion "${id}" not found`,
2421
- durationMs: Date.now() - startTime,
2422
- truncated: false
2423
- };
2424
- }
2425
- const status = {
2426
- type: "passed",
2427
- verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
2428
- ...reason ? { reason } : {}
2429
- };
2430
- context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
2431
- context.sessionManager.addCriterionAttempt(context.sessionId, id, {
2432
- attemptNumber: criterion.attempts.length + 1,
2433
- status: "passed",
2434
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2435
- ...reason && { details: reason }
2436
- });
2437
- return {
2438
- success: true,
2439
- output: `Criterion "${id}" verified as PASSED.${reason ? ` Verification: ${reason}` : ""}`,
2440
- durationMs: Date.now() - startTime,
2441
- truncated: false
2442
- };
2443
- }
2444
- if (action === "fail") {
2445
- if (!id) {
2446
- return {
2447
- success: false,
2448
- error: "Missing required field: id",
2449
- durationMs: Date.now() - startTime,
2450
- truncated: false
2451
- };
2452
- }
2453
- if (!reason) {
2454
- return {
2455
- success: false,
2456
- error: "Missing required field: reason",
2457
- durationMs: Date.now() - startTime,
2458
- truncated: false
2459
- };
2460
- }
2461
- const criterion = session.criteria.find((c) => c.id === id);
2462
- if (!criterion) {
2463
- return {
2464
- success: false,
2465
- error: `Criterion "${id}" not found`,
2466
- durationMs: Date.now() - startTime,
2467
- truncated: false
2468
- };
2469
- }
2470
- const status = {
2471
- type: "failed",
2472
- failedAt: (/* @__PURE__ */ new Date()).toISOString(),
2473
- reason
2474
- };
2475
- context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
2476
- context.sessionManager.addCriterionAttempt(context.sessionId, id, {
2477
- attemptNumber: criterion.attempts.length + 1,
2478
- status: "failed",
2479
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2480
- details: reason
2481
- });
2482
- return {
2483
- success: true,
2484
- output: `Criterion "${id}" marked as FAILED. Reason: ${reason}`,
2485
- durationMs: Date.now() - startTime,
2486
- truncated: false
2487
- };
2488
- }
2489
- return {
2490
- success: false,
2491
- error: "Unexpected error",
2492
- durationMs: Date.now() - startTime,
2493
- truncated: false
2494
- };
2495
- } catch (error) {
2496
- return {
2497
- success: false,
2498
- error: error instanceof Error ? error.message : "Unknown error",
2499
- durationMs: Date.now() - startTime,
2500
- truncated: false
2276
+ async (args, context, helpers) => {
2277
+ const actionError = validateActionWithPermission(args.action, ["get", "add", "update", "remove", "complete", "pass", "fail"], "criterion", context.permittedActions);
2278
+ if (actionError) return actionError;
2279
+ const session = requireSession(context.sessionManager, context.sessionId);
2280
+ if (args.action === "get") {
2281
+ return helpers.success(
2282
+ session.criteria.length === 0 ? "No criteria defined yet." : JSON.stringify(session.criteria.map((c) => ({ id: c.id, description: c.description })), null, 2)
2283
+ );
2284
+ }
2285
+ if (args.action === "add") {
2286
+ if (!args.description) return helpers.error("Missing required field: description");
2287
+ const criterion = {
2288
+ id: args.id || "",
2289
+ description: args.description,
2290
+ status: { type: "pending" },
2291
+ attempts: []
2501
2292
  };
2293
+ const result = context.sessionManager.addCriterion(context.sessionId, criterion);
2294
+ if ("error" in result) return helpers.error(result.error);
2295
+ return helpers.success(`Added criterion "${result.actualId}". Current criteria:
2296
+ ${formatCriteriaList(result.criteria)}`);
2297
+ }
2298
+ if (args.action === "update") {
2299
+ if (!args.id) return helpers.error("Missing required field: id");
2300
+ if (!args.description) return helpers.error("Missing required field: description");
2301
+ if (!session.criteria.find((c) => c.id === args.id)) return helpers.error(`Criterion "${args.id}" not found`);
2302
+ const criteria = context.sessionManager.updateCriterionFull(context.sessionId, args.id, { description: args.description });
2303
+ return helpers.success(`Updated criterion "${args.id}". Current criteria:
2304
+ ${formatCriteriaList(criteria)}`);
2305
+ }
2306
+ if (args.action === "remove") {
2307
+ if (!args.id) return helpers.error("Missing required field: id");
2308
+ if (!session.criteria.find((c) => c.id === args.id)) return helpers.error(`Criterion "${args.id}" not found`);
2309
+ const criteria = context.sessionManager.removeCriterion(context.sessionId, args.id);
2310
+ return helpers.success(
2311
+ criteria.length === 0 ? `Removed criterion "${args.id}". No criteria remaining.` : `Removed criterion "${args.id}". Current criteria:
2312
+ ${formatCriteriaList(criteria)}`
2313
+ );
2314
+ }
2315
+ if (args.action === "complete") {
2316
+ if (!args.id) return helpers.error("Missing required field: id");
2317
+ const criterion = session.criteria.find((c) => c.id === args.id);
2318
+ if (!criterion) return helpers.error(`Criterion "${args.id}" not found. Available: ${session.criteria.map((c) => c.id).join(", ")}`);
2319
+ context.sessionManager.updateCriterionStatus(context.sessionId, args.id, {
2320
+ type: "completed",
2321
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
2322
+ ...args.reason ? { reason: args.reason } : {}
2323
+ });
2324
+ return helpers.success(`Criterion "${args.id}" marked as completed.${args.reason ? ` Reason: ${args.reason}` : ""}`);
2325
+ }
2326
+ if (args.action === "pass") {
2327
+ if (!args.id) return helpers.error("Missing required field: id");
2328
+ const result = completeCriterion(session, context, args.id, "passed", args.reason);
2329
+ return result.success ? helpers.success(result.output) : helpers.error(result.output);
2502
2330
  }
2331
+ if (args.action === "fail") {
2332
+ if (!args.id) return helpers.error("Missing required field: id");
2333
+ if (!args.reason) return helpers.error("Missing required field: reason");
2334
+ const result = completeCriterion(session, context, args.id, "failed", args.reason);
2335
+ return result.success ? helpers.success(result.output) : helpers.error(result.output);
2336
+ }
2337
+ return helpers.error("Unexpected error");
2503
2338
  }
2504
- };
2339
+ );
2505
2340
 
2506
2341
  // src/server/tools/todo.ts
2507
2342
  var sessionTodos = /* @__PURE__ */ new Map();
2508
2343
  var onTodoUpdate = null;
2509
- var todoTool = {
2510
- name: "todo",
2511
- permittedActions: ["list", "write", "add", "update", "remove"],
2512
- definition: {
2344
+ function buildTaskListSummary(todosList) {
2345
+ const pending = todosList.filter((t) => t.status === "pending").length;
2346
+ const inProgress = todosList.filter((t) => t.status === "in_progress").length;
2347
+ const completed = todosList.filter((t) => t.status === "completed").length;
2348
+ return `${completed} completed, ${inProgress} in progress, ${pending} pending`;
2349
+ }
2350
+ function validateIndex(index, todosList, helpers) {
2351
+ if (index === void 0) return helpers.error("Missing required field: index");
2352
+ if (index < 0 || index >= todosList.length) {
2353
+ return helpers.error(`Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`);
2354
+ }
2355
+ return todosList[index];
2356
+ }
2357
+ var todoTool = createTool(
2358
+ "todo",
2359
+ {
2513
2360
  type: "function",
2514
2361
  function: {
2515
2362
  name: "todo",
@@ -2552,235 +2399,65 @@ var todoTool = {
2552
2399
  }
2553
2400
  }
2554
2401
  },
2555
- async execute(args, context) {
2556
- const startTime = Date.now();
2557
- try {
2558
- const action = args["action"];
2559
- const todos = args["todos"];
2560
- const content = args["content"];
2561
- const index = args["index"];
2562
- const status = args["status"];
2563
- if (!action || !["list", "write", "add", "update", "remove"].includes(action)) {
2564
- return {
2565
- success: false,
2566
- error: `Invalid action: ${action}. Must be one of: list, write, add, update, remove`,
2567
- durationMs: Date.now() - startTime,
2568
- truncated: false
2569
- };
2570
- }
2571
- const permittedActions = context.permittedActions?.["todo"];
2572
- if (permittedActions && !permittedActions.includes(action)) {
2573
- return {
2574
- success: false,
2575
- error: `Action '${action}' not allowed. Available: ${permittedActions.join(", ")}`,
2576
- durationMs: Date.now() - startTime,
2577
- truncated: false
2578
- };
2579
- }
2580
- if (action === "list") {
2581
- const todosList = sessionTodos.get(context.sessionId) ?? [];
2582
- return {
2583
- success: true,
2584
- output: todosList.length === 0 ? "No tasks defined yet." : JSON.stringify(todosList, null, 2),
2585
- durationMs: Date.now() - startTime,
2586
- truncated: false
2587
- };
2588
- }
2589
- if (action === "write") {
2590
- if (!todos) {
2591
- return {
2592
- success: false,
2593
- error: "Missing required field: todos",
2594
- durationMs: Date.now() - startTime,
2595
- truncated: false
2596
- };
2597
- }
2598
- if (!Array.isArray(todos)) {
2599
- return {
2600
- success: false,
2601
- error: "todos must be an array",
2602
- durationMs: Date.now() - startTime,
2603
- truncated: false
2604
- };
2605
- }
2606
- for (const todo of todos) {
2607
- if (!todo.content || !todo.status) {
2608
- return {
2609
- success: false,
2610
- error: "Each todo must have content and status",
2611
- durationMs: Date.now() - startTime,
2612
- truncated: false
2613
- };
2614
- }
2615
- if (!["pending", "in_progress", "completed"].includes(todo.status)) {
2616
- return {
2617
- success: false,
2618
- error: `Invalid status: ${todo.status}. Must be pending, in_progress, or completed`,
2619
- durationMs: Date.now() - startTime,
2620
- truncated: false
2621
- };
2622
- }
2623
- }
2624
- sessionTodos.set(context.sessionId, todos);
2625
- if (onTodoUpdate) {
2626
- onTodoUpdate(context.sessionId, todos);
2627
- }
2628
- const pending = todos.filter((t) => t.status === "pending").length;
2629
- const inProgress = todos.filter((t) => t.status === "in_progress").length;
2630
- const completed = todos.filter((t) => t.status === "completed").length;
2631
- return {
2632
- success: true,
2633
- output: `Task list updated: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
2634
- durationMs: Date.now() - startTime,
2635
- truncated: false
2636
- };
2637
- }
2638
- if (action === "add") {
2639
- if (!content) {
2640
- return {
2641
- success: false,
2642
- error: "Missing required field: content",
2643
- durationMs: Date.now() - startTime,
2644
- truncated: false
2645
- };
2646
- }
2647
- const todosList = sessionTodos.get(context.sessionId) ?? [];
2648
- const newTodo = { content, status: "pending" };
2649
- todosList.push(newTodo);
2650
- sessionTodos.set(context.sessionId, todosList);
2651
- if (onTodoUpdate) {
2652
- onTodoUpdate(context.sessionId, todosList);
2653
- }
2654
- const pending = todosList.filter((t) => t.status === "pending").length;
2655
- const inProgress = todosList.filter((t) => t.status === "in_progress").length;
2656
- const completed = todosList.filter((t) => t.status === "completed").length;
2657
- return {
2658
- success: true,
2659
- output: `Added task "${content}". Task list: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
2660
- durationMs: Date.now() - startTime,
2661
- truncated: false
2662
- };
2663
- }
2664
- if (action === "update") {
2665
- if (index === void 0) {
2666
- return {
2667
- success: false,
2668
- error: "Missing required field: index",
2669
- durationMs: Date.now() - startTime,
2670
- truncated: false
2671
- };
2672
- }
2673
- if (!content && !status) {
2674
- return {
2675
- success: false,
2676
- error: "update requires content or status",
2677
- durationMs: Date.now() - startTime,
2678
- truncated: false
2679
- };
2680
- }
2681
- if (status && !["pending", "in_progress", "completed"].includes(status)) {
2682
- return {
2683
- success: false,
2684
- error: `Invalid status: ${status}. Must be pending, in_progress, or completed`,
2685
- durationMs: Date.now() - startTime,
2686
- truncated: false
2687
- };
2688
- }
2689
- const todosList = sessionTodos.get(context.sessionId) ?? [];
2690
- if (index < 0 || index >= todosList.length) {
2691
- return {
2692
- success: false,
2693
- error: `Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`,
2694
- durationMs: Date.now() - startTime,
2695
- truncated: false
2696
- };
2697
- }
2698
- const todo = todosList[index];
2699
- if (!todo) {
2700
- return {
2701
- success: false,
2702
- error: `Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`,
2703
- durationMs: Date.now() - startTime,
2704
- truncated: false
2705
- };
2706
- }
2707
- if (content) {
2708
- todo.content = content;
2709
- }
2710
- if (status) {
2711
- todo.status = status;
2712
- }
2713
- sessionTodos.set(context.sessionId, todosList);
2714
- if (onTodoUpdate) {
2715
- onTodoUpdate(context.sessionId, todosList);
2402
+ async (args, context, helpers) => {
2403
+ const actionError = validateActionWithPermission(args.action, ["list", "write", "add", "update", "remove"], "todo", context.permittedActions);
2404
+ if (actionError) return actionError;
2405
+ if (args.action === "list") {
2406
+ const todosList = sessionTodos.get(context.sessionId) ?? [];
2407
+ return helpers.success(
2408
+ todosList.length === 0 ? "No tasks defined yet." : JSON.stringify(todosList, null, 2)
2409
+ );
2410
+ }
2411
+ if (args.action === "write") {
2412
+ if (!args.todos) return helpers.error("Missing required field: todos");
2413
+ if (!Array.isArray(args.todos)) return helpers.error("todos must be an array");
2414
+ for (const todo of args.todos) {
2415
+ if (!todo.content || !todo.status) return helpers.error("Each todo must have content and status");
2416
+ if (!["pending", "in_progress", "completed"].includes(todo.status)) {
2417
+ return helpers.error(`Invalid status: ${todo.status}. Must be pending, in_progress, or completed`);
2716
2418
  }
2717
- const pending = todosList.filter((t) => t.status === "pending").length;
2718
- const inProgress = todosList.filter((t) => t.status === "in_progress").length;
2719
- const completed = todosList.filter((t) => t.status === "completed").length;
2720
- return {
2721
- success: true,
2722
- output: `Updated task ${index}. Task list: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
2723
- durationMs: Date.now() - startTime,
2724
- truncated: false
2725
- };
2726
2419
  }
2727
- if (action === "remove") {
2728
- if (index === void 0) {
2729
- return {
2730
- success: false,
2731
- error: "Missing required field: index",
2732
- durationMs: Date.now() - startTime,
2733
- truncated: false
2734
- };
2735
- }
2736
- const todosList = sessionTodos.get(context.sessionId) ?? [];
2737
- if (index < 0 || index >= todosList.length) {
2738
- return {
2739
- success: false,
2740
- error: `Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`,
2741
- durationMs: Date.now() - startTime,
2742
- truncated: false
2743
- };
2744
- }
2745
- const removed = todosList.splice(index, 1)[0];
2746
- sessionTodos.set(context.sessionId, todosList);
2747
- if (onTodoUpdate) {
2748
- onTodoUpdate(context.sessionId, todosList);
2749
- }
2750
- if (todosList.length === 0) {
2751
- return {
2752
- success: true,
2753
- output: `Removed task "${removed.content}". No tasks remaining.`,
2754
- durationMs: Date.now() - startTime,
2755
- truncated: false
2756
- };
2757
- }
2758
- const pending = todosList.filter((t) => t.status === "pending").length;
2759
- const inProgress = todosList.filter((t) => t.status === "in_progress").length;
2760
- const completed = todosList.filter((t) => t.status === "completed").length;
2761
- return {
2762
- success: true,
2763
- output: `Removed task "${removed.content}". Task list: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
2764
- durationMs: Date.now() - startTime,
2765
- truncated: false
2766
- };
2420
+ sessionTodos.set(context.sessionId, args.todos);
2421
+ if (onTodoUpdate) onTodoUpdate(context.sessionId, args.todos);
2422
+ return helpers.success(`${args.todos.filter((t) => t.status === "completed").length} completed, ${args.todos.filter((t) => t.status === "in_progress").length} in progress, ${args.todos.filter((t) => t.status === "pending").length} pending`);
2423
+ }
2424
+ if (args.action === "add") {
2425
+ if (!args.content) return helpers.error("Missing required field: content");
2426
+ const todosList = sessionTodos.get(context.sessionId) ?? [];
2427
+ todosList.push({ content: args.content, status: "pending" });
2428
+ sessionTodos.set(context.sessionId, todosList);
2429
+ if (onTodoUpdate) onTodoUpdate(context.sessionId, todosList);
2430
+ return helpers.success(`Added task "${args.content}". Task list: ${buildTaskListSummary(todosList)}`);
2431
+ }
2432
+ if (args.action === "update") {
2433
+ if (!args.content && !args.status) return helpers.error("update requires content or status");
2434
+ if (args.status && !["pending", "in_progress", "completed"].includes(args.status)) {
2435
+ return helpers.error(`Invalid status: ${args.status}. Must be pending, in_progress, or completed`);
2767
2436
  }
2768
- return {
2769
- success: false,
2770
- error: "Unexpected error",
2771
- durationMs: Date.now() - startTime,
2772
- truncated: false
2773
- };
2774
- } catch (error) {
2775
- return {
2776
- success: false,
2777
- error: error instanceof Error ? error.message : "Unknown error",
2778
- durationMs: Date.now() - startTime,
2779
- truncated: false
2780
- };
2437
+ const todosList = sessionTodos.get(context.sessionId) ?? [];
2438
+ const indexResult = validateIndex(args.index, todosList, helpers);
2439
+ if ("success" in indexResult && !indexResult.success) return indexResult;
2440
+ const todo = indexResult;
2441
+ if (args.content) todo.content = args.content;
2442
+ if (args.status) todo.status = args.status;
2443
+ sessionTodos.set(context.sessionId, todosList);
2444
+ if (onTodoUpdate) onTodoUpdate(context.sessionId, todosList);
2445
+ return helpers.success(`Updated task ${args.index}. Task list: ${buildTaskListSummary(todosList)}`);
2446
+ }
2447
+ if (args.action === "remove") {
2448
+ const todosList = sessionTodos.get(context.sessionId) ?? [];
2449
+ const indexResult = validateIndex(args.index, todosList, helpers);
2450
+ if ("success" in indexResult && !indexResult.success) return indexResult;
2451
+ const removed = todosList.splice(args.index, 1)[0];
2452
+ sessionTodos.set(context.sessionId, todosList);
2453
+ if (onTodoUpdate) onTodoUpdate(context.sessionId, todosList);
2454
+ return helpers.success(
2455
+ todosList.length === 0 ? `Removed task "${removed.content}". No tasks remaining.` : `Removed task "${removed.content}". Task list: ${buildTaskListSummary(todosList)}`
2456
+ );
2781
2457
  }
2458
+ return helpers.error("Unexpected error");
2782
2459
  }
2783
- };
2460
+ );
2784
2461
 
2785
2462
  // src/server/chat/tool-streaming.ts
2786
2463
  function parseProgressMessage(message) {
@@ -2800,7 +2477,7 @@ function createToolProgressHandler(messageId, callId, onMessage) {
2800
2477
  }
2801
2478
 
2802
2479
  // src/server/skills/registry.ts
2803
- import { readdir as readdir2, readFile as readFile6, writeFile as writeFile4, copyFile as copyFile2, mkdir as mkdir3, access as access3, unlink as unlink2 } from "fs/promises";
2480
+ import { readdir as readdir2, readFile as readFile6, writeFile as writeFile4, mkdir as mkdir3, access as access3, unlink as unlink2 } from "fs/promises";
2804
2481
  import { join as join4, dirname as dirname4 } from "path";
2805
2482
  import { constants as constants3 } from "fs";
2806
2483
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -2813,7 +2490,7 @@ var SKILL_SETTING_PREFIX = "skill.enabled.";
2813
2490
  function getSkillsDir(configDir) {
2814
2491
  return join4(configDir, "skills");
2815
2492
  }
2816
- async function dirExists(path) {
2493
+ async function pathExists2(path) {
2817
2494
  try {
2818
2495
  await access3(path, constants3.R_OK);
2819
2496
  return true;
@@ -2821,56 +2498,40 @@ async function dirExists(path) {
2821
2498
  return false;
2822
2499
  }
2823
2500
  }
2824
- async function ensureDefaultSkills(configDir) {
2825
- const skillsDir = getSkillsDir(configDir);
2826
- if (!await dirExists(skillsDir)) {
2827
- await mkdir3(skillsDir, { recursive: true });
2828
- }
2829
- let defaultFiles;
2830
- let sourceDir;
2501
+ async function loadDefaultSkills() {
2502
+ let defaults = [];
2831
2503
  try {
2832
- defaultFiles = (await readdir2(DEFAULTS_DIR2)).filter((f) => f.endsWith(SKILL_EXTENSION));
2833
- sourceDir = DEFAULTS_DIR2;
2504
+ defaults = await loadSkillsFromDir(DEFAULTS_DIR2);
2834
2505
  } catch {
2506
+ }
2507
+ if (!defaults.length) {
2835
2508
  try {
2836
- defaultFiles = (await readdir2(DEFAULTS_DIR_ALT2)).filter((f) => f.endsWith(SKILL_EXTENSION));
2837
- sourceDir = DEFAULTS_DIR_ALT2;
2509
+ defaults = await loadSkillsFromDir(DEFAULTS_DIR_ALT2);
2838
2510
  } catch {
2839
- logger.warn("No bundled skill defaults found", { dir: DEFAULTS_DIR2 });
2840
- return;
2841
- }
2842
- }
2843
- for (const file of defaultFiles) {
2844
- const targetPath = join4(skillsDir, file);
2845
- if (!await dirExists(targetPath)) {
2846
- try {
2847
- await copyFile2(join4(sourceDir, file), targetPath);
2848
- logger.info("Installed default skill", { file });
2849
- } catch (err) {
2850
- logger.error("Failed to copy default skill", { file, error: err instanceof Error ? err.message : String(err) });
2851
- }
2852
2511
  }
2853
2512
  }
2513
+ return defaults;
2854
2514
  }
2855
- async function loadAllSkills(configDir) {
2856
- const skillsDir = getSkillsDir(configDir);
2857
- if (!await dirExists(skillsDir)) {
2515
+ async function loadSkillsFromDir(dir) {
2516
+ if (!await pathExists2(dir)) {
2858
2517
  return [];
2859
2518
  }
2860
2519
  let files;
2861
2520
  try {
2862
- files = (await readdir2(skillsDir)).filter((f) => f.endsWith(SKILL_EXTENSION));
2521
+ files = (await readdir2(dir)).filter((f) => f.endsWith(SKILL_EXTENSION));
2863
2522
  } catch {
2864
2523
  return [];
2865
2524
  }
2866
2525
  const skills = [];
2867
2526
  for (const file of files) {
2868
2527
  try {
2869
- const raw = await readFile6(join4(skillsDir, file), "utf-8");
2528
+ const raw = await readFile6(join4(dir, file), "utf-8");
2870
2529
  const { data, content } = matter2(raw);
2871
- const metadata = data;
2872
- if (metadata.id && content.trim()) {
2873
- skills.push({ metadata, prompt: content.trim() });
2530
+ if (data.id && content.trim()) {
2531
+ skills.push({
2532
+ metadata: data,
2533
+ prompt: content.trim()
2534
+ });
2874
2535
  } else {
2875
2536
  logger.warn("Skipping invalid skill file", { file });
2876
2537
  }
@@ -2880,6 +2541,23 @@ async function loadAllSkills(configDir) {
2880
2541
  }
2881
2542
  return skills;
2882
2543
  }
2544
+ async function loadUserSkills(configDir) {
2545
+ return loadSkillsFromDir(getSkillsDir(configDir));
2546
+ }
2547
+ async function loadAllSkills(configDir) {
2548
+ const [defaultSkills, userSkills] = await Promise.all([
2549
+ loadDefaultSkills(),
2550
+ loadUserSkills(configDir)
2551
+ ]);
2552
+ const skillMap = /* @__PURE__ */ new Map();
2553
+ for (const skill of defaultSkills) {
2554
+ skillMap.set(skill.metadata.id, skill);
2555
+ }
2556
+ for (const skill of userSkills) {
2557
+ skillMap.set(skill.metadata.id, skill);
2558
+ }
2559
+ return Array.from(skillMap.values());
2560
+ }
2883
2561
  async function getEnabledSkills(configDir) {
2884
2562
  const all = await loadAllSkills(configDir);
2885
2563
  return all.filter((s) => isSkillEnabled(s.metadata.id));
@@ -2896,71 +2574,36 @@ function isSkillEnabled(skillId) {
2896
2574
  function setSkillEnabled(skillId, enabled) {
2897
2575
  setSetting(`${SKILL_SETTING_PREFIX}${skillId}`, String(enabled));
2898
2576
  }
2899
- async function getDefaultSkillIds() {
2900
- for (const dir of [DEFAULTS_DIR2, DEFAULTS_DIR_ALT2]) {
2901
- try {
2902
- const files = (await readdir2(dir)).filter((f) => f.endsWith(SKILL_EXTENSION));
2903
- return files.map((f) => f.replace(SKILL_EXTENSION, ""));
2904
- } catch {
2905
- }
2577
+ async function getDefaultIds(dir, extension) {
2578
+ try {
2579
+ const files = (await readdir2(dir)).filter((f) => f.endsWith(extension));
2580
+ return files.map((f) => f.replace(extension, ""));
2581
+ } catch {
2582
+ return [];
2906
2583
  }
2907
- return [];
2908
2584
  }
2909
- async function restoreDefaultSkill(configDir, skillId) {
2910
- const filename = `${skillId}${SKILL_EXTENSION}`;
2911
- for (const dir of [DEFAULTS_DIR2, DEFAULTS_DIR_ALT2]) {
2912
- const sourcePath = join4(dir, filename);
2913
- if (await dirExists(sourcePath)) {
2914
- const targetPath = join4(getSkillsDir(configDir), filename);
2915
- await copyFile2(sourcePath, targetPath);
2916
- return true;
2917
- }
2918
- }
2919
- return false;
2585
+ async function getDefaultSkillIds() {
2586
+ const ids = await getDefaultIds(DEFAULTS_DIR2, SKILL_EXTENSION);
2587
+ if (ids.length) return ids;
2588
+ return getDefaultIds(DEFAULTS_DIR_ALT2, SKILL_EXTENSION);
2920
2589
  }
2921
- async function getModifiedDefaultSkillIds(configDir) {
2922
- const defaultIds = await getDefaultSkillIds();
2923
- const modified = [];
2924
- for (const id of defaultIds) {
2925
- const filename = `${id}${SKILL_EXTENSION}`;
2926
- const userPath = join4(getSkillsDir(configDir), filename);
2927
- let bundledContent = null;
2928
- for (const dir of [DEFAULTS_DIR2, DEFAULTS_DIR_ALT2]) {
2929
- try {
2930
- bundledContent = await readFile6(join4(dir, filename), "utf-8");
2931
- break;
2932
- } catch {
2933
- }
2934
- }
2935
- if (!bundledContent) continue;
2936
- try {
2937
- const userContent = await readFile6(userPath, "utf-8");
2938
- if (userContent !== bundledContent) {
2939
- modified.push(id);
2940
- }
2941
- } catch {
2942
- }
2943
- }
2944
- return modified;
2590
+ async function getDefaultSkillContent(skillId) {
2591
+ const defaults = await loadDefaultSkills();
2592
+ return defaults.find((s) => s.metadata.id === skillId) ?? null;
2945
2593
  }
2946
- async function restoreAllDefaultSkills(configDir) {
2947
- const ids = await getDefaultSkillIds();
2948
- let count = 0;
2949
- for (const id of ids) {
2950
- if (await restoreDefaultSkill(configDir, id)) count++;
2951
- }
2952
- return count;
2594
+ async function isDefaultSkill(skillId) {
2595
+ const defaultIds = await getDefaultSkillIds();
2596
+ return defaultIds.includes(skillId);
2953
2597
  }
2954
2598
  function findSkillById(skillId, skills) {
2955
2599
  return skills.find((s) => s.metadata.id === skillId);
2956
2600
  }
2957
2601
  async function skillExists(configDir, skillId) {
2958
- const filePath = join4(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`);
2959
- return dirExists(filePath);
2602
+ return pathExists2(join4(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`));
2960
2603
  }
2961
2604
  async function saveSkill(configDir, skill) {
2962
2605
  const skillsDir = getSkillsDir(configDir);
2963
- if (!await dirExists(skillsDir)) {
2606
+ if (!await pathExists2(skillsDir)) {
2964
2607
  await mkdir3(skillsDir, { recursive: true });
2965
2608
  }
2966
2609
  const filePath = join4(skillsDir, `${skill.metadata.id}${SKILL_EXTENSION}`);
@@ -2968,19 +2611,31 @@ async function saveSkill(configDir, skill) {
2968
2611
  await writeFile4(filePath, content, "utf-8");
2969
2612
  }
2970
2613
  async function deleteSkill(configDir, skillId) {
2614
+ const isDefault = await isDefaultSkill(skillId);
2615
+ if (isDefault) {
2616
+ return { success: false, reason: "Cannot delete built-in defaults" };
2617
+ }
2971
2618
  const filePath = join4(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`);
2972
2619
  try {
2973
2620
  await unlink2(filePath);
2974
- const { deleteSetting } = await import("./settings-NCLN4JDZ.js");
2975
2621
  deleteSetting(`${SKILL_SETTING_PREFIX}${skillId}`);
2976
- return true;
2622
+ return { success: true };
2977
2623
  } catch {
2978
- return false;
2624
+ return { success: false };
2979
2625
  }
2980
2626
  }
2981
2627
 
2982
2628
  // src/server/chat/agent-loop.ts
2983
2629
  import stripAnsi from "strip-ansi";
2630
+ function emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, promptContext, eventStore) {
2631
+ const stats = turnMetrics.buildStats(statsIdentity, mode);
2632
+ eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
2633
+ stats,
2634
+ partial: true,
2635
+ promptContext
2636
+ }));
2637
+ eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats));
2638
+ }
2984
2639
  function getCurrentWindowMessageOptions(sessionId) {
2985
2640
  const contextWindowId = getCurrentContextWindowId(sessionId);
2986
2641
  return contextWindowId ? { contextWindowId } : void 0;
@@ -3211,13 +2866,7 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
3211
2866
  }
3212
2867
  }
3213
2868
  if (result.aborted) {
3214
- const stats2 = turnMetrics.buildStats(statsIdentity, mode);
3215
- eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
3216
- stats: stats2,
3217
- partial: true,
3218
- promptContext: assembledRequest.promptContext
3219
- }));
3220
- eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats2));
2869
+ emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, assembledRequest.promptContext, eventStore);
3221
2870
  throw new Error("Aborted");
3222
2871
  }
3223
2872
  turnMetrics.addLLMCall(result.timing, result.usage.promptTokens, result.usage.completionTokens);
@@ -3252,25 +2901,13 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
3252
2901
  }
3253
2902
  } catch (error) {
3254
2903
  if (error instanceof Error && error.message === "Aborted") {
3255
- const stats2 = turnMetrics.buildStats(statsIdentity, mode);
3256
- eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
3257
- stats: stats2,
3258
- partial: true,
3259
- promptContext: assembledRequest.promptContext
3260
- }));
3261
- eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats2));
2904
+ emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, assembledRequest.promptContext, eventStore);
3262
2905
  throw error;
3263
2906
  }
3264
2907
  throw error;
3265
2908
  }
3266
2909
  if (signal?.aborted) {
3267
- const stats2 = turnMetrics.buildStats(statsIdentity, mode);
3268
- eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
3269
- stats: stats2,
3270
- partial: true,
3271
- promptContext: assembledRequest.promptContext
3272
- }));
3273
- eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats2));
2910
+ emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, assembledRequest.promptContext, eventStore);
3274
2911
  throw new Error("Aborted");
3275
2912
  }
3276
2913
  const asapMessages = sessionManager.drainAsapMessages(sessionId);
@@ -3308,12 +2945,32 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
3308
2945
  };
3309
2946
  }
3310
2947
 
2948
+ // src/server/context/nudge-helpers.ts
2949
+ function appendNudgeMessage(eventStore, sessionId, content, currentWindowMessageOptions, options) {
2950
+ const msgId = crypto.randomUUID();
2951
+ eventStore.append(sessionId, createMessageStartEvent(msgId, "user", content, {
2952
+ ...currentWindowMessageOptions ?? {},
2953
+ isSystemGenerated: true,
2954
+ messageKind: "correction",
2955
+ subAgentId: options.subAgentId,
2956
+ subAgentType: options.subAgentType
2957
+ }));
2958
+ eventStore.append(sessionId, { type: "message.done", data: { messageId: msgId } });
2959
+ }
2960
+
3311
2961
  // src/server/sub-agents/manager.ts
3312
2962
  var RETURN_VALUE_INSTRUCTION = `
3313
2963
 
3314
2964
  ## RETURN VALUE
3315
2965
  As the very last thing you do, call \`return_value\` ONCE with a structured summary of your work. This is how your findings get passed back to the calling agent. Do not finish without calling return_value.`;
3316
2966
  var RETURN_VALUE_NUDGE = "You must call return_value with a summary of your findings before finishing. Call return_value now.";
2967
+ function buildSubAgentResult(returnValueContent, returnValueResult, subAgentType, failed, remaining) {
2968
+ return {
2969
+ content: returnValueContent ?? "",
2970
+ ...returnValueResult !== null && returnValueResult !== void 0 ? { result: returnValueResult } : {},
2971
+ ...subAgentType === "verifier" ? { allPassed: failed.length === 0 && remaining.length === 0, failed } : {}
2972
+ };
2973
+ }
3317
2974
  async function resolveAgentDef(agentId) {
3318
2975
  const allAgents = await loadAllAgentsDefault();
3319
2976
  const def = findAgentById(agentId, allAgents);
@@ -3483,19 +3140,11 @@ async function executeSubAgent(options) {
3483
3140
  if (consecutiveEmptyStops < nudgeConfig.maxConsecutiveNudges) {
3484
3141
  consecutiveEmptyStops += 1;
3485
3142
  const nudgeContent = nudgeConfig.buildNudgeContent(criteriaAwaiting);
3486
- const nudgeMsgId = crypto.randomUUID();
3487
3143
  eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
3488
3144
  segments: result.segments,
3489
3145
  promptContext
3490
3146
  }));
3491
- eventStore.append(sessionId, createMessageStartEvent(nudgeMsgId, "user", nudgeContent, {
3492
- ...currentWindowMessageOptions ?? {},
3493
- isSystemGenerated: true,
3494
- messageKind: "correction",
3495
- subAgentId,
3496
- subAgentType
3497
- }));
3498
- eventStore.append(sessionId, { type: "message.done", data: { messageId: nudgeMsgId } });
3147
+ appendNudgeMessage(eventStore, sessionId, nudgeContent, currentWindowMessageOptions, { subAgentId, subAgentType });
3499
3148
  customMessages = [...customMessages, { role: "user", content: nudgeContent, source: "runtime" }];
3500
3149
  continue;
3501
3150
  }
@@ -3512,19 +3161,11 @@ async function executeSubAgent(options) {
3512
3161
  }
3513
3162
  if (!returnValueContent && !returnValueNudged) {
3514
3163
  returnValueNudged = true;
3515
- const nudgeMsgId = crypto.randomUUID();
3516
3164
  eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
3517
3165
  segments: result.segments,
3518
3166
  promptContext
3519
3167
  }));
3520
- eventStore.append(sessionId, createMessageStartEvent(nudgeMsgId, "user", RETURN_VALUE_NUDGE, {
3521
- ...currentWindowMessageOptions ?? {},
3522
- isSystemGenerated: true,
3523
- messageKind: "correction",
3524
- subAgentId,
3525
- subAgentType
3526
- }));
3527
- eventStore.append(sessionId, { type: "message.done", data: { messageId: nudgeMsgId } });
3168
+ appendNudgeMessage(eventStore, sessionId, RETURN_VALUE_NUDGE, currentWindowMessageOptions, { subAgentId, subAgentType });
3528
3169
  customMessages = [...customMessages, { role: "user", content: RETURN_VALUE_NUDGE, source: "runtime" }];
3529
3170
  continue;
3530
3171
  }
@@ -3580,21 +3221,9 @@ async function executeSubAgent(options) {
3580
3221
  subAgentId,
3581
3222
  resultLength: finalContent.length
3582
3223
  });
3583
- if (subAgentType === "verifier") {
3584
- session = sessionManager.requireSession(sessionId);
3585
- const remaining = nudgeConfig?.getCriteriaAwaiting(session.criteria) ?? [];
3586
- const failed = session.criteria.filter((c) => c.status.type === "failed").map((c) => ({
3587
- id: c.id,
3588
- reason: c.status.type === "failed" ? c.status.reason : "unknown"
3589
- }));
3590
- return {
3591
- content: returnValueContent ?? finalContent,
3592
- ...returnValueResult ? { result: returnValueResult } : {},
3593
- allPassed: failed.length === 0 && remaining.length === 0,
3594
- failed
3595
- };
3596
- }
3597
- return { content: returnValueContent ?? finalContent, ...returnValueResult ? { result: returnValueResult } : { result: "success" } };
3224
+ const failed = session.criteria.filter((c) => c.status.type === "failed").map((c) => ({ id: c.id, reason: c.status.reason ?? "unknown" }));
3225
+ const remaining = nudgeConfig?.getCriteriaAwaiting(session.criteria) ?? [];
3226
+ return buildSubAgentResult(returnValueContent ?? void 0, returnValueResult ?? (subAgentType !== "verifier" ? "success" : void 0), subAgentType, failed, remaining);
3598
3227
  }
3599
3228
 
3600
3229
  // src/server/tools/sub-agent.ts
@@ -3662,7 +3291,7 @@ var callSubAgentTool = {
3662
3291
  };
3663
3292
  }
3664
3293
  try {
3665
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-PLVK22CF.js");
3294
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-55YICCVH.js");
3666
3295
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3667
3296
  const turnMetrics = new TurnMetrics();
3668
3297
  const result = await executeSubAgent({
@@ -3814,7 +3443,7 @@ function convertHTMLToMarkdown(html) {
3814
3443
  return turndown.turndown(html);
3815
3444
  }
3816
3445
  function stripHTMLTags(html) {
3817
- let text = html.replace(/<(script|style|noscript|iframe|object|embed)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<[^>]+>/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
3446
+ const text = html.replace(/<(script|style|noscript|iframe|object|embed)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<[^>]+>/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
3818
3447
  return text;
3819
3448
  }
3820
3449
  var webFetchTool = createTool(
@@ -3916,7 +3545,7 @@ var webFetchTool = createTool(
3916
3545
  );
3917
3546
 
3918
3547
  // src/server/dev-server/manager.ts
3919
- import { spawn as spawn3 } from "child_process";
3548
+ import { spawn as spawn4 } from "child_process";
3920
3549
  import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
3921
3550
  import { resolve as resolve5, join as join5 } from "path";
3922
3551
  var MAX_LOG_LINES = 2e3;
@@ -4010,7 +3639,7 @@ var DevServerManager = class {
4010
3639
  instance.exited = false;
4011
3640
  const resolved = this.resolveWorkdir(workdir);
4012
3641
  const shell = getPlatformShell();
4013
- const proc = spawn3(shell.command, [...shell.args, config.command], {
3642
+ const proc = spawn4(shell.command, [...shell.args, config.command], {
4014
3643
  cwd: resolved,
4015
3644
  env: { ...process.env, FORCE_COLOR: "1" },
4016
3645
  stdio: ["ignore", "pipe", "pipe"],
@@ -4582,14 +4211,7 @@ function getCurrentWindowMessageOptions3(sessionId) {
4582
4211
  return contextWindowId ? { contextWindowId } : void 0;
4583
4212
  }
4584
4213
  function toRequestContextMessages2(messages) {
4585
- return messages.map((message) => ({
4586
- role: message.role,
4587
- content: message.content,
4588
- source: "history",
4589
- ...message.toolCalls ? { toolCalls: message.toolCalls } : {},
4590
- ...message.toolCallId ? { toolCallId: message.toolCallId } : {},
4591
- ...message.attachments ? { attachments: message.attachments } : {}
4592
- }));
4214
+ return minimalMessagesToRequestContextMessages(messages, "history");
4593
4215
  }
4594
4216
  async function maybeAutoCompactContext(options) {
4595
4217
  const config = getRuntimeConfig();
@@ -4744,13 +4366,14 @@ export {
4744
4366
  PathAccessDeniedError,
4745
4367
  providePathConfirmation,
4746
4368
  cancelPathConfirmationsForSession,
4747
- ensureDefaultAgents,
4369
+ checkAborted,
4370
+ spawnShellProcess,
4371
+ loadDefaultAgents,
4372
+ loadUserAgents,
4748
4373
  loadAllAgents,
4749
4374
  loadAllAgentsDefault,
4750
4375
  getDefaultAgentIds,
4751
- restoreDefaultAgent,
4752
- getModifiedDefaultAgentIds,
4753
- restoreAllDefaultAgents,
4376
+ getDefaultAgentContent,
4754
4377
  findAgentById,
4755
4378
  getSubAgents,
4756
4379
  agentExists,
@@ -4767,15 +4390,14 @@ export {
4767
4390
  createChatDoneEvent,
4768
4391
  getAllInstructions,
4769
4392
  assembleAgentRequest,
4770
- ensureDefaultSkills,
4393
+ loadDefaultSkills,
4394
+ loadUserSkills,
4771
4395
  loadAllSkills,
4772
4396
  getEnabledSkillMetadata,
4773
4397
  isSkillEnabled,
4774
4398
  setSkillEnabled,
4775
4399
  getDefaultSkillIds,
4776
- restoreDefaultSkill,
4777
- getModifiedDefaultSkillIds,
4778
- restoreAllDefaultSkills,
4400
+ getDefaultSkillContent,
4779
4401
  findSkillById,
4780
4402
  skillExists,
4781
4403
  saveSkill,
@@ -4795,4 +4417,4 @@ export {
4795
4417
  getToolRegistryForAgent,
4796
4418
  createToolRegistry
4797
4419
  };
4798
- //# sourceMappingURL=chunk-N25QUEPO.js.map
4420
+ //# sourceMappingURL=chunk-4OI4VPZ6.js.map