@femtomc/mu-agent 26.2.72 → 26.2.74
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 +24 -45
- package/dist/extensions/branding.d.ts +1 -1
- package/dist/extensions/branding.js +3 -3
- package/dist/extensions/index.d.ts +3 -17
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +5 -19
- package/dist/extensions/mu-operator.d.ts +2 -2
- package/dist/extensions/mu-operator.d.ts.map +1 -1
- package/dist/extensions/mu-operator.js +2 -6
- package/dist/extensions/mu-serve.d.ts +2 -2
- package/dist/extensions/mu-serve.d.ts.map +1 -1
- package/dist/extensions/mu-serve.js +2 -14
- package/dist/extensions/shared.d.ts +2 -21
- package/dist/extensions/shared.d.ts.map +1 -1
- package/dist/extensions/shared.js +0 -90
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/operator.d.ts +15 -0
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +366 -14
- package/dist/session_factory.d.ts +12 -0
- package/dist/session_factory.d.ts.map +1 -1
- package/dist/session_factory.js +21 -2
- package/package.json +2 -3
- package/prompts/roles/operator.md +22 -45
- package/prompts/roles/orchestrator.md +17 -12
- package/prompts/roles/reviewer.md +7 -8
- package/prompts/roles/worker.md +6 -11
- package/dist/extensions/activities.d.ts +0 -7
- package/dist/extensions/activities.d.ts.map +0 -1
- package/dist/extensions/activities.js +0 -236
- package/dist/extensions/cron.d.ts +0 -7
- package/dist/extensions/cron.d.ts.map +0 -1
- package/dist/extensions/cron.js +0 -247
- package/dist/extensions/heartbeats.d.ts +0 -7
- package/dist/extensions/heartbeats.d.ts.map +0 -1
- package/dist/extensions/heartbeats.js +0 -192
- package/dist/extensions/messaging-setup/actions.d.ts +0 -22
- package/dist/extensions/messaging-setup/actions.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/actions.js +0 -229
- package/dist/extensions/messaging-setup/adapters.d.ts +0 -24
- package/dist/extensions/messaging-setup/adapters.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/adapters.js +0 -170
- package/dist/extensions/messaging-setup/index.d.ts +0 -17
- package/dist/extensions/messaging-setup/index.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/index.js +0 -261
- package/dist/extensions/messaging-setup/parser.d.ts +0 -33
- package/dist/extensions/messaging-setup/parser.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/parser.js +0 -240
- package/dist/extensions/messaging-setup/runtime.d.ts +0 -16
- package/dist/extensions/messaging-setup/runtime.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/runtime.js +0 -110
- package/dist/extensions/messaging-setup/types.d.ts +0 -157
- package/dist/extensions/messaging-setup/types.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/types.js +0 -4
- package/dist/extensions/messaging-setup/ui.d.ts +0 -15
- package/dist/extensions/messaging-setup/ui.d.ts.map +0 -1
- package/dist/extensions/messaging-setup/ui.js +0 -173
- package/dist/extensions/messaging-setup.d.ts +0 -3
- package/dist/extensions/messaging-setup.d.ts.map +0 -1
- package/dist/extensions/messaging-setup.js +0 -2
- package/dist/extensions/mu-full-tools.d.ts +0 -10
- package/dist/extensions/mu-full-tools.d.ts.map +0 -1
- package/dist/extensions/mu-full-tools.js +0 -25
- package/dist/extensions/mu-query-tools.d.ts +0 -10
- package/dist/extensions/mu-query-tools.d.ts.map +0 -1
- package/dist/extensions/mu-query-tools.js +0 -11
- package/dist/extensions/operator-command.d.ts +0 -14
- package/dist/extensions/operator-command.d.ts.map +0 -1
- package/dist/extensions/operator-command.js +0 -231
- package/dist/extensions/orchestration-runs-readonly.d.ts +0 -4
- package/dist/extensions/orchestration-runs-readonly.d.ts.map +0 -1
- package/dist/extensions/orchestration-runs-readonly.js +0 -226
- package/dist/extensions/orchestration-runs.d.ts +0 -4
- package/dist/extensions/orchestration-runs.d.ts.map +0 -1
- package/dist/extensions/orchestration-runs.js +0 -315
- package/dist/extensions/server-tools-readonly.d.ts +0 -4
- package/dist/extensions/server-tools-readonly.d.ts.map +0 -1
- package/dist/extensions/server-tools-readonly.js +0 -5
- package/dist/extensions/server-tools.d.ts +0 -25
- package/dist/extensions/server-tools.d.ts.map +0 -1
- package/dist/extensions/server-tools.js +0 -833
- package/prompts/skills/messaging-setup-brief.md +0 -25
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mu_command — Operator mutation tool.
|
|
3
|
-
*
|
|
4
|
-
* Single execution path:
|
|
5
|
-
* - Requires MU_SERVER_URL.
|
|
6
|
-
* - Always POSTs to /api/commands/submit.
|
|
7
|
-
*
|
|
8
|
-
* All command validation/execution must route through the server command pipeline.
|
|
9
|
-
*/
|
|
10
|
-
import { StringEnum } from "@mariozechner/pi-ai";
|
|
11
|
-
import { Type } from "@sinclair/typebox";
|
|
12
|
-
export const MU_COMMAND_TOOL_NAME = "mu_command";
|
|
13
|
-
const READONLY_COMMAND_KINDS = new Set([
|
|
14
|
-
"status",
|
|
15
|
-
"ready",
|
|
16
|
-
"issue_list",
|
|
17
|
-
"issue_get",
|
|
18
|
-
"forum_read",
|
|
19
|
-
"run_list",
|
|
20
|
-
"run_status",
|
|
21
|
-
]);
|
|
22
|
-
function trimOrNull(value) {
|
|
23
|
-
if (value == null)
|
|
24
|
-
return null;
|
|
25
|
-
const trimmed = value.trim();
|
|
26
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
27
|
-
}
|
|
28
|
-
function normalizedLimit(value, fallback, min, max) {
|
|
29
|
-
if (value == null || !Number.isFinite(value)) {
|
|
30
|
-
return fallback;
|
|
31
|
-
}
|
|
32
|
-
return Math.max(min, Math.min(max, Math.trunc(value)));
|
|
33
|
-
}
|
|
34
|
-
async function fetchReadOnlyMirror(serverUrl, params) {
|
|
35
|
-
if (!READONLY_COMMAND_KINDS.has(params.kind)) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
const base = serverUrl.replace(/\/+$/, "");
|
|
39
|
-
const limit = normalizedLimit(params.limit, 20, 1, 200);
|
|
40
|
-
let path = null;
|
|
41
|
-
switch (params.kind) {
|
|
42
|
-
case "status":
|
|
43
|
-
path = "/api/status";
|
|
44
|
-
break;
|
|
45
|
-
case "ready":
|
|
46
|
-
path = `/api/issues/ready?limit=${limit}`;
|
|
47
|
-
break;
|
|
48
|
-
case "issue_list":
|
|
49
|
-
path = `/api/issues?status=open&limit=${limit}`;
|
|
50
|
-
break;
|
|
51
|
-
case "issue_get": {
|
|
52
|
-
const issueId = trimOrNull(params.issue_id);
|
|
53
|
-
if (!issueId)
|
|
54
|
-
return { error: "issue_id is required for issue_get mirror fetch" };
|
|
55
|
-
path = `/api/issues/${encodeURIComponent(issueId)}`;
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
case "forum_read": {
|
|
59
|
-
const topic = trimOrNull(params.topic);
|
|
60
|
-
if (!topic)
|
|
61
|
-
return { error: "topic is required for forum_read mirror fetch" };
|
|
62
|
-
path = `/api/forum/read?topic=${encodeURIComponent(topic)}&limit=${limit}`;
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
case "run_list":
|
|
66
|
-
path = `/api/runs?limit=${limit}`;
|
|
67
|
-
break;
|
|
68
|
-
case "run_status": {
|
|
69
|
-
const rootIssueId = trimOrNull(params.root_issue_id);
|
|
70
|
-
if (!rootIssueId)
|
|
71
|
-
return { error: "root_issue_id is required for run_status mirror fetch" };
|
|
72
|
-
path = `/api/runs/${encodeURIComponent(rootIssueId)}`;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
default:
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
if (!path) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const response = await fetch(`${base}${path}`);
|
|
83
|
-
const raw = await response.text();
|
|
84
|
-
let payload;
|
|
85
|
-
try {
|
|
86
|
-
payload = JSON.parse(raw);
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
payload = raw;
|
|
90
|
-
}
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
return { error: `mirror fetch failed (${response.status})`, path, payload };
|
|
93
|
-
}
|
|
94
|
-
return payload;
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
return { error: err instanceof Error ? err.message : String(err), path };
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async function executeViaServer(serverUrl, params) {
|
|
101
|
-
const url = `${serverUrl.replace(/\/+$/, "")}/api/commands/submit`;
|
|
102
|
-
const response = await fetch(url, {
|
|
103
|
-
method: "POST",
|
|
104
|
-
headers: { "Content-Type": "application/json" },
|
|
105
|
-
body: JSON.stringify(params),
|
|
106
|
-
});
|
|
107
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
108
|
-
const raw = await response.text();
|
|
109
|
-
let body = null;
|
|
110
|
-
try {
|
|
111
|
-
body = JSON.parse(raw);
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
body = null;
|
|
115
|
-
}
|
|
116
|
-
if (!body) {
|
|
117
|
-
const preview = raw.slice(0, 200).replaceAll(/\s+/g, " ").trim();
|
|
118
|
-
return {
|
|
119
|
-
content: [
|
|
120
|
-
{
|
|
121
|
-
type: "text",
|
|
122
|
-
text: [
|
|
123
|
-
`Command API mismatch at ${url}.`,
|
|
124
|
-
`Expected JSON response, got content-type ${contentType || "unknown"} (status ${response.status}).`,
|
|
125
|
-
preview ? `Body preview: ${preview}` : "",
|
|
126
|
-
"This usually means MU_SERVER_URL points at an outdated server or wrong base URL.",
|
|
127
|
-
]
|
|
128
|
-
.filter(Boolean)
|
|
129
|
-
.join("\n"),
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
details: {
|
|
133
|
-
kind: params.kind,
|
|
134
|
-
error: "command_api_mismatch",
|
|
135
|
-
status: response.status,
|
|
136
|
-
content_type: contentType,
|
|
137
|
-
body_preview: preview,
|
|
138
|
-
url,
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
if (!response.ok) {
|
|
143
|
-
const error = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
144
|
-
return {
|
|
145
|
-
content: [{ type: "text", text: `Command failed: ${error}` }],
|
|
146
|
-
details: { kind: params.kind, error, status: response.status },
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
const result = body.result;
|
|
150
|
-
const resultKind = typeof result?.kind === "string" ? result.kind : "unknown";
|
|
151
|
-
let summary;
|
|
152
|
-
let readResult = null;
|
|
153
|
-
if (resultKind === "completed" || resultKind === "awaiting_confirmation") {
|
|
154
|
-
const command = result?.command;
|
|
155
|
-
summary = `Command ${resultKind}: ${params.kind}`;
|
|
156
|
-
if (command?.target_type) {
|
|
157
|
-
summary += ` (${command.target_type})`;
|
|
158
|
-
}
|
|
159
|
-
if (resultKind === "completed" && command?.result) {
|
|
160
|
-
summary += `\n${JSON.stringify(command.result, null, 2)}`;
|
|
161
|
-
}
|
|
162
|
-
if (resultKind === "completed" && READONLY_COMMAND_KINDS.has(params.kind)) {
|
|
163
|
-
readResult = await fetchReadOnlyMirror(serverUrl, params);
|
|
164
|
-
if (readResult != null) {
|
|
165
|
-
summary += `\n${JSON.stringify({ query_result: readResult }, null, 2)}`;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
else if (resultKind === "denied" || resultKind === "invalid" || resultKind === "failed") {
|
|
170
|
-
const reason = result?.reason ?? "unknown";
|
|
171
|
-
summary = `Command ${resultKind}: ${reason}`;
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
summary = `Command result: ${resultKind}`;
|
|
175
|
-
}
|
|
176
|
-
return {
|
|
177
|
-
content: [{ type: "text", text: summary }],
|
|
178
|
-
details: { kind: params.kind, pipeline_result: result, query_result: readResult },
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
export function operatorCommandExtension(pi) {
|
|
182
|
-
const CommandParams = Type.Object({
|
|
183
|
-
kind: StringEnum([
|
|
184
|
-
"status",
|
|
185
|
-
"ready",
|
|
186
|
-
"issue_list",
|
|
187
|
-
"issue_get",
|
|
188
|
-
"forum_read",
|
|
189
|
-
"run_list",
|
|
190
|
-
"run_status",
|
|
191
|
-
"run_start",
|
|
192
|
-
"run_resume",
|
|
193
|
-
"run_interrupt",
|
|
194
|
-
"reload",
|
|
195
|
-
"update",
|
|
196
|
-
]),
|
|
197
|
-
prompt: Type.Optional(Type.String({ description: "Prompt for run_start" })),
|
|
198
|
-
issue_id: Type.Optional(Type.String({ description: "Issue ID for issue_get" })),
|
|
199
|
-
topic: Type.Optional(Type.String({ description: "Topic for forum_read" })),
|
|
200
|
-
limit: Type.Optional(Type.Number({ description: "Limit for forum_read / run_resume" })),
|
|
201
|
-
root_issue_id: Type.Optional(Type.String({ description: "Root issue ID for run_status / run_resume / run_interrupt" })),
|
|
202
|
-
max_steps: Type.Optional(Type.Number({ description: "Max steps for run_start / run_resume" })),
|
|
203
|
-
});
|
|
204
|
-
pi.registerTool({
|
|
205
|
-
name: MU_COMMAND_TOOL_NAME,
|
|
206
|
-
label: "Command",
|
|
207
|
-
description: [
|
|
208
|
-
"Propose an approved mu command for execution.",
|
|
209
|
-
"This is the ONLY way to trigger mutations (starting runs, resuming runs, interrupting runs).",
|
|
210
|
-
"Read-only queries (status, issue_list, etc.) can also be proposed here.",
|
|
211
|
-
"The command will be validated and executed through the control-plane pipeline.",
|
|
212
|
-
].join(" "),
|
|
213
|
-
parameters: CommandParams,
|
|
214
|
-
async execute(_toolCallId, params) {
|
|
215
|
-
const serverUrl = process.env.MU_SERVER_URL;
|
|
216
|
-
if (serverUrl) {
|
|
217
|
-
return await executeViaServer(serverUrl, params);
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
content: [
|
|
221
|
-
{
|
|
222
|
-
type: "text",
|
|
223
|
-
text: "No server running. Start with `mu serve` for command execution.",
|
|
224
|
-
},
|
|
225
|
-
],
|
|
226
|
-
details: { kind: params.kind, error: "no_server" },
|
|
227
|
-
};
|
|
228
|
-
},
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
export default operatorCommandExtension;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orchestration-runs-readonly.d.ts","sourceRoot":"","sources":["../../src/extensions/orchestration-runs-readonly.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA2JlE,wBAAgB,kCAAkC,CAAC,EAAE,EAAE,YAAY,QA+FlE;AAED,eAAe,kCAAkC,CAAC"}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { StringEnum } from "@mariozechner/pi-ai";
|
|
2
|
-
import { Type } from "@sinclair/typebox";
|
|
3
|
-
import { asArray, asNumber, asRecord, asString, clampInt, fetchMuJson, parseFieldPaths, previewLines, previewText, selectFields, textResult, toJsonText, } from "./shared.js";
|
|
4
|
-
function trimOrNull(value) {
|
|
5
|
-
if (value == null)
|
|
6
|
-
return null;
|
|
7
|
-
const trimmed = value.trim();
|
|
8
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
9
|
-
}
|
|
10
|
-
function summarizeRun(run) {
|
|
11
|
-
return {
|
|
12
|
-
job_id: asString(run.job_id),
|
|
13
|
-
root_issue_id: asString(run.root_issue_id),
|
|
14
|
-
status: asString(run.status),
|
|
15
|
-
source: asString(run.source),
|
|
16
|
-
started_at_ms: asNumber(run.started_at_ms),
|
|
17
|
-
finished_at_ms: asNumber(run.finished_at_ms),
|
|
18
|
-
exit_code: asNumber(run.exit_code),
|
|
19
|
-
provider: asString(run.provider),
|
|
20
|
-
model: asString(run.model),
|
|
21
|
-
reasoning: asString(run.reasoning),
|
|
22
|
-
prompt_preview: previewText(run.prompt, 140),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function summarizeTrace(trace, lineLimit) {
|
|
26
|
-
const run = asRecord(trace.run);
|
|
27
|
-
return {
|
|
28
|
-
run: run ? summarizeRun(run) : null,
|
|
29
|
-
stdout: previewLines(trace.stdout, { maxLines: lineLimit, maxCharsPerLine: 220 }),
|
|
30
|
-
stderr: previewLines(trace.stderr, { maxLines: lineLimit, maxCharsPerLine: 220 }),
|
|
31
|
-
log_hints: asArray(trace.log_hints).slice(0, 20),
|
|
32
|
-
trace_files: asArray(trace.trace_files).slice(0, 20),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function runFromStartEvent(event) {
|
|
36
|
-
const runId = asString(event.run_id);
|
|
37
|
-
if (!runId) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
const payload = asRecord(event.payload);
|
|
41
|
-
const issueId = asString(event.issue_id);
|
|
42
|
-
const role = payload ? asString(payload.role) : null;
|
|
43
|
-
const explicitRoot = payload ? asString(payload.root_issue_id) : null;
|
|
44
|
-
const rootIssueId = explicitRoot ?? (role === "orchestrator" ? issueId : null);
|
|
45
|
-
return {
|
|
46
|
-
job_id: runId,
|
|
47
|
-
root_issue_id: rootIssueId,
|
|
48
|
-
issue_id: issueId,
|
|
49
|
-
role,
|
|
50
|
-
status: "history",
|
|
51
|
-
source: "event_log",
|
|
52
|
-
started_at_ms: asNumber(event.ts_ms),
|
|
53
|
-
finished_at_ms: null,
|
|
54
|
-
exit_code: null,
|
|
55
|
-
provider: payload ? asString(payload.provider) : null,
|
|
56
|
-
model: payload ? asString(payload.model) : null,
|
|
57
|
-
reasoning: payload ? asString(payload.reasoning) : null,
|
|
58
|
-
prompt: payload ? asString(payload.prompt) : null,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
async function fetchHistoricalRuns(limit) {
|
|
62
|
-
const payload = await fetchMuJson(`/api/events?type=backend.run.start&limit=${Math.max(limit * 4, 80)}`);
|
|
63
|
-
const payloadRecord = asRecord(payload);
|
|
64
|
-
const events = Array.isArray(payload) ? payload : asArray(payloadRecord?.events);
|
|
65
|
-
const runIndex = new Map();
|
|
66
|
-
const runs = [];
|
|
67
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
68
|
-
const event = asRecord(events[index]);
|
|
69
|
-
if (!event) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
const run = runFromStartEvent(event);
|
|
73
|
-
if (!run) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
const runId = asString(run.job_id);
|
|
77
|
-
if (!runId) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const existingIdx = runIndex.get(runId);
|
|
81
|
-
if (existingIdx == null) {
|
|
82
|
-
if (runs.length >= limit) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
runIndex.set(runId, runs.length);
|
|
86
|
-
runs.push(run);
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
const existing = runs[existingIdx];
|
|
90
|
-
if (!asString(existing.root_issue_id)) {
|
|
91
|
-
existing.root_issue_id = asString(run.root_issue_id) ?? asString(run.issue_id);
|
|
92
|
-
}
|
|
93
|
-
if (!asString(existing.provider)) {
|
|
94
|
-
existing.provider = asString(run.provider);
|
|
95
|
-
}
|
|
96
|
-
if (!asString(existing.model)) {
|
|
97
|
-
existing.model = asString(run.model);
|
|
98
|
-
}
|
|
99
|
-
if (!asString(existing.reasoning)) {
|
|
100
|
-
existing.reasoning = asString(run.reasoning);
|
|
101
|
-
}
|
|
102
|
-
if (!asString(existing.prompt)) {
|
|
103
|
-
existing.prompt = asString(run.prompt);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return runs;
|
|
107
|
-
}
|
|
108
|
-
async function findHistoricalRun(idOrRoot) {
|
|
109
|
-
const payload = await fetchMuJson("/api/events?type=backend.run.start&limit=200");
|
|
110
|
-
const payloadRecord = asRecord(payload);
|
|
111
|
-
const events = Array.isArray(payload) ? payload : asArray(payloadRecord?.events);
|
|
112
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
113
|
-
const event = asRecord(events[index]);
|
|
114
|
-
if (!event) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const run = runFromStartEvent(event);
|
|
118
|
-
if (!run) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
const runId = asString(run.job_id);
|
|
122
|
-
const rootIssueId = asString(run.root_issue_id);
|
|
123
|
-
if (runId === idOrRoot || rootIssueId === idOrRoot) {
|
|
124
|
-
return run;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
function isRunNotFoundError(error) {
|
|
130
|
-
if (!(error instanceof Error)) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
const message = error.message.toLowerCase();
|
|
134
|
-
return message.includes("mu server 404") || message.includes("run not found");
|
|
135
|
-
}
|
|
136
|
-
export function orchestrationRunsReadOnlyExtension(pi) {
|
|
137
|
-
const RunsParams = Type.Object({
|
|
138
|
-
action: StringEnum(["list", "status", "trace"]),
|
|
139
|
-
job_id: Type.Optional(Type.String({ description: "Run job ID" })),
|
|
140
|
-
root_issue_id: Type.Optional(Type.String({ description: "Run root issue ID (mu-...)" })),
|
|
141
|
-
limit: Type.Optional(Type.Number({ description: "Optional limit (list/trace). Defaults to 20 for list and 40 lines for trace." })),
|
|
142
|
-
status: Type.Optional(Type.String({ description: "Optional status filter for list" })),
|
|
143
|
-
fields: Type.Optional(Type.String({
|
|
144
|
-
description: "Comma-separated fields for status/trace selection (e.g. status,exit_code,prompt)",
|
|
145
|
-
})),
|
|
146
|
-
});
|
|
147
|
-
pi.registerTool({
|
|
148
|
-
name: "mu_runs",
|
|
149
|
-
label: "Runs",
|
|
150
|
-
description: "Read-only run inspection. Actions: list, status, trace. Returns compact summaries; use fields for precise retrieval.",
|
|
151
|
-
parameters: RunsParams,
|
|
152
|
-
async execute(_toolCallId, params) {
|
|
153
|
-
switch (params.action) {
|
|
154
|
-
case "list": {
|
|
155
|
-
const query = new URLSearchParams();
|
|
156
|
-
const status = trimOrNull(params.status);
|
|
157
|
-
if (status)
|
|
158
|
-
query.set("status", status);
|
|
159
|
-
const limit = clampInt(params.limit, 20, 1, 500);
|
|
160
|
-
query.set("limit", String(limit));
|
|
161
|
-
const payload = await fetchMuJson(`/api/runs?${query.toString()}`);
|
|
162
|
-
let records = asArray(payload.runs)
|
|
163
|
-
.map((run) => asRecord(run))
|
|
164
|
-
.filter((run) => run != null);
|
|
165
|
-
let source = "run_supervisor";
|
|
166
|
-
if (records.length === 0 && (status == null || status === "history")) {
|
|
167
|
-
records = await fetchHistoricalRuns(limit);
|
|
168
|
-
source = "event_log";
|
|
169
|
-
}
|
|
170
|
-
const runs = records.map((run) => summarizeRun(run));
|
|
171
|
-
return textResult(toJsonText({ count: runs.length, source, runs }), {
|
|
172
|
-
action: "list",
|
|
173
|
-
status,
|
|
174
|
-
limit,
|
|
175
|
-
source,
|
|
176
|
-
payload,
|
|
177
|
-
runs: records,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
case "status": {
|
|
181
|
-
const id = trimOrNull(params.job_id) ?? trimOrNull(params.root_issue_id);
|
|
182
|
-
if (!id)
|
|
183
|
-
return textResult("status requires job_id or root_issue_id");
|
|
184
|
-
let payload = null;
|
|
185
|
-
let source = "run_supervisor";
|
|
186
|
-
try {
|
|
187
|
-
payload = await fetchMuJson(`/api/runs/${encodeURIComponent(id)}`);
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
if (!isRunNotFoundError(err)) {
|
|
191
|
-
throw err;
|
|
192
|
-
}
|
|
193
|
-
payload = await findHistoricalRun(id);
|
|
194
|
-
source = "event_log";
|
|
195
|
-
if (!payload) {
|
|
196
|
-
throw err;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
if (!payload) {
|
|
200
|
-
return textResult(`run not found: ${id}`);
|
|
201
|
-
}
|
|
202
|
-
const fields = parseFieldPaths(trimOrNull(params.fields) ?? undefined);
|
|
203
|
-
const content = fields.length > 0
|
|
204
|
-
? { id, source, selected: selectFields(payload, fields) }
|
|
205
|
-
: { source, run: summarizeRun(payload) };
|
|
206
|
-
return textResult(toJsonText(content), { action: "status", id, source, fields, payload });
|
|
207
|
-
}
|
|
208
|
-
case "trace": {
|
|
209
|
-
const id = trimOrNull(params.job_id) ?? trimOrNull(params.root_issue_id);
|
|
210
|
-
if (!id)
|
|
211
|
-
return textResult("trace requires job_id or root_issue_id");
|
|
212
|
-
const lineLimit = clampInt(params.limit, 40, 1, 200);
|
|
213
|
-
const payload = await fetchMuJson(`/api/runs/${encodeURIComponent(id)}/trace?limit=${Math.max(lineLimit, 80)}`);
|
|
214
|
-
const fields = parseFieldPaths(trimOrNull(params.fields) ?? undefined);
|
|
215
|
-
const content = fields.length > 0
|
|
216
|
-
? { id, selected: selectFields(payload, fields) }
|
|
217
|
-
: summarizeTrace(payload, lineLimit);
|
|
218
|
-
return textResult(toJsonText(content), { action: "trace", id, lineLimit, fields, payload });
|
|
219
|
-
}
|
|
220
|
-
default:
|
|
221
|
-
return textResult(`unknown action: ${params.action}`);
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
export default orchestrationRunsReadOnlyExtension;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orchestration-runs.d.ts","sourceRoot":"","sources":["../../src/extensions/orchestration-runs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA2JlE,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,YAAY,QA8L1D;AAED,eAAe,0BAA0B,CAAC"}
|