hoomanjs 1.11.4 → 1.12.1
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/README.md +25 -17
- package/package.json +2 -2
- 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 +2 -1
- package/src/core/mcp/manager.ts +18 -15
- package/src/core/prompts/system.ts +9 -7
- package/src/core/tools/wiki.ts +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>Hooman</h1>
|
|
3
3
|
<p>
|
|
4
|
-
Hooman is a Bun-powered
|
|
4
|
+
Hooman is a hackable, Bun-powered AI agent toolkit for local workflows. It is built with TypeScript, <a href="https://www.npmjs.com/package/@strands-agents/sdk">Strands Agents SDK</a>, and <a href="https://github.com/vadimdemedes/ink">Ink</a>.
|
|
5
5
|
</p>
|
|
6
6
|
<p>
|
|
7
7
|
<a href="https://bun.com"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun&logoColor=000000" alt="Bun" /></a>
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
</p>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
It gives you:
|
|
19
|
+
It gives you a practical toolkit to build and run agent workflows:
|
|
20
20
|
|
|
21
21
|
- a one-shot `exec` command for single prompts
|
|
22
|
-
- a stateful `chat` interface for
|
|
23
|
-
- a `daemon` command for
|
|
24
|
-
- an Ink-powered `configure` workflow for
|
|
22
|
+
- a stateful `chat` interface for iterative sessions
|
|
23
|
+
- a `daemon` command for channel-driven MCP automation
|
|
24
|
+
- an Ink-powered `configure` workflow for app config, prompts, MCP servers, and installed skills
|
|
25
25
|
- an `acp` command for running Hooman as an Agent Client Protocol (ACP) agent over stdio
|
|
26
26
|
|
|
27
27
|
## Features
|
|
@@ -32,6 +32,7 @@ It gives you:
|
|
|
32
32
|
- MCP server `instructions` support: server-provided instructions are appended to the agent system prompt
|
|
33
33
|
- MCP channel notification support through `hooman daemon --channels`
|
|
34
34
|
- Skill discovery / install / removal through the integrated configure flow
|
|
35
|
+
- Toolkit-oriented architecture with configurable tools, prompts, memory, and transports
|
|
35
36
|
- Interactive terminal UI for chat and configuration
|
|
36
37
|
|
|
37
38
|
## Requirements
|
|
@@ -157,18 +158,20 @@ hooman daemon --channels --yolo
|
|
|
157
158
|
|
|
158
159
|
### Feature Flags
|
|
159
160
|
|
|
160
|
-
Runtime tools and prompt sections are controlled from `config.json` under `
|
|
161
|
+
Runtime tools and prompt sections are controlled from `config.json` under `tools`:
|
|
161
162
|
|
|
162
|
-
- `
|
|
163
|
-
- `
|
|
164
|
-
- `
|
|
165
|
-
- `
|
|
166
|
-
- `
|
|
163
|
+
- `tools.fetch.enabled`
|
|
164
|
+
- `tools.filesystem.enabled`
|
|
165
|
+
- `tools.shell.enabled`
|
|
166
|
+
- `tools.ltm.enabled`
|
|
167
|
+
- `tools.wiki.enabled`
|
|
168
|
+
- `tools.mcp.enabled` (enables MCP management tools + prefixed MCP server tools/instructions)
|
|
169
|
+
- `tools.skills.enabled` (enables skills management tools + skills prompt sections)
|
|
167
170
|
|
|
168
171
|
Both `ltm` and `wiki` include dedicated Chroma settings under:
|
|
169
172
|
|
|
170
|
-
- `
|
|
171
|
-
- `
|
|
173
|
+
- `tools.ltm.chroma` (default collection: `memory`)
|
|
174
|
+
- `tools.wiki.chroma` (default collection: `wiki`)
|
|
172
175
|
|
|
173
176
|
### `hooman configure`
|
|
174
177
|
|
|
@@ -211,7 +214,7 @@ Hooman stores its data in:
|
|
|
211
214
|
|
|
212
215
|
Important files and folders:
|
|
213
216
|
|
|
214
|
-
- `config.json` - app name, LLM provider/model, tool
|
|
217
|
+
- `config.json` - app name, LLM provider/model, tool flags, LTM/wiki settings, compaction
|
|
215
218
|
- `instructions.md` - system instructions used to build the agent prompt
|
|
216
219
|
- `mcp.json` - MCP server definitions
|
|
217
220
|
- `skills/` - installed skills
|
|
@@ -231,9 +234,6 @@ This is the shape managed by `hooman configure`:
|
|
|
231
234
|
"params": {}
|
|
232
235
|
},
|
|
233
236
|
"tools": {
|
|
234
|
-
"allowed": []
|
|
235
|
-
},
|
|
236
|
-
"features": {
|
|
237
237
|
"fetch": {
|
|
238
238
|
"enabled": true
|
|
239
239
|
},
|
|
@@ -260,6 +260,12 @@ This is the shape managed by `hooman configure`:
|
|
|
260
260
|
"wiki": "wiki"
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
|
+
},
|
|
264
|
+
"mcp": {
|
|
265
|
+
"enabled": false
|
|
266
|
+
},
|
|
267
|
+
"skills": {
|
|
268
|
+
"enabled": false
|
|
263
269
|
}
|
|
264
270
|
},
|
|
265
271
|
"compaction": {
|
|
@@ -269,6 +275,8 @@ This is the shape managed by `hooman configure`:
|
|
|
269
275
|
}
|
|
270
276
|
```
|
|
271
277
|
|
|
278
|
+
Tool approvals are session-scoped and are not persisted in `config.json`.
|
|
279
|
+
|
|
272
280
|
Supported `llm.provider` values:
|
|
273
281
|
|
|
274
282
|
- `ollama`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hoomanjs",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Bun-powered
|
|
3
|
+
"version": "1.12.1",
|
|
4
|
+
"description": "Hackable Bun-powered AI agent toolkit for building local CLI, ACP, MCP, and channel-driven workflows.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vaibhav Pandey",
|
|
7
7
|
"email": "contact@vaibhavpandey.com"
|
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
|
@@ -26,7 +26,8 @@ export function createMcpConfig(path: string): Config {
|
|
|
26
26
|
|
|
27
27
|
export function createMcpManager(
|
|
28
28
|
config: Config,
|
|
29
|
+
acp = false,
|
|
29
30
|
mcpServers: readonly NamedMcpTransport[] = [],
|
|
30
31
|
): Manager {
|
|
31
|
-
return new Manager(config, mcpServers);
|
|
32
|
+
return new Manager(config, acp, mcpServers);
|
|
32
33
|
}
|
package/src/core/mcp/manager.ts
CHANGED
|
@@ -121,7 +121,7 @@ function readSourceValue(value: unknown): string | undefined {
|
|
|
121
121
|
*/
|
|
122
122
|
export class Manager {
|
|
123
123
|
private instances: Map<string, McpClient> | null = null;
|
|
124
|
-
private readonly
|
|
124
|
+
private readonly permissions = new Map<
|
|
125
125
|
string,
|
|
126
126
|
{
|
|
127
127
|
resolve: (behavior: ChannelPermissionBehavior) => void;
|
|
@@ -132,7 +132,8 @@ export class Manager {
|
|
|
132
132
|
|
|
133
133
|
public constructor(
|
|
134
134
|
private readonly config: Config,
|
|
135
|
-
private readonly
|
|
135
|
+
private readonly acp = false,
|
|
136
|
+
private readonly servers: readonly NamedMcpTransport[] = [],
|
|
136
137
|
) {}
|
|
137
138
|
|
|
138
139
|
/** Lazily builds clients from the current in-memory config (reloads file first). */
|
|
@@ -148,13 +149,15 @@ export class Manager {
|
|
|
148
149
|
* previous clients (stdio subprocesses, HTTP sessions).
|
|
149
150
|
*/
|
|
150
151
|
public reload(): void {
|
|
151
|
-
this.
|
|
152
|
+
if (!this.acp) {
|
|
153
|
+
this.config.reload();
|
|
154
|
+
}
|
|
152
155
|
const previous = this.instances;
|
|
153
156
|
const next = new Map<string, McpClient>();
|
|
154
157
|
const transports = [
|
|
155
|
-
...this.config.list(),
|
|
156
|
-
// Session-scoped
|
|
157
|
-
...this.
|
|
158
|
+
...(this.acp ? [] : this.config.list()),
|
|
159
|
+
// Session-scoped servers override local config entries on name conflicts.
|
|
160
|
+
...this.servers,
|
|
158
161
|
];
|
|
159
162
|
for (const { name, transport } of transports) {
|
|
160
163
|
next.set(
|
|
@@ -173,11 +176,11 @@ export class Manager {
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
public async disconnect(): Promise<void> {
|
|
176
|
-
for (const [key, pending] of this.
|
|
179
|
+
for (const [key, pending] of this.permissions.entries()) {
|
|
177
180
|
clearTimeout(pending.timer);
|
|
178
181
|
pending.reject(new Error(`Pending permission "${key}" cancelled.`));
|
|
179
182
|
}
|
|
180
|
-
this.
|
|
183
|
+
this.permissions.clear();
|
|
181
184
|
const toClose = this.instances;
|
|
182
185
|
this.instances = null;
|
|
183
186
|
if (!toClose?.size) {
|
|
@@ -281,11 +284,11 @@ export class Manager {
|
|
|
281
284
|
return;
|
|
282
285
|
}
|
|
283
286
|
const key = `${server}:${requestId}`;
|
|
284
|
-
const pending = this.
|
|
287
|
+
const pending = this.permissions.get(key);
|
|
285
288
|
if (!pending) {
|
|
286
289
|
return;
|
|
287
290
|
}
|
|
288
|
-
this.
|
|
291
|
+
this.permissions.delete(key);
|
|
289
292
|
clearTimeout(pending.timer);
|
|
290
293
|
pending.resolve(behavior);
|
|
291
294
|
};
|
|
@@ -390,21 +393,21 @@ export class Manager {
|
|
|
390
393
|
throw new Error("requestId is required.");
|
|
391
394
|
}
|
|
392
395
|
const key = `${server}:${requestId}`;
|
|
393
|
-
if (this.
|
|
396
|
+
if (this.permissions.has(key)) {
|
|
394
397
|
throw new Error(`Permission request "${requestId}" is already pending.`);
|
|
395
398
|
}
|
|
396
399
|
|
|
397
400
|
const response = new Promise<ChannelPermissionBehavior>(
|
|
398
401
|
(resolve, reject) => {
|
|
399
402
|
const timer = setTimeout(() => {
|
|
400
|
-
this.
|
|
403
|
+
this.permissions.delete(key);
|
|
401
404
|
reject(
|
|
402
405
|
new Error(
|
|
403
406
|
`Permission request "${requestId}" timed out after ${timeoutMs}ms.`,
|
|
404
407
|
),
|
|
405
408
|
);
|
|
406
409
|
}, timeoutMs);
|
|
407
|
-
this.
|
|
410
|
+
this.permissions.set(key, { resolve, reject, timer });
|
|
408
411
|
},
|
|
409
412
|
);
|
|
410
413
|
|
|
@@ -439,10 +442,10 @@ export class Manager {
|
|
|
439
442
|
});
|
|
440
443
|
return await response;
|
|
441
444
|
} catch (error) {
|
|
442
|
-
const pending = this.
|
|
445
|
+
const pending = this.permissions.get(key);
|
|
443
446
|
if (pending) {
|
|
444
447
|
clearTimeout(pending.timer);
|
|
445
|
-
this.
|
|
448
|
+
this.permissions.delete(key);
|
|
446
449
|
}
|
|
447
450
|
throw error;
|
|
448
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
|
}
|