@kontextmind/kxm 0.7.7 → 0.7.8
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/.claude-plugin/marketplace.json +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +210 -0
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/runtime.ts +1 -0
- package/plugins/kxm/src/subagent-control.ts +312 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.8",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
|
@@ -17036,7 +17036,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
|
|
|
17036
17036
|
}
|
|
17037
17037
|
|
|
17038
17038
|
// plugins/kxm/src/mcp-server.ts
|
|
17039
|
-
var VERSION = "0.7.
|
|
17039
|
+
var VERSION = "0.7.8";
|
|
17040
17040
|
var inbox = /* @__PURE__ */ new Map();
|
|
17041
17041
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17042
17042
|
var meshClient;
|
|
@@ -28815,6 +28815,212 @@ function executeSshRun(params) {
|
|
|
28815
28815
|
durationMs: Date.now() - startTime
|
|
28816
28816
|
};
|
|
28817
28817
|
}
|
|
28818
|
+
|
|
28819
|
+
// plugins/kxm/src/subagent-control.ts
|
|
28820
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28821
|
+
var SUBAGENT_TYPES = Object.freeze([
|
|
28822
|
+
"general",
|
|
28823
|
+
"Explore",
|
|
28824
|
+
"Plan",
|
|
28825
|
+
"Reviewer",
|
|
28826
|
+
"Auditor"
|
|
28827
|
+
]);
|
|
28828
|
+
var DEFAULT_SUBAGENT_MODELS = Object.freeze({
|
|
28829
|
+
general: "grok/grok-4.6",
|
|
28830
|
+
Explore: "openrouter/qwen/qwen3-coder-plus",
|
|
28831
|
+
Plan: "claude/fable",
|
|
28832
|
+
Reviewer: "openai/gpt-5.6-sol",
|
|
28833
|
+
Auditor: "claude/fable"
|
|
28834
|
+
});
|
|
28835
|
+
function narrowSubagentTools(requestedTools, parentTools) {
|
|
28836
|
+
if (!requestedTools || requestedTools.length === 0) {
|
|
28837
|
+
return parentTools ? [...parentTools] : ["read", "grep", "find"];
|
|
28838
|
+
}
|
|
28839
|
+
if (!parentTools || parentTools.length === 0) {
|
|
28840
|
+
return [...requestedTools];
|
|
28841
|
+
}
|
|
28842
|
+
const parentSet = new Set(parentTools);
|
|
28843
|
+
const narrowed = [];
|
|
28844
|
+
for (const tool of requestedTools) {
|
|
28845
|
+
if (parentSet.has(tool)) {
|
|
28846
|
+
narrowed.push(tool);
|
|
28847
|
+
}
|
|
28848
|
+
}
|
|
28849
|
+
if (narrowed.length === 0 && requestedTools.length > 0) {
|
|
28850
|
+
throw new Error(
|
|
28851
|
+
`Cannot spawn subagent: requested tools [${requestedTools.join(", ")}] expand beyond parent capabilities.`
|
|
28852
|
+
);
|
|
28853
|
+
}
|
|
28854
|
+
return narrowed;
|
|
28855
|
+
}
|
|
28856
|
+
var SubagentManager = class {
|
|
28857
|
+
subagents = /* @__PURE__ */ new Map();
|
|
28858
|
+
/**
|
|
28859
|
+
* Spawns a new managed subagent with strict capability boundaries.
|
|
28860
|
+
*/
|
|
28861
|
+
spawn(params) {
|
|
28862
|
+
if (!params.prompt || !params.prompt.trim()) {
|
|
28863
|
+
throw new Error('Subagent "prompt" is required.');
|
|
28864
|
+
}
|
|
28865
|
+
if (!params.description || !params.description.trim()) {
|
|
28866
|
+
throw new Error('Subagent "description" is required.');
|
|
28867
|
+
}
|
|
28868
|
+
const type = params.type ?? "general";
|
|
28869
|
+
const model = params.model ?? DEFAULT_SUBAGENT_MODELS[type] ?? "grok/grok-4.6";
|
|
28870
|
+
const thinking = params.thinking ?? "low";
|
|
28871
|
+
const allowedTools = narrowSubagentTools(params.allowed_tools, params.parent_tools);
|
|
28872
|
+
const id = `ag_${randomUUID5().replaceAll("-", "").slice(0, 10)}`;
|
|
28873
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28874
|
+
const record2 = {
|
|
28875
|
+
id,
|
|
28876
|
+
description: params.description.trim(),
|
|
28877
|
+
type,
|
|
28878
|
+
model,
|
|
28879
|
+
thinking,
|
|
28880
|
+
allowedTools,
|
|
28881
|
+
status: params.background === false ? "completed" : "running",
|
|
28882
|
+
turnsMax: params.turns ?? 10,
|
|
28883
|
+
turnsCompleted: 0,
|
|
28884
|
+
prompt: params.prompt.trim(),
|
|
28885
|
+
createdAt: now,
|
|
28886
|
+
updatedAt: now,
|
|
28887
|
+
steeringMessages: []
|
|
28888
|
+
};
|
|
28889
|
+
this.subagents.set(id, record2);
|
|
28890
|
+
return record2;
|
|
28891
|
+
}
|
|
28892
|
+
/**
|
|
28893
|
+
* Dispatches an action from the `agent_control` tool.
|
|
28894
|
+
*/
|
|
28895
|
+
control(params) {
|
|
28896
|
+
const action = params.action;
|
|
28897
|
+
if (action === "info") {
|
|
28898
|
+
const kind = params.kind ?? "active";
|
|
28899
|
+
if (kind === "types") {
|
|
28900
|
+
return {
|
|
28901
|
+
ok: true,
|
|
28902
|
+
action: "info",
|
|
28903
|
+
types: SUBAGENT_TYPES
|
|
28904
|
+
};
|
|
28905
|
+
}
|
|
28906
|
+
if (kind === "models") {
|
|
28907
|
+
return {
|
|
28908
|
+
ok: true,
|
|
28909
|
+
action: "info",
|
|
28910
|
+
result: JSON.stringify(DEFAULT_SUBAGENT_MODELS)
|
|
28911
|
+
};
|
|
28912
|
+
}
|
|
28913
|
+
const active = Array.from(this.subagents.values()).map((ag) => ({
|
|
28914
|
+
id: ag.id,
|
|
28915
|
+
description: ag.description,
|
|
28916
|
+
type: ag.type,
|
|
28917
|
+
status: ag.status
|
|
28918
|
+
}));
|
|
28919
|
+
return {
|
|
28920
|
+
ok: true,
|
|
28921
|
+
action: "info",
|
|
28922
|
+
activeAgents: active
|
|
28923
|
+
};
|
|
28924
|
+
}
|
|
28925
|
+
if (!params.agent_id) {
|
|
28926
|
+
return {
|
|
28927
|
+
ok: false,
|
|
28928
|
+
action,
|
|
28929
|
+
error: 'Parameter "agent_id" is required for result, steer, and stop actions.'
|
|
28930
|
+
};
|
|
28931
|
+
}
|
|
28932
|
+
const agent = this.subagents.get(params.agent_id);
|
|
28933
|
+
if (!agent) {
|
|
28934
|
+
return {
|
|
28935
|
+
ok: false,
|
|
28936
|
+
action,
|
|
28937
|
+
agent_id: params.agent_id,
|
|
28938
|
+
error: `Subagent "${params.agent_id}" not found.`
|
|
28939
|
+
};
|
|
28940
|
+
}
|
|
28941
|
+
if (action === "result") {
|
|
28942
|
+
return {
|
|
28943
|
+
ok: true,
|
|
28944
|
+
action: "result",
|
|
28945
|
+
agent_id: agent.id,
|
|
28946
|
+
status: agent.status,
|
|
28947
|
+
result: agent.output ?? `[Subagent is currently ${agent.status}]`
|
|
28948
|
+
};
|
|
28949
|
+
}
|
|
28950
|
+
if (action === "steer") {
|
|
28951
|
+
if (!params.message || !params.message.trim()) {
|
|
28952
|
+
return {
|
|
28953
|
+
ok: false,
|
|
28954
|
+
action: "steer",
|
|
28955
|
+
agent_id: agent.id,
|
|
28956
|
+
error: 'Parameter "message" is required for steer action.'
|
|
28957
|
+
};
|
|
28958
|
+
}
|
|
28959
|
+
if (agent.status === "completed" || agent.status === "stopped" || agent.status === "failed") {
|
|
28960
|
+
return {
|
|
28961
|
+
ok: false,
|
|
28962
|
+
action: "steer",
|
|
28963
|
+
agent_id: agent.id,
|
|
28964
|
+
error: `Cannot steer subagent "${agent.id}" because it is already ${agent.status}.`
|
|
28965
|
+
};
|
|
28966
|
+
}
|
|
28967
|
+
agent.steeringMessages.push({
|
|
28968
|
+
text: params.message.trim(),
|
|
28969
|
+
injectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28970
|
+
processed: false
|
|
28971
|
+
});
|
|
28972
|
+
agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28973
|
+
return {
|
|
28974
|
+
ok: true,
|
|
28975
|
+
action: "steer",
|
|
28976
|
+
agent_id: agent.id,
|
|
28977
|
+
status: agent.status,
|
|
28978
|
+
steered: true
|
|
28979
|
+
};
|
|
28980
|
+
}
|
|
28981
|
+
if (action === "stop") {
|
|
28982
|
+
agent.status = "stopped";
|
|
28983
|
+
agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28984
|
+
return {
|
|
28985
|
+
ok: true,
|
|
28986
|
+
action: "stop",
|
|
28987
|
+
agent_id: agent.id,
|
|
28988
|
+
status: "stopped",
|
|
28989
|
+
stopped: true
|
|
28990
|
+
};
|
|
28991
|
+
}
|
|
28992
|
+
return {
|
|
28993
|
+
ok: false,
|
|
28994
|
+
action,
|
|
28995
|
+
error: `Unknown control action "${action}".`
|
|
28996
|
+
};
|
|
28997
|
+
}
|
|
28998
|
+
/**
|
|
28999
|
+
* Retrieves an agent record by ID.
|
|
29000
|
+
*/
|
|
29001
|
+
get(agentId) {
|
|
29002
|
+
return this.subagents.get(agentId);
|
|
29003
|
+
}
|
|
29004
|
+
/**
|
|
29005
|
+
* Marks a subagent as completed with output.
|
|
29006
|
+
*/
|
|
29007
|
+
complete(agentId, output) {
|
|
29008
|
+
const agent = this.subagents.get(agentId);
|
|
29009
|
+
if (agent) {
|
|
29010
|
+
agent.status = "completed";
|
|
29011
|
+
agent.output = output;
|
|
29012
|
+
agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29013
|
+
}
|
|
29014
|
+
}
|
|
29015
|
+
/**
|
|
29016
|
+
* Returns all active running subagents.
|
|
29017
|
+
*/
|
|
29018
|
+
listActive() {
|
|
29019
|
+
return Array.from(this.subagents.values()).filter(
|
|
29020
|
+
(ag) => ag.status === "running" || ag.status === "pending" || ag.status === "blocked"
|
|
29021
|
+
);
|
|
29022
|
+
}
|
|
29023
|
+
};
|
|
28818
29024
|
export {
|
|
28819
29025
|
BUILTIN_HARNESSES,
|
|
28820
29026
|
BUILTIN_HARNESS_IDS,
|
|
@@ -28825,6 +29031,7 @@ export {
|
|
|
28825
29031
|
DEFAULT_LOG_MAX_FILES,
|
|
28826
29032
|
DEFAULT_MODES_CONFIG,
|
|
28827
29033
|
DEFAULT_SOCKET_DIR,
|
|
29034
|
+
DEFAULT_SUBAGENT_MODELS,
|
|
28828
29035
|
IMPROVEMENT_REPORT_SCHEMA,
|
|
28829
29036
|
IMPROVEMENT_REPORT_V1_SCHEMA,
|
|
28830
29037
|
LOG_LEVEL_PRIORITY,
|
|
@@ -28835,7 +29042,9 @@ export {
|
|
|
28835
29042
|
PI_NATIVE_BRAKE_PROVIDERS,
|
|
28836
29043
|
PiSession,
|
|
28837
29044
|
SAFE_HARNESS_COMMAND_ID,
|
|
29045
|
+
SUBAGENT_TYPES,
|
|
28838
29046
|
SteelClient,
|
|
29047
|
+
SubagentManager,
|
|
28839
29048
|
VIEWPORT_PRESETS,
|
|
28840
29049
|
VNEXT_ABSENT_MEMORY_REVISION,
|
|
28841
29050
|
VNEXT_EVENT_STORE_SCHEMA_VERSION,
|
|
@@ -28898,6 +29107,7 @@ export {
|
|
|
28898
29107
|
isVnextRuntimeContextClosed,
|
|
28899
29108
|
isWindowsHarnessShim,
|
|
28900
29109
|
loadModesConfig,
|
|
29110
|
+
narrowSubagentTools,
|
|
28901
29111
|
newVnextAssignmentId,
|
|
28902
29112
|
newVnextAttemptId,
|
|
28903
29113
|
newVnextCommandId,
|
package/plugins/kxm/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
|
|
|
7
7
|
import { deliverInboxNotification } from "./inbox.ts";
|
|
8
8
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
9
9
|
|
|
10
|
-
const VERSION = "0.7.
|
|
10
|
+
const VERSION = "0.7.8";
|
|
11
11
|
const inbox = new Map<string, MessageRecord>();
|
|
12
12
|
const notifiedInbox = new Set<string>();
|
|
13
13
|
let meshClient: HubClient | undefined;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supervisory agent control, in-process real-time steering, and work-splitting.
|
|
3
|
+
*
|
|
4
|
+
* Implements:
|
|
5
|
+
* - Dual-tool control plane: `agent` (spawn) and `agent_control` (manage, poll, steer, stop).
|
|
6
|
+
* - Strict capability narrowing via `allowed_tools[]` allowlists.
|
|
7
|
+
* - Mid-flight steering instruction injection.
|
|
8
|
+
* - Subagent state lifecycle management (working, blocked, idle, done, error).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { assertCommandSeatbelt } from "./safety-integrity.ts";
|
|
13
|
+
|
|
14
|
+
export type SubagentType = "general" | "Explore" | "Plan" | "Reviewer" | "Auditor";
|
|
15
|
+
export type SubagentStatus = "pending" | "running" | "blocked" | "completed" | "stopped" | "failed";
|
|
16
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high";
|
|
17
|
+
|
|
18
|
+
export interface AgentSpawnParams {
|
|
19
|
+
prompt: string;
|
|
20
|
+
description: string;
|
|
21
|
+
type?: SubagentType | undefined;
|
|
22
|
+
model?: string | undefined;
|
|
23
|
+
thinking?: ThinkingLevel | undefined;
|
|
24
|
+
allowed_tools?: string[] | undefined;
|
|
25
|
+
turns?: number | undefined;
|
|
26
|
+
background?: boolean | undefined;
|
|
27
|
+
parent_tools?: readonly string[] | undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AgentControlParams {
|
|
31
|
+
action: "info" | "result" | "steer" | "stop";
|
|
32
|
+
kind?: "types" | "models" | "active" | undefined;
|
|
33
|
+
agent_id?: string | undefined;
|
|
34
|
+
message?: string | undefined;
|
|
35
|
+
turns?: number | undefined;
|
|
36
|
+
verbose?: boolean | undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SubagentRecord {
|
|
40
|
+
id: string;
|
|
41
|
+
description: string;
|
|
42
|
+
type: SubagentType;
|
|
43
|
+
model: string;
|
|
44
|
+
thinking: ThinkingLevel;
|
|
45
|
+
allowedTools: string[];
|
|
46
|
+
status: SubagentStatus;
|
|
47
|
+
turnsMax: number;
|
|
48
|
+
turnsCompleted: number;
|
|
49
|
+
prompt: string;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
updatedAt: string;
|
|
52
|
+
steeringMessages: Array<{ text: string; injectedAt: string; processed: boolean }>;
|
|
53
|
+
output?: string | undefined;
|
|
54
|
+
error?: string | undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SubagentExecutionReceipt {
|
|
58
|
+
ok: boolean;
|
|
59
|
+
action: string;
|
|
60
|
+
agent_id?: string | undefined;
|
|
61
|
+
status?: SubagentStatus | undefined;
|
|
62
|
+
activeAgents?: Array<{ id: string; description: string; type: SubagentType; status: SubagentStatus }> | undefined;
|
|
63
|
+
types?: readonly SubagentType[] | undefined;
|
|
64
|
+
result?: string | undefined;
|
|
65
|
+
steered?: boolean | undefined;
|
|
66
|
+
stopped?: boolean | undefined;
|
|
67
|
+
error?: string | undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const SUBAGENT_TYPES: readonly SubagentType[] = Object.freeze([
|
|
71
|
+
"general",
|
|
72
|
+
"Explore",
|
|
73
|
+
"Plan",
|
|
74
|
+
"Reviewer",
|
|
75
|
+
"Auditor",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
export const DEFAULT_SUBAGENT_MODELS: Readonly<Record<SubagentType, string>> = Object.freeze({
|
|
79
|
+
general: "grok/grok-4.6",
|
|
80
|
+
Explore: "openrouter/qwen/qwen3-coder-plus",
|
|
81
|
+
Plan: "claude/fable",
|
|
82
|
+
Reviewer: "openai/gpt-5.6-sol",
|
|
83
|
+
Auditor: "claude/fable",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validates and intersects child allowed tools against the parent tool surface.
|
|
88
|
+
* Enforces the safety invariant that a child can only narrow, never expand authority.
|
|
89
|
+
*/
|
|
90
|
+
export function narrowSubagentTools(
|
|
91
|
+
requestedTools: readonly string[] | undefined,
|
|
92
|
+
parentTools?: readonly string[] | undefined,
|
|
93
|
+
): string[] {
|
|
94
|
+
if (!requestedTools || requestedTools.length === 0) {
|
|
95
|
+
return parentTools ? [...parentTools] : ["read", "grep", "find"];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!parentTools || parentTools.length === 0) {
|
|
99
|
+
return [...requestedTools];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const parentSet = new Set(parentTools);
|
|
103
|
+
const narrowed: string[] = [];
|
|
104
|
+
|
|
105
|
+
for (const tool of requestedTools) {
|
|
106
|
+
if (parentSet.has(tool)) {
|
|
107
|
+
narrowed.push(tool);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (narrowed.length === 0 && requestedTools.length > 0) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Cannot spawn subagent: requested tools [${requestedTools.join(", ")}] expand beyond parent capabilities.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return narrowed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* In-Process Subagent Manager.
|
|
122
|
+
*/
|
|
123
|
+
export class SubagentManager {
|
|
124
|
+
private readonly subagents = new Map<string, SubagentRecord>();
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Spawns a new managed subagent with strict capability boundaries.
|
|
128
|
+
*/
|
|
129
|
+
spawn(params: AgentSpawnParams): SubagentRecord {
|
|
130
|
+
if (!params.prompt || !params.prompt.trim()) {
|
|
131
|
+
throw new Error('Subagent "prompt" is required.');
|
|
132
|
+
}
|
|
133
|
+
if (!params.description || !params.description.trim()) {
|
|
134
|
+
throw new Error('Subagent "description" is required.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const type = params.type ?? "general";
|
|
138
|
+
const model = params.model ?? DEFAULT_SUBAGENT_MODELS[type] ?? "grok/grok-4.6";
|
|
139
|
+
const thinking = params.thinking ?? "low";
|
|
140
|
+
const allowedTools = narrowSubagentTools(params.allowed_tools, params.parent_tools);
|
|
141
|
+
const id = `ag_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
|
|
142
|
+
const now = new Date().toISOString();
|
|
143
|
+
|
|
144
|
+
const record: SubagentRecord = {
|
|
145
|
+
id,
|
|
146
|
+
description: params.description.trim(),
|
|
147
|
+
type,
|
|
148
|
+
model,
|
|
149
|
+
thinking,
|
|
150
|
+
allowedTools,
|
|
151
|
+
status: params.background === false ? "completed" : "running",
|
|
152
|
+
turnsMax: params.turns ?? 10,
|
|
153
|
+
turnsCompleted: 0,
|
|
154
|
+
prompt: params.prompt.trim(),
|
|
155
|
+
createdAt: now,
|
|
156
|
+
updatedAt: now,
|
|
157
|
+
steeringMessages: [],
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
this.subagents.set(id, record);
|
|
161
|
+
return record;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Dispatches an action from the `agent_control` tool.
|
|
166
|
+
*/
|
|
167
|
+
control(params: AgentControlParams): SubagentExecutionReceipt {
|
|
168
|
+
const action = params.action;
|
|
169
|
+
|
|
170
|
+
// 1. Action: "info"
|
|
171
|
+
if (action === "info") {
|
|
172
|
+
const kind = params.kind ?? "active";
|
|
173
|
+
if (kind === "types") {
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
action: "info",
|
|
177
|
+
types: SUBAGENT_TYPES,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (kind === "models") {
|
|
181
|
+
return {
|
|
182
|
+
ok: true,
|
|
183
|
+
action: "info",
|
|
184
|
+
result: JSON.stringify(DEFAULT_SUBAGENT_MODELS),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const active = Array.from(this.subagents.values()).map((ag) => ({
|
|
188
|
+
id: ag.id,
|
|
189
|
+
description: ag.description,
|
|
190
|
+
type: ag.type,
|
|
191
|
+
status: ag.status,
|
|
192
|
+
}));
|
|
193
|
+
return {
|
|
194
|
+
ok: true,
|
|
195
|
+
action: "info",
|
|
196
|
+
activeAgents: active,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!params.agent_id) {
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
action,
|
|
204
|
+
error: 'Parameter "agent_id" is required for result, steer, and stop actions.',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const agent = this.subagents.get(params.agent_id);
|
|
209
|
+
if (!agent) {
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
action,
|
|
213
|
+
agent_id: params.agent_id,
|
|
214
|
+
error: `Subagent "${params.agent_id}" not found.`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 2. Action: "result"
|
|
219
|
+
if (action === "result") {
|
|
220
|
+
return {
|
|
221
|
+
ok: true,
|
|
222
|
+
action: "result",
|
|
223
|
+
agent_id: agent.id,
|
|
224
|
+
status: agent.status,
|
|
225
|
+
result: agent.output ?? `[Subagent is currently ${agent.status}]`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 3. Action: "steer"
|
|
230
|
+
if (action === "steer") {
|
|
231
|
+
if (!params.message || !params.message.trim()) {
|
|
232
|
+
return {
|
|
233
|
+
ok: false,
|
|
234
|
+
action: "steer",
|
|
235
|
+
agent_id: agent.id,
|
|
236
|
+
error: 'Parameter "message" is required for steer action.',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (agent.status === "completed" || agent.status === "stopped" || agent.status === "failed") {
|
|
241
|
+
return {
|
|
242
|
+
ok: false,
|
|
243
|
+
action: "steer",
|
|
244
|
+
agent_id: agent.id,
|
|
245
|
+
error: `Cannot steer subagent "${agent.id}" because it is already ${agent.status}.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
agent.steeringMessages.push({
|
|
250
|
+
text: params.message.trim(),
|
|
251
|
+
injectedAt: new Date().toISOString(),
|
|
252
|
+
processed: false,
|
|
253
|
+
});
|
|
254
|
+
agent.updatedAt = new Date().toISOString();
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
ok: true,
|
|
258
|
+
action: "steer",
|
|
259
|
+
agent_id: agent.id,
|
|
260
|
+
status: agent.status,
|
|
261
|
+
steered: true,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 4. Action: "stop"
|
|
266
|
+
if (action === "stop") {
|
|
267
|
+
agent.status = "stopped";
|
|
268
|
+
agent.updatedAt = new Date().toISOString();
|
|
269
|
+
return {
|
|
270
|
+
ok: true,
|
|
271
|
+
action: "stop",
|
|
272
|
+
agent_id: agent.id,
|
|
273
|
+
status: "stopped",
|
|
274
|
+
stopped: true,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
action,
|
|
281
|
+
error: `Unknown control action "${action}".`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Retrieves an agent record by ID.
|
|
287
|
+
*/
|
|
288
|
+
get(agentId: string): SubagentRecord | undefined {
|
|
289
|
+
return this.subagents.get(agentId);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Marks a subagent as completed with output.
|
|
294
|
+
*/
|
|
295
|
+
complete(agentId: string, output: string): void {
|
|
296
|
+
const agent = this.subagents.get(agentId);
|
|
297
|
+
if (agent) {
|
|
298
|
+
agent.status = "completed";
|
|
299
|
+
agent.output = output;
|
|
300
|
+
agent.updatedAt = new Date().toISOString();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Returns all active running subagents.
|
|
306
|
+
*/
|
|
307
|
+
listActive(): SubagentRecord[] {
|
|
308
|
+
return Array.from(this.subagents.values()).filter(
|
|
309
|
+
(ag) => ag.status === "running" || ag.status === "pending" || ag.status === "blocked",
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|