openfox 1.6.13 → 1.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ask-JQQNGB5K.js → ask-3RK5YJZE.js} +2 -2
- package/dist/{auto-compaction-QPFQEWJG.js → auto-compaction-JZIM73ZY.js} +6 -6
- package/dist/{chat-handler-FDHHTHS3.js → chat-handler-F2A2IMQB.js} +27 -57
- package/dist/{chunk-5LDTVERY.js → chunk-22CTURMH.js} +1 -3
- package/dist/{chunk-WQ4W5H6A.js → chunk-2KP34IDL.js} +163 -205
- package/dist/{chunk-POJO7A5H.js → chunk-3WHZ47PY.js} +511 -793
- package/dist/{chunk-XFXOSPYH.js → chunk-55N6FAAZ.js} +1 -1
- package/dist/{chunk-ZRDRYL6W.js → chunk-6L3X7T4K.js} +82 -160
- package/dist/{chunk-QDEKU5RL.js → chunk-F54ZJN4X.js} +38 -2
- package/dist/{chunk-7T2CA2C6.js → chunk-G4E72ST3.js} +117 -242
- package/dist/{chunk-AKFLBPPY.js → chunk-IN5EP4ZB.js} +2 -2
- package/dist/{chunk-HCZT5BDI.js → chunk-JFKYCGZ5.js} +6 -6
- package/dist/{chunk-ZHGP4PBJ.js → chunk-KOUMYBYM.js} +53 -112
- package/dist/{chunk-E6FCNB3B.js → chunk-OVLFEBRR.js} +76 -94
- package/dist/chunk-SN7OBEVL.js +44 -0
- package/dist/{chunk-LMIBGLOB.js → chunk-YM6VHAPM.js} +4 -4
- package/dist/{chunk-TXQNRBMZ.js → chunk-ZDNXCVW4.js} +2 -2
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{config-TUA644W4.js → config-67AX6CNS.js} +5 -5
- package/dist/{events-EILPWKYE.js → events-2ETDOE5B.js} +3 -3
- package/dist/{folding-NZYOXNKK.js → folding-M7FMUBOL.js} +6 -4
- package/dist/{orchestrator-YKQJI3PH.js → orchestrator-SRSG2SIZ.js} +7 -7
- package/dist/package.json +14 -3
- package/dist/{processor-JU7MSV4F.js → processor-H5BQ5HQM.js} +24 -49
- package/dist/{provider-FNSGWDIW.js → provider-DKGBQHUS.js} +7 -7
- package/dist/{serve-O62TKFVA.js → serve-5AY7GMN7.js} +13 -13
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +11 -11
- package/dist/{tools-JWAF3MAR.js → tools-VOWNOP6V.js} +6 -6
- package/dist/{vision-fallback-BWCKA2FZ.js → vision-fallback-ADYRFFD4.js} +2 -2
- package/dist/web/assets/{index-MCWDS5UQ.css → index-CNeIjdxm.css} +1 -1
- package/dist/web/assets/index-mBLhctLW.js +150 -0
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +14 -3
- package/dist/web/assets/index-Cp8gfbgP.js +0 -150
- /package/dist/{command-defaults → server/commands/defaults}/commit-push.command.md +0 -0
- /package/dist/{command-defaults → server/commands/defaults}/init.command.md +0 -0
- /package/dist/{command-defaults → server/commands/defaults}/test-ui.command.md +0 -0
- /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-
|
|
16
|
+
} from "./chunk-55N6FAAZ.js";
|
|
17
17
|
import {
|
|
18
18
|
getContextMessages,
|
|
19
19
|
getCurrentContextWindowId,
|
|
20
20
|
getEventStore
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-6L3X7T4K.js";
|
|
22
22
|
import {
|
|
23
23
|
createChatDoneMessage,
|
|
24
24
|
createChatMessageMessage,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
AskUserInterrupt,
|
|
33
33
|
askUserTool
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-22CTURMH.js";
|
|
35
35
|
import {
|
|
36
36
|
SETTINGS_KEYS,
|
|
37
37
|
getSetting,
|
|
@@ -375,6 +375,31 @@ You may use available tools to read files and verify changes if needed.`;
|
|
|
375
375
|
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
376
|
|
|
377
377
|
// src/server/chat/request-context.ts
|
|
378
|
+
function minimalMessagesToRequestContextMessages(messages, source = "history") {
|
|
379
|
+
return messages.map((message) => minimalMessageToRequestContextMessage(message, source));
|
|
380
|
+
}
|
|
381
|
+
function spreadMessageProps(message) {
|
|
382
|
+
return {
|
|
383
|
+
...message.toolCalls ? { toolCalls: message.toolCalls } : {},
|
|
384
|
+
...message.toolCallId ? { toolCallId: message.toolCallId } : {},
|
|
385
|
+
...message.attachments ? { attachments: message.attachments } : {}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function minimalMessageToRequestContextMessage(message, source = "history") {
|
|
389
|
+
return {
|
|
390
|
+
role: message.role,
|
|
391
|
+
content: message.content,
|
|
392
|
+
source,
|
|
393
|
+
...spreadMessageProps(message)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function messageToMinimal(message) {
|
|
397
|
+
return {
|
|
398
|
+
role: message.role,
|
|
399
|
+
content: message.content,
|
|
400
|
+
...spreadMessageProps(message)
|
|
401
|
+
};
|
|
402
|
+
}
|
|
378
403
|
function getTriggerUserMessage(messages) {
|
|
379
404
|
const stripRuntimeReminders = (content) => {
|
|
380
405
|
return content.replace(/\n*<system-reminder>[\s\S]*<\/system-reminder>\s*/gi, "").trim();
|
|
@@ -396,12 +421,8 @@ function createPromptContext(input) {
|
|
|
396
421
|
injectedFiles: input.injectedFiles,
|
|
397
422
|
userMessage: input.userMessage ?? getTriggerUserMessage(input.messages),
|
|
398
423
|
messages: input.messages.map((message) => ({
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
source: message.source,
|
|
402
|
-
...message.toolCalls ? { toolCalls: message.toolCalls } : {},
|
|
403
|
-
...message.toolCallId ? { toolCallId: message.toolCallId } : {},
|
|
404
|
-
...message.attachments ? { attachments: message.attachments } : {}
|
|
424
|
+
...messageToMinimal(message),
|
|
425
|
+
source: message.source
|
|
405
426
|
})),
|
|
406
427
|
tools: input.requestTools.map((tool) => ({
|
|
407
428
|
name: tool.function.name,
|
|
@@ -419,19 +440,25 @@ function createAssemblyResult(input) {
|
|
|
419
440
|
const messagesForLLM = input.messages.filter((m) => m.source !== "runtime");
|
|
420
441
|
return {
|
|
421
442
|
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
|
-
})),
|
|
443
|
+
messages: messagesForLLM.map((message) => messageToMinimal(message)),
|
|
429
444
|
promptContext: createPromptContext({
|
|
430
445
|
...input,
|
|
431
446
|
userMessage: triggerUserMessage
|
|
432
447
|
})
|
|
433
448
|
};
|
|
434
449
|
}
|
|
450
|
+
function buildAssemblyInput(systemPrompt, baseInput) {
|
|
451
|
+
return createAssemblyResult({
|
|
452
|
+
systemPrompt,
|
|
453
|
+
messages: baseInput.messages,
|
|
454
|
+
injectedFiles: baseInput.injectedFiles,
|
|
455
|
+
requestTools: baseInput.requestTools ?? baseInput.promptTools,
|
|
456
|
+
toolChoice: baseInput.toolChoice ?? "auto",
|
|
457
|
+
disableThinking: baseInput.disableThinking ?? false,
|
|
458
|
+
...baseInput.customInstructions ? { customInstructions: baseInput.customInstructions } : {},
|
|
459
|
+
...baseInput.skills && baseInput.skills.length > 0 ? { skills: baseInput.skills } : {}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
435
462
|
function assembleAgentRequest(input) {
|
|
436
463
|
const { agentDef, subAgentDefs, ...baseInput } = input;
|
|
437
464
|
if (agentDef.metadata.subagent) {
|
|
@@ -440,14 +467,7 @@ function assembleAgentRequest(input) {
|
|
|
440
467
|
agentDef,
|
|
441
468
|
baseInput.skills
|
|
442
469
|
);
|
|
443
|
-
return
|
|
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
|
-
});
|
|
470
|
+
return buildAssemblyInput(systemPrompt2, baseInput);
|
|
451
471
|
}
|
|
452
472
|
const systemPrompt = buildTopLevelSystemPrompt(
|
|
453
473
|
baseInput.workdir,
|
|
@@ -455,14 +475,25 @@ function assembleAgentRequest(input) {
|
|
|
455
475
|
baseInput.skills,
|
|
456
476
|
subAgentDefs
|
|
457
477
|
);
|
|
458
|
-
return
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
478
|
+
return buildAssemblyInput(systemPrompt, baseInput);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/server/chat/stream-utils.ts
|
|
482
|
+
function buildStreamRequestObject(params) {
|
|
483
|
+
const { messages, tools, toolChoice, disableThinking, signal, onVisionFallbackStart, onVisionFallbackDone } = params;
|
|
484
|
+
const streamRequest = {
|
|
485
|
+
messages,
|
|
486
|
+
...tools && { tools },
|
|
487
|
+
...toolChoice && { toolChoice },
|
|
488
|
+
disableThinking: disableThinking ?? false,
|
|
489
|
+
...signal && { signal }
|
|
490
|
+
};
|
|
491
|
+
if (onVisionFallbackStart) streamRequest.onVisionFallbackStart = onVisionFallbackStart;
|
|
492
|
+
if (onVisionFallbackDone) streamRequest.onVisionFallbackDone = onVisionFallbackDone;
|
|
493
|
+
return streamRequest;
|
|
494
|
+
}
|
|
495
|
+
function buildStreamRequest(client, options) {
|
|
496
|
+
return streamWithSegments(client, buildStreamRequestObject(options));
|
|
466
497
|
}
|
|
467
498
|
|
|
468
499
|
// src/server/chat/stats.ts
|
|
@@ -483,19 +514,29 @@ function computeAggregatedStats(input) {
|
|
|
483
514
|
}
|
|
484
515
|
|
|
485
516
|
// src/server/chat/stream-pure.ts
|
|
517
|
+
function createEmptyStreamResult(aborted, xmlFormatError) {
|
|
518
|
+
return {
|
|
519
|
+
content: "",
|
|
520
|
+
toolCalls: [],
|
|
521
|
+
segments: [],
|
|
522
|
+
usage: { promptTokens: 0, completionTokens: 0 },
|
|
523
|
+
timing: { ttft: 0, completionTime: 0, tps: 0, prefillTps: 0 },
|
|
524
|
+
aborted,
|
|
525
|
+
xmlFormatError
|
|
526
|
+
};
|
|
527
|
+
}
|
|
486
528
|
async function* streamLLMPure(options) {
|
|
487
529
|
const { messageId, systemPrompt, llmClient, messages, tools, toolChoice, signal, disableThinking, onVisionFallbackStart, onVisionFallbackDone } = options;
|
|
488
530
|
const llmMessages = [{ role: "system", content: systemPrompt }, ...messages];
|
|
489
|
-
const
|
|
531
|
+
const stream = buildStreamRequest(llmClient, {
|
|
490
532
|
messages: llmMessages,
|
|
491
|
-
|
|
492
|
-
|
|
533
|
+
tools,
|
|
534
|
+
toolChoice,
|
|
493
535
|
disableThinking: disableThinking ?? false,
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const stream = streamWithSegments(llmClient, streamRequest);
|
|
536
|
+
signal,
|
|
537
|
+
onVisionFallbackStart,
|
|
538
|
+
onVisionFallbackDone
|
|
539
|
+
});
|
|
499
540
|
const seenToolIndices = /* @__PURE__ */ new Set();
|
|
500
541
|
const toolNames = /* @__PURE__ */ new Map();
|
|
501
542
|
const toolIds = /* @__PURE__ */ new Map();
|
|
@@ -594,15 +635,7 @@ async function* streamLLMPure(options) {
|
|
|
594
635
|
}
|
|
595
636
|
}
|
|
596
637
|
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
|
-
};
|
|
638
|
+
return createEmptyStreamResult(aborted, xmlFormatError);
|
|
606
639
|
}
|
|
607
640
|
const baseResult = {
|
|
608
641
|
content: result.content,
|
|
@@ -768,15 +801,7 @@ async function consumeStreamWithToolLoop(options) {
|
|
|
768
801
|
let iterations = 0;
|
|
769
802
|
for (; ; ) {
|
|
770
803
|
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
|
-
};
|
|
804
|
+
return createEmptyStreamResult(true, false);
|
|
780
805
|
}
|
|
781
806
|
if (++iterations > MAX_TOOL_LOOP_ITERATIONS) {
|
|
782
807
|
throw new Error("Max tool loop iterations exceeded during compaction");
|
|
@@ -844,7 +869,7 @@ async function consumeStreamWithToolLoop(options) {
|
|
|
844
869
|
}
|
|
845
870
|
async function executeToolBatchWithContext(assistantMsgId, toolCalls, ctx, onEvent) {
|
|
846
871
|
const toolMessages = [];
|
|
847
|
-
|
|
872
|
+
const criteriaChanged = false;
|
|
848
873
|
for (const toolCall of toolCalls) {
|
|
849
874
|
if (ctx.signal?.aborted) {
|
|
850
875
|
throw new Error("Aborted");
|
|
@@ -884,17 +909,48 @@ Error: ${toolResult.error}` : `Error: ${toolResult.error}`;
|
|
|
884
909
|
}
|
|
885
910
|
|
|
886
911
|
// src/server/agents/registry.ts
|
|
887
|
-
import { readdir, readFile as
|
|
888
|
-
import { join as
|
|
912
|
+
import { readdir, readFile as readFile3, writeFile, copyFile, mkdir, access as access2, unlink } from "fs/promises";
|
|
913
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
889
914
|
import { constants as constants2 } from "fs";
|
|
890
915
|
import { fileURLToPath } from "url";
|
|
891
916
|
import matter from "gray-matter";
|
|
917
|
+
|
|
918
|
+
// src/server/utils/defaults.ts
|
|
919
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
920
|
+
import { join as join2 } from "path";
|
|
921
|
+
async function findModifiedDefaultFiles(defaultIds, extension, bundledDirs, userDir) {
|
|
922
|
+
const [primaryDir, altDir] = bundledDirs;
|
|
923
|
+
const modified = [];
|
|
924
|
+
for (const id of defaultIds) {
|
|
925
|
+
const filename = `${id}${extension}`;
|
|
926
|
+
const userPath = join2(userDir, filename);
|
|
927
|
+
let bundledContent = null;
|
|
928
|
+
for (const dir of [primaryDir, altDir]) {
|
|
929
|
+
try {
|
|
930
|
+
bundledContent = await readFile2(join2(dir, filename), "utf-8");
|
|
931
|
+
break;
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (!bundledContent) continue;
|
|
936
|
+
try {
|
|
937
|
+
const userContent = await readFile2(userPath, "utf-8");
|
|
938
|
+
if (userContent !== bundledContent) {
|
|
939
|
+
modified.push(id);
|
|
940
|
+
}
|
|
941
|
+
} catch {
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return modified;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// src/server/agents/registry.ts
|
|
892
948
|
var __bundleDir = dirname2(fileURLToPath(import.meta.url));
|
|
893
|
-
var DEFAULTS_DIR =
|
|
894
|
-
var DEFAULTS_DIR_ALT =
|
|
949
|
+
var DEFAULTS_DIR = join3(__bundleDir, "defaults");
|
|
950
|
+
var DEFAULTS_DIR_ALT = join3(__bundleDir, "agent-defaults");
|
|
895
951
|
var AGENT_EXTENSION = ".agent.md";
|
|
896
952
|
function getAgentsDir(configDir) {
|
|
897
|
-
return
|
|
953
|
+
return join3(configDir, "agents");
|
|
898
954
|
}
|
|
899
955
|
async function pathExists(path) {
|
|
900
956
|
try {
|
|
@@ -924,10 +980,10 @@ async function ensureDefaultAgents(configDir) {
|
|
|
924
980
|
}
|
|
925
981
|
}
|
|
926
982
|
for (const file of defaultFiles) {
|
|
927
|
-
const targetPath =
|
|
983
|
+
const targetPath = join3(agentsDir, file);
|
|
928
984
|
if (!await pathExists(targetPath)) {
|
|
929
985
|
try {
|
|
930
|
-
await copyFile(
|
|
986
|
+
await copyFile(join3(sourceDir, file), targetPath);
|
|
931
987
|
logger.info("Installed default agent", { file });
|
|
932
988
|
} catch (err) {
|
|
933
989
|
logger.error("Failed to copy default agent", { file, error: err instanceof Error ? err.message : String(err) });
|
|
@@ -966,7 +1022,7 @@ async function loadAgentsFromDir(dir) {
|
|
|
966
1022
|
const agents = [];
|
|
967
1023
|
for (const file of files) {
|
|
968
1024
|
try {
|
|
969
|
-
const raw = await
|
|
1025
|
+
const raw = await readFile3(join3(dir, file), "utf-8");
|
|
970
1026
|
const agent = parseAgentFile(raw, file);
|
|
971
1027
|
if (agent) {
|
|
972
1028
|
agents.push(agent);
|
|
@@ -1017,7 +1073,7 @@ async function getDefaultAgentIds() {
|
|
|
1017
1073
|
async function restoreDefaultAgent(configDir, agentId) {
|
|
1018
1074
|
const defaultIds = await getDefaultAgentIds();
|
|
1019
1075
|
if (!defaultIds.includes(agentId)) return false;
|
|
1020
|
-
const userPath =
|
|
1076
|
+
const userPath = join3(getAgentsDir(configDir), `${agentId}${AGENT_EXTENSION}`);
|
|
1021
1077
|
try {
|
|
1022
1078
|
await unlink(userPath);
|
|
1023
1079
|
} catch {
|
|
@@ -1026,28 +1082,7 @@ async function restoreDefaultAgent(configDir, agentId) {
|
|
|
1026
1082
|
}
|
|
1027
1083
|
async function getModifiedDefaultAgentIds(configDir) {
|
|
1028
1084
|
const defaultIds = await getDefaultAgentIds();
|
|
1029
|
-
|
|
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;
|
|
1085
|
+
return findModifiedDefaultFiles(defaultIds, AGENT_EXTENSION, [DEFAULTS_DIR, DEFAULTS_DIR_ALT], getAgentsDir(configDir));
|
|
1051
1086
|
}
|
|
1052
1087
|
async function restoreAllDefaultAgents(configDir) {
|
|
1053
1088
|
const ids = await getDefaultAgentIds();
|
|
@@ -1057,6 +1092,13 @@ async function restoreAllDefaultAgents(configDir) {
|
|
|
1057
1092
|
}
|
|
1058
1093
|
return count;
|
|
1059
1094
|
}
|
|
1095
|
+
function getAgentFilePaths(agentsDir, agentId) {
|
|
1096
|
+
const hyphenated = agentId.replace(/_/g, "-");
|
|
1097
|
+
return [
|
|
1098
|
+
join3(agentsDir, `${agentId}${AGENT_EXTENSION}`),
|
|
1099
|
+
join3(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)
|
|
1100
|
+
];
|
|
1101
|
+
}
|
|
1060
1102
|
function findAgentById(agentId, agents) {
|
|
1061
1103
|
return agents.find((a) => a.metadata.id === agentId);
|
|
1062
1104
|
}
|
|
@@ -1065,11 +1107,7 @@ function getSubAgents(agents) {
|
|
|
1065
1107
|
}
|
|
1066
1108
|
async function agentExists(configDir, agentId) {
|
|
1067
1109
|
const agentsDir = getAgentsDir(configDir);
|
|
1068
|
-
const
|
|
1069
|
-
const paths = [
|
|
1070
|
-
join2(agentsDir, `${agentId}${AGENT_EXTENSION}`),
|
|
1071
|
-
join2(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)
|
|
1072
|
-
];
|
|
1110
|
+
const paths = getAgentFilePaths(agentsDir, agentId);
|
|
1073
1111
|
for (const filePath of paths) {
|
|
1074
1112
|
if (await pathExists(filePath)) {
|
|
1075
1113
|
return true;
|
|
@@ -1082,17 +1120,13 @@ async function saveAgent(configDir, agent) {
|
|
|
1082
1120
|
if (!await pathExists(agentsDir)) {
|
|
1083
1121
|
await mkdir(agentsDir, { recursive: true });
|
|
1084
1122
|
}
|
|
1085
|
-
const filePath =
|
|
1123
|
+
const filePath = join3(agentsDir, `${agent.metadata.id}${AGENT_EXTENSION}`);
|
|
1086
1124
|
const content = matter.stringify(agent.prompt, agent.metadata);
|
|
1087
1125
|
await writeFile(filePath, content, "utf-8");
|
|
1088
1126
|
}
|
|
1089
1127
|
async function deleteAgent(configDir, agentId) {
|
|
1090
1128
|
const agentsDir = getAgentsDir(configDir);
|
|
1091
|
-
const
|
|
1092
|
-
const paths = [
|
|
1093
|
-
join2(agentsDir, `${agentId}${AGENT_EXTENSION}`),
|
|
1094
|
-
join2(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)
|
|
1095
|
-
];
|
|
1129
|
+
const paths = getAgentFilePaths(agentsDir, agentId);
|
|
1096
1130
|
for (const filePath of paths) {
|
|
1097
1131
|
try {
|
|
1098
1132
|
await unlink(filePath);
|
|
@@ -1105,7 +1139,7 @@ async function deleteAgent(configDir, agentId) {
|
|
|
1105
1139
|
}
|
|
1106
1140
|
|
|
1107
1141
|
// src/server/tools/read.ts
|
|
1108
|
-
import { readFile as
|
|
1142
|
+
import { readFile as readFile5, stat as stat2 } from "fs/promises";
|
|
1109
1143
|
import { extname } from "path";
|
|
1110
1144
|
|
|
1111
1145
|
// src/server/tools/types.ts
|
|
@@ -1136,7 +1170,7 @@ import { resolve as resolve2, isAbsolute } from "path";
|
|
|
1136
1170
|
|
|
1137
1171
|
// src/server/tools/path-security.ts
|
|
1138
1172
|
import { realpath } from "fs/promises";
|
|
1139
|
-
import { resolve, normalize, join as
|
|
1173
|
+
import { resolve, normalize, join as join4, basename, sep } from "path";
|
|
1140
1174
|
import { homedir, tmpdir } from "os";
|
|
1141
1175
|
var SAFE_PATHS = /* @__PURE__ */ new Set([
|
|
1142
1176
|
"/dev/null",
|
|
@@ -1271,7 +1305,7 @@ function extractAbsolutePathsFromCommand(command) {
|
|
|
1271
1305
|
}
|
|
1272
1306
|
} else if (content.startsWith("~")) {
|
|
1273
1307
|
const pathPart = content.slice(1);
|
|
1274
|
-
const fullPath =
|
|
1308
|
+
const fullPath = join4(home, pathPart);
|
|
1275
1309
|
const resolved = normalize(resolve(fullPath));
|
|
1276
1310
|
if (!isSafePath(resolved)) {
|
|
1277
1311
|
paths.push(resolved);
|
|
@@ -1416,9 +1450,6 @@ var PathAccessDeniedError = class extends Error {
|
|
|
1416
1450
|
this.reason = reason;
|
|
1417
1451
|
this.name = "PathAccessDeniedError";
|
|
1418
1452
|
}
|
|
1419
|
-
paths;
|
|
1420
|
-
tool;
|
|
1421
|
-
reason;
|
|
1422
1453
|
};
|
|
1423
1454
|
var pendingConfirmations = /* @__PURE__ */ new Map();
|
|
1424
1455
|
function registerPathConfirmation(callId, paths, sessionId) {
|
|
@@ -1464,6 +1495,39 @@ function cancelPathConfirmationsForSession(sessionId, reason) {
|
|
|
1464
1495
|
}
|
|
1465
1496
|
|
|
1466
1497
|
// src/server/tools/tool-helpers.ts
|
|
1498
|
+
function validateAction(action, allowed, startTime) {
|
|
1499
|
+
if (!action || !allowed.includes(action)) {
|
|
1500
|
+
return {
|
|
1501
|
+
success: false,
|
|
1502
|
+
error: `Invalid action: ${action}. Must be one of: ${allowed.join(", ")}`,
|
|
1503
|
+
durationMs: Date.now() - startTime,
|
|
1504
|
+
truncated: false
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
return void 0;
|
|
1508
|
+
}
|
|
1509
|
+
function checkActionPermission(action, permittedActions, startTime) {
|
|
1510
|
+
if (action && permittedActions && !permittedActions.includes(action)) {
|
|
1511
|
+
return {
|
|
1512
|
+
success: false,
|
|
1513
|
+
error: `Action '${action}' not allowed. Available: ${permittedActions.join(", ")}`,
|
|
1514
|
+
durationMs: Date.now() - startTime,
|
|
1515
|
+
truncated: false
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
return void 0;
|
|
1519
|
+
}
|
|
1520
|
+
function requireSession(sessionManager, sessionId) {
|
|
1521
|
+
return sessionManager.requireSession(sessionId);
|
|
1522
|
+
}
|
|
1523
|
+
function validateActionWithPermission(action, allowedActions, toolName, permittedActions, startTime) {
|
|
1524
|
+
const actionError = validateAction(action, allowedActions, startTime ?? Date.now());
|
|
1525
|
+
if (actionError) return actionError;
|
|
1526
|
+
const permittedToolActions = permittedActions?.[toolName];
|
|
1527
|
+
const permissionError = checkActionPermission(action, permittedToolActions, startTime ?? Date.now());
|
|
1528
|
+
if (permissionError) return permissionError;
|
|
1529
|
+
return void 0;
|
|
1530
|
+
}
|
|
1467
1531
|
function createTool(name, definition, handler2) {
|
|
1468
1532
|
return {
|
|
1469
1533
|
name,
|
|
@@ -1519,7 +1583,7 @@ function createTool(name, definition, handler2) {
|
|
|
1519
1583
|
|
|
1520
1584
|
// src/server/tools/file-tracker.ts
|
|
1521
1585
|
import { createHash } from "crypto";
|
|
1522
|
-
import { readFile as
|
|
1586
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1523
1587
|
import { resolve as resolve3 } from "path";
|
|
1524
1588
|
var FileNotReadError = class extends Error {
|
|
1525
1589
|
constructor(path) {
|
|
@@ -1535,7 +1599,7 @@ var FileChangedExternallyError = class extends Error {
|
|
|
1535
1599
|
};
|
|
1536
1600
|
async function computeFileHash(filePath) {
|
|
1537
1601
|
try {
|
|
1538
|
-
const content = await
|
|
1602
|
+
const content = await readFile4(filePath);
|
|
1539
1603
|
const hash = createHash("sha256");
|
|
1540
1604
|
hash.update(content);
|
|
1541
1605
|
return hash.digest("hex");
|
|
@@ -1668,7 +1732,7 @@ var readFileTool = createTool(
|
|
|
1668
1732
|
} catch {
|
|
1669
1733
|
return helpers.error(`File not found: ${args.path}`);
|
|
1670
1734
|
}
|
|
1671
|
-
const rawBuffer = await
|
|
1735
|
+
const rawBuffer = await readFile5(fullPath);
|
|
1672
1736
|
const mimeType = detectImageType(rawBuffer, args.path);
|
|
1673
1737
|
if (mimeType) {
|
|
1674
1738
|
const base64Data = rawBuffer.toString("base64");
|
|
@@ -1802,7 +1866,7 @@ var writeFileTool = createTool(
|
|
|
1802
1866
|
);
|
|
1803
1867
|
|
|
1804
1868
|
// src/server/tools/edit.ts
|
|
1805
|
-
import { readFile as
|
|
1869
|
+
import { readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
1806
1870
|
|
|
1807
1871
|
// src/server/tools/edit-context.ts
|
|
1808
1872
|
var CONTEXT_LINES = 4;
|
|
@@ -1958,7 +2022,7 @@ var editFileTool = createTool(
|
|
|
1958
2022
|
}
|
|
1959
2023
|
let content;
|
|
1960
2024
|
try {
|
|
1961
|
-
content = await
|
|
2025
|
+
content = await readFile6(fullPath, "utf-8");
|
|
1962
2026
|
} catch {
|
|
1963
2027
|
return helpers.error(`File not found: ${args.path}`);
|
|
1964
2028
|
}
|
|
@@ -2024,11 +2088,26 @@ Make sure whitespace and indentation match exactly.`
|
|
|
2024
2088
|
);
|
|
2025
2089
|
|
|
2026
2090
|
// src/server/tools/shell.ts
|
|
2027
|
-
import
|
|
2091
|
+
import "child_process";
|
|
2028
2092
|
import { resolve as resolve4, isAbsolute as isAbsolute2 } from "path";
|
|
2029
2093
|
|
|
2030
|
-
// src/server/utils/
|
|
2094
|
+
// src/server/utils/shell.ts
|
|
2031
2095
|
import { spawn } from "child_process";
|
|
2096
|
+
function checkAborted(signal) {
|
|
2097
|
+
return !!signal?.aborted;
|
|
2098
|
+
}
|
|
2099
|
+
function spawnShellProcess(command, cwd, signal, detached = false) {
|
|
2100
|
+
const shell = getPlatformShell();
|
|
2101
|
+
return spawn(shell.command, [...shell.args, command], {
|
|
2102
|
+
cwd,
|
|
2103
|
+
env: { ...process.env, FORCE_COLOR: "0" },
|
|
2104
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2105
|
+
...detached ? { detached: true } : {}
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
// src/server/utils/process-tree.ts
|
|
2110
|
+
import { spawn as spawn2 } from "child_process";
|
|
2032
2111
|
import { setTimeout as sleep } from "timers/promises";
|
|
2033
2112
|
var SIGKILL_TIMEOUT_MS = 200;
|
|
2034
2113
|
async function terminateProcessTree(proc, options) {
|
|
@@ -2038,7 +2117,7 @@ async function terminateProcessTree(proc, options) {
|
|
|
2038
2117
|
}
|
|
2039
2118
|
if (process.platform === "win32") {
|
|
2040
2119
|
await new Promise((resolve6) => {
|
|
2041
|
-
const killer =
|
|
2120
|
+
const killer = spawn2("taskkill", ["/pid", String(pid), "/f", "/t"], {
|
|
2042
2121
|
stdio: "ignore",
|
|
2043
2122
|
windowsHide: true
|
|
2044
2123
|
});
|
|
@@ -2142,18 +2221,11 @@ ${result.stderr}`;
|
|
|
2142
2221
|
);
|
|
2143
2222
|
function executeCommand(command, cwd, timeout, signal, onProgress) {
|
|
2144
2223
|
return new Promise((resolve6, reject) => {
|
|
2145
|
-
if (signal
|
|
2224
|
+
if (checkAborted(signal)) {
|
|
2146
2225
|
reject(new Error("Command aborted before execution"));
|
|
2147
2226
|
return;
|
|
2148
2227
|
}
|
|
2149
|
-
const
|
|
2150
|
-
const proc = spawn2(shell.command, [...shell.args, command], {
|
|
2151
|
-
cwd,
|
|
2152
|
-
env: { ...process.env, FORCE_COLOR: "0" },
|
|
2153
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2154
|
-
detached: true
|
|
2155
|
-
// Create new process group so we can kill all children
|
|
2156
|
-
});
|
|
2228
|
+
const proc = spawnShellProcess(command, cwd, signal, true);
|
|
2157
2229
|
let stdout = "";
|
|
2158
2230
|
let stderr = "";
|
|
2159
2231
|
let killed = false;
|
|
@@ -2171,12 +2243,12 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
|
|
|
2171
2243
|
}
|
|
2172
2244
|
};
|
|
2173
2245
|
signal?.addEventListener("abort", onAbort);
|
|
2174
|
-
proc.stdout
|
|
2246
|
+
proc.stdout?.on("data", (data) => {
|
|
2175
2247
|
const chunk = data.toString();
|
|
2176
2248
|
stdout += chunk;
|
|
2177
2249
|
onProgress?.(`[stdout] ${chunk}`);
|
|
2178
2250
|
});
|
|
2179
|
-
proc.stderr
|
|
2251
|
+
proc.stderr?.on("data", (data) => {
|
|
2180
2252
|
const chunk = data.toString();
|
|
2181
2253
|
stderr += chunk;
|
|
2182
2254
|
onProgress?.(`[stderr] ${chunk}`);
|
|
@@ -2219,10 +2291,27 @@ function formatCriteriaList(criteria) {
|
|
|
2219
2291
|
if (criteria.length === 0) return "No criteria defined.";
|
|
2220
2292
|
return criteria.map((c, i) => `${i + 1}. [${c.id}] ${c.description}`).join("\n");
|
|
2221
2293
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2294
|
+
function completeCriterion(session, context, id, statusType, reason) {
|
|
2295
|
+
const criterion = session.criteria.find((c) => c.id === id);
|
|
2296
|
+
if (!criterion) {
|
|
2297
|
+
return { success: false, output: `Criterion "${id}" not found` };
|
|
2298
|
+
}
|
|
2299
|
+
const status = statusType === "passed" ? { type: "passed", verifiedAt: (/* @__PURE__ */ new Date()).toISOString(), ...reason && { reason } } : { type: "failed", failedAt: (/* @__PURE__ */ new Date()).toISOString(), reason };
|
|
2300
|
+
context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
|
|
2301
|
+
context.sessionManager.addCriterionAttempt(context.sessionId, id, {
|
|
2302
|
+
attemptNumber: criterion.attempts.length + 1,
|
|
2303
|
+
status: statusType,
|
|
2304
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2305
|
+
...reason ? { details: reason } : {}
|
|
2306
|
+
});
|
|
2307
|
+
return {
|
|
2308
|
+
success: true,
|
|
2309
|
+
output: statusType === "passed" ? `Criterion "${id}" verified as PASSED.${reason ? ` Verification: ${reason}` : ""}` : `Criterion "${id}" marked as FAILED. Reason: ${reason}`
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
var criterionTool = createTool(
|
|
2313
|
+
"criterion",
|
|
2314
|
+
{
|
|
2226
2315
|
type: "function",
|
|
2227
2316
|
function: {
|
|
2228
2317
|
name: "criterion",
|
|
@@ -2252,267 +2341,90 @@ var criterionTool = {
|
|
|
2252
2341
|
}
|
|
2253
2342
|
}
|
|
2254
2343
|
},
|
|
2255
|
-
async
|
|
2256
|
-
const
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
if (permittedActions && !permittedActions.includes(action)) {
|
|
2272
|
-
return {
|
|
2273
|
-
success: false,
|
|
2274
|
-
error: `Action '${action}' not allowed. Available: ${permittedActions.join(", ")}`,
|
|
2275
|
-
durationMs: Date.now() - startTime,
|
|
2276
|
-
truncated: false
|
|
2277
|
-
};
|
|
2278
|
-
}
|
|
2279
|
-
const session = context.sessionManager.requireSession(context.sessionId);
|
|
2280
|
-
if (action === "get") {
|
|
2281
|
-
const criteria = session.criteria;
|
|
2282
|
-
return {
|
|
2283
|
-
success: true,
|
|
2284
|
-
output: criteria.length === 0 ? "No criteria defined yet." : JSON.stringify(criteria.map((c) => ({
|
|
2285
|
-
id: c.id,
|
|
2286
|
-
description: c.description
|
|
2287
|
-
})), null, 2),
|
|
2288
|
-
durationMs: Date.now() - startTime,
|
|
2289
|
-
truncated: false
|
|
2290
|
-
};
|
|
2291
|
-
}
|
|
2292
|
-
if (action === "add") {
|
|
2293
|
-
if (!description) {
|
|
2294
|
-
return {
|
|
2295
|
-
success: false,
|
|
2296
|
-
error: "Missing required field: description",
|
|
2297
|
-
durationMs: Date.now() - startTime,
|
|
2298
|
-
truncated: false
|
|
2299
|
-
};
|
|
2300
|
-
}
|
|
2301
|
-
const criterion = {
|
|
2302
|
-
id: id || "",
|
|
2303
|
-
description,
|
|
2304
|
-
status: { type: "pending" },
|
|
2305
|
-
attempts: []
|
|
2306
|
-
};
|
|
2307
|
-
const result = context.sessionManager.addCriterion(context.sessionId, criterion);
|
|
2308
|
-
if ("error" in result) {
|
|
2309
|
-
return { success: false, error: result.error, durationMs: Date.now() - startTime, truncated: false };
|
|
2310
|
-
}
|
|
2311
|
-
return {
|
|
2312
|
-
success: true,
|
|
2313
|
-
output: `Added criterion "${result.actualId}". Current criteria:
|
|
2314
|
-
${formatCriteriaList(result.criteria)}`,
|
|
2315
|
-
durationMs: Date.now() - startTime,
|
|
2316
|
-
truncated: false
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
if (action === "update") {
|
|
2320
|
-
if (!id) {
|
|
2321
|
-
return {
|
|
2322
|
-
success: false,
|
|
2323
|
-
error: "Missing required field: id",
|
|
2324
|
-
durationMs: Date.now() - startTime,
|
|
2325
|
-
truncated: false
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
if (!description) {
|
|
2329
|
-
return {
|
|
2330
|
-
success: false,
|
|
2331
|
-
error: "Missing required field: description",
|
|
2332
|
-
durationMs: Date.now() - startTime,
|
|
2333
|
-
truncated: false
|
|
2334
|
-
};
|
|
2335
|
-
}
|
|
2336
|
-
if (!session.criteria.find((c) => c.id === id)) {
|
|
2337
|
-
return {
|
|
2338
|
-
success: false,
|
|
2339
|
-
error: `Criterion "${id}" not found`,
|
|
2340
|
-
durationMs: Date.now() - startTime,
|
|
2341
|
-
truncated: false
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
const criteria = context.sessionManager.updateCriterionFull(context.sessionId, id, { description });
|
|
2345
|
-
return {
|
|
2346
|
-
success: true,
|
|
2347
|
-
output: `Updated criterion "${id}". Current criteria:
|
|
2348
|
-
${formatCriteriaList(criteria)}`,
|
|
2349
|
-
durationMs: Date.now() - startTime,
|
|
2350
|
-
truncated: false
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
|
-
if (action === "remove") {
|
|
2354
|
-
if (!id) {
|
|
2355
|
-
return {
|
|
2356
|
-
success: false,
|
|
2357
|
-
error: "Missing required field: id",
|
|
2358
|
-
durationMs: Date.now() - startTime,
|
|
2359
|
-
truncated: false
|
|
2360
|
-
};
|
|
2361
|
-
}
|
|
2362
|
-
if (!session.criteria.find((c) => c.id === id)) {
|
|
2363
|
-
return {
|
|
2364
|
-
success: false,
|
|
2365
|
-
error: `Criterion "${id}" not found`,
|
|
2366
|
-
durationMs: Date.now() - startTime,
|
|
2367
|
-
truncated: false
|
|
2368
|
-
};
|
|
2369
|
-
}
|
|
2370
|
-
const criteria = context.sessionManager.removeCriterion(context.sessionId, id);
|
|
2371
|
-
return {
|
|
2372
|
-
success: true,
|
|
2373
|
-
output: criteria.length === 0 ? `Removed criterion "${id}". No criteria remaining.` : `Removed criterion "${id}". Current criteria:
|
|
2374
|
-
${formatCriteriaList(criteria)}`,
|
|
2375
|
-
durationMs: Date.now() - startTime,
|
|
2376
|
-
truncated: false
|
|
2377
|
-
};
|
|
2378
|
-
}
|
|
2379
|
-
if (action === "complete") {
|
|
2380
|
-
if (!id) {
|
|
2381
|
-
return {
|
|
2382
|
-
success: false,
|
|
2383
|
-
error: "Missing required field: id",
|
|
2384
|
-
durationMs: Date.now() - startTime,
|
|
2385
|
-
truncated: false
|
|
2386
|
-
};
|
|
2387
|
-
}
|
|
2388
|
-
const criterion = session.criteria.find((c) => c.id === id);
|
|
2389
|
-
if (!criterion) {
|
|
2390
|
-
return {
|
|
2391
|
-
success: false,
|
|
2392
|
-
error: `Criterion "${id}" not found. Available: ${session.criteria.map((c) => c.id).join(", ")}`,
|
|
2393
|
-
durationMs: Date.now() - startTime,
|
|
2394
|
-
truncated: false
|
|
2395
|
-
};
|
|
2396
|
-
}
|
|
2397
|
-
const status = {
|
|
2398
|
-
type: "completed",
|
|
2399
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2400
|
-
...reason ? { reason } : {}
|
|
2401
|
-
};
|
|
2402
|
-
context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
|
|
2403
|
-
return {
|
|
2404
|
-
success: true,
|
|
2405
|
-
output: `Criterion "${id}" marked as completed.${reason ? ` Reason: ${reason}` : ""}`,
|
|
2406
|
-
durationMs: Date.now() - startTime,
|
|
2407
|
-
truncated: false
|
|
2408
|
-
};
|
|
2409
|
-
}
|
|
2410
|
-
if (action === "pass") {
|
|
2411
|
-
if (!id) {
|
|
2412
|
-
return {
|
|
2413
|
-
success: false,
|
|
2414
|
-
error: "Missing required field: id",
|
|
2415
|
-
durationMs: Date.now() - startTime,
|
|
2416
|
-
truncated: false
|
|
2417
|
-
};
|
|
2418
|
-
}
|
|
2419
|
-
const criterion = session.criteria.find((c) => c.id === id);
|
|
2420
|
-
if (!criterion) {
|
|
2421
|
-
return {
|
|
2422
|
-
success: false,
|
|
2423
|
-
error: `Criterion "${id}" not found`,
|
|
2424
|
-
durationMs: Date.now() - startTime,
|
|
2425
|
-
truncated: false
|
|
2426
|
-
};
|
|
2427
|
-
}
|
|
2428
|
-
const status = {
|
|
2429
|
-
type: "passed",
|
|
2430
|
-
verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2431
|
-
...reason ? { reason } : {}
|
|
2432
|
-
};
|
|
2433
|
-
context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
|
|
2434
|
-
context.sessionManager.addCriterionAttempt(context.sessionId, id, {
|
|
2435
|
-
attemptNumber: criterion.attempts.length + 1,
|
|
2436
|
-
status: "passed",
|
|
2437
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2438
|
-
...reason && { details: reason }
|
|
2439
|
-
});
|
|
2440
|
-
return {
|
|
2441
|
-
success: true,
|
|
2442
|
-
output: `Criterion "${id}" verified as PASSED.${reason ? ` Verification: ${reason}` : ""}`,
|
|
2443
|
-
durationMs: Date.now() - startTime,
|
|
2444
|
-
truncated: false
|
|
2445
|
-
};
|
|
2446
|
-
}
|
|
2447
|
-
if (action === "fail") {
|
|
2448
|
-
if (!id) {
|
|
2449
|
-
return {
|
|
2450
|
-
success: false,
|
|
2451
|
-
error: "Missing required field: id",
|
|
2452
|
-
durationMs: Date.now() - startTime,
|
|
2453
|
-
truncated: false
|
|
2454
|
-
};
|
|
2455
|
-
}
|
|
2456
|
-
if (!reason) {
|
|
2457
|
-
return {
|
|
2458
|
-
success: false,
|
|
2459
|
-
error: "Missing required field: reason",
|
|
2460
|
-
durationMs: Date.now() - startTime,
|
|
2461
|
-
truncated: false
|
|
2462
|
-
};
|
|
2463
|
-
}
|
|
2464
|
-
const criterion = session.criteria.find((c) => c.id === id);
|
|
2465
|
-
if (!criterion) {
|
|
2466
|
-
return {
|
|
2467
|
-
success: false,
|
|
2468
|
-
error: `Criterion "${id}" not found`,
|
|
2469
|
-
durationMs: Date.now() - startTime,
|
|
2470
|
-
truncated: false
|
|
2471
|
-
};
|
|
2472
|
-
}
|
|
2473
|
-
const status = {
|
|
2474
|
-
type: "failed",
|
|
2475
|
-
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2476
|
-
reason
|
|
2477
|
-
};
|
|
2478
|
-
context.sessionManager.updateCriterionStatus(context.sessionId, id, status);
|
|
2479
|
-
context.sessionManager.addCriterionAttempt(context.sessionId, id, {
|
|
2480
|
-
attemptNumber: criterion.attempts.length + 1,
|
|
2481
|
-
status: "failed",
|
|
2482
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2483
|
-
details: reason
|
|
2484
|
-
});
|
|
2485
|
-
return {
|
|
2486
|
-
success: true,
|
|
2487
|
-
output: `Criterion "${id}" marked as FAILED. Reason: ${reason}`,
|
|
2488
|
-
durationMs: Date.now() - startTime,
|
|
2489
|
-
truncated: false
|
|
2490
|
-
};
|
|
2491
|
-
}
|
|
2492
|
-
return {
|
|
2493
|
-
success: false,
|
|
2494
|
-
error: "Unexpected error",
|
|
2495
|
-
durationMs: Date.now() - startTime,
|
|
2496
|
-
truncated: false
|
|
2497
|
-
};
|
|
2498
|
-
} catch (error) {
|
|
2499
|
-
return {
|
|
2500
|
-
success: false,
|
|
2501
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
2502
|
-
durationMs: Date.now() - startTime,
|
|
2503
|
-
truncated: false
|
|
2344
|
+
async (args, context, helpers) => {
|
|
2345
|
+
const actionError = validateActionWithPermission(args.action, ["get", "add", "update", "remove", "complete", "pass", "fail"], "criterion", context.permittedActions);
|
|
2346
|
+
if (actionError) return actionError;
|
|
2347
|
+
const session = requireSession(context.sessionManager, context.sessionId);
|
|
2348
|
+
if (args.action === "get") {
|
|
2349
|
+
return helpers.success(
|
|
2350
|
+
session.criteria.length === 0 ? "No criteria defined yet." : JSON.stringify(session.criteria.map((c) => ({ id: c.id, description: c.description })), null, 2)
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
if (args.action === "add") {
|
|
2354
|
+
if (!args.description) return helpers.error("Missing required field: description");
|
|
2355
|
+
const criterion = {
|
|
2356
|
+
id: args.id || "",
|
|
2357
|
+
description: args.description,
|
|
2358
|
+
status: { type: "pending" },
|
|
2359
|
+
attempts: []
|
|
2504
2360
|
};
|
|
2361
|
+
const result = context.sessionManager.addCriterion(context.sessionId, criterion);
|
|
2362
|
+
if ("error" in result) return helpers.error(result.error);
|
|
2363
|
+
return helpers.success(`Added criterion "${result.actualId}". Current criteria:
|
|
2364
|
+
${formatCriteriaList(result.criteria)}`);
|
|
2365
|
+
}
|
|
2366
|
+
if (args.action === "update") {
|
|
2367
|
+
if (!args.id) return helpers.error("Missing required field: id");
|
|
2368
|
+
if (!args.description) return helpers.error("Missing required field: description");
|
|
2369
|
+
if (!session.criteria.find((c) => c.id === args.id)) return helpers.error(`Criterion "${args.id}" not found`);
|
|
2370
|
+
const criteria = context.sessionManager.updateCriterionFull(context.sessionId, args.id, { description: args.description });
|
|
2371
|
+
return helpers.success(`Updated criterion "${args.id}". Current criteria:
|
|
2372
|
+
${formatCriteriaList(criteria)}`);
|
|
2373
|
+
}
|
|
2374
|
+
if (args.action === "remove") {
|
|
2375
|
+
if (!args.id) return helpers.error("Missing required field: id");
|
|
2376
|
+
if (!session.criteria.find((c) => c.id === args.id)) return helpers.error(`Criterion "${args.id}" not found`);
|
|
2377
|
+
const criteria = context.sessionManager.removeCriterion(context.sessionId, args.id);
|
|
2378
|
+
return helpers.success(
|
|
2379
|
+
criteria.length === 0 ? `Removed criterion "${args.id}". No criteria remaining.` : `Removed criterion "${args.id}". Current criteria:
|
|
2380
|
+
${formatCriteriaList(criteria)}`
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
if (args.action === "complete") {
|
|
2384
|
+
if (!args.id) return helpers.error("Missing required field: id");
|
|
2385
|
+
const criterion = session.criteria.find((c) => c.id === args.id);
|
|
2386
|
+
if (!criterion) return helpers.error(`Criterion "${args.id}" not found. Available: ${session.criteria.map((c) => c.id).join(", ")}`);
|
|
2387
|
+
context.sessionManager.updateCriterionStatus(context.sessionId, args.id, {
|
|
2388
|
+
type: "completed",
|
|
2389
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2390
|
+
...args.reason ? { reason: args.reason } : {}
|
|
2391
|
+
});
|
|
2392
|
+
return helpers.success(`Criterion "${args.id}" marked as completed.${args.reason ? ` Reason: ${args.reason}` : ""}`);
|
|
2393
|
+
}
|
|
2394
|
+
if (args.action === "pass") {
|
|
2395
|
+
if (!args.id) return helpers.error("Missing required field: id");
|
|
2396
|
+
const result = completeCriterion(session, context, args.id, "passed", args.reason);
|
|
2397
|
+
return result.success ? helpers.success(result.output) : helpers.error(result.output);
|
|
2505
2398
|
}
|
|
2399
|
+
if (args.action === "fail") {
|
|
2400
|
+
if (!args.id) return helpers.error("Missing required field: id");
|
|
2401
|
+
if (!args.reason) return helpers.error("Missing required field: reason");
|
|
2402
|
+
const result = completeCriterion(session, context, args.id, "failed", args.reason);
|
|
2403
|
+
return result.success ? helpers.success(result.output) : helpers.error(result.output);
|
|
2404
|
+
}
|
|
2405
|
+
return helpers.error("Unexpected error");
|
|
2506
2406
|
}
|
|
2507
|
-
|
|
2407
|
+
);
|
|
2508
2408
|
|
|
2509
2409
|
// src/server/tools/todo.ts
|
|
2510
2410
|
var sessionTodos = /* @__PURE__ */ new Map();
|
|
2511
2411
|
var onTodoUpdate = null;
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2412
|
+
function buildTaskListSummary(todosList) {
|
|
2413
|
+
const pending = todosList.filter((t) => t.status === "pending").length;
|
|
2414
|
+
const inProgress = todosList.filter((t) => t.status === "in_progress").length;
|
|
2415
|
+
const completed = todosList.filter((t) => t.status === "completed").length;
|
|
2416
|
+
return `${completed} completed, ${inProgress} in progress, ${pending} pending`;
|
|
2417
|
+
}
|
|
2418
|
+
function validateIndex(index, todosList, helpers) {
|
|
2419
|
+
if (index === void 0) return helpers.error("Missing required field: index");
|
|
2420
|
+
if (index < 0 || index >= todosList.length) {
|
|
2421
|
+
return helpers.error(`Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`);
|
|
2422
|
+
}
|
|
2423
|
+
return todosList[index];
|
|
2424
|
+
}
|
|
2425
|
+
var todoTool = createTool(
|
|
2426
|
+
"todo",
|
|
2427
|
+
{
|
|
2516
2428
|
type: "function",
|
|
2517
2429
|
function: {
|
|
2518
2430
|
name: "todo",
|
|
@@ -2555,235 +2467,65 @@ var todoTool = {
|
|
|
2555
2467
|
}
|
|
2556
2468
|
}
|
|
2557
2469
|
},
|
|
2558
|
-
async
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
const
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
const permittedActions = context.permittedActions?.["todo"];
|
|
2575
|
-
if (permittedActions && !permittedActions.includes(action)) {
|
|
2576
|
-
return {
|
|
2577
|
-
success: false,
|
|
2578
|
-
error: `Action '${action}' not allowed. Available: ${permittedActions.join(", ")}`,
|
|
2579
|
-
durationMs: Date.now() - startTime,
|
|
2580
|
-
truncated: false
|
|
2581
|
-
};
|
|
2582
|
-
}
|
|
2583
|
-
if (action === "list") {
|
|
2584
|
-
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2585
|
-
return {
|
|
2586
|
-
success: true,
|
|
2587
|
-
output: todosList.length === 0 ? "No tasks defined yet." : JSON.stringify(todosList, null, 2),
|
|
2588
|
-
durationMs: Date.now() - startTime,
|
|
2589
|
-
truncated: false
|
|
2590
|
-
};
|
|
2591
|
-
}
|
|
2592
|
-
if (action === "write") {
|
|
2593
|
-
if (!todos) {
|
|
2594
|
-
return {
|
|
2595
|
-
success: false,
|
|
2596
|
-
error: "Missing required field: todos",
|
|
2597
|
-
durationMs: Date.now() - startTime,
|
|
2598
|
-
truncated: false
|
|
2599
|
-
};
|
|
2600
|
-
}
|
|
2601
|
-
if (!Array.isArray(todos)) {
|
|
2602
|
-
return {
|
|
2603
|
-
success: false,
|
|
2604
|
-
error: "todos must be an array",
|
|
2605
|
-
durationMs: Date.now() - startTime,
|
|
2606
|
-
truncated: false
|
|
2607
|
-
};
|
|
2608
|
-
}
|
|
2609
|
-
for (const todo of todos) {
|
|
2610
|
-
if (!todo.content || !todo.status) {
|
|
2611
|
-
return {
|
|
2612
|
-
success: false,
|
|
2613
|
-
error: "Each todo must have content and status",
|
|
2614
|
-
durationMs: Date.now() - startTime,
|
|
2615
|
-
truncated: false
|
|
2616
|
-
};
|
|
2617
|
-
}
|
|
2618
|
-
if (!["pending", "in_progress", "completed"].includes(todo.status)) {
|
|
2619
|
-
return {
|
|
2620
|
-
success: false,
|
|
2621
|
-
error: `Invalid status: ${todo.status}. Must be pending, in_progress, or completed`,
|
|
2622
|
-
durationMs: Date.now() - startTime,
|
|
2623
|
-
truncated: false
|
|
2624
|
-
};
|
|
2625
|
-
}
|
|
2626
|
-
}
|
|
2627
|
-
sessionTodos.set(context.sessionId, todos);
|
|
2628
|
-
if (onTodoUpdate) {
|
|
2629
|
-
onTodoUpdate(context.sessionId, todos);
|
|
2630
|
-
}
|
|
2631
|
-
const pending = todos.filter((t) => t.status === "pending").length;
|
|
2632
|
-
const inProgress = todos.filter((t) => t.status === "in_progress").length;
|
|
2633
|
-
const completed = todos.filter((t) => t.status === "completed").length;
|
|
2634
|
-
return {
|
|
2635
|
-
success: true,
|
|
2636
|
-
output: `Task list updated: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
|
|
2637
|
-
durationMs: Date.now() - startTime,
|
|
2638
|
-
truncated: false
|
|
2639
|
-
};
|
|
2640
|
-
}
|
|
2641
|
-
if (action === "add") {
|
|
2642
|
-
if (!content) {
|
|
2643
|
-
return {
|
|
2644
|
-
success: false,
|
|
2645
|
-
error: "Missing required field: content",
|
|
2646
|
-
durationMs: Date.now() - startTime,
|
|
2647
|
-
truncated: false
|
|
2648
|
-
};
|
|
2649
|
-
}
|
|
2650
|
-
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2651
|
-
const newTodo = { content, status: "pending" };
|
|
2652
|
-
todosList.push(newTodo);
|
|
2653
|
-
sessionTodos.set(context.sessionId, todosList);
|
|
2654
|
-
if (onTodoUpdate) {
|
|
2655
|
-
onTodoUpdate(context.sessionId, todosList);
|
|
2656
|
-
}
|
|
2657
|
-
const pending = todosList.filter((t) => t.status === "pending").length;
|
|
2658
|
-
const inProgress = todosList.filter((t) => t.status === "in_progress").length;
|
|
2659
|
-
const completed = todosList.filter((t) => t.status === "completed").length;
|
|
2660
|
-
return {
|
|
2661
|
-
success: true,
|
|
2662
|
-
output: `Added task "${content}". Task list: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
|
|
2663
|
-
durationMs: Date.now() - startTime,
|
|
2664
|
-
truncated: false
|
|
2665
|
-
};
|
|
2666
|
-
}
|
|
2667
|
-
if (action === "update") {
|
|
2668
|
-
if (index === void 0) {
|
|
2669
|
-
return {
|
|
2670
|
-
success: false,
|
|
2671
|
-
error: "Missing required field: index",
|
|
2672
|
-
durationMs: Date.now() - startTime,
|
|
2673
|
-
truncated: false
|
|
2674
|
-
};
|
|
2675
|
-
}
|
|
2676
|
-
if (!content && !status) {
|
|
2677
|
-
return {
|
|
2678
|
-
success: false,
|
|
2679
|
-
error: "update requires content or status",
|
|
2680
|
-
durationMs: Date.now() - startTime,
|
|
2681
|
-
truncated: false
|
|
2682
|
-
};
|
|
2683
|
-
}
|
|
2684
|
-
if (status && !["pending", "in_progress", "completed"].includes(status)) {
|
|
2685
|
-
return {
|
|
2686
|
-
success: false,
|
|
2687
|
-
error: `Invalid status: ${status}. Must be pending, in_progress, or completed`,
|
|
2688
|
-
durationMs: Date.now() - startTime,
|
|
2689
|
-
truncated: false
|
|
2690
|
-
};
|
|
2691
|
-
}
|
|
2692
|
-
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2693
|
-
if (index < 0 || index >= todosList.length) {
|
|
2694
|
-
return {
|
|
2695
|
-
success: false,
|
|
2696
|
-
error: `Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`,
|
|
2697
|
-
durationMs: Date.now() - startTime,
|
|
2698
|
-
truncated: false
|
|
2699
|
-
};
|
|
2700
|
-
}
|
|
2701
|
-
const todo = todosList[index];
|
|
2702
|
-
if (!todo) {
|
|
2703
|
-
return {
|
|
2704
|
-
success: false,
|
|
2705
|
-
error: `Index out of range: ${index}. Valid range: 0-${todosList.length - 1}`,
|
|
2706
|
-
durationMs: Date.now() - startTime,
|
|
2707
|
-
truncated: false
|
|
2708
|
-
};
|
|
2709
|
-
}
|
|
2710
|
-
if (content) {
|
|
2711
|
-
todo.content = content;
|
|
2712
|
-
}
|
|
2713
|
-
if (status) {
|
|
2714
|
-
todo.status = status;
|
|
2715
|
-
}
|
|
2716
|
-
sessionTodos.set(context.sessionId, todosList);
|
|
2717
|
-
if (onTodoUpdate) {
|
|
2718
|
-
onTodoUpdate(context.sessionId, todosList);
|
|
2470
|
+
async (args, context, helpers) => {
|
|
2471
|
+
const actionError = validateActionWithPermission(args.action, ["list", "write", "add", "update", "remove"], "todo", context.permittedActions);
|
|
2472
|
+
if (actionError) return actionError;
|
|
2473
|
+
if (args.action === "list") {
|
|
2474
|
+
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2475
|
+
return helpers.success(
|
|
2476
|
+
todosList.length === 0 ? "No tasks defined yet." : JSON.stringify(todosList, null, 2)
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
if (args.action === "write") {
|
|
2480
|
+
if (!args.todos) return helpers.error("Missing required field: todos");
|
|
2481
|
+
if (!Array.isArray(args.todos)) return helpers.error("todos must be an array");
|
|
2482
|
+
for (const todo of args.todos) {
|
|
2483
|
+
if (!todo.content || !todo.status) return helpers.error("Each todo must have content and status");
|
|
2484
|
+
if (!["pending", "in_progress", "completed"].includes(todo.status)) {
|
|
2485
|
+
return helpers.error(`Invalid status: ${todo.status}. Must be pending, in_progress, or completed`);
|
|
2719
2486
|
}
|
|
2720
|
-
const pending = todosList.filter((t) => t.status === "pending").length;
|
|
2721
|
-
const inProgress = todosList.filter((t) => t.status === "in_progress").length;
|
|
2722
|
-
const completed = todosList.filter((t) => t.status === "completed").length;
|
|
2723
|
-
return {
|
|
2724
|
-
success: true,
|
|
2725
|
-
output: `Updated task ${index}. Task list: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
|
|
2726
|
-
durationMs: Date.now() - startTime,
|
|
2727
|
-
truncated: false
|
|
2728
|
-
};
|
|
2729
2487
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
};
|
|
2747
|
-
}
|
|
2748
|
-
const removed = todosList.splice(index, 1)[0];
|
|
2749
|
-
sessionTodos.set(context.sessionId, todosList);
|
|
2750
|
-
if (onTodoUpdate) {
|
|
2751
|
-
onTodoUpdate(context.sessionId, todosList);
|
|
2752
|
-
}
|
|
2753
|
-
if (todosList.length === 0) {
|
|
2754
|
-
return {
|
|
2755
|
-
success: true,
|
|
2756
|
-
output: `Removed task "${removed.content}". No tasks remaining.`,
|
|
2757
|
-
durationMs: Date.now() - startTime,
|
|
2758
|
-
truncated: false
|
|
2759
|
-
};
|
|
2760
|
-
}
|
|
2761
|
-
const pending = todosList.filter((t) => t.status === "pending").length;
|
|
2762
|
-
const inProgress = todosList.filter((t) => t.status === "in_progress").length;
|
|
2763
|
-
const completed = todosList.filter((t) => t.status === "completed").length;
|
|
2764
|
-
return {
|
|
2765
|
-
success: true,
|
|
2766
|
-
output: `Removed task "${removed.content}". Task list: ${completed} completed, ${inProgress} in progress, ${pending} pending`,
|
|
2767
|
-
durationMs: Date.now() - startTime,
|
|
2768
|
-
truncated: false
|
|
2769
|
-
};
|
|
2488
|
+
sessionTodos.set(context.sessionId, args.todos);
|
|
2489
|
+
if (onTodoUpdate) onTodoUpdate(context.sessionId, args.todos);
|
|
2490
|
+
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`);
|
|
2491
|
+
}
|
|
2492
|
+
if (args.action === "add") {
|
|
2493
|
+
if (!args.content) return helpers.error("Missing required field: content");
|
|
2494
|
+
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2495
|
+
todosList.push({ content: args.content, status: "pending" });
|
|
2496
|
+
sessionTodos.set(context.sessionId, todosList);
|
|
2497
|
+
if (onTodoUpdate) onTodoUpdate(context.sessionId, todosList);
|
|
2498
|
+
return helpers.success(`Added task "${args.content}". Task list: ${buildTaskListSummary(todosList)}`);
|
|
2499
|
+
}
|
|
2500
|
+
if (args.action === "update") {
|
|
2501
|
+
if (!args.content && !args.status) return helpers.error("update requires content or status");
|
|
2502
|
+
if (args.status && !["pending", "in_progress", "completed"].includes(args.status)) {
|
|
2503
|
+
return helpers.error(`Invalid status: ${args.status}. Must be pending, in_progress, or completed`);
|
|
2770
2504
|
}
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2505
|
+
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2506
|
+
const indexResult = validateIndex(args.index, todosList, helpers);
|
|
2507
|
+
if ("success" in indexResult && !indexResult.success) return indexResult;
|
|
2508
|
+
const todo = indexResult;
|
|
2509
|
+
if (args.content) todo.content = args.content;
|
|
2510
|
+
if (args.status) todo.status = args.status;
|
|
2511
|
+
sessionTodos.set(context.sessionId, todosList);
|
|
2512
|
+
if (onTodoUpdate) onTodoUpdate(context.sessionId, todosList);
|
|
2513
|
+
return helpers.success(`Updated task ${args.index}. Task list: ${buildTaskListSummary(todosList)}`);
|
|
2514
|
+
}
|
|
2515
|
+
if (args.action === "remove") {
|
|
2516
|
+
const todosList = sessionTodos.get(context.sessionId) ?? [];
|
|
2517
|
+
const indexResult = validateIndex(args.index, todosList, helpers);
|
|
2518
|
+
if ("success" in indexResult && !indexResult.success) return indexResult;
|
|
2519
|
+
const removed = todosList.splice(args.index, 1)[0];
|
|
2520
|
+
sessionTodos.set(context.sessionId, todosList);
|
|
2521
|
+
if (onTodoUpdate) onTodoUpdate(context.sessionId, todosList);
|
|
2522
|
+
return helpers.success(
|
|
2523
|
+
todosList.length === 0 ? `Removed task "${removed.content}". No tasks remaining.` : `Removed task "${removed.content}". Task list: ${buildTaskListSummary(todosList)}`
|
|
2524
|
+
);
|
|
2784
2525
|
}
|
|
2526
|
+
return helpers.error("Unexpected error");
|
|
2785
2527
|
}
|
|
2786
|
-
|
|
2528
|
+
);
|
|
2787
2529
|
|
|
2788
2530
|
// src/server/chat/tool-streaming.ts
|
|
2789
2531
|
function parseProgressMessage(message) {
|
|
@@ -2803,18 +2545,17 @@ function createToolProgressHandler(messageId, callId, onMessage) {
|
|
|
2803
2545
|
}
|
|
2804
2546
|
|
|
2805
2547
|
// src/server/skills/registry.ts
|
|
2806
|
-
import { readdir as
|
|
2807
|
-
import { join as
|
|
2548
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
2549
|
+
import { join as join6 } from "path";
|
|
2550
|
+
|
|
2551
|
+
// src/server/commands/registry-utils.ts
|
|
2552
|
+
import { readdir as readdir2, readFile as readFile7, writeFile as writeFile4, copyFile as copyFile2, mkdir as mkdir3, access as access3, unlink as unlink2 } from "fs/promises";
|
|
2553
|
+
import { join as join5, dirname as dirname4 } from "path";
|
|
2808
2554
|
import { constants as constants3 } from "fs";
|
|
2809
2555
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2810
2556
|
import matter2 from "gray-matter";
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
var DEFAULTS_DIR_ALT2 = join4(__bundleDir2, "skill-defaults");
|
|
2814
|
-
var SKILL_EXTENSION = ".skill.md";
|
|
2815
|
-
var SKILL_SETTING_PREFIX = "skill.enabled.";
|
|
2816
|
-
function getSkillsDir(configDir) {
|
|
2817
|
-
return join4(configDir, "skills");
|
|
2557
|
+
function getBundleDir() {
|
|
2558
|
+
return dirname4(fileURLToPath2(import.meta.url));
|
|
2818
2559
|
}
|
|
2819
2560
|
async function dirExists(path) {
|
|
2820
2561
|
try {
|
|
@@ -2824,64 +2565,92 @@ async function dirExists(path) {
|
|
|
2824
2565
|
return false;
|
|
2825
2566
|
}
|
|
2826
2567
|
}
|
|
2827
|
-
async function
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
await mkdir3(skillsDir, { recursive: true });
|
|
2831
|
-
}
|
|
2832
|
-
let defaultFiles;
|
|
2833
|
-
let sourceDir;
|
|
2834
|
-
try {
|
|
2835
|
-
defaultFiles = (await readdir2(DEFAULTS_DIR2)).filter((f) => f.endsWith(SKILL_EXTENSION));
|
|
2836
|
-
sourceDir = DEFAULTS_DIR2;
|
|
2837
|
-
} catch {
|
|
2838
|
-
try {
|
|
2839
|
-
defaultFiles = (await readdir2(DEFAULTS_DIR_ALT2)).filter((f) => f.endsWith(SKILL_EXTENSION));
|
|
2840
|
-
sourceDir = DEFAULTS_DIR_ALT2;
|
|
2841
|
-
} catch {
|
|
2842
|
-
logger.warn("No bundled skill defaults found", { dir: DEFAULTS_DIR2 });
|
|
2843
|
-
return;
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
for (const file of defaultFiles) {
|
|
2847
|
-
const targetPath = join4(skillsDir, file);
|
|
2848
|
-
if (!await dirExists(targetPath)) {
|
|
2849
|
-
try {
|
|
2850
|
-
await copyFile2(join4(sourceDir, file), targetPath);
|
|
2851
|
-
logger.info("Installed default skill", { file });
|
|
2852
|
-
} catch (err) {
|
|
2853
|
-
logger.error("Failed to copy default skill", { file, error: err instanceof Error ? err.message : String(err) });
|
|
2854
|
-
}
|
|
2855
|
-
}
|
|
2568
|
+
async function ensureDir(path) {
|
|
2569
|
+
if (!await dirExists(path)) {
|
|
2570
|
+
await mkdir3(path, { recursive: true });
|
|
2856
2571
|
}
|
|
2857
2572
|
}
|
|
2858
|
-
async function
|
|
2859
|
-
|
|
2860
|
-
if (!await dirExists(skillsDir)) {
|
|
2861
|
-
return [];
|
|
2862
|
-
}
|
|
2573
|
+
async function loadItems(dir, extension) {
|
|
2574
|
+
if (!await dirExists(dir)) return [];
|
|
2863
2575
|
let files;
|
|
2864
2576
|
try {
|
|
2865
|
-
files = (await readdir2(
|
|
2577
|
+
files = (await readdir2(dir)).filter((f) => f.endsWith(extension));
|
|
2866
2578
|
} catch {
|
|
2867
2579
|
return [];
|
|
2868
2580
|
}
|
|
2869
|
-
const
|
|
2581
|
+
const items = [];
|
|
2870
2582
|
for (const file of files) {
|
|
2871
2583
|
try {
|
|
2872
|
-
const raw = await
|
|
2584
|
+
const raw = await readFile7(join5(dir, file), "utf-8");
|
|
2873
2585
|
const { data, content } = matter2(raw);
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
skills.push({ metadata, prompt: content.trim() });
|
|
2586
|
+
if (data.id && content.trim()) {
|
|
2587
|
+
items.push({ metadata: data, prompt: content.trim() });
|
|
2877
2588
|
} else {
|
|
2878
|
-
logger.warn(
|
|
2589
|
+
logger.warn(`Skipping invalid ${extension} file`, { file });
|
|
2879
2590
|
}
|
|
2880
2591
|
} catch (err) {
|
|
2881
|
-
logger.warn(
|
|
2592
|
+
logger.warn(`Failed to parse ${extension} file`, { file, error: err instanceof Error ? err.message : String(err) });
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return items;
|
|
2596
|
+
}
|
|
2597
|
+
async function saveItem(dir, id, extension, item) {
|
|
2598
|
+
await ensureDir(dir);
|
|
2599
|
+
const filePath = join5(dir, `${id}${extension}`);
|
|
2600
|
+
const content = matter2.stringify(item.prompt, item.metadata);
|
|
2601
|
+
await writeFile4(filePath, content, "utf-8");
|
|
2602
|
+
}
|
|
2603
|
+
async function deleteItem(dir, id, extension) {
|
|
2604
|
+
const filePath = join5(dir, `${id}${extension}`);
|
|
2605
|
+
try {
|
|
2606
|
+
await unlink2(filePath);
|
|
2607
|
+
return true;
|
|
2608
|
+
} catch {
|
|
2609
|
+
return false;
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
function findById(id, items) {
|
|
2613
|
+
return items.find((i) => i.metadata.id === id);
|
|
2614
|
+
}
|
|
2615
|
+
async function getDefaultIds(bundleDir, defaultsDir, extension) {
|
|
2616
|
+
try {
|
|
2617
|
+
const files = (await readdir2(join5(bundleDir, defaultsDir))).filter((f) => f.endsWith(extension));
|
|
2618
|
+
return files.map((f) => f.replace(extension, ""));
|
|
2619
|
+
} catch {
|
|
2620
|
+
return [];
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
async function restoreDefault(itemDir, bundleDir, defaultsDir, id, extension) {
|
|
2624
|
+
const filename = `${id}${extension}`;
|
|
2625
|
+
const src = join5(bundleDir, defaultsDir, filename);
|
|
2626
|
+
if (await dirExists(src)) {
|
|
2627
|
+
await copyFile2(src, join5(itemDir, filename));
|
|
2628
|
+
return true;
|
|
2629
|
+
}
|
|
2630
|
+
return false;
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
// src/server/skills/registry.ts
|
|
2634
|
+
var DEFAULTS_DIR2 = "defaults";
|
|
2635
|
+
var DEFAULTS_DIR_ALT2 = "skill-defaults";
|
|
2636
|
+
var SKILL_EXTENSION = ".skill.md";
|
|
2637
|
+
var SKILL_SETTING_PREFIX = "skill.enabled.";
|
|
2638
|
+
function getSkillsDir(configDir) {
|
|
2639
|
+
return join6(configDir, "skills");
|
|
2640
|
+
}
|
|
2641
|
+
async function ensureDefaultSkills(configDir) {
|
|
2642
|
+
const bundleDir = getBundleDir();
|
|
2643
|
+
const skillsDir = getSkillsDir(configDir);
|
|
2644
|
+
await ensureDir(skillsDir);
|
|
2645
|
+
const ids = await getDefaultIds(bundleDir, DEFAULTS_DIR2, SKILL_EXTENSION);
|
|
2646
|
+
for (const id of ids) {
|
|
2647
|
+
if (!await restoreDefault(skillsDir, bundleDir, DEFAULTS_DIR2, id, SKILL_EXTENSION)) {
|
|
2648
|
+
await restoreDefault(skillsDir, bundleDir, DEFAULTS_DIR_ALT2, id, SKILL_EXTENSION);
|
|
2882
2649
|
}
|
|
2883
2650
|
}
|
|
2884
|
-
|
|
2651
|
+
}
|
|
2652
|
+
async function loadAllSkills(configDir) {
|
|
2653
|
+
return loadItems(getSkillsDir(configDir), SKILL_EXTENSION);
|
|
2885
2654
|
}
|
|
2886
2655
|
async function getEnabledSkills(configDir) {
|
|
2887
2656
|
const all = await loadAllSkills(configDir);
|
|
@@ -2900,51 +2669,21 @@ function setSkillEnabled(skillId, enabled) {
|
|
|
2900
2669
|
setSetting(`${SKILL_SETTING_PREFIX}${skillId}`, String(enabled));
|
|
2901
2670
|
}
|
|
2902
2671
|
async function getDefaultSkillIds() {
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
} catch {
|
|
2908
|
-
}
|
|
2909
|
-
}
|
|
2910
|
-
return [];
|
|
2672
|
+
const bundleDir = getBundleDir();
|
|
2673
|
+
const ids = await getDefaultIds(bundleDir, DEFAULTS_DIR2, SKILL_EXTENSION);
|
|
2674
|
+
if (ids.length) return ids;
|
|
2675
|
+
return getDefaultIds(bundleDir, DEFAULTS_DIR_ALT2, SKILL_EXTENSION);
|
|
2911
2676
|
}
|
|
2912
2677
|
async function restoreDefaultSkill(configDir, skillId) {
|
|
2913
|
-
const
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
if (await dirExists(sourcePath)) {
|
|
2917
|
-
const targetPath = join4(getSkillsDir(configDir), filename);
|
|
2918
|
-
await copyFile2(sourcePath, targetPath);
|
|
2919
|
-
return true;
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
return false;
|
|
2678
|
+
const bundleDir = getBundleDir();
|
|
2679
|
+
const skillsDir = getSkillsDir(configDir);
|
|
2680
|
+
return restoreDefault(skillsDir, bundleDir, DEFAULTS_DIR2, skillId, SKILL_EXTENSION) || restoreDefault(skillsDir, bundleDir, DEFAULTS_DIR_ALT2, skillId, SKILL_EXTENSION);
|
|
2923
2681
|
}
|
|
2924
2682
|
async function getModifiedDefaultSkillIds(configDir) {
|
|
2925
|
-
const
|
|
2926
|
-
const
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
const userPath = join4(getSkillsDir(configDir), filename);
|
|
2930
|
-
let bundledContent = null;
|
|
2931
|
-
for (const dir of [DEFAULTS_DIR2, DEFAULTS_DIR_ALT2]) {
|
|
2932
|
-
try {
|
|
2933
|
-
bundledContent = await readFile6(join4(dir, filename), "utf-8");
|
|
2934
|
-
break;
|
|
2935
|
-
} catch {
|
|
2936
|
-
}
|
|
2937
|
-
}
|
|
2938
|
-
if (!bundledContent) continue;
|
|
2939
|
-
try {
|
|
2940
|
-
const userContent = await readFile6(userPath, "utf-8");
|
|
2941
|
-
if (userContent !== bundledContent) {
|
|
2942
|
-
modified.push(id);
|
|
2943
|
-
}
|
|
2944
|
-
} catch {
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
return modified;
|
|
2683
|
+
const bundleDir = getBundleDir();
|
|
2684
|
+
const ids = await getDefaultSkillIds();
|
|
2685
|
+
const files = (await readdir3(join6(bundleDir, DEFAULTS_DIR2))).filter((f) => f.endsWith(SKILL_EXTENSION));
|
|
2686
|
+
return files.map((f) => f.replace(SKILL_EXTENSION, "")).filter((id) => ids.includes(id));
|
|
2948
2687
|
}
|
|
2949
2688
|
async function restoreAllDefaultSkills(configDir) {
|
|
2950
2689
|
const ids = await getDefaultSkillIds();
|
|
@@ -2955,34 +2694,34 @@ async function restoreAllDefaultSkills(configDir) {
|
|
|
2955
2694
|
return count;
|
|
2956
2695
|
}
|
|
2957
2696
|
function findSkillById(skillId, skills) {
|
|
2958
|
-
return
|
|
2697
|
+
return findById(skillId, skills);
|
|
2959
2698
|
}
|
|
2960
2699
|
async function skillExists(configDir, skillId) {
|
|
2961
|
-
|
|
2962
|
-
return dirExists(filePath);
|
|
2700
|
+
return dirExists(join6(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`));
|
|
2963
2701
|
}
|
|
2964
2702
|
async function saveSkill(configDir, skill) {
|
|
2965
|
-
|
|
2966
|
-
if (!await dirExists(skillsDir)) {
|
|
2967
|
-
await mkdir3(skillsDir, { recursive: true });
|
|
2968
|
-
}
|
|
2969
|
-
const filePath = join4(skillsDir, `${skill.metadata.id}${SKILL_EXTENSION}`);
|
|
2970
|
-
const content = matter2.stringify(skill.prompt, skill.metadata);
|
|
2971
|
-
await writeFile4(filePath, content, "utf-8");
|
|
2703
|
+
return saveItem(getSkillsDir(configDir), skill.metadata.id, SKILL_EXTENSION, skill);
|
|
2972
2704
|
}
|
|
2973
2705
|
async function deleteSkill(configDir, skillId) {
|
|
2974
|
-
const
|
|
2975
|
-
|
|
2976
|
-
await unlink2(filePath);
|
|
2706
|
+
const deleted = await deleteItem(getSkillsDir(configDir), skillId, SKILL_EXTENSION);
|
|
2707
|
+
if (deleted) {
|
|
2977
2708
|
const { deleteSetting } = await import("./settings-NCLN4JDZ.js");
|
|
2978
2709
|
deleteSetting(`${SKILL_SETTING_PREFIX}${skillId}`);
|
|
2979
|
-
return true;
|
|
2980
|
-
} catch {
|
|
2981
|
-
return false;
|
|
2982
2710
|
}
|
|
2711
|
+
return deleted;
|
|
2983
2712
|
}
|
|
2984
2713
|
|
|
2985
2714
|
// src/server/chat/agent-loop.ts
|
|
2715
|
+
import stripAnsi from "strip-ansi";
|
|
2716
|
+
function emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, promptContext, eventStore) {
|
|
2717
|
+
const stats = turnMetrics.buildStats(statsIdentity, mode);
|
|
2718
|
+
eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
|
|
2719
|
+
stats,
|
|
2720
|
+
partial: true,
|
|
2721
|
+
promptContext
|
|
2722
|
+
}));
|
|
2723
|
+
eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats));
|
|
2724
|
+
}
|
|
2986
2725
|
function getCurrentWindowMessageOptions(sessionId) {
|
|
2987
2726
|
const contextWindowId = getCurrentContextWindowId(sessionId);
|
|
2988
2727
|
return contextWindowId ? { contextWindowId } : void 0;
|
|
@@ -3048,7 +2787,7 @@ async function executeToolBatch(assistantMsgId, toolCalls, ctx) {
|
|
|
3048
2787
|
type: "chat.ask_user",
|
|
3049
2788
|
data: { callId: error.callId, question: error.question }
|
|
3050
2789
|
});
|
|
3051
|
-
const { awaitAnswer } = await import("./ask-
|
|
2790
|
+
const { awaitAnswer } = await import("./ask-3RK5YJZE.js");
|
|
3052
2791
|
const answerPromise = awaitAnswer(error.callId);
|
|
3053
2792
|
if (!answerPromise) {
|
|
3054
2793
|
throw new Error(`No pending question found for callId: ${error.callId}`);
|
|
@@ -3073,9 +2812,9 @@ async function executeToolBatch(assistantMsgId, toolCalls, ctx) {
|
|
|
3073
2812
|
}
|
|
3074
2813
|
toolMessages.push({
|
|
3075
2814
|
role: "tool",
|
|
3076
|
-
content: toolResult.success ? toolResult.output ?? "Success" : toolResult.output ? `${toolResult.output}
|
|
2815
|
+
content: stripAnsi(toolResult.success ? toolResult.output ?? "Success" : toolResult.output ? `${toolResult.output}
|
|
3077
2816
|
|
|
3078
|
-
Error: ${toolResult.error}` : `Error: ${toolResult.error}
|
|
2817
|
+
Error: ${toolResult.error}` : `Error: ${toolResult.error}`),
|
|
3079
2818
|
source: "history",
|
|
3080
2819
|
toolCallId: toolCall.id
|
|
3081
2820
|
});
|
|
@@ -3213,13 +2952,7 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
|
|
|
3213
2952
|
}
|
|
3214
2953
|
}
|
|
3215
2954
|
if (result.aborted) {
|
|
3216
|
-
|
|
3217
|
-
eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
|
|
3218
|
-
stats: stats2,
|
|
3219
|
-
partial: true,
|
|
3220
|
-
promptContext: assembledRequest.promptContext
|
|
3221
|
-
}));
|
|
3222
|
-
eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats2));
|
|
2955
|
+
emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, assembledRequest.promptContext, eventStore);
|
|
3223
2956
|
throw new Error("Aborted");
|
|
3224
2957
|
}
|
|
3225
2958
|
turnMetrics.addLLMCall(result.timing, result.usage.promptTokens, result.usage.completionTokens);
|
|
@@ -3254,25 +2987,13 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
|
|
|
3254
2987
|
}
|
|
3255
2988
|
} catch (error) {
|
|
3256
2989
|
if (error instanceof Error && error.message === "Aborted") {
|
|
3257
|
-
|
|
3258
|
-
eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
|
|
3259
|
-
stats: stats2,
|
|
3260
|
-
partial: true,
|
|
3261
|
-
promptContext: assembledRequest.promptContext
|
|
3262
|
-
}));
|
|
3263
|
-
eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats2));
|
|
2990
|
+
emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, assembledRequest.promptContext, eventStore);
|
|
3264
2991
|
throw error;
|
|
3265
2992
|
}
|
|
3266
2993
|
throw error;
|
|
3267
2994
|
}
|
|
3268
2995
|
if (signal?.aborted) {
|
|
3269
|
-
|
|
3270
|
-
eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
|
|
3271
|
-
stats: stats2,
|
|
3272
|
-
partial: true,
|
|
3273
|
-
promptContext: assembledRequest.promptContext
|
|
3274
|
-
}));
|
|
3275
|
-
eventStore.append(sessionId, createChatDoneEvent(assistantMsgId, "stopped", stats2));
|
|
2996
|
+
emitPartialDoneEvents(sessionId, assistantMsgId, statsIdentity, mode, turnMetrics, assembledRequest.promptContext, eventStore);
|
|
3276
2997
|
throw new Error("Aborted");
|
|
3277
2998
|
}
|
|
3278
2999
|
const asapMessages = sessionManager.drainAsapMessages(sessionId);
|
|
@@ -3310,12 +3031,32 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
|
|
|
3310
3031
|
};
|
|
3311
3032
|
}
|
|
3312
3033
|
|
|
3034
|
+
// src/server/context/nudge-helpers.ts
|
|
3035
|
+
function appendNudgeMessage(eventStore, sessionId, content, currentWindowMessageOptions, options) {
|
|
3036
|
+
const msgId = crypto.randomUUID();
|
|
3037
|
+
eventStore.append(sessionId, createMessageStartEvent(msgId, "user", content, {
|
|
3038
|
+
...currentWindowMessageOptions ?? {},
|
|
3039
|
+
isSystemGenerated: true,
|
|
3040
|
+
messageKind: "correction",
|
|
3041
|
+
subAgentId: options.subAgentId,
|
|
3042
|
+
subAgentType: options.subAgentType
|
|
3043
|
+
}));
|
|
3044
|
+
eventStore.append(sessionId, { type: "message.done", data: { messageId: msgId } });
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3313
3047
|
// src/server/sub-agents/manager.ts
|
|
3314
3048
|
var RETURN_VALUE_INSTRUCTION = `
|
|
3315
3049
|
|
|
3316
3050
|
## RETURN VALUE
|
|
3317
3051
|
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.`;
|
|
3318
3052
|
var RETURN_VALUE_NUDGE = "You must call return_value with a summary of your findings before finishing. Call return_value now.";
|
|
3053
|
+
function buildSubAgentResult(returnValueContent, returnValueResult, subAgentType, failed, remaining) {
|
|
3054
|
+
return {
|
|
3055
|
+
content: returnValueContent ?? "",
|
|
3056
|
+
...returnValueResult !== null && returnValueResult !== void 0 ? { result: returnValueResult } : {},
|
|
3057
|
+
...subAgentType === "verifier" ? { allPassed: failed.length === 0 && remaining.length === 0, failed } : {}
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
3319
3060
|
async function resolveAgentDef(agentId) {
|
|
3320
3061
|
const allAgents = await loadAllAgentsDefault();
|
|
3321
3062
|
const def = findAgentById(agentId, allAgents);
|
|
@@ -3485,19 +3226,11 @@ async function executeSubAgent(options) {
|
|
|
3485
3226
|
if (consecutiveEmptyStops < nudgeConfig.maxConsecutiveNudges) {
|
|
3486
3227
|
consecutiveEmptyStops += 1;
|
|
3487
3228
|
const nudgeContent = nudgeConfig.buildNudgeContent(criteriaAwaiting);
|
|
3488
|
-
const nudgeMsgId = crypto.randomUUID();
|
|
3489
3229
|
eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
|
|
3490
3230
|
segments: result.segments,
|
|
3491
3231
|
promptContext
|
|
3492
3232
|
}));
|
|
3493
|
-
eventStore
|
|
3494
|
-
...currentWindowMessageOptions ?? {},
|
|
3495
|
-
isSystemGenerated: true,
|
|
3496
|
-
messageKind: "correction",
|
|
3497
|
-
subAgentId,
|
|
3498
|
-
subAgentType
|
|
3499
|
-
}));
|
|
3500
|
-
eventStore.append(sessionId, { type: "message.done", data: { messageId: nudgeMsgId } });
|
|
3233
|
+
appendNudgeMessage(eventStore, sessionId, nudgeContent, currentWindowMessageOptions, { subAgentId, subAgentType });
|
|
3501
3234
|
customMessages = [...customMessages, { role: "user", content: nudgeContent, source: "runtime" }];
|
|
3502
3235
|
continue;
|
|
3503
3236
|
}
|
|
@@ -3514,19 +3247,11 @@ async function executeSubAgent(options) {
|
|
|
3514
3247
|
}
|
|
3515
3248
|
if (!returnValueContent && !returnValueNudged) {
|
|
3516
3249
|
returnValueNudged = true;
|
|
3517
|
-
const nudgeMsgId = crypto.randomUUID();
|
|
3518
3250
|
eventStore.append(sessionId, createMessageDoneEvent(assistantMsgId, {
|
|
3519
3251
|
segments: result.segments,
|
|
3520
3252
|
promptContext
|
|
3521
3253
|
}));
|
|
3522
|
-
eventStore
|
|
3523
|
-
...currentWindowMessageOptions ?? {},
|
|
3524
|
-
isSystemGenerated: true,
|
|
3525
|
-
messageKind: "correction",
|
|
3526
|
-
subAgentId,
|
|
3527
|
-
subAgentType
|
|
3528
|
-
}));
|
|
3529
|
-
eventStore.append(sessionId, { type: "message.done", data: { messageId: nudgeMsgId } });
|
|
3254
|
+
appendNudgeMessage(eventStore, sessionId, RETURN_VALUE_NUDGE, currentWindowMessageOptions, { subAgentId, subAgentType });
|
|
3530
3255
|
customMessages = [...customMessages, { role: "user", content: RETURN_VALUE_NUDGE, source: "runtime" }];
|
|
3531
3256
|
continue;
|
|
3532
3257
|
}
|
|
@@ -3582,21 +3307,9 @@ async function executeSubAgent(options) {
|
|
|
3582
3307
|
subAgentId,
|
|
3583
3308
|
resultLength: finalContent.length
|
|
3584
3309
|
});
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
const failed = session.criteria.filter((c) => c.status.type === "failed").map((c) => ({
|
|
3589
|
-
id: c.id,
|
|
3590
|
-
reason: c.status.type === "failed" ? c.status.reason : "unknown"
|
|
3591
|
-
}));
|
|
3592
|
-
return {
|
|
3593
|
-
content: returnValueContent ?? finalContent,
|
|
3594
|
-
...returnValueResult ? { result: returnValueResult } : {},
|
|
3595
|
-
allPassed: failed.length === 0 && remaining.length === 0,
|
|
3596
|
-
failed
|
|
3597
|
-
};
|
|
3598
|
-
}
|
|
3599
|
-
return { content: returnValueContent ?? finalContent, ...returnValueResult ? { result: returnValueResult } : { result: "success" } };
|
|
3310
|
+
const failed = session.criteria.filter((c) => c.status.type === "failed").map((c) => ({ id: c.id, reason: c.status.reason ?? "unknown" }));
|
|
3311
|
+
const remaining = nudgeConfig?.getCriteriaAwaiting(session.criteria) ?? [];
|
|
3312
|
+
return buildSubAgentResult(returnValueContent ?? void 0, returnValueResult ?? (subAgentType !== "verifier" ? "success" : void 0), subAgentType, failed, remaining);
|
|
3600
3313
|
}
|
|
3601
3314
|
|
|
3602
3315
|
// src/server/tools/sub-agent.ts
|
|
@@ -3664,7 +3377,7 @@ var callSubAgentTool = {
|
|
|
3664
3377
|
};
|
|
3665
3378
|
}
|
|
3666
3379
|
try {
|
|
3667
|
-
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-
|
|
3380
|
+
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-VOWNOP6V.js");
|
|
3668
3381
|
const toolRegistry = getToolRegistryForAgent2(agentDef);
|
|
3669
3382
|
const turnMetrics = new TurnMetrics();
|
|
3670
3383
|
const result = await executeSubAgent({
|
|
@@ -3816,7 +3529,7 @@ function convertHTMLToMarkdown(html) {
|
|
|
3816
3529
|
return turndown.turndown(html);
|
|
3817
3530
|
}
|
|
3818
3531
|
function stripHTMLTags(html) {
|
|
3819
|
-
|
|
3532
|
+
const text = html.replace(/<(script|style|noscript|iframe|object|embed)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<[^>]+>/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
3820
3533
|
return text;
|
|
3821
3534
|
}
|
|
3822
3535
|
var webFetchTool = createTool(
|
|
@@ -3918,9 +3631,9 @@ var webFetchTool = createTool(
|
|
|
3918
3631
|
);
|
|
3919
3632
|
|
|
3920
3633
|
// src/server/dev-server/manager.ts
|
|
3921
|
-
import { spawn as
|
|
3922
|
-
import { readFile as
|
|
3923
|
-
import { resolve as resolve5, join as
|
|
3634
|
+
import { spawn as spawn4 } from "child_process";
|
|
3635
|
+
import { readFile as readFile8, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
3636
|
+
import { resolve as resolve5, join as join7 } from "path";
|
|
3924
3637
|
var MAX_LOG_LINES = 2e3;
|
|
3925
3638
|
var MAX_LOG_BYTES = 1e5;
|
|
3926
3639
|
var CONFIG_PATH = ".openfox/dev.json";
|
|
@@ -3976,8 +3689,8 @@ var DevServerManager = class {
|
|
|
3976
3689
|
}
|
|
3977
3690
|
async loadConfig(workdir) {
|
|
3978
3691
|
try {
|
|
3979
|
-
const configPath =
|
|
3980
|
-
const raw = await
|
|
3692
|
+
const configPath = join7(this.resolveWorkdir(workdir), CONFIG_PATH);
|
|
3693
|
+
const raw = await readFile8(configPath, "utf-8");
|
|
3981
3694
|
const parsed = JSON.parse(raw);
|
|
3982
3695
|
if (!parsed.command || !parsed.url) return null;
|
|
3983
3696
|
return {
|
|
@@ -3991,9 +3704,9 @@ var DevServerManager = class {
|
|
|
3991
3704
|
}
|
|
3992
3705
|
async saveConfig(workdir, config) {
|
|
3993
3706
|
const resolved = this.resolveWorkdir(workdir);
|
|
3994
|
-
const dirPath =
|
|
3707
|
+
const dirPath = join7(resolved, ".openfox");
|
|
3995
3708
|
await mkdir4(dirPath, { recursive: true });
|
|
3996
|
-
const configPath =
|
|
3709
|
+
const configPath = join7(resolved, CONFIG_PATH);
|
|
3997
3710
|
await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3998
3711
|
}
|
|
3999
3712
|
async start(workdir) {
|
|
@@ -4012,7 +3725,7 @@ var DevServerManager = class {
|
|
|
4012
3725
|
instance.exited = false;
|
|
4013
3726
|
const resolved = this.resolveWorkdir(workdir);
|
|
4014
3727
|
const shell = getPlatformShell();
|
|
4015
|
-
const proc =
|
|
3728
|
+
const proc = spawn4(shell.command, [...shell.args, config.command], {
|
|
4016
3729
|
cwd: resolved,
|
|
4017
3730
|
env: { ...process.env, FORCE_COLOR: "1" },
|
|
4018
3731
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4584,14 +4297,7 @@ function getCurrentWindowMessageOptions3(sessionId) {
|
|
|
4584
4297
|
return contextWindowId ? { contextWindowId } : void 0;
|
|
4585
4298
|
}
|
|
4586
4299
|
function toRequestContextMessages2(messages) {
|
|
4587
|
-
return messages
|
|
4588
|
-
role: message.role,
|
|
4589
|
-
content: message.content,
|
|
4590
|
-
source: "history",
|
|
4591
|
-
...message.toolCalls ? { toolCalls: message.toolCalls } : {},
|
|
4592
|
-
...message.toolCallId ? { toolCallId: message.toolCallId } : {},
|
|
4593
|
-
...message.attachments ? { attachments: message.attachments } : {}
|
|
4594
|
-
}));
|
|
4300
|
+
return minimalMessagesToRequestContextMessages(messages, "history");
|
|
4595
4301
|
}
|
|
4596
4302
|
async function maybeAutoCompactContext(options) {
|
|
4597
4303
|
const config = getRuntimeConfig();
|
|
@@ -4746,6 +4452,9 @@ export {
|
|
|
4746
4452
|
PathAccessDeniedError,
|
|
4747
4453
|
providePathConfirmation,
|
|
4748
4454
|
cancelPathConfirmationsForSession,
|
|
4455
|
+
checkAborted,
|
|
4456
|
+
spawnShellProcess,
|
|
4457
|
+
findModifiedDefaultFiles,
|
|
4749
4458
|
ensureDefaultAgents,
|
|
4750
4459
|
loadAllAgents,
|
|
4751
4460
|
loadAllAgentsDefault,
|
|
@@ -4769,6 +4478,15 @@ export {
|
|
|
4769
4478
|
createChatDoneEvent,
|
|
4770
4479
|
getAllInstructions,
|
|
4771
4480
|
assembleAgentRequest,
|
|
4481
|
+
getBundleDir,
|
|
4482
|
+
dirExists,
|
|
4483
|
+
ensureDir,
|
|
4484
|
+
loadItems,
|
|
4485
|
+
saveItem,
|
|
4486
|
+
deleteItem,
|
|
4487
|
+
findById,
|
|
4488
|
+
getDefaultIds,
|
|
4489
|
+
restoreDefault,
|
|
4772
4490
|
ensureDefaultSkills,
|
|
4773
4491
|
loadAllSkills,
|
|
4774
4492
|
getEnabledSkillMetadata,
|
|
@@ -4797,4 +4515,4 @@ export {
|
|
|
4797
4515
|
getToolRegistryForAgent,
|
|
4798
4516
|
createToolRegistry
|
|
4799
4517
|
};
|
|
4800
|
-
//# sourceMappingURL=chunk-
|
|
4518
|
+
//# sourceMappingURL=chunk-3WHZ47PY.js.map
|