mu-harness 0.17.3 → 0.17.5
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/esm/agents/registry.d.ts +5 -0
- package/esm/agents/registry.js +13 -6
- package/esm/harness/create.js +21 -7
- package/esm/harness/types.d.ts +11 -0
- package/esm/skills/registry.d.ts +5 -0
- package/esm/skills/registry.js +8 -3
- package/package.json +3 -3
- package/script/agents/registry.d.ts +5 -0
- package/script/agents/registry.js +13 -6
- package/script/harness/create.js +21 -7
- package/script/harness/types.d.ts +11 -0
- package/script/skills/registry.d.ts +5 -0
- package/script/skills/registry.js +8 -3
package/esm/agents/registry.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface AgentRegistry {
|
|
|
9
9
|
* re-resolved so they pick up the change.
|
|
10
10
|
*/
|
|
11
11
|
add(agent: Agent): void;
|
|
12
|
+
/**
|
|
13
|
+
* Replace the entire set in place (rebuild) — used by hot-reload to reflect
|
|
14
|
+
* created, edited, and deleted definitions. Existing references see the new set.
|
|
15
|
+
*/
|
|
16
|
+
replaceAll(agents: Agent[]): void;
|
|
12
17
|
}
|
|
13
18
|
export declare const grantArg: (tool: string, input: unknown) => string | undefined;
|
|
14
19
|
export declare const toolDecision: (agent: Agent, tool: string, arg?: string) => ToolDecision;
|
package/esm/agents/registry.js
CHANGED
|
@@ -64,9 +64,7 @@ const merge = (base, child) => ({
|
|
|
64
64
|
});
|
|
65
65
|
export const createAgentRegistry = (agents = []) => {
|
|
66
66
|
const raw = new Map();
|
|
67
|
-
|
|
68
|
-
if (!raw.has(agent.name))
|
|
69
|
-
raw.set(agent.name, agent);
|
|
67
|
+
const byName = new Map();
|
|
70
68
|
const resolve = (name, seen) => {
|
|
71
69
|
const agent = raw.get(name);
|
|
72
70
|
if (!agent.extends)
|
|
@@ -78,9 +76,17 @@ export const createAgentRegistry = (agents = []) => {
|
|
|
78
76
|
throw new Error(`AgentRegistry: "${name}" extends unknown agent "${agent.extends}"`);
|
|
79
77
|
return merge(resolve(agent.extends, new Set(seen).add(name)), agent);
|
|
80
78
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
// Rebuild both maps in place so existing holders of this registry see the new set.
|
|
80
|
+
const load = (list) => {
|
|
81
|
+
raw.clear();
|
|
82
|
+
for (const agent of list)
|
|
83
|
+
if (!raw.has(agent.name))
|
|
84
|
+
raw.set(agent.name, agent);
|
|
85
|
+
byName.clear();
|
|
86
|
+
for (const name of raw.keys())
|
|
87
|
+
byName.set(name, resolve(name, new Set()));
|
|
88
|
+
};
|
|
89
|
+
load(agents);
|
|
84
90
|
return {
|
|
85
91
|
list: () => [...byName.values()],
|
|
86
92
|
get: (name) => byName.get(name),
|
|
@@ -91,5 +97,6 @@ export const createAgentRegistry = (agents = []) => {
|
|
|
91
97
|
if (a.extends === agent.name)
|
|
92
98
|
byName.set(name, resolve(name, new Set()));
|
|
93
99
|
},
|
|
100
|
+
replaceAll: (list) => load(list),
|
|
94
101
|
};
|
|
95
102
|
};
|
package/esm/harness/create.js
CHANGED
|
@@ -20,7 +20,7 @@ const TITLE_AGENT = {
|
|
|
20
20
|
tools: [],
|
|
21
21
|
};
|
|
22
22
|
export const createHarness = async (options) => {
|
|
23
|
-
const { hostName, xdg, providers, model, agents: hostAgents = [], skills: hostSkills = [], skillScope, agentScope, agentDirs, title, titleModel, scheduler: enableScheduler = false, approvals, ...sessionDefaults } = options;
|
|
23
|
+
const { hostName, xdg, providers, model, agents: hostAgents = [], defaultAgents = [], skills: hostSkills = [], skillScope, agentScope, agentDirs, title, titleModel, scheduler: enableScheduler = false, approvals, ...sessionDefaults } = options;
|
|
24
24
|
const cwd = options.cwd ?? process.cwd();
|
|
25
25
|
const config = createHarnessConfig({ hostName, xdg });
|
|
26
26
|
const models = createModelRegistry({ providers, default: model });
|
|
@@ -30,9 +30,15 @@ export const createHarness = async (options) => {
|
|
|
30
30
|
const plugins = createPluginStore({ dir: pluginsDir });
|
|
31
31
|
const newId = () => crypto.randomUUID();
|
|
32
32
|
const pluginAgents = (sessionDefaults.plugins ?? []).flatMap((plugin) => plugin.agents ?? []);
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
33
|
+
const loadDiskAgents = async () => [...await loadAgents(localAgentsDir), ...await loadAgents(agentsDir)];
|
|
34
|
+
// Priority (first wins): host > plugin > local dir > config dir > default fallback.
|
|
35
|
+
const mergedAgents = async () => [
|
|
36
|
+
...hostAgents,
|
|
37
|
+
...pluginAgents,
|
|
38
|
+
...await loadDiskAgents(),
|
|
39
|
+
...defaultAgents,
|
|
40
|
+
];
|
|
41
|
+
const agents = createAgentRegistry(await mergedAgents());
|
|
36
42
|
const skillsDir = join(config.configDir, 'skills');
|
|
37
43
|
const cwdSkillsDir = join(cwd, 'skills');
|
|
38
44
|
const envBlock = environmentBlock({
|
|
@@ -48,9 +54,13 @@ export const createHarness = async (options) => {
|
|
|
48
54
|
prepareRequest: ({ system }) => ({ system: system ? `${system}\n\n${envBlock}` : envBlock }),
|
|
49
55
|
};
|
|
50
56
|
const pluginSkills = (sessionDefaults.plugins ?? []).flatMap((plugin) => plugin.skills ?? []);
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
const mergedSkills = async () => [
|
|
58
|
+
...hostSkills,
|
|
59
|
+
...pluginSkills,
|
|
60
|
+
...await loadSkills(cwdSkillsDir),
|
|
61
|
+
...await loadSkills(skillsDir),
|
|
62
|
+
];
|
|
63
|
+
const skills = createSkillRegistry(await mergedSkills());
|
|
54
64
|
const skillWriterTool = createSkillWriterTool({
|
|
55
65
|
dirs: { local: cwdSkillsDir, config: skillsDir },
|
|
56
66
|
registry: skills,
|
|
@@ -179,6 +189,10 @@ export const createHarness = async (options) => {
|
|
|
179
189
|
throw new Error(`unknown sub-agent "${agent}"`);
|
|
180
190
|
return await runSubAgent(def, task, { spawn, runs, parentId });
|
|
181
191
|
},
|
|
192
|
+
reloadDefinitions: async () => {
|
|
193
|
+
agents.replaceAll(await mergedAgents());
|
|
194
|
+
skills.replaceAll(await mergedSkills());
|
|
195
|
+
},
|
|
182
196
|
scheduler,
|
|
183
197
|
tasks,
|
|
184
198
|
commands,
|
package/esm/harness/types.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export type HarnessOptions = HarnessConfigOptions & Omit<AgentSessionConfig, 'pr
|
|
|
14
14
|
providers: Record<string, Provider>;
|
|
15
15
|
model: string;
|
|
16
16
|
agents?: Agent[];
|
|
17
|
+
/**
|
|
18
|
+
* Lowest-priority fallback agents (e.g. a host's built-in primary). Merged
|
|
19
|
+
* last, so disk- or host-defined agents of the same name override them.
|
|
20
|
+
*/
|
|
21
|
+
defaultAgents?: Agent[];
|
|
17
22
|
skills?: Skill[];
|
|
18
23
|
/**
|
|
19
24
|
* Forces the save location of `create_skill`. When set, the model's `scope`
|
|
@@ -57,6 +62,12 @@ export interface Harness {
|
|
|
57
62
|
readonly skills: SkillRegistry;
|
|
58
63
|
readonly subAgents: SubAgentRegistry;
|
|
59
64
|
dispatchSubAgent(agent: string, task: string, parentId: string): Promise<SubAgentResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Re-read the on-disk agent and skill directories and refresh the registries
|
|
67
|
+
* in place (create/edit/delete), preserving host/plugin/default contributions.
|
|
68
|
+
* Lets a host hot-reload definitions without a restart.
|
|
69
|
+
*/
|
|
70
|
+
reloadDefinitions(): Promise<void>;
|
|
60
71
|
readonly commands: CommandRegistry;
|
|
61
72
|
readonly scheduler?: Scheduler;
|
|
62
73
|
readonly tasks?: TaskStore;
|
package/esm/skills/registry.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ export interface SkillRegistry {
|
|
|
3
3
|
list(): Skill[];
|
|
4
4
|
get(name: string): Skill | undefined;
|
|
5
5
|
add(skill: Skill): void;
|
|
6
|
+
/**
|
|
7
|
+
* Replace the entire set in place (rebuild) — used by hot-reload to reflect
|
|
8
|
+
* created, edited, and deleted skills. Existing references see the new set.
|
|
9
|
+
*/
|
|
10
|
+
replaceAll(skills: Skill[]): void;
|
|
6
11
|
select(names: string[]): SkillRegistry;
|
|
7
12
|
}
|
|
8
13
|
export declare const createSkillRegistry: (skills?: Skill[]) => SkillRegistry;
|
package/esm/skills/registry.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export const createSkillRegistry = (skills = []) => {
|
|
2
2
|
const byName = new Map();
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const load = (list) => {
|
|
4
|
+
byName.clear();
|
|
5
|
+
for (const skill of list)
|
|
6
|
+
if (!byName.has(skill.name))
|
|
7
|
+
byName.set(skill.name, skill);
|
|
8
|
+
};
|
|
9
|
+
load(skills);
|
|
6
10
|
const view = (allow) => ({
|
|
7
11
|
list: () => [...byName.values()].filter((skill) => !allow || allow.has(skill.name)),
|
|
8
12
|
get: (name) => (!allow || allow.has(name) ? byName.get(name) : undefined),
|
|
@@ -10,6 +14,7 @@ export const createSkillRegistry = (skills = []) => {
|
|
|
10
14
|
byName.set(skill.name, skill);
|
|
11
15
|
allow?.add(skill.name);
|
|
12
16
|
},
|
|
17
|
+
replaceAll: (list) => load(list),
|
|
13
18
|
select: (names) => view(new Set(allow ? names.filter((name) => allow.has(name)) : names)),
|
|
14
19
|
});
|
|
15
20
|
return view();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mu-harness",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.5",
|
|
4
4
|
"description": "Agent harness: createHarness wires mu-core into a host — XDG paths, model registry, plugins, disk-loaded agents & skills, sub-agents, sessions (JSONL + SQLite catalog), slash commands, permission/approval hooks, an optional scheduler, and a composable TUI chat app",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./script/index.js",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"@swc/wasm-typescript": "^1.15.0",
|
|
24
24
|
"cli-highlight": "^2.1.11",
|
|
25
25
|
"croner": "^9.0.0",
|
|
26
|
-
"mu-core": "^0.17.
|
|
27
|
-
"mu-tui": "^0.17.
|
|
26
|
+
"mu-core": "^0.17.5",
|
|
27
|
+
"mu-tui": "^0.17.5"
|
|
28
28
|
},
|
|
29
29
|
"_generatedBy": "dnt@dev",
|
|
30
30
|
"types": "./esm/index.d.ts"
|
|
@@ -9,6 +9,11 @@ export interface AgentRegistry {
|
|
|
9
9
|
* re-resolved so they pick up the change.
|
|
10
10
|
*/
|
|
11
11
|
add(agent: Agent): void;
|
|
12
|
+
/**
|
|
13
|
+
* Replace the entire set in place (rebuild) — used by hot-reload to reflect
|
|
14
|
+
* created, edited, and deleted definitions. Existing references see the new set.
|
|
15
|
+
*/
|
|
16
|
+
replaceAll(agents: Agent[]): void;
|
|
12
17
|
}
|
|
13
18
|
export declare const grantArg: (tool: string, input: unknown) => string | undefined;
|
|
14
19
|
export declare const toolDecision: (agent: Agent, tool: string, arg?: string) => ToolDecision;
|
|
@@ -70,9 +70,7 @@ const merge = (base, child) => ({
|
|
|
70
70
|
});
|
|
71
71
|
const createAgentRegistry = (agents = []) => {
|
|
72
72
|
const raw = new Map();
|
|
73
|
-
|
|
74
|
-
if (!raw.has(agent.name))
|
|
75
|
-
raw.set(agent.name, agent);
|
|
73
|
+
const byName = new Map();
|
|
76
74
|
const resolve = (name, seen) => {
|
|
77
75
|
const agent = raw.get(name);
|
|
78
76
|
if (!agent.extends)
|
|
@@ -84,9 +82,17 @@ const createAgentRegistry = (agents = []) => {
|
|
|
84
82
|
throw new Error(`AgentRegistry: "${name}" extends unknown agent "${agent.extends}"`);
|
|
85
83
|
return merge(resolve(agent.extends, new Set(seen).add(name)), agent);
|
|
86
84
|
};
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
// Rebuild both maps in place so existing holders of this registry see the new set.
|
|
86
|
+
const load = (list) => {
|
|
87
|
+
raw.clear();
|
|
88
|
+
for (const agent of list)
|
|
89
|
+
if (!raw.has(agent.name))
|
|
90
|
+
raw.set(agent.name, agent);
|
|
91
|
+
byName.clear();
|
|
92
|
+
for (const name of raw.keys())
|
|
93
|
+
byName.set(name, resolve(name, new Set()));
|
|
94
|
+
};
|
|
95
|
+
load(agents);
|
|
90
96
|
return {
|
|
91
97
|
list: () => [...byName.values()],
|
|
92
98
|
get: (name) => byName.get(name),
|
|
@@ -97,6 +103,7 @@ const createAgentRegistry = (agents = []) => {
|
|
|
97
103
|
if (a.extends === agent.name)
|
|
98
104
|
byName.set(name, resolve(name, new Set()));
|
|
99
105
|
},
|
|
106
|
+
replaceAll: (list) => load(list),
|
|
100
107
|
};
|
|
101
108
|
};
|
|
102
109
|
exports.createAgentRegistry = createAgentRegistry;
|
package/script/harness/create.js
CHANGED
|
@@ -26,7 +26,7 @@ const TITLE_AGENT = {
|
|
|
26
26
|
tools: [],
|
|
27
27
|
};
|
|
28
28
|
const createHarness = async (options) => {
|
|
29
|
-
const { hostName, xdg, providers, model, agents: hostAgents = [], skills: hostSkills = [], skillScope, agentScope, agentDirs, title, titleModel, scheduler: enableScheduler = false, approvals, ...sessionDefaults } = options;
|
|
29
|
+
const { hostName, xdg, providers, model, agents: hostAgents = [], defaultAgents = [], skills: hostSkills = [], skillScope, agentScope, agentDirs, title, titleModel, scheduler: enableScheduler = false, approvals, ...sessionDefaults } = options;
|
|
30
30
|
const cwd = options.cwd ?? node_process_1.default.cwd();
|
|
31
31
|
const config = (0, index_js_3.createHarnessConfig)({ hostName, xdg });
|
|
32
32
|
const models = (0, models_js_1.createModelRegistry)({ providers, default: model });
|
|
@@ -36,9 +36,15 @@ const createHarness = async (options) => {
|
|
|
36
36
|
const plugins = (0, index_js_6.createPluginStore)({ dir: pluginsDir });
|
|
37
37
|
const newId = () => crypto.randomUUID();
|
|
38
38
|
const pluginAgents = (sessionDefaults.plugins ?? []).flatMap((plugin) => plugin.agents ?? []);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
39
|
+
const loadDiskAgents = async () => [...await (0, index_js_1.loadAgents)(localAgentsDir), ...await (0, index_js_1.loadAgents)(agentsDir)];
|
|
40
|
+
// Priority (first wins): host > plugin > local dir > config dir > default fallback.
|
|
41
|
+
const mergedAgents = async () => [
|
|
42
|
+
...hostAgents,
|
|
43
|
+
...pluginAgents,
|
|
44
|
+
...await loadDiskAgents(),
|
|
45
|
+
...defaultAgents,
|
|
46
|
+
];
|
|
47
|
+
const agents = (0, index_js_1.createAgentRegistry)(await mergedAgents());
|
|
42
48
|
const skillsDir = (0, node_path_1.join)(config.configDir, 'skills');
|
|
43
49
|
const cwdSkillsDir = (0, node_path_1.join)(cwd, 'skills');
|
|
44
50
|
const envBlock = (0, environment_js_1.environmentBlock)({
|
|
@@ -54,9 +60,13 @@ const createHarness = async (options) => {
|
|
|
54
60
|
prepareRequest: ({ system }) => ({ system: system ? `${system}\n\n${envBlock}` : envBlock }),
|
|
55
61
|
};
|
|
56
62
|
const pluginSkills = (sessionDefaults.plugins ?? []).flatMap((plugin) => plugin.skills ?? []);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
const mergedSkills = async () => [
|
|
64
|
+
...hostSkills,
|
|
65
|
+
...pluginSkills,
|
|
66
|
+
...await (0, index_js_9.loadSkills)(cwdSkillsDir),
|
|
67
|
+
...await (0, index_js_9.loadSkills)(skillsDir),
|
|
68
|
+
];
|
|
69
|
+
const skills = (0, index_js_9.createSkillRegistry)(await mergedSkills());
|
|
60
70
|
const skillWriterTool = (0, index_js_9.createSkillWriterTool)({
|
|
61
71
|
dirs: { local: cwdSkillsDir, config: skillsDir },
|
|
62
72
|
registry: skills,
|
|
@@ -185,6 +195,10 @@ const createHarness = async (options) => {
|
|
|
185
195
|
throw new Error(`unknown sub-agent "${agent}"`);
|
|
186
196
|
return await (0, index_js_10.runSubAgent)(def, task, { spawn, runs, parentId });
|
|
187
197
|
},
|
|
198
|
+
reloadDefinitions: async () => {
|
|
199
|
+
agents.replaceAll(await mergedAgents());
|
|
200
|
+
skills.replaceAll(await mergedSkills());
|
|
201
|
+
},
|
|
188
202
|
scheduler,
|
|
189
203
|
tasks,
|
|
190
204
|
commands,
|
|
@@ -14,6 +14,11 @@ export type HarnessOptions = HarnessConfigOptions & Omit<AgentSessionConfig, 'pr
|
|
|
14
14
|
providers: Record<string, Provider>;
|
|
15
15
|
model: string;
|
|
16
16
|
agents?: Agent[];
|
|
17
|
+
/**
|
|
18
|
+
* Lowest-priority fallback agents (e.g. a host's built-in primary). Merged
|
|
19
|
+
* last, so disk- or host-defined agents of the same name override them.
|
|
20
|
+
*/
|
|
21
|
+
defaultAgents?: Agent[];
|
|
17
22
|
skills?: Skill[];
|
|
18
23
|
/**
|
|
19
24
|
* Forces the save location of `create_skill`. When set, the model's `scope`
|
|
@@ -57,6 +62,12 @@ export interface Harness {
|
|
|
57
62
|
readonly skills: SkillRegistry;
|
|
58
63
|
readonly subAgents: SubAgentRegistry;
|
|
59
64
|
dispatchSubAgent(agent: string, task: string, parentId: string): Promise<SubAgentResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Re-read the on-disk agent and skill directories and refresh the registries
|
|
67
|
+
* in place (create/edit/delete), preserving host/plugin/default contributions.
|
|
68
|
+
* Lets a host hot-reload definitions without a restart.
|
|
69
|
+
*/
|
|
70
|
+
reloadDefinitions(): Promise<void>;
|
|
60
71
|
readonly commands: CommandRegistry;
|
|
61
72
|
readonly scheduler?: Scheduler;
|
|
62
73
|
readonly tasks?: TaskStore;
|
|
@@ -3,6 +3,11 @@ export interface SkillRegistry {
|
|
|
3
3
|
list(): Skill[];
|
|
4
4
|
get(name: string): Skill | undefined;
|
|
5
5
|
add(skill: Skill): void;
|
|
6
|
+
/**
|
|
7
|
+
* Replace the entire set in place (rebuild) — used by hot-reload to reflect
|
|
8
|
+
* created, edited, and deleted skills. Existing references see the new set.
|
|
9
|
+
*/
|
|
10
|
+
replaceAll(skills: Skill[]): void;
|
|
6
11
|
select(names: string[]): SkillRegistry;
|
|
7
12
|
}
|
|
8
13
|
export declare const createSkillRegistry: (skills?: Skill[]) => SkillRegistry;
|
|
@@ -3,9 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createSkillRegistry = void 0;
|
|
4
4
|
const createSkillRegistry = (skills = []) => {
|
|
5
5
|
const byName = new Map();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const load = (list) => {
|
|
7
|
+
byName.clear();
|
|
8
|
+
for (const skill of list)
|
|
9
|
+
if (!byName.has(skill.name))
|
|
10
|
+
byName.set(skill.name, skill);
|
|
11
|
+
};
|
|
12
|
+
load(skills);
|
|
9
13
|
const view = (allow) => ({
|
|
10
14
|
list: () => [...byName.values()].filter((skill) => !allow || allow.has(skill.name)),
|
|
11
15
|
get: (name) => (!allow || allow.has(name) ? byName.get(name) : undefined),
|
|
@@ -13,6 +17,7 @@ const createSkillRegistry = (skills = []) => {
|
|
|
13
17
|
byName.set(skill.name, skill);
|
|
14
18
|
allow?.add(skill.name);
|
|
15
19
|
},
|
|
20
|
+
replaceAll: (list) => load(list),
|
|
16
21
|
select: (names) => view(new Set(allow ? names.filter((name) => allow.has(name)) : names)),
|
|
17
22
|
});
|
|
18
23
|
return view();
|