chainlesschain 0.45.12 → 0.45.20
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/package.json +1 -1
- package/src/assets/web-panel/assets/{AppLayout-BfLjLMsm.js → AppLayout-B00RARl2.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DP7PO9Li.js → Chat-DXtvKoM0.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-DyQF-7R1.js → Cron-BJ4ODHOy.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-3iIpp3zd.js +3 -0
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Logs-BOii-AoO.js → Logs-CSeKZEG_.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DmiJtJYr.js → McpTools-BYQAK11r.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-CDRMMobU.js → Memory-gkUAPyuZ.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CVhqqoS1.js → Notes-bjNrQgAo.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Dkt7021l.js → Providers-Dbf57Tbv.js} +1 -1
- package/src/assets/web-panel/assets/{Services-DUDL_UGb.js → Services-CS0oMdxh.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DXXELJc3.js → Skills-B2fgruv8.js} +1 -1
- package/src/assets/web-panel/assets/Tasks-BJjN_YEm.css +1 -0
- package/src/assets/web-panel/assets/Tasks-qULws8pc.js +1 -0
- package/src/assets/web-panel/assets/chat-DnH09sSR.js +1 -0
- package/src/assets/web-panel/assets/{index-vW799KpE.js → index-CF2CqPYX.js} +2 -2
- package/src/assets/web-panel/assets/ws-DjelKkD6.js +1 -0
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +7 -8
- package/src/commands/chat.js +9 -11
- package/src/commands/serve.js +11 -106
- package/src/commands/session.js +101 -0
- package/src/commands/ui.js +10 -151
- package/src/gateways/repl/agent-repl.js +1 -0
- package/src/gateways/repl/chat-repl.js +1 -0
- package/src/gateways/ui/web-ui-server.js +1 -0
- package/src/gateways/ws/action-protocol.js +83 -0
- package/src/gateways/ws/message-dispatcher.js +73 -0
- package/src/gateways/ws/session-protocol.js +396 -0
- package/src/gateways/ws/task-protocol.js +55 -0
- package/src/gateways/ws/worktree-protocol.js +315 -0
- package/src/gateways/ws/ws-server.js +4 -0
- package/src/gateways/ws/ws-session-gateway.js +1 -0
- package/src/harness/background-task-manager.js +506 -0
- package/src/harness/background-task-worker.js +48 -0
- package/src/harness/compression-telemetry.js +214 -0
- package/src/harness/feature-flags.js +157 -0
- package/src/harness/jsonl-session-store.js +452 -0
- package/src/harness/prompt-compressor.js +416 -0
- package/src/harness/worktree-isolator.js +845 -0
- package/src/lib/agent-core.js +246 -45
- package/src/lib/background-task-manager.js +1 -305
- package/src/lib/background-task-worker.js +1 -50
- package/src/lib/compression-telemetry.js +5 -0
- package/src/lib/feature-flags.js +7 -182
- package/src/lib/interaction-adapter.js +32 -6
- package/src/lib/jsonl-session-store.js +21 -237
- package/src/lib/prompt-compressor.js +10 -481
- package/src/lib/sub-agent-context.js +21 -1
- package/src/lib/worktree-isolator.js +13 -231
- package/src/lib/ws-agent-handler.js +1 -0
- package/src/lib/ws-server.js +138 -387
- package/src/lib/ws-session-manager.js +82 -1
- package/src/repl/agent-repl.js +11 -0
- package/src/runtime/agent-runtime.js +417 -0
- package/src/runtime/contracts/agent-turn.js +11 -0
- package/src/runtime/contracts/session-record.js +31 -0
- package/src/runtime/contracts/task-record.js +18 -0
- package/src/runtime/contracts/telemetry-record.js +23 -0
- package/src/runtime/contracts/worktree-record.js +14 -0
- package/src/runtime/index.js +13 -0
- package/src/runtime/policies/agent-policy.js +45 -0
- package/src/runtime/runtime-context.js +14 -0
- package/src/runtime/runtime-events.js +37 -0
- package/src/runtime/runtime-factory.js +50 -0
- package/src/tools/index.js +22 -0
- package/src/tools/legacy-agent-tools.js +171 -0
- package/src/tools/registry.js +141 -0
- package/src/tools/tool-context.js +28 -0
- package/src/tools/tool-permissions.js +28 -0
- package/src/tools/tool-telemetry.js +39 -0
- package/src/assets/web-panel/assets/Dashboard-BGGdnr6t.js +0 -3
- package/src/assets/web-panel/assets/Dashboard-CRFnDUFh.css +0 -1
- package/src/assets/web-panel/assets/Tasks-BwZ63-mq.js +0 -1
- package/src/assets/web-panel/assets/Tasks-Cr_XXNyQ.css +0 -1
- package/src/assets/web-panel/assets/chat-C_hu-qNs.js +0 -1
- package/src/assets/web-panel/assets/ws-DwluTqT5.js +0 -1
package/src/lib/agent-core.js
CHANGED
|
@@ -24,6 +24,13 @@ import { executeHooks, HookEvents } from "./hook-manager.js";
|
|
|
24
24
|
import { detectPython } from "./cli-anything-bridge.js";
|
|
25
25
|
import { findProjectRoot, loadProjectConfig } from "./project-detector.js";
|
|
26
26
|
import { SubAgentContext } from "./sub-agent-context.js";
|
|
27
|
+
import {
|
|
28
|
+
createLegacyAgentToolRegistry,
|
|
29
|
+
getRuntimeToolDescriptor,
|
|
30
|
+
} from "../tools/legacy-agent-tools.js";
|
|
31
|
+
import { createToolContext } from "../tools/tool-context.js";
|
|
32
|
+
import { createToolTelemetryRecord } from "../tools/tool-telemetry.js";
|
|
33
|
+
import { DEFAULT_TOOL_DESCRIPTORS } from "../tools/registry.js";
|
|
27
34
|
|
|
28
35
|
// ─── Tool definitions ────────────────────────────────────────────────────
|
|
29
36
|
|
|
@@ -249,6 +256,60 @@ export const AGENT_TOOLS = [
|
|
|
249
256
|
},
|
|
250
257
|
];
|
|
251
258
|
|
|
259
|
+
const STATIC_AGENT_TOOL_NAMES = new Set(
|
|
260
|
+
AGENT_TOOLS.map((tool) => tool.function.name),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
export const AGENT_TOOL_REGISTRY = createLegacyAgentToolRegistry(AGENT_TOOLS);
|
|
264
|
+
const DEFAULT_TOOL_DESCRIPTOR_MAP = new Map(
|
|
265
|
+
DEFAULT_TOOL_DESCRIPTORS.map((descriptor) => [descriptor.name, descriptor]),
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
function mergeToolDefinitions(baseTools = [], extraTools = []) {
|
|
269
|
+
const merged = new Map();
|
|
270
|
+
|
|
271
|
+
for (const tool of [...baseTools, ...extraTools]) {
|
|
272
|
+
const name = tool?.function?.name;
|
|
273
|
+
if (!name) continue;
|
|
274
|
+
merged.set(name, tool);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return Array.from(merged.values());
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function getAgentToolDefinitions({
|
|
281
|
+
names = null,
|
|
282
|
+
disabledTools = [],
|
|
283
|
+
extraTools = [],
|
|
284
|
+
} = {}) {
|
|
285
|
+
const allowedNames =
|
|
286
|
+
Array.isArray(names) && names.length > 0 ? new Set(names) : null;
|
|
287
|
+
const disabledNames = new Set(
|
|
288
|
+
Array.isArray(disabledTools) ? disabledTools : [],
|
|
289
|
+
);
|
|
290
|
+
const allTools = mergeToolDefinitions(
|
|
291
|
+
AGENT_TOOLS,
|
|
292
|
+
Array.isArray(extraTools) ? extraTools : [],
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
return allTools.filter((tool) => {
|
|
296
|
+
const name = tool?.function?.name;
|
|
297
|
+
if (!name) return false;
|
|
298
|
+
if (allowedNames && !allowedNames.has(name)) return false;
|
|
299
|
+
if (disabledNames.has(name)) return false;
|
|
300
|
+
return true;
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function getAgentToolDescriptors(options = {}) {
|
|
305
|
+
const allowedNames = new Set(
|
|
306
|
+
getAgentToolDefinitions(options).map((tool) => tool.function.name),
|
|
307
|
+
);
|
|
308
|
+
return AGENT_TOOL_REGISTRY.list({ enabledOnly: options.enabledOnly }).filter(
|
|
309
|
+
(descriptor) => allowedNames.has(descriptor.name),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
252
313
|
// ─── Shared skill loader ──────────────────────────────────────────────────
|
|
253
314
|
|
|
254
315
|
const _defaultSkillLoader = new CLISkillLoader();
|
|
@@ -514,6 +575,12 @@ export async function executeTool(name, args, context = {}) {
|
|
|
514
575
|
const hookDb = context.hookDb || null;
|
|
515
576
|
const skillLoader = context.skillLoader || _defaultSkillLoader;
|
|
516
577
|
const cwd = context.cwd || process.cwd();
|
|
578
|
+
const runtimeDescriptor = getRuntimeToolDescriptor(name);
|
|
579
|
+
const toolContext = createToolContext({
|
|
580
|
+
toolName: runtimeDescriptor?.name || name,
|
|
581
|
+
cwd,
|
|
582
|
+
metadata: { descriptor: runtimeDescriptor },
|
|
583
|
+
});
|
|
517
584
|
|
|
518
585
|
// Persona toolsDisabled guard
|
|
519
586
|
const persona = _loadProjectPersona(cwd);
|
|
@@ -523,9 +590,35 @@ export async function executeTool(name, args, context = {}) {
|
|
|
523
590
|
};
|
|
524
591
|
}
|
|
525
592
|
|
|
593
|
+
const toolPolicies =
|
|
594
|
+
context.hostManagedToolPolicy?.tools ||
|
|
595
|
+
context.hostManagedToolPolicy?.toolPolicies ||
|
|
596
|
+
null;
|
|
597
|
+
const hostToolPolicy =
|
|
598
|
+
toolPolicies && typeof toolPolicies === "object"
|
|
599
|
+
? toolPolicies[name]
|
|
600
|
+
: null;
|
|
601
|
+
const isExternalHostTool =
|
|
602
|
+
hostToolPolicy && !STATIC_AGENT_TOOL_NAMES.has(name);
|
|
603
|
+
if (hostToolPolicy && hostToolPolicy.allowed === false) {
|
|
604
|
+
return {
|
|
605
|
+
error: `[Host Policy] Tool "${name}" is blocked by desktop host policy. ${hostToolPolicy.reason || "Desktop approval has not been synchronized yet."}`,
|
|
606
|
+
policy: {
|
|
607
|
+
decision: hostToolPolicy.decision || "blocked",
|
|
608
|
+
requiresPlanApproval: hostToolPolicy.requiresPlanApproval === true,
|
|
609
|
+
requiresConfirmation: hostToolPolicy.requiresConfirmation === true,
|
|
610
|
+
riskLevel: hostToolPolicy.riskLevel || null,
|
|
611
|
+
},
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
526
615
|
// Plan mode: check if tool is allowed
|
|
527
616
|
const planManager = getPlanModeManager();
|
|
528
|
-
if (
|
|
617
|
+
if (
|
|
618
|
+
planManager.isActive() &&
|
|
619
|
+
!planManager.isToolAllowed(name) &&
|
|
620
|
+
!(isExternalHostTool && hostToolPolicy?.allowed === true)
|
|
621
|
+
) {
|
|
529
622
|
planManager.addPlanItem({
|
|
530
623
|
title: `${name}: ${formatToolArgs(name, args)}`,
|
|
531
624
|
tool: name,
|
|
@@ -549,18 +642,23 @@ export async function executeTool(name, args, context = {}) {
|
|
|
549
642
|
tool: name,
|
|
550
643
|
args,
|
|
551
644
|
timestamp: new Date().toISOString(),
|
|
645
|
+
descriptor: runtimeDescriptor,
|
|
646
|
+
context: toolContext,
|
|
552
647
|
});
|
|
553
648
|
} catch (_err) {
|
|
554
649
|
// Hook failure should not block tool execution
|
|
555
650
|
}
|
|
556
651
|
}
|
|
557
652
|
|
|
653
|
+
const startTime = Date.now();
|
|
558
654
|
let toolResult;
|
|
559
655
|
try {
|
|
560
656
|
toolResult = await executeToolInner(name, args, {
|
|
561
657
|
skillLoader,
|
|
562
658
|
cwd,
|
|
563
659
|
parentMessages: context.parentMessages,
|
|
660
|
+
interaction: context.interaction,
|
|
661
|
+
hostManagedToolPolicy: context.hostManagedToolPolicy || null,
|
|
564
662
|
});
|
|
565
663
|
} catch (err) {
|
|
566
664
|
if (hookDb) {
|
|
@@ -577,6 +675,19 @@ export async function executeTool(name, args, context = {}) {
|
|
|
577
675
|
throw err;
|
|
578
676
|
}
|
|
579
677
|
|
|
678
|
+
const durationMs = Date.now() - startTime;
|
|
679
|
+
const status = toolResult?.error ? "error" : "completed";
|
|
680
|
+
const telemetryRecord = createToolTelemetryRecord({
|
|
681
|
+
descriptor: runtimeDescriptor,
|
|
682
|
+
status,
|
|
683
|
+
durationMs,
|
|
684
|
+
sessionId: context.sessionId || null,
|
|
685
|
+
metadata: { args },
|
|
686
|
+
});
|
|
687
|
+
if (toolResult && typeof toolResult === "object") {
|
|
688
|
+
toolResult.toolTelemetryRecord = telemetryRecord;
|
|
689
|
+
}
|
|
690
|
+
|
|
580
691
|
// PostToolUse hook
|
|
581
692
|
if (hookDb) {
|
|
582
693
|
try {
|
|
@@ -587,6 +698,8 @@ export async function executeTool(name, args, context = {}) {
|
|
|
587
698
|
typeof toolResult === "object"
|
|
588
699
|
? JSON.stringify(toolResult).substring(0, 500)
|
|
589
700
|
: String(toolResult).substring(0, 500),
|
|
701
|
+
descriptor: runtimeDescriptor,
|
|
702
|
+
context: toolContext,
|
|
590
703
|
});
|
|
591
704
|
} catch (_err) {
|
|
592
705
|
// Non-critical
|
|
@@ -602,22 +715,59 @@ export async function executeTool(name, args, context = {}) {
|
|
|
602
715
|
async function executeToolInner(
|
|
603
716
|
name,
|
|
604
717
|
args,
|
|
605
|
-
{ skillLoader, cwd, parentMessages },
|
|
718
|
+
{ skillLoader, cwd, parentMessages, interaction, hostManagedToolPolicy },
|
|
606
719
|
) {
|
|
720
|
+
const runtimeDescriptor = getRuntimeToolDescriptor(name);
|
|
721
|
+
const hostToolPolicies =
|
|
722
|
+
hostManagedToolPolicy?.tools || hostManagedToolPolicy?.toolPolicies || null;
|
|
723
|
+
const hostToolPolicy =
|
|
724
|
+
hostToolPolicies && typeof hostToolPolicies === "object"
|
|
725
|
+
? hostToolPolicies[name]
|
|
726
|
+
: null;
|
|
727
|
+
const hostToolDefinition = Array.isArray(
|
|
728
|
+
hostManagedToolPolicy?.toolDefinitions,
|
|
729
|
+
)
|
|
730
|
+
? hostManagedToolPolicy.toolDefinitions.find(
|
|
731
|
+
(tool) => tool?.function?.name === name,
|
|
732
|
+
) || null
|
|
733
|
+
: null;
|
|
734
|
+
const buildPayload = (descriptor) =>
|
|
735
|
+
descriptor
|
|
736
|
+
? {
|
|
737
|
+
name: descriptor.name,
|
|
738
|
+
kind: descriptor.kind || descriptor.category || descriptor.source,
|
|
739
|
+
category: descriptor.category,
|
|
740
|
+
}
|
|
741
|
+
: null;
|
|
742
|
+
const descriptorPayload = buildPayload(runtimeDescriptor);
|
|
743
|
+
const attachDescriptor = (payload, overrideDescriptor = null) => {
|
|
744
|
+
const descriptor = buildPayload(overrideDescriptor || runtimeDescriptor);
|
|
745
|
+
return descriptor ? { ...payload, toolDescriptor: descriptor } : payload;
|
|
746
|
+
};
|
|
747
|
+
const resolveShellDescriptor = (command) => {
|
|
748
|
+
const trimmed = (command || "").trim();
|
|
749
|
+
if (!trimmed) return null;
|
|
750
|
+
const parts = trimmed.split(/\s+/);
|
|
751
|
+
if (parts[0] === "git") return DEFAULT_TOOL_DESCRIPTOR_MAP.get("git");
|
|
752
|
+
if (parts[0] === "mcp" || parts.includes("mcp")) {
|
|
753
|
+
return DEFAULT_TOOL_DESCRIPTOR_MAP.get("mcp");
|
|
754
|
+
}
|
|
755
|
+
return DEFAULT_TOOL_DESCRIPTOR_MAP.get("shell");
|
|
756
|
+
};
|
|
607
757
|
switch (name) {
|
|
608
758
|
case "read_file": {
|
|
609
759
|
const filePath = path.resolve(cwd, args.path);
|
|
610
760
|
if (!fs.existsSync(filePath)) {
|
|
611
|
-
return { error: `File not found: ${filePath}` };
|
|
761
|
+
return attachDescriptor({ error: `File not found: ${filePath}` });
|
|
612
762
|
}
|
|
613
763
|
const content = fs.readFileSync(filePath, "utf8");
|
|
614
764
|
if (content.length > 50000) {
|
|
615
|
-
return {
|
|
765
|
+
return attachDescriptor({
|
|
616
766
|
content: content.substring(0, 50000) + "\n...(truncated)",
|
|
617
767
|
size: content.length,
|
|
618
|
-
};
|
|
768
|
+
});
|
|
619
769
|
}
|
|
620
|
-
return { content };
|
|
770
|
+
return attachDescriptor({ content });
|
|
621
771
|
}
|
|
622
772
|
|
|
623
773
|
case "write_file": {
|
|
@@ -627,21 +777,25 @@ async function executeToolInner(
|
|
|
627
777
|
fs.mkdirSync(dir, { recursive: true });
|
|
628
778
|
}
|
|
629
779
|
fs.writeFileSync(filePath, args.content, "utf8");
|
|
630
|
-
return {
|
|
780
|
+
return attachDescriptor({
|
|
781
|
+
success: true,
|
|
782
|
+
path: filePath,
|
|
783
|
+
size: args.content.length,
|
|
784
|
+
});
|
|
631
785
|
}
|
|
632
786
|
|
|
633
787
|
case "edit_file": {
|
|
634
788
|
const filePath = path.resolve(cwd, args.path);
|
|
635
789
|
if (!fs.existsSync(filePath)) {
|
|
636
|
-
return { error: `File not found: ${filePath}` };
|
|
790
|
+
return attachDescriptor({ error: `File not found: ${filePath}` });
|
|
637
791
|
}
|
|
638
792
|
const content = fs.readFileSync(filePath, "utf8");
|
|
639
793
|
if (!content.includes(args.old_string)) {
|
|
640
|
-
return { error: "old_string not found in file" };
|
|
794
|
+
return attachDescriptor({ error: "old_string not found in file" });
|
|
641
795
|
}
|
|
642
796
|
const newContent = content.replace(args.old_string, args.new_string);
|
|
643
797
|
fs.writeFileSync(filePath, newContent, "utf8");
|
|
644
|
-
return { success: true, path: filePath };
|
|
798
|
+
return attachDescriptor({ success: true, path: filePath });
|
|
645
799
|
}
|
|
646
800
|
|
|
647
801
|
case "run_shell": {
|
|
@@ -652,22 +806,34 @@ async function executeToolInner(
|
|
|
652
806
|
timeout: 60000,
|
|
653
807
|
maxBuffer: 1024 * 1024,
|
|
654
808
|
});
|
|
655
|
-
|
|
809
|
+
const override = resolveShellDescriptor(args.command);
|
|
810
|
+
return attachDescriptor(
|
|
811
|
+
{
|
|
812
|
+
stdout: output.substring(0, 30000),
|
|
813
|
+
},
|
|
814
|
+
override || runtimeDescriptor,
|
|
815
|
+
);
|
|
656
816
|
} catch (err) {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
817
|
+
const override = resolveShellDescriptor(args.command);
|
|
818
|
+
return attachDescriptor(
|
|
819
|
+
{
|
|
820
|
+
error: err.message.substring(0, 2000),
|
|
821
|
+
stderr: (err.stderr || "").substring(0, 2000),
|
|
822
|
+
exitCode: err.status,
|
|
823
|
+
},
|
|
824
|
+
override || runtimeDescriptor,
|
|
825
|
+
);
|
|
662
826
|
}
|
|
663
827
|
}
|
|
664
828
|
|
|
665
829
|
case "run_code": {
|
|
666
|
-
return _executeRunCode(args, cwd);
|
|
830
|
+
return attachDescriptor(await _executeRunCode(args, cwd));
|
|
667
831
|
}
|
|
668
832
|
|
|
669
833
|
case "spawn_sub_agent": {
|
|
670
|
-
return
|
|
834
|
+
return attachDescriptor(
|
|
835
|
+
await _executeSpawnSubAgent(args, { skillLoader, cwd, parentMessages }),
|
|
836
|
+
);
|
|
671
837
|
}
|
|
672
838
|
|
|
673
839
|
case "search_files": {
|
|
@@ -683,7 +849,9 @@ async function executeToolInner(
|
|
|
683
849
|
encoding: "utf8",
|
|
684
850
|
timeout: 10000,
|
|
685
851
|
});
|
|
686
|
-
return {
|
|
852
|
+
return attachDescriptor({
|
|
853
|
+
matches: output.trim().split("\n").slice(0, 20),
|
|
854
|
+
});
|
|
687
855
|
} else {
|
|
688
856
|
const cmd =
|
|
689
857
|
process.platform === "win32"
|
|
@@ -694,44 +862,47 @@ async function executeToolInner(
|
|
|
694
862
|
encoding: "utf8",
|
|
695
863
|
timeout: 10000,
|
|
696
864
|
});
|
|
697
|
-
return {
|
|
865
|
+
return attachDescriptor({
|
|
698
866
|
files: output.trim().split("\n").filter(Boolean).slice(0, 20),
|
|
699
|
-
};
|
|
867
|
+
});
|
|
700
868
|
}
|
|
701
869
|
} catch {
|
|
702
|
-
return {
|
|
870
|
+
return attachDescriptor({
|
|
871
|
+
files: [],
|
|
872
|
+
message: "No matches found",
|
|
873
|
+
});
|
|
703
874
|
}
|
|
704
875
|
}
|
|
705
876
|
|
|
706
877
|
case "list_dir": {
|
|
707
878
|
const dirPath = args.path ? path.resolve(cwd, args.path) : cwd;
|
|
708
879
|
if (!fs.existsSync(dirPath)) {
|
|
709
|
-
return { error: `Directory not found: ${dirPath}` };
|
|
880
|
+
return attachDescriptor({ error: `Directory not found: ${dirPath}` });
|
|
710
881
|
}
|
|
711
882
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
712
|
-
return {
|
|
883
|
+
return attachDescriptor({
|
|
713
884
|
entries: entries.map((e) => ({
|
|
714
885
|
name: e.name,
|
|
715
886
|
type: e.isDirectory() ? "dir" : "file",
|
|
716
887
|
})),
|
|
717
|
-
};
|
|
888
|
+
});
|
|
718
889
|
}
|
|
719
890
|
|
|
720
891
|
case "run_skill": {
|
|
721
892
|
const allSkills = skillLoader.getResolvedSkills();
|
|
722
893
|
if (allSkills.length === 0) {
|
|
723
|
-
return {
|
|
894
|
+
return attachDescriptor({
|
|
724
895
|
error:
|
|
725
896
|
"No skills found. Make sure you're in the ChainlessChain project root or have skills installed.",
|
|
726
|
-
};
|
|
897
|
+
});
|
|
727
898
|
}
|
|
728
899
|
const match = allSkills.find(
|
|
729
900
|
(s) => s.id === args.skill_name || s.dirName === args.skill_name,
|
|
730
901
|
);
|
|
731
902
|
if (!match || !match.hasHandler) {
|
|
732
|
-
return {
|
|
903
|
+
return attachDescriptor({
|
|
733
904
|
error: `Skill "${args.skill_name}" not found or has no handler. Use list_skills to see available skills.`,
|
|
734
|
-
};
|
|
905
|
+
});
|
|
735
906
|
}
|
|
736
907
|
|
|
737
908
|
// Check if skill requests isolation (via SKILL.md frontmatter)
|
|
@@ -746,15 +917,17 @@ async function executeToolInner(
|
|
|
746
917
|
});
|
|
747
918
|
try {
|
|
748
919
|
const result = await subCtx.run(args.input);
|
|
749
|
-
return {
|
|
920
|
+
return attachDescriptor({
|
|
750
921
|
success: true,
|
|
751
922
|
isolated: true,
|
|
752
923
|
skill: args.skill_name,
|
|
753
924
|
summary: result.summary,
|
|
754
925
|
toolsUsed: result.toolsUsed,
|
|
755
|
-
};
|
|
926
|
+
});
|
|
756
927
|
} catch (err) {
|
|
757
|
-
return {
|
|
928
|
+
return attachDescriptor({
|
|
929
|
+
error: `Isolated skill execution failed: ${err.message}`,
|
|
930
|
+
});
|
|
758
931
|
}
|
|
759
932
|
}
|
|
760
933
|
|
|
@@ -775,16 +948,18 @@ async function executeToolInner(
|
|
|
775
948
|
workspacePath: cwd,
|
|
776
949
|
};
|
|
777
950
|
const result = await handler.execute(task, taskContext, match);
|
|
778
|
-
return result;
|
|
951
|
+
return attachDescriptor(result);
|
|
779
952
|
} catch (err) {
|
|
780
|
-
return {
|
|
953
|
+
return attachDescriptor({
|
|
954
|
+
error: `Skill execution failed: ${err.message}`,
|
|
955
|
+
});
|
|
781
956
|
}
|
|
782
957
|
}
|
|
783
958
|
|
|
784
959
|
case "list_skills": {
|
|
785
960
|
let skills = skillLoader.getResolvedSkills();
|
|
786
961
|
if (skills.length === 0) {
|
|
787
|
-
return { error: "No skills found." };
|
|
962
|
+
return attachDescriptor({ error: "No skills found." });
|
|
788
963
|
}
|
|
789
964
|
if (args.category) {
|
|
790
965
|
skills = skills.filter(
|
|
@@ -800,7 +975,7 @@ async function executeToolInner(
|
|
|
800
975
|
s.category.toLowerCase().includes(q),
|
|
801
976
|
);
|
|
802
977
|
}
|
|
803
|
-
return {
|
|
978
|
+
return attachDescriptor({
|
|
804
979
|
count: skills.length,
|
|
805
980
|
skills: skills.map((s) => ({
|
|
806
981
|
id: s.id,
|
|
@@ -809,11 +984,38 @@ async function executeToolInner(
|
|
|
809
984
|
hasHandler: s.hasHandler,
|
|
810
985
|
description: (s.description || "").substring(0, 80),
|
|
811
986
|
})),
|
|
812
|
-
};
|
|
987
|
+
});
|
|
813
988
|
}
|
|
814
989
|
|
|
815
990
|
default:
|
|
816
|
-
|
|
991
|
+
if (
|
|
992
|
+
hostToolDefinition &&
|
|
993
|
+
interaction &&
|
|
994
|
+
typeof interaction.requestHostTool === "function"
|
|
995
|
+
) {
|
|
996
|
+
const hostedResult = await interaction.requestHostTool(name, args);
|
|
997
|
+
if (hostedResult?.success === false) {
|
|
998
|
+
return attachDescriptor({
|
|
999
|
+
error:
|
|
1000
|
+
hostedResult.error || `Hosted tool execution failed: ${name}`,
|
|
1001
|
+
policy: hostToolPolicy || null,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
if (hostedResult?.result && typeof hostedResult.result === "object") {
|
|
1006
|
+
return hostedResult.result;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return attachDescriptor({
|
|
1010
|
+
result:
|
|
1011
|
+
hostedResult &&
|
|
1012
|
+
Object.prototype.hasOwnProperty.call(hostedResult, "result")
|
|
1013
|
+
? hostedResult.result
|
|
1014
|
+
: hostedResult,
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
return attachDescriptor({ error: `Unknown tool: ${name}` });
|
|
817
1019
|
}
|
|
818
1020
|
}
|
|
819
1021
|
|
|
@@ -1128,14 +1330,11 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
1128
1330
|
export async function chatWithTools(rawMessages, options) {
|
|
1129
1331
|
const { provider, model, baseUrl, apiKey, contextEngine: ce } = options;
|
|
1130
1332
|
|
|
1131
|
-
// Filter tools based on persona.toolsDisabled
|
|
1132
|
-
let tools = AGENT_TOOLS;
|
|
1133
1333
|
const persona = _loadProjectPersona(options.cwd);
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1334
|
+
const tools = getAgentToolDefinitions({
|
|
1335
|
+
disabledTools: persona?.toolsDisabled,
|
|
1336
|
+
extraTools: options.hostManagedToolPolicy?.toolDefinitions || [],
|
|
1337
|
+
});
|
|
1139
1338
|
|
|
1140
1339
|
const lastUserMsg = [...rawMessages].reverse().find((m) => m.role === "user");
|
|
1141
1340
|
const messages = ce
|
|
@@ -1319,7 +1518,9 @@ export async function* agentLoop(messages, options) {
|
|
|
1319
1518
|
hookDb: options.hookDb || null,
|
|
1320
1519
|
skillLoader: options.skillLoader || _defaultSkillLoader,
|
|
1321
1520
|
cwd: options.cwd || process.cwd(),
|
|
1521
|
+
hostManagedToolPolicy: options.hostManagedToolPolicy || null,
|
|
1322
1522
|
parentMessages: messages, // pass parent messages for sub-agent auto-condensation
|
|
1523
|
+
interaction: options.interaction || null,
|
|
1323
1524
|
};
|
|
1324
1525
|
|
|
1325
1526
|
// ── Slot-filling phase ──────────────────────────────────────────────
|