hoomanjs 1.11.3 → 1.12.0
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/acp/acp-agent.ts +8 -4
- package/src/acp/sessions/config-options.ts +4 -4
- package/src/configure/app.tsx +88 -70
- package/src/core/agent/index.ts +23 -11
- package/src/core/config.ts +45 -46
- package/src/core/index.ts +18 -8
- package/src/core/mcp/index.ts +11 -2
- package/src/core/mcp/manager.ts +40 -21
- package/src/core/prompts/system.ts +9 -7
- package/src/core/tools/wiki.ts +2 -2
- package/src/daemon/index.ts +16 -2
package/package.json
CHANGED
package/src/acp/acp-agent.ts
CHANGED
|
@@ -337,8 +337,10 @@ export class AcpAgent implements AgentContract {
|
|
|
337
337
|
{
|
|
338
338
|
userId: bootstrapUserId,
|
|
339
339
|
sessionId,
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
acp: {
|
|
341
|
+
mcpServers,
|
|
342
|
+
...(clientSystemPrompt ? { systemPrompt: clientSystemPrompt } : {}),
|
|
343
|
+
},
|
|
342
344
|
},
|
|
343
345
|
false,
|
|
344
346
|
);
|
|
@@ -433,8 +435,10 @@ export class AcpAgent implements AgentContract {
|
|
|
433
435
|
{
|
|
434
436
|
userId: bootstrapUserId,
|
|
435
437
|
sessionId: params.sessionId,
|
|
436
|
-
|
|
437
|
-
|
|
438
|
+
acp: {
|
|
439
|
+
mcpServers,
|
|
440
|
+
...(clientSystemPrompt ? { systemPrompt: clientSystemPrompt } : {}),
|
|
441
|
+
},
|
|
438
442
|
},
|
|
439
443
|
false,
|
|
440
444
|
);
|
|
@@ -63,8 +63,8 @@ export function applySessionConfigOption(
|
|
|
63
63
|
if (params.configId === HOOMAN_LTM_CONFIG_ID) {
|
|
64
64
|
const chroma = config.ltm.chroma;
|
|
65
65
|
config.update({
|
|
66
|
-
|
|
67
|
-
...config.
|
|
66
|
+
tools: {
|
|
67
|
+
...config.tools,
|
|
68
68
|
ltm: {
|
|
69
69
|
enabled: value === "on",
|
|
70
70
|
chroma: {
|
|
@@ -79,8 +79,8 @@ export function applySessionConfigOption(
|
|
|
79
79
|
|
|
80
80
|
const chroma = config.wiki.chroma;
|
|
81
81
|
config.update({
|
|
82
|
-
|
|
83
|
-
...config.
|
|
82
|
+
tools: {
|
|
83
|
+
...config.tools,
|
|
84
84
|
wiki: {
|
|
85
85
|
enabled: value === "on",
|
|
86
86
|
chroma: {
|
package/src/configure/app.tsx
CHANGED
|
@@ -132,7 +132,6 @@ export function ConfigureApp({
|
|
|
132
132
|
name: config.name,
|
|
133
133
|
llm: config.llm,
|
|
134
134
|
tools: config.tools,
|
|
135
|
-
features: config.features,
|
|
136
135
|
compaction: config.compaction,
|
|
137
136
|
}) satisfies ConfigData,
|
|
138
137
|
[config, revision],
|
|
@@ -433,79 +432,98 @@ export function ConfigureApp({
|
|
|
433
432
|
}),
|
|
434
433
|
},
|
|
435
434
|
{
|
|
436
|
-
label: `
|
|
437
|
-
value: () =>
|
|
438
|
-
promptValue({
|
|
439
|
-
title: "Update allowed list",
|
|
440
|
-
label: "Allowed",
|
|
441
|
-
initialValue: compactJson(configData.tools.allowed),
|
|
442
|
-
placeholder: '["tool_a","tool_b"]',
|
|
443
|
-
onSubmit: async (value) => {
|
|
444
|
-
const allowed = parseStringArray(value, "Allowed");
|
|
445
|
-
updateConfig({ tools: { allowed } }, "Updated allowed list.");
|
|
446
|
-
setPrompt(null);
|
|
447
|
-
},
|
|
448
|
-
}),
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
label: `Fetch feature • ${configData.features.fetch.enabled ? "Enabled" : "Disabled"}`,
|
|
435
|
+
label: `Fetch tool • ${configData.tools.fetch.enabled ? "Enabled" : "Disabled"}`,
|
|
452
436
|
value: () => {
|
|
453
437
|
updateConfig(
|
|
454
438
|
{
|
|
455
|
-
|
|
456
|
-
...config.
|
|
439
|
+
tools: {
|
|
440
|
+
...config.tools,
|
|
457
441
|
fetch: {
|
|
458
|
-
enabled: !configData.
|
|
442
|
+
enabled: !configData.tools.fetch.enabled,
|
|
459
443
|
},
|
|
460
444
|
},
|
|
461
445
|
},
|
|
462
|
-
`Fetch
|
|
446
|
+
`Fetch tool ${configData.tools.fetch.enabled ? "disabled" : "enabled"}.`,
|
|
463
447
|
);
|
|
464
448
|
setScreen({ kind: "config" });
|
|
465
449
|
},
|
|
466
450
|
},
|
|
467
451
|
{
|
|
468
|
-
label: `Filesystem
|
|
452
|
+
label: `Filesystem tool • ${configData.tools.filesystem.enabled ? "Enabled" : "Disabled"}`,
|
|
469
453
|
value: () => {
|
|
470
454
|
updateConfig(
|
|
471
455
|
{
|
|
472
|
-
|
|
473
|
-
...config.
|
|
456
|
+
tools: {
|
|
457
|
+
...config.tools,
|
|
474
458
|
filesystem: {
|
|
475
|
-
enabled: !configData.
|
|
459
|
+
enabled: !configData.tools.filesystem.enabled,
|
|
476
460
|
},
|
|
477
461
|
},
|
|
478
462
|
},
|
|
479
|
-
`Filesystem
|
|
463
|
+
`Filesystem tool ${configData.tools.filesystem.enabled ? "disabled" : "enabled"}.`,
|
|
480
464
|
);
|
|
481
465
|
setScreen({ kind: "config" });
|
|
482
466
|
},
|
|
483
467
|
},
|
|
484
468
|
{
|
|
485
|
-
label: `Shell
|
|
469
|
+
label: `Shell tool • ${configData.tools.shell.enabled ? "Enabled" : "Disabled"}`,
|
|
486
470
|
value: () => {
|
|
487
471
|
updateConfig(
|
|
488
472
|
{
|
|
489
|
-
|
|
490
|
-
...config.
|
|
473
|
+
tools: {
|
|
474
|
+
...config.tools,
|
|
491
475
|
shell: {
|
|
492
|
-
enabled: !configData.
|
|
476
|
+
enabled: !configData.tools.shell.enabled,
|
|
493
477
|
},
|
|
494
478
|
},
|
|
495
479
|
},
|
|
496
|
-
`Shell
|
|
480
|
+
`Shell tool ${configData.tools.shell.enabled ? "disabled" : "enabled"}.`,
|
|
497
481
|
);
|
|
498
482
|
setScreen({ kind: "config" });
|
|
499
483
|
},
|
|
500
484
|
},
|
|
501
485
|
{
|
|
502
|
-
label: `Long-term memory • ${configData.
|
|
486
|
+
label: `Long-term memory • ${configData.tools.ltm.enabled ? "Enabled" : "Disabled"} • ${configData.tools.ltm.chroma.collection.memory}`,
|
|
503
487
|
value: () => setScreen({ kind: "config-ltm" }),
|
|
504
488
|
},
|
|
505
489
|
{
|
|
506
|
-
label: `Wiki
|
|
490
|
+
label: `Wiki tool • ${configData.tools.wiki.enabled ? "Enabled" : "Disabled"} • ${configData.tools.wiki.chroma.collection.wiki}`,
|
|
507
491
|
value: () => setScreen({ kind: "config-wiki" }),
|
|
508
492
|
},
|
|
493
|
+
{
|
|
494
|
+
label: `MCP tools • ${configData.tools.mcp.enabled ? "Enabled" : "Disabled"}`,
|
|
495
|
+
value: () => {
|
|
496
|
+
updateConfig(
|
|
497
|
+
{
|
|
498
|
+
tools: {
|
|
499
|
+
...config.tools,
|
|
500
|
+
mcp: {
|
|
501
|
+
enabled: !configData.tools.mcp.enabled,
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
`MCP tools ${configData.tools.mcp.enabled ? "disabled" : "enabled"}.`,
|
|
506
|
+
);
|
|
507
|
+
setScreen({ kind: "config" });
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
label: `Skills tools • ${configData.tools.skills.enabled ? "Enabled" : "Disabled"}`,
|
|
512
|
+
value: () => {
|
|
513
|
+
updateConfig(
|
|
514
|
+
{
|
|
515
|
+
tools: {
|
|
516
|
+
...config.tools,
|
|
517
|
+
skills: {
|
|
518
|
+
enabled: !configData.tools.skills.enabled,
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
`Skills tools ${configData.tools.skills.enabled ? "disabled" : "enabled"}.`,
|
|
523
|
+
);
|
|
524
|
+
setScreen({ kind: "config" });
|
|
525
|
+
},
|
|
526
|
+
},
|
|
509
527
|
{
|
|
510
528
|
label: `Compaction ratio • ${configData.compaction.ratio}`,
|
|
511
529
|
value: () =>
|
|
@@ -604,30 +622,30 @@ export function ConfigureApp({
|
|
|
604
622
|
const renderLtmConfigMenu = () => {
|
|
605
623
|
const items: MenuItem[] = [
|
|
606
624
|
{
|
|
607
|
-
label: `Enabled • ${configData.
|
|
625
|
+
label: `Enabled • ${configData.tools.ltm.enabled ? "On" : "Off"}`,
|
|
608
626
|
value: () => {
|
|
609
627
|
updateConfig(
|
|
610
628
|
{
|
|
611
|
-
|
|
612
|
-
...config.
|
|
629
|
+
tools: {
|
|
630
|
+
...config.tools,
|
|
613
631
|
ltm: {
|
|
614
|
-
...config.
|
|
615
|
-
enabled: !configData.
|
|
632
|
+
...config.tools.ltm,
|
|
633
|
+
enabled: !configData.tools.ltm.enabled,
|
|
616
634
|
},
|
|
617
635
|
},
|
|
618
636
|
},
|
|
619
|
-
`Long-term memory ${configData.
|
|
637
|
+
`Long-term memory ${configData.tools.ltm.enabled ? "disabled" : "enabled"}.`,
|
|
620
638
|
);
|
|
621
639
|
setScreen({ kind: "config-ltm" });
|
|
622
640
|
},
|
|
623
641
|
},
|
|
624
642
|
{
|
|
625
|
-
label: `Chroma URL • ${configData.
|
|
643
|
+
label: `Chroma URL • ${configData.tools.ltm.chroma.url}`,
|
|
626
644
|
value: () =>
|
|
627
645
|
promptValue({
|
|
628
646
|
title: "Update LTM Chroma URL",
|
|
629
647
|
label: "URL",
|
|
630
|
-
initialValue: configData.
|
|
648
|
+
initialValue: configData.tools.ltm.chroma.url,
|
|
631
649
|
onSubmit: async (value) => {
|
|
632
650
|
const url = value.trim();
|
|
633
651
|
if (!url) {
|
|
@@ -635,12 +653,12 @@ export function ConfigureApp({
|
|
|
635
653
|
}
|
|
636
654
|
updateConfig(
|
|
637
655
|
{
|
|
638
|
-
|
|
639
|
-
...config.
|
|
656
|
+
tools: {
|
|
657
|
+
...config.tools,
|
|
640
658
|
ltm: {
|
|
641
|
-
...config.
|
|
659
|
+
...config.tools.ltm,
|
|
642
660
|
chroma: {
|
|
643
|
-
...config.
|
|
661
|
+
...config.tools.ltm.chroma,
|
|
644
662
|
url,
|
|
645
663
|
},
|
|
646
664
|
},
|
|
@@ -653,12 +671,12 @@ export function ConfigureApp({
|
|
|
653
671
|
}),
|
|
654
672
|
},
|
|
655
673
|
{
|
|
656
|
-
label: `Chroma collection • ${configData.
|
|
674
|
+
label: `Chroma collection • ${configData.tools.ltm.chroma.collection.memory}`,
|
|
657
675
|
value: () =>
|
|
658
676
|
promptValue({
|
|
659
677
|
title: "Update LTM Chroma collection",
|
|
660
678
|
label: "Collection name",
|
|
661
|
-
initialValue: configData.
|
|
679
|
+
initialValue: configData.tools.ltm.chroma.collection.memory,
|
|
662
680
|
onSubmit: async (value) => {
|
|
663
681
|
const memory = value.trim();
|
|
664
682
|
if (!memory) {
|
|
@@ -666,12 +684,12 @@ export function ConfigureApp({
|
|
|
666
684
|
}
|
|
667
685
|
updateConfig(
|
|
668
686
|
{
|
|
669
|
-
|
|
670
|
-
...config.
|
|
687
|
+
tools: {
|
|
688
|
+
...config.tools,
|
|
671
689
|
ltm: {
|
|
672
|
-
...config.
|
|
690
|
+
...config.tools.ltm,
|
|
673
691
|
chroma: {
|
|
674
|
-
...config.
|
|
692
|
+
...config.tools.ltm.chroma,
|
|
675
693
|
collection: { memory },
|
|
676
694
|
},
|
|
677
695
|
},
|
|
@@ -701,30 +719,30 @@ export function ConfigureApp({
|
|
|
701
719
|
const renderWikiConfigMenu = () => {
|
|
702
720
|
const items: MenuItem[] = [
|
|
703
721
|
{
|
|
704
|
-
label: `Enabled • ${configData.
|
|
722
|
+
label: `Enabled • ${configData.tools.wiki.enabled ? "On" : "Off"}`,
|
|
705
723
|
value: () => {
|
|
706
724
|
updateConfig(
|
|
707
725
|
{
|
|
708
|
-
|
|
709
|
-
...config.
|
|
726
|
+
tools: {
|
|
727
|
+
...config.tools,
|
|
710
728
|
wiki: {
|
|
711
|
-
...config.
|
|
712
|
-
enabled: !configData.
|
|
729
|
+
...config.tools.wiki,
|
|
730
|
+
enabled: !configData.tools.wiki.enabled,
|
|
713
731
|
},
|
|
714
732
|
},
|
|
715
733
|
},
|
|
716
|
-
`Wiki
|
|
734
|
+
`Wiki tool ${configData.tools.wiki.enabled ? "disabled" : "enabled"}.`,
|
|
717
735
|
);
|
|
718
736
|
setScreen({ kind: "config-wiki" });
|
|
719
737
|
},
|
|
720
738
|
},
|
|
721
739
|
{
|
|
722
|
-
label: `Chroma URL • ${configData.
|
|
740
|
+
label: `Chroma URL • ${configData.tools.wiki.chroma.url}`,
|
|
723
741
|
value: () =>
|
|
724
742
|
promptValue({
|
|
725
743
|
title: "Update Wiki Chroma URL",
|
|
726
744
|
label: "URL",
|
|
727
|
-
initialValue: configData.
|
|
745
|
+
initialValue: configData.tools.wiki.chroma.url,
|
|
728
746
|
onSubmit: async (value) => {
|
|
729
747
|
const url = value.trim();
|
|
730
748
|
if (!url) {
|
|
@@ -732,12 +750,12 @@ export function ConfigureApp({
|
|
|
732
750
|
}
|
|
733
751
|
updateConfig(
|
|
734
752
|
{
|
|
735
|
-
|
|
736
|
-
...config.
|
|
753
|
+
tools: {
|
|
754
|
+
...config.tools,
|
|
737
755
|
wiki: {
|
|
738
|
-
...config.
|
|
756
|
+
...config.tools.wiki,
|
|
739
757
|
chroma: {
|
|
740
|
-
...config.
|
|
758
|
+
...config.tools.wiki.chroma,
|
|
741
759
|
url,
|
|
742
760
|
},
|
|
743
761
|
},
|
|
@@ -750,12 +768,12 @@ export function ConfigureApp({
|
|
|
750
768
|
}),
|
|
751
769
|
},
|
|
752
770
|
{
|
|
753
|
-
label: `Chroma collection • ${configData.
|
|
771
|
+
label: `Chroma collection • ${configData.tools.wiki.chroma.collection.wiki}`,
|
|
754
772
|
value: () =>
|
|
755
773
|
promptValue({
|
|
756
774
|
title: "Update Wiki Chroma collection",
|
|
757
775
|
label: "Collection name",
|
|
758
|
-
initialValue: configData.
|
|
776
|
+
initialValue: configData.tools.wiki.chroma.collection.wiki,
|
|
759
777
|
onSubmit: async (value) => {
|
|
760
778
|
const wiki = value.trim();
|
|
761
779
|
if (!wiki) {
|
|
@@ -763,12 +781,12 @@ export function ConfigureApp({
|
|
|
763
781
|
}
|
|
764
782
|
updateConfig(
|
|
765
783
|
{
|
|
766
|
-
|
|
767
|
-
...config.
|
|
784
|
+
tools: {
|
|
785
|
+
...config.tools,
|
|
768
786
|
wiki: {
|
|
769
|
-
...config.
|
|
787
|
+
...config.tools.wiki,
|
|
770
788
|
chroma: {
|
|
771
|
-
...config.
|
|
789
|
+
...config.tools.wiki.chroma,
|
|
772
790
|
collection: { wiki },
|
|
773
791
|
},
|
|
774
792
|
},
|
|
@@ -789,7 +807,7 @@ export function ConfigureApp({
|
|
|
789
807
|
return (
|
|
790
808
|
<MenuScreen
|
|
791
809
|
title="Wiki"
|
|
792
|
-
description="Configure wiki
|
|
810
|
+
description="Configure wiki tool and Chroma-backed wiki search."
|
|
793
811
|
items={items}
|
|
794
812
|
/>
|
|
795
813
|
);
|
package/src/core/agent/index.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Agent } from "@strands-agents/sdk";
|
|
2
2
|
import type { Config } from "../config.ts";
|
|
3
3
|
import { modelProviders } from "../models";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createMcpTools,
|
|
6
|
+
type Config as McpConfig,
|
|
7
|
+
type Manager as McpManager,
|
|
8
|
+
} from "../mcp";
|
|
5
9
|
import type { System as SystemPrompt } from "../prompts";
|
|
6
10
|
import { skills as createSkillsPrompt } from "../prompts";
|
|
7
11
|
import {
|
|
@@ -9,7 +13,7 @@ import {
|
|
|
9
13
|
createLongTermMemoryStore,
|
|
10
14
|
createLongTermMemoryTools,
|
|
11
15
|
} from "../memory";
|
|
12
|
-
import type
|
|
16
|
+
import { createSkillsTools, type Registry } from "../skills";
|
|
13
17
|
import {
|
|
14
18
|
createFetchTools,
|
|
15
19
|
createFilesystemTools,
|
|
@@ -37,13 +41,19 @@ export async function create(
|
|
|
37
41
|
const userId = meta.userId ?? sessionId;
|
|
38
42
|
const llm = await modelProviders[config.llm.provider]!();
|
|
39
43
|
const stm = createShortTermMemory(sessionId);
|
|
40
|
-
const ltm = config.
|
|
44
|
+
const ltm = config.tools.ltm.enabled
|
|
41
45
|
? createLongTermMemoryStore(config)
|
|
42
46
|
: null;
|
|
43
|
-
const skills =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
+
const skills = config.tools.skills.enabled
|
|
48
|
+
? (await createSkillsPrompt(registry)).content
|
|
49
|
+
: "";
|
|
50
|
+
const tools = config.tools.mcp.enabled
|
|
51
|
+
? await mcp.manager.listPrefixedTools()
|
|
52
|
+
: [];
|
|
53
|
+
const append = config.tools.mcp.enabled
|
|
54
|
+
? await mcp.manager.listServerInstructions()
|
|
55
|
+
: [];
|
|
56
|
+
const prompt = [system.content, meta.systemPrompt, ...append, skills]
|
|
47
57
|
.filter((x) => !!x)
|
|
48
58
|
.join(SECTION_BREAK);
|
|
49
59
|
return new Agent({
|
|
@@ -56,11 +66,13 @@ export async function create(
|
|
|
56
66
|
},
|
|
57
67
|
tools: [
|
|
58
68
|
...createTimeTools(),
|
|
59
|
-
...(config.
|
|
69
|
+
...(config.tools.fetch.enabled ? createFetchTools() : []),
|
|
60
70
|
...(ltm ? createLongTermMemoryTools(ltm) : []),
|
|
61
|
-
...(config.
|
|
62
|
-
...(config.
|
|
63
|
-
...(config.
|
|
71
|
+
...(config.tools.filesystem.enabled ? createFilesystemTools() : []),
|
|
72
|
+
...(config.tools.shell.enabled ? createShellTools() : []),
|
|
73
|
+
...(config.tools.wiki.enabled ? createWikiTools(config) : []),
|
|
74
|
+
...(config.tools.mcp.enabled ? createMcpTools(mcp.config) : []),
|
|
75
|
+
...(config.tools.skills.enabled ? createSkillsTools(registry) : []),
|
|
64
76
|
...createThinkingTools(),
|
|
65
77
|
...tools,
|
|
66
78
|
],
|
package/src/core/config.ts
CHANGED
|
@@ -67,49 +67,45 @@ const WikiPartialSchema = z.object({
|
|
|
67
67
|
chroma: WikiChromaPartialSchema.optional(),
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
const
|
|
70
|
+
const ToolTogglePartialSchema = z.object({
|
|
71
71
|
enabled: z.boolean().optional(),
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
const
|
|
75
|
-
fetch:
|
|
76
|
-
filesystem:
|
|
77
|
-
shell:
|
|
74
|
+
const ToolsPartialSchema = z.object({
|
|
75
|
+
fetch: ToolTogglePartialSchema.optional(),
|
|
76
|
+
filesystem: ToolTogglePartialSchema.optional(),
|
|
77
|
+
shell: ToolTogglePartialSchema.optional(),
|
|
78
78
|
ltm: LtmPartialSchema.optional(),
|
|
79
79
|
wiki: WikiPartialSchema.optional(),
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const ToolsPartialSchema = z.object({
|
|
83
|
-
allowed: z.array(z.string().min(1)).default([]),
|
|
80
|
+
mcp: ToolTogglePartialSchema.optional(),
|
|
81
|
+
skills: ToolTogglePartialSchema.optional(),
|
|
84
82
|
});
|
|
85
83
|
|
|
86
84
|
const ConfigSchema = z
|
|
87
85
|
.object({
|
|
88
86
|
name: z.string().min(1),
|
|
89
87
|
llm: LlmSchema,
|
|
90
|
-
tools: ToolsPartialSchema.
|
|
91
|
-
features: FeaturesPartialSchema.nullish(),
|
|
88
|
+
tools: ToolsPartialSchema.nullish(),
|
|
92
89
|
compaction: CompactionPartialSchema.nullish().transform((c) => ({
|
|
93
90
|
ratio: c?.ratio ?? DEFAULT_COMPACTION.ratio,
|
|
94
91
|
keep: c?.keep ?? DEFAULT_COMPACTION.keep,
|
|
95
92
|
})),
|
|
96
93
|
})
|
|
97
94
|
.transform((input) => {
|
|
98
|
-
const ltm = input.
|
|
99
|
-
const wiki = input.
|
|
95
|
+
const ltm = input.tools?.ltm;
|
|
96
|
+
const wiki = input.tools?.wiki;
|
|
100
97
|
return {
|
|
101
98
|
name: input.name,
|
|
102
99
|
llm: input.llm,
|
|
103
|
-
tools:
|
|
104
|
-
features: {
|
|
100
|
+
tools: {
|
|
105
101
|
fetch: {
|
|
106
|
-
enabled: input.
|
|
102
|
+
enabled: input.tools?.fetch?.enabled ?? true,
|
|
107
103
|
},
|
|
108
104
|
filesystem: {
|
|
109
|
-
enabled: input.
|
|
105
|
+
enabled: input.tools?.filesystem?.enabled ?? true,
|
|
110
106
|
},
|
|
111
107
|
shell: {
|
|
112
|
-
enabled: input.
|
|
108
|
+
enabled: input.tools?.shell?.enabled ?? true,
|
|
113
109
|
},
|
|
114
110
|
ltm: {
|
|
115
111
|
enabled: ltm?.enabled ?? false,
|
|
@@ -133,6 +129,12 @@ const ConfigSchema = z
|
|
|
133
129
|
},
|
|
134
130
|
},
|
|
135
131
|
},
|
|
132
|
+
mcp: {
|
|
133
|
+
enabled: input.tools?.mcp?.enabled ?? false,
|
|
134
|
+
},
|
|
135
|
+
skills: {
|
|
136
|
+
enabled: input.tools?.skills?.enabled ?? false,
|
|
137
|
+
},
|
|
136
138
|
},
|
|
137
139
|
compaction: input.compaction,
|
|
138
140
|
};
|
|
@@ -141,10 +143,9 @@ const ConfigSchema = z
|
|
|
141
143
|
export type ConfigData = z.infer<typeof ConfigSchema>;
|
|
142
144
|
export type LlmConfig = z.infer<typeof LlmSchema>;
|
|
143
145
|
export type CompactionConfig = ConfigData["compaction"];
|
|
144
|
-
export type LtmConfig = ConfigData["
|
|
145
|
-
export type WikiConfig = ConfigData["
|
|
146
|
+
export type LtmConfig = ConfigData["tools"]["ltm"];
|
|
147
|
+
export type WikiConfig = ConfigData["tools"]["wiki"];
|
|
146
148
|
export type ToolsConfig = ConfigData["tools"];
|
|
147
|
-
export type FeaturesConfig = ConfigData["features"];
|
|
148
149
|
|
|
149
150
|
const defaultConfigData = (): ConfigData => ({
|
|
150
151
|
name: "Hooman",
|
|
@@ -154,9 +155,6 @@ const defaultConfigData = (): ConfigData => ({
|
|
|
154
155
|
params: {},
|
|
155
156
|
},
|
|
156
157
|
tools: {
|
|
157
|
-
allowed: [],
|
|
158
|
-
},
|
|
159
|
-
features: {
|
|
160
158
|
fetch: {
|
|
161
159
|
enabled: true,
|
|
162
160
|
},
|
|
@@ -180,6 +178,12 @@ const defaultConfigData = (): ConfigData => ({
|
|
|
180
178
|
collection: { wiki: "wiki" },
|
|
181
179
|
},
|
|
182
180
|
},
|
|
181
|
+
mcp: {
|
|
182
|
+
enabled: false,
|
|
183
|
+
},
|
|
184
|
+
skills: {
|
|
185
|
+
enabled: false,
|
|
186
|
+
},
|
|
183
187
|
},
|
|
184
188
|
compaction: {
|
|
185
189
|
ratio: 0.75,
|
|
@@ -207,43 +211,38 @@ export class Config {
|
|
|
207
211
|
get tools(): ToolsConfig {
|
|
208
212
|
return {
|
|
209
213
|
...this.data.tools,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
get compaction(): CompactionConfig {
|
|
215
|
-
return this.data.compaction;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
get features(): FeaturesConfig {
|
|
219
|
-
return {
|
|
220
|
-
...this.data.features,
|
|
221
|
-
fetch: { ...this.data.features.fetch },
|
|
222
|
-
filesystem: { ...this.data.features.filesystem },
|
|
223
|
-
shell: { ...this.data.features.shell },
|
|
214
|
+
fetch: { ...this.data.tools.fetch },
|
|
215
|
+
filesystem: { ...this.data.tools.filesystem },
|
|
216
|
+
shell: { ...this.data.tools.shell },
|
|
224
217
|
ltm: {
|
|
225
|
-
...this.data.
|
|
218
|
+
...this.data.tools.ltm,
|
|
226
219
|
chroma: {
|
|
227
|
-
...this.data.
|
|
228
|
-
collection: { ...this.data.
|
|
220
|
+
...this.data.tools.ltm.chroma,
|
|
221
|
+
collection: { ...this.data.tools.ltm.chroma.collection },
|
|
229
222
|
},
|
|
230
223
|
},
|
|
231
224
|
wiki: {
|
|
232
|
-
...this.data.
|
|
225
|
+
...this.data.tools.wiki,
|
|
233
226
|
chroma: {
|
|
234
|
-
...this.data.
|
|
235
|
-
collection: { ...this.data.
|
|
227
|
+
...this.data.tools.wiki.chroma,
|
|
228
|
+
collection: { ...this.data.tools.wiki.chroma.collection },
|
|
236
229
|
},
|
|
237
230
|
},
|
|
231
|
+
mcp: { ...this.data.tools.mcp },
|
|
232
|
+
skills: { ...this.data.tools.skills },
|
|
238
233
|
};
|
|
239
234
|
}
|
|
240
235
|
|
|
236
|
+
get compaction(): CompactionConfig {
|
|
237
|
+
return this.data.compaction;
|
|
238
|
+
}
|
|
239
|
+
|
|
241
240
|
get ltm(): LtmConfig {
|
|
242
|
-
return this.
|
|
241
|
+
return this.tools.ltm;
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
get wiki(): WikiConfig {
|
|
246
|
-
return this.
|
|
245
|
+
return this.tools.wiki;
|
|
247
246
|
}
|
|
248
247
|
|
|
249
248
|
private readJson(): unknown {
|
package/src/core/index.ts
CHANGED
|
@@ -18,13 +18,19 @@ import {
|
|
|
18
18
|
mcpJsonPath,
|
|
19
19
|
} from "./utils/paths.ts";
|
|
20
20
|
|
|
21
|
+
export type BootstrapMeta = {
|
|
22
|
+
userId?: string;
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
acp?: AcpMeta;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type AcpMeta = {
|
|
28
|
+
systemPrompt?: string;
|
|
29
|
+
mcpServers?: NamedMcpTransport[];
|
|
30
|
+
};
|
|
31
|
+
|
|
21
32
|
export async function bootstrap(
|
|
22
|
-
meta:
|
|
23
|
-
userId?: string;
|
|
24
|
-
sessionId?: string;
|
|
25
|
-
systemPrompt?: string;
|
|
26
|
-
mcpServers?: NamedMcpTransport[];
|
|
27
|
-
},
|
|
33
|
+
meta: BootstrapMeta,
|
|
28
34
|
print: boolean = false,
|
|
29
35
|
): Promise<{
|
|
30
36
|
config: Config;
|
|
@@ -34,14 +40,18 @@ export async function bootstrap(
|
|
|
34
40
|
}> {
|
|
35
41
|
const config = new Config(configJsonPath());
|
|
36
42
|
const mcpConfig = createMcpConfig(mcpJsonPath());
|
|
37
|
-
const mcpManager = createMcpManager(
|
|
43
|
+
const mcpManager = createMcpManager(
|
|
44
|
+
mcpConfig,
|
|
45
|
+
meta.acp !== undefined,
|
|
46
|
+
meta.acp?.mcpServers ?? [],
|
|
47
|
+
);
|
|
38
48
|
const mcp = { config: mcpConfig, manager: mcpManager };
|
|
39
49
|
const registry = createSkillsRegistry(basePath());
|
|
40
50
|
const system = await createSystemPrompt(instructionsMdPath(), config);
|
|
41
51
|
const agent = await createAgent(config, system, registry, mcp, print, {
|
|
42
52
|
userId: meta?.userId ?? meta?.sessionId,
|
|
43
53
|
sessionId: meta?.sessionId,
|
|
44
|
-
systemPrompt: meta?.systemPrompt,
|
|
54
|
+
systemPrompt: meta?.acp?.systemPrompt,
|
|
45
55
|
});
|
|
46
56
|
return { config, agent, mcp, registry };
|
|
47
57
|
}
|
package/src/core/mcp/index.ts
CHANGED
|
@@ -5,11 +5,19 @@ import {
|
|
|
5
5
|
HOOMAN_CHANNEL_PERMISSION,
|
|
6
6
|
type ChannelMessage,
|
|
7
7
|
type ChannelPermissionBehavior,
|
|
8
|
+
type ChannelSubscription,
|
|
9
|
+
type ChannelSubscriptionHandle,
|
|
8
10
|
} from "./manager.ts";
|
|
9
11
|
|
|
10
12
|
export { Config, Manager };
|
|
11
13
|
export { HOOMAN_CHANNEL, HOOMAN_CHANNEL_PERMISSION };
|
|
12
|
-
export type {
|
|
14
|
+
export type {
|
|
15
|
+
ChannelMessage,
|
|
16
|
+
ChannelPermissionBehavior,
|
|
17
|
+
ChannelSubscription,
|
|
18
|
+
ChannelSubscriptionHandle,
|
|
19
|
+
NamedMcpTransport,
|
|
20
|
+
};
|
|
13
21
|
export { createMcpTools } from "./tools.ts";
|
|
14
22
|
|
|
15
23
|
export function createMcpConfig(path: string): Config {
|
|
@@ -18,7 +26,8 @@ export function createMcpConfig(path: string): Config {
|
|
|
18
26
|
|
|
19
27
|
export function createMcpManager(
|
|
20
28
|
config: Config,
|
|
29
|
+
acp = false,
|
|
21
30
|
mcpServers: readonly NamedMcpTransport[] = [],
|
|
22
31
|
): Manager {
|
|
23
|
-
return new Manager(config, mcpServers);
|
|
32
|
+
return new Manager(config, acp, mcpServers);
|
|
24
33
|
}
|
package/src/core/mcp/manager.ts
CHANGED
|
@@ -33,6 +33,16 @@ export type ChannelMessage = {
|
|
|
33
33
|
|
|
34
34
|
export type ChannelPermissionBehavior = "allow_once" | "allow_always" | "deny";
|
|
35
35
|
|
|
36
|
+
export type ChannelSubscription = {
|
|
37
|
+
server: string;
|
|
38
|
+
channel: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ChannelSubscriptionHandle = {
|
|
42
|
+
unsubscribe: () => void;
|
|
43
|
+
subscriptions: ChannelSubscription[];
|
|
44
|
+
};
|
|
45
|
+
|
|
36
46
|
type ChannelPermissionRequest = {
|
|
37
47
|
requestId: string;
|
|
38
48
|
tool: string;
|
|
@@ -111,7 +121,7 @@ function readSourceValue(value: unknown): string | undefined {
|
|
|
111
121
|
*/
|
|
112
122
|
export class Manager {
|
|
113
123
|
private instances: Map<string, McpClient> | null = null;
|
|
114
|
-
private readonly
|
|
124
|
+
private readonly permissions = new Map<
|
|
115
125
|
string,
|
|
116
126
|
{
|
|
117
127
|
resolve: (behavior: ChannelPermissionBehavior) => void;
|
|
@@ -122,7 +132,8 @@ export class Manager {
|
|
|
122
132
|
|
|
123
133
|
public constructor(
|
|
124
134
|
private readonly config: Config,
|
|
125
|
-
private readonly
|
|
135
|
+
private readonly acp = false,
|
|
136
|
+
private readonly servers: readonly NamedMcpTransport[] = [],
|
|
126
137
|
) {}
|
|
127
138
|
|
|
128
139
|
/** Lazily builds clients from the current in-memory config (reloads file first). */
|
|
@@ -138,13 +149,15 @@ export class Manager {
|
|
|
138
149
|
* previous clients (stdio subprocesses, HTTP sessions).
|
|
139
150
|
*/
|
|
140
151
|
public reload(): void {
|
|
141
|
-
this.
|
|
152
|
+
if (!this.acp) {
|
|
153
|
+
this.config.reload();
|
|
154
|
+
}
|
|
142
155
|
const previous = this.instances;
|
|
143
156
|
const next = new Map<string, McpClient>();
|
|
144
157
|
const transports = [
|
|
145
|
-
...this.config.list(),
|
|
146
|
-
// Session-scoped
|
|
147
|
-
...this.
|
|
158
|
+
...(this.acp ? [] : this.config.list()),
|
|
159
|
+
// Session-scoped servers override local config entries on name conflicts.
|
|
160
|
+
...this.servers,
|
|
148
161
|
];
|
|
149
162
|
for (const { name, transport } of transports) {
|
|
150
163
|
next.set(
|
|
@@ -163,11 +176,11 @@ export class Manager {
|
|
|
163
176
|
}
|
|
164
177
|
|
|
165
178
|
public async disconnect(): Promise<void> {
|
|
166
|
-
for (const [key, pending] of this.
|
|
179
|
+
for (const [key, pending] of this.permissions.entries()) {
|
|
167
180
|
clearTimeout(pending.timer);
|
|
168
181
|
pending.reject(new Error(`Pending permission "${key}" cancelled.`));
|
|
169
182
|
}
|
|
170
|
-
this.
|
|
183
|
+
this.permissions.clear();
|
|
171
184
|
const toClose = this.instances;
|
|
172
185
|
this.instances = null;
|
|
173
186
|
if (!toClose?.size) {
|
|
@@ -224,7 +237,7 @@ export class Manager {
|
|
|
224
237
|
public async subscribeToChannels(
|
|
225
238
|
channels: readonly string[],
|
|
226
239
|
onMessage: (message: ChannelMessage) => void,
|
|
227
|
-
): Promise<
|
|
240
|
+
): Promise<ChannelSubscriptionHandle> {
|
|
228
241
|
if (this.instances === null) {
|
|
229
242
|
this.reload();
|
|
230
243
|
}
|
|
@@ -234,10 +247,11 @@ export class Manager {
|
|
|
234
247
|
...new Set(channels.map((c) => c.trim()).filter(Boolean)),
|
|
235
248
|
];
|
|
236
249
|
if (requested.length === 0) {
|
|
237
|
-
return () => {};
|
|
250
|
+
return { unsubscribe: () => {}, subscriptions: [] };
|
|
238
251
|
}
|
|
239
252
|
|
|
240
253
|
const unsubs: Array<() => void> = [];
|
|
254
|
+
const subscriptions: ChannelSubscription[] = [];
|
|
241
255
|
for (const [server, client] of map.entries()) {
|
|
242
256
|
await client.connect();
|
|
243
257
|
const experimental =
|
|
@@ -270,11 +284,11 @@ export class Manager {
|
|
|
270
284
|
return;
|
|
271
285
|
}
|
|
272
286
|
const key = `${server}:${requestId}`;
|
|
273
|
-
const pending = this.
|
|
287
|
+
const pending = this.permissions.get(key);
|
|
274
288
|
if (!pending) {
|
|
275
289
|
return;
|
|
276
290
|
}
|
|
277
|
-
this.
|
|
291
|
+
this.permissions.delete(key);
|
|
278
292
|
clearTimeout(pending.timer);
|
|
279
293
|
pending.resolve(behavior);
|
|
280
294
|
};
|
|
@@ -289,6 +303,8 @@ export class Manager {
|
|
|
289
303
|
continue;
|
|
290
304
|
}
|
|
291
305
|
|
|
306
|
+
subscriptions.push({ server, channel });
|
|
307
|
+
|
|
292
308
|
const method = `notifications/${channel}`;
|
|
293
309
|
const schema = z.object({
|
|
294
310
|
method: z.literal(method),
|
|
@@ -327,10 +343,13 @@ export class Manager {
|
|
|
327
343
|
}
|
|
328
344
|
}
|
|
329
345
|
|
|
330
|
-
return
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
346
|
+
return {
|
|
347
|
+
subscriptions,
|
|
348
|
+
unsubscribe: () => {
|
|
349
|
+
for (const off of unsubs) {
|
|
350
|
+
off();
|
|
351
|
+
}
|
|
352
|
+
},
|
|
334
353
|
};
|
|
335
354
|
}
|
|
336
355
|
|
|
@@ -374,21 +393,21 @@ export class Manager {
|
|
|
374
393
|
throw new Error("requestId is required.");
|
|
375
394
|
}
|
|
376
395
|
const key = `${server}:${requestId}`;
|
|
377
|
-
if (this.
|
|
396
|
+
if (this.permissions.has(key)) {
|
|
378
397
|
throw new Error(`Permission request "${requestId}" is already pending.`);
|
|
379
398
|
}
|
|
380
399
|
|
|
381
400
|
const response = new Promise<ChannelPermissionBehavior>(
|
|
382
401
|
(resolve, reject) => {
|
|
383
402
|
const timer = setTimeout(() => {
|
|
384
|
-
this.
|
|
403
|
+
this.permissions.delete(key);
|
|
385
404
|
reject(
|
|
386
405
|
new Error(
|
|
387
406
|
`Permission request "${requestId}" timed out after ${timeoutMs}ms.`,
|
|
388
407
|
),
|
|
389
408
|
);
|
|
390
409
|
}, timeoutMs);
|
|
391
|
-
this.
|
|
410
|
+
this.permissions.set(key, { resolve, reject, timer });
|
|
392
411
|
},
|
|
393
412
|
);
|
|
394
413
|
|
|
@@ -423,10 +442,10 @@ export class Manager {
|
|
|
423
442
|
});
|
|
424
443
|
return await response;
|
|
425
444
|
} catch (error) {
|
|
426
|
-
const pending = this.
|
|
445
|
+
const pending = this.permissions.get(key);
|
|
427
446
|
if (pending) {
|
|
428
447
|
clearTimeout(pending.timer);
|
|
429
|
-
this.
|
|
448
|
+
this.permissions.delete(key);
|
|
430
449
|
}
|
|
431
450
|
throw error;
|
|
432
451
|
}
|
|
@@ -36,15 +36,17 @@ export class System {
|
|
|
36
36
|
return STATIC_PROMPT_FILES.filter((file) => {
|
|
37
37
|
switch (file) {
|
|
38
38
|
case "ltm.md":
|
|
39
|
-
return this.config.
|
|
39
|
+
return this.config.tools.ltm.enabled;
|
|
40
40
|
case "fetch.md":
|
|
41
|
-
return this.config.
|
|
41
|
+
return this.config.tools.fetch.enabled;
|
|
42
42
|
case "filesystem.md":
|
|
43
|
-
return this.config.
|
|
43
|
+
return this.config.tools.filesystem.enabled;
|
|
44
44
|
case "shell.md":
|
|
45
|
-
return this.config.
|
|
45
|
+
return this.config.tools.shell.enabled;
|
|
46
46
|
case "wiki.md":
|
|
47
|
-
return this.config.
|
|
47
|
+
return this.config.tools.wiki.enabled;
|
|
48
|
+
case "skills.md":
|
|
49
|
+
return this.config.tools.skills.enabled;
|
|
48
50
|
case "thinking.md":
|
|
49
51
|
default:
|
|
50
52
|
return true;
|
|
@@ -96,8 +98,8 @@ export class System {
|
|
|
96
98
|
return {
|
|
97
99
|
name: this.config.name,
|
|
98
100
|
llm: this.config.llm,
|
|
99
|
-
ltm: this.config.
|
|
100
|
-
wiki: this.config.
|
|
101
|
+
ltm: this.config.tools.ltm,
|
|
102
|
+
wiki: this.config.tools.wiki,
|
|
101
103
|
compaction: this.config.compaction,
|
|
102
104
|
};
|
|
103
105
|
}
|
package/src/core/tools/wiki.ts
CHANGED
|
@@ -353,14 +353,14 @@ function searchMetadata(page: PageRecord): Record<string, string | number> {
|
|
|
353
353
|
export function createWikiTools(config: Config) {
|
|
354
354
|
const root = wikiRoot();
|
|
355
355
|
const client = new ChromaClient({
|
|
356
|
-
...chromaClientArgsFromUrl(config.
|
|
356
|
+
...chromaClientArgsFromUrl(config.tools.wiki.chroma.url),
|
|
357
357
|
});
|
|
358
358
|
let collectionPromise: Promise<Collection> | null = null;
|
|
359
359
|
|
|
360
360
|
const collection = async (): Promise<Collection> => {
|
|
361
361
|
if (!collectionPromise) {
|
|
362
362
|
collectionPromise = client.getOrCreateCollection({
|
|
363
|
-
name: config.
|
|
363
|
+
name: config.tools.wiki.chroma.collection.wiki,
|
|
364
364
|
embeddingFunction: new HFEmbedding(),
|
|
365
365
|
});
|
|
366
366
|
}
|
package/src/daemon/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Agent } from "@strands-agents/sdk";
|
|
|
3
3
|
import { HOOMAN_CHANNEL } from "../core/mcp/index.ts";
|
|
4
4
|
import type {
|
|
5
5
|
ChannelMessage,
|
|
6
|
+
ChannelSubscription,
|
|
6
7
|
Manager as McpManager,
|
|
7
8
|
} from "../core/mcp/index.ts";
|
|
8
9
|
import { createQueue } from "./queue.ts";
|
|
@@ -42,6 +43,18 @@ function resolveUserId(
|
|
|
42
43
|
return `${message.meta.server}:${raw}`;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
function formatSubscriptions(
|
|
47
|
+
subscriptions: readonly ChannelSubscription[],
|
|
48
|
+
): string {
|
|
49
|
+
if (subscriptions.length === 0) {
|
|
50
|
+
return "none";
|
|
51
|
+
}
|
|
52
|
+
const servers = [
|
|
53
|
+
...new Set(subscriptions.map((subscription) => subscription.server)),
|
|
54
|
+
].sort((left, right) => left.localeCompare(right));
|
|
55
|
+
return `${servers.length} MCP server(s): ${servers.join(", ")}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
export async function main(options: RunDaemonOptions): Promise<void> {
|
|
46
59
|
if (!options.channels) {
|
|
47
60
|
throw new Error("No daemon inputs enabled. Pass --channels.");
|
|
@@ -94,7 +107,7 @@ export async function main(options: RunDaemonOptions): Promise<void> {
|
|
|
94
107
|
() => unsubscribe(),
|
|
95
108
|
);
|
|
96
109
|
|
|
97
|
-
|
|
110
|
+
const handle = await options.manager.subscribeToChannels(
|
|
98
111
|
channels,
|
|
99
112
|
(message) => {
|
|
100
113
|
debug(
|
|
@@ -103,7 +116,8 @@ export async function main(options: RunDaemonOptions): Promise<void> {
|
|
|
103
116
|
void queue.push(message);
|
|
104
117
|
},
|
|
105
118
|
);
|
|
106
|
-
|
|
119
|
+
unsubscribe = handle.unsubscribe;
|
|
120
|
+
debug(`subscribed → ${formatSubscriptions(handle.subscriptions)}`);
|
|
107
121
|
|
|
108
122
|
try {
|
|
109
123
|
await stop();
|