@femtomc/mu-agent 26.2.44 → 26.2.45
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/dist/extensions/index.d.ts +1 -0
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/operator-command.d.ts +14 -0
- package/dist/extensions/operator-command.d.ts.map +1 -0
- package/dist/extensions/operator-command.js +52 -0
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +26 -156
- package/package.json +2 -2
- package/prompts/operator.md +10 -8
|
@@ -3,6 +3,7 @@ export { brandingExtension } from "./branding.js";
|
|
|
3
3
|
export { eventLogExtension } from "./event-log.js";
|
|
4
4
|
export { heartbeatsExtension } from "./heartbeats.js";
|
|
5
5
|
export { messagingSetupExtension } from "./messaging-setup.js";
|
|
6
|
+
export { operatorCommandExtension } from "./operator-command.js";
|
|
6
7
|
export { orchestrationRunsExtension } from "./orchestration-runs.js";
|
|
7
8
|
export { orchestrationRunsReadOnlyExtension } from "./orchestration-runs-readonly.js";
|
|
8
9
|
export { serverToolsExtension, serverToolsReadOnlyExtension } from "./server-tools.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,kCAAkC,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,kCAAkC,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AA0B1E;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,UAE/B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,UAElC,CAAC"}
|
package/dist/extensions/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { brandingExtension } from "./branding.js";
|
|
|
3
3
|
export { eventLogExtension } from "./event-log.js";
|
|
4
4
|
export { heartbeatsExtension } from "./heartbeats.js";
|
|
5
5
|
export { messagingSetupExtension } from "./messaging-setup.js";
|
|
6
|
+
export { operatorCommandExtension } from "./operator-command.js";
|
|
6
7
|
export { orchestrationRunsExtension } from "./orchestration-runs.js";
|
|
7
8
|
export { orchestrationRunsReadOnlyExtension } from "./orchestration-runs-readonly.js";
|
|
8
9
|
export { serverToolsExtension, serverToolsReadOnlyExtension } from "./server-tools.js";
|
|
@@ -22,6 +23,7 @@ const OPERATOR_EXTENSION_MODULE_BASENAMES = [
|
|
|
22
23
|
"event-log",
|
|
23
24
|
"messaging-setup",
|
|
24
25
|
"orchestration-runs-readonly",
|
|
26
|
+
"operator-command",
|
|
25
27
|
];
|
|
26
28
|
const RUNTIME_EXTENSION = import.meta.url.endsWith(".ts") ? "ts" : "js";
|
|
27
29
|
function resolveBundledExtensionPath(moduleBasename) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu_command — Operator mutation tool.
|
|
3
|
+
*
|
|
4
|
+
* Registered only in operator sessions. The LLM calls this tool with a
|
|
5
|
+
* structured command proposal instead of emitting fragile text directives.
|
|
6
|
+
* The tool itself does nothing except confirm receipt — the actual command
|
|
7
|
+
* is captured by the PiMessagingOperatorBackend subscriber on
|
|
8
|
+
* tool_execution_start and routed through the existing broker/audit pipeline.
|
|
9
|
+
*/
|
|
10
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
11
|
+
export declare const MU_COMMAND_TOOL_NAME = "mu_command";
|
|
12
|
+
export declare function operatorCommandExtension(pi: ExtensionAPI): void;
|
|
13
|
+
export default operatorCommandExtension;
|
|
14
|
+
//# sourceMappingURL=operator-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operator-command.d.ts","sourceRoot":"","sources":["../../src/extensions/operator-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,YAAY,QAuCxD;AAED,eAAe,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu_command — Operator mutation tool.
|
|
3
|
+
*
|
|
4
|
+
* Registered only in operator sessions. The LLM calls this tool with a
|
|
5
|
+
* structured command proposal instead of emitting fragile text directives.
|
|
6
|
+
* The tool itself does nothing except confirm receipt — the actual command
|
|
7
|
+
* is captured by the PiMessagingOperatorBackend subscriber on
|
|
8
|
+
* tool_execution_start and routed through the existing broker/audit 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
|
+
export function operatorCommandExtension(pi) {
|
|
14
|
+
const CommandParams = Type.Object({
|
|
15
|
+
kind: StringEnum([
|
|
16
|
+
"status",
|
|
17
|
+
"ready",
|
|
18
|
+
"issue_list",
|
|
19
|
+
"issue_get",
|
|
20
|
+
"forum_read",
|
|
21
|
+
"run_list",
|
|
22
|
+
"run_status",
|
|
23
|
+
"run_start",
|
|
24
|
+
"run_resume",
|
|
25
|
+
"run_interrupt",
|
|
26
|
+
]),
|
|
27
|
+
prompt: Type.Optional(Type.String({ description: "Prompt for run_start" })),
|
|
28
|
+
issue_id: Type.Optional(Type.String({ description: "Issue ID for issue_get" })),
|
|
29
|
+
topic: Type.Optional(Type.String({ description: "Topic for forum_read" })),
|
|
30
|
+
limit: Type.Optional(Type.Number({ description: "Limit for forum_read / run_resume" })),
|
|
31
|
+
root_issue_id: Type.Optional(Type.String({ description: "Root issue ID for run_status / run_resume / run_interrupt" })),
|
|
32
|
+
max_steps: Type.Optional(Type.Number({ description: "Max steps for run_start / run_resume" })),
|
|
33
|
+
});
|
|
34
|
+
pi.registerTool({
|
|
35
|
+
name: MU_COMMAND_TOOL_NAME,
|
|
36
|
+
label: "Command",
|
|
37
|
+
description: [
|
|
38
|
+
"Propose an approved mu command for execution.",
|
|
39
|
+
"This is the ONLY way to trigger mutations (starting runs, resuming runs, interrupting runs).",
|
|
40
|
+
"Read-only queries (status, issue_list, etc.) can also be proposed here.",
|
|
41
|
+
"The command will be validated and executed through the control-plane pipeline.",
|
|
42
|
+
].join(" "),
|
|
43
|
+
parameters: CommandParams,
|
|
44
|
+
async execute(_toolCallId, params) {
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: "text", text: `Command proposal accepted: ${params.kind}` }],
|
|
47
|
+
details: { kind: params.kind },
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export default operatorCommandExtension;
|
package/dist/operator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAmB,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AAEtE,MAAM,MAAM,gCAAgC,GAAG;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uBAAuB,EAAE,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,eAAe,GAAG,gCAAgC,CAAC;AACxD,KAAK,eAAe,GAAG,gCAAgC,CAAC;AAIxD,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA6BxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAG1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
|
package/dist/operator.js
CHANGED
|
@@ -39,10 +39,6 @@ export const OperatorBackendTurnResultSchema = z.discriminatedUnion("kind", [
|
|
|
39
39
|
z.object({ kind: z.literal("respond"), message: z.string().trim().min(1).max(2000) }),
|
|
40
40
|
z.object({ kind: z.literal("command"), command: OperatorApprovedCommandSchema }),
|
|
41
41
|
]);
|
|
42
|
-
const OperatorDecisionEnvelopeSchema = z.discriminatedUnion("kind", [
|
|
43
|
-
z.object({ kind: z.literal("respond"), message: z.string().trim().min(1).max(2000) }),
|
|
44
|
-
z.object({ kind: z.literal("command"), command: OperatorApprovedCommandSchema }),
|
|
45
|
-
]);
|
|
46
42
|
function splitPromptIntoTokens(prompt) {
|
|
47
43
|
return prompt
|
|
48
44
|
.split(/\s+/)
|
|
@@ -279,107 +275,7 @@ export class MessagingOperatorRuntime {
|
|
|
279
275
|
}
|
|
280
276
|
}
|
|
281
277
|
export { DEFAULT_OPERATOR_SYSTEM_PROMPT };
|
|
282
|
-
const
|
|
283
|
-
const OPERATOR_DECISION_PREFIX = "MU_DECISION:";
|
|
284
|
-
function stripOperatorDirectiveLines(text) {
|
|
285
|
-
return text
|
|
286
|
-
.split(/\r?\n/)
|
|
287
|
-
.filter((line) => {
|
|
288
|
-
const trimmed = line.trim();
|
|
289
|
-
return !(trimmed.startsWith(OPERATOR_COMMAND_PREFIX) || trimmed.startsWith(OPERATOR_DECISION_PREFIX));
|
|
290
|
-
})
|
|
291
|
-
.join("\n")
|
|
292
|
-
.trim();
|
|
293
|
-
}
|
|
294
|
-
function parseOperatorDirective(text) {
|
|
295
|
-
const whole = text.trim();
|
|
296
|
-
if (whole.startsWith("{") && whole.endsWith("}")) {
|
|
297
|
-
try {
|
|
298
|
-
const parsed = JSON.parse(whole);
|
|
299
|
-
const envelope = OperatorDecisionEnvelopeSchema.safeParse(parsed);
|
|
300
|
-
if (envelope.success) {
|
|
301
|
-
return envelope.data.kind === "command"
|
|
302
|
-
? { kind: "command", command: envelope.data.command }
|
|
303
|
-
: { kind: "respond", message: envelope.data.message };
|
|
304
|
-
}
|
|
305
|
-
const legacy = OperatorApprovedCommandSchema.safeParse(parsed);
|
|
306
|
-
if (legacy.success) {
|
|
307
|
-
return { kind: "command", command: legacy.data };
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
catch {
|
|
311
|
-
// fall through to line-based directives / plain text fallback.
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
const lines = text.split(/\r?\n/);
|
|
315
|
-
for (const line of lines) {
|
|
316
|
-
const trimmed = line.trim();
|
|
317
|
-
if (trimmed.startsWith(OPERATOR_DECISION_PREFIX)) {
|
|
318
|
-
const payloadText = trimmed.slice(OPERATOR_DECISION_PREFIX.length).trim();
|
|
319
|
-
if (payloadText.length === 0) {
|
|
320
|
-
return {
|
|
321
|
-
kind: "invalid",
|
|
322
|
-
reason: "operator_decision_directive_missing_payload",
|
|
323
|
-
fallbackMessage: stripOperatorDirectiveLines(text),
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
let parsed;
|
|
327
|
-
try {
|
|
328
|
-
parsed = JSON.parse(payloadText);
|
|
329
|
-
}
|
|
330
|
-
catch (err) {
|
|
331
|
-
return {
|
|
332
|
-
kind: "invalid",
|
|
333
|
-
reason: `operator_decision_directive_invalid_json: ${err instanceof Error ? err.message : String(err)}`,
|
|
334
|
-
fallbackMessage: stripOperatorDirectiveLines(text),
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
const envelope = OperatorDecisionEnvelopeSchema.safeParse(parsed);
|
|
338
|
-
if (!envelope.success) {
|
|
339
|
-
return {
|
|
340
|
-
kind: "invalid",
|
|
341
|
-
reason: `operator_decision_directive_invalid_payload: ${envelope.error.message}`,
|
|
342
|
-
fallbackMessage: stripOperatorDirectiveLines(text),
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
return envelope.data.kind === "command"
|
|
346
|
-
? { kind: "command", command: envelope.data.command }
|
|
347
|
-
: { kind: "respond", message: envelope.data.message };
|
|
348
|
-
}
|
|
349
|
-
if (!trimmed.startsWith(OPERATOR_COMMAND_PREFIX)) {
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
const payloadText = trimmed.slice(OPERATOR_COMMAND_PREFIX.length).trim();
|
|
353
|
-
if (payloadText.length === 0) {
|
|
354
|
-
return {
|
|
355
|
-
kind: "invalid",
|
|
356
|
-
reason: "operator_command_directive_missing_payload",
|
|
357
|
-
fallbackMessage: stripOperatorDirectiveLines(text),
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
let parsed;
|
|
361
|
-
try {
|
|
362
|
-
parsed = JSON.parse(payloadText);
|
|
363
|
-
}
|
|
364
|
-
catch (err) {
|
|
365
|
-
return {
|
|
366
|
-
kind: "invalid",
|
|
367
|
-
reason: `operator_command_directive_invalid_json: ${err instanceof Error ? err.message : String(err)}`,
|
|
368
|
-
fallbackMessage: stripOperatorDirectiveLines(text),
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
const command = OperatorApprovedCommandSchema.safeParse(parsed);
|
|
372
|
-
if (!command.success) {
|
|
373
|
-
return {
|
|
374
|
-
kind: "invalid",
|
|
375
|
-
reason: `operator_command_directive_invalid_payload: ${command.error.message}`,
|
|
376
|
-
fallbackMessage: stripOperatorDirectiveLines(text),
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
return { kind: "command", command: command.data };
|
|
380
|
-
}
|
|
381
|
-
return { kind: "none" };
|
|
382
|
-
}
|
|
278
|
+
const MU_COMMAND_TOOL_NAME = "mu_command";
|
|
383
279
|
function buildOperatorPrompt(input) {
|
|
384
280
|
return [
|
|
385
281
|
`[Messaging context]`,
|
|
@@ -516,7 +412,9 @@ export class PiMessagingOperatorBackend {
|
|
|
516
412
|
const sessionRecord = await this.#resolveSession(input.sessionId, input.inbound.repo_root);
|
|
517
413
|
const session = sessionRecord.session;
|
|
518
414
|
let assistantText = "";
|
|
415
|
+
let capturedCommand = null;
|
|
519
416
|
const unsub = session.subscribe((event) => {
|
|
417
|
+
// Capture assistant text for fallback responses.
|
|
520
418
|
if (event?.type === "message_end" && event?.message?.role === "assistant") {
|
|
521
419
|
const msg = event.message;
|
|
522
420
|
if (typeof msg.text === "string") {
|
|
@@ -536,6 +434,13 @@ export class PiMessagingOperatorBackend {
|
|
|
536
434
|
assistantText = parts.join("\n");
|
|
537
435
|
}
|
|
538
436
|
}
|
|
437
|
+
// Capture mu_command tool calls — structured command proposals.
|
|
438
|
+
if (event?.type === "tool_execution_start" && event?.toolName === MU_COMMAND_TOOL_NAME) {
|
|
439
|
+
const parsed = OperatorApprovedCommandSchema.safeParse(event.args);
|
|
440
|
+
if (parsed.success) {
|
|
441
|
+
capturedCommand = parsed.data;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
539
444
|
});
|
|
540
445
|
const timeoutPromise = new Promise((_, reject) => {
|
|
541
446
|
setTimeout(() => reject(new Error("pi operator timeout")), this.#timeoutMs);
|
|
@@ -558,6 +463,16 @@ export class PiMessagingOperatorBackend {
|
|
|
558
463
|
unsub();
|
|
559
464
|
sessionRecord.lastUsedAtMs = Math.trunc(this.#nowMs());
|
|
560
465
|
}
|
|
466
|
+
// If the operator called mu_command, use the captured structured command.
|
|
467
|
+
if (capturedCommand) {
|
|
468
|
+
await this.#auditTurn(input, {
|
|
469
|
+
outcome: "command",
|
|
470
|
+
command: capturedCommand,
|
|
471
|
+
messagePreview: assistantText,
|
|
472
|
+
});
|
|
473
|
+
return { kind: "command", command: capturedCommand };
|
|
474
|
+
}
|
|
475
|
+
// Otherwise treat the assistant text as a plain response.
|
|
561
476
|
const message = assistantText.trim();
|
|
562
477
|
if (!message) {
|
|
563
478
|
await this.#auditTurn(input, {
|
|
@@ -566,57 +481,12 @@ export class PiMessagingOperatorBackend {
|
|
|
566
481
|
});
|
|
567
482
|
throw new Error("operator_empty_response");
|
|
568
483
|
}
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
messagePreview: message,
|
|
576
|
-
});
|
|
577
|
-
return {
|
|
578
|
-
kind: "command",
|
|
579
|
-
command: parsed.command,
|
|
580
|
-
};
|
|
581
|
-
case "respond": {
|
|
582
|
-
const responseMessage = parsed.message.trim().slice(0, 2000);
|
|
583
|
-
if (!SAFE_RESPONSE_RE.test(responseMessage)) {
|
|
584
|
-
const fallback = buildOperatorFailureFallbackMessage("operator_invalid_response_payload");
|
|
585
|
-
await this.#auditTurn(input, {
|
|
586
|
-
outcome: "invalid_directive",
|
|
587
|
-
reason: "operator_invalid_response_payload",
|
|
588
|
-
messagePreview: message,
|
|
589
|
-
});
|
|
590
|
-
return { kind: "respond", message: fallback };
|
|
591
|
-
}
|
|
592
|
-
await this.#auditTurn(input, {
|
|
593
|
-
outcome: "respond",
|
|
594
|
-
messagePreview: responseMessage,
|
|
595
|
-
});
|
|
596
|
-
return { kind: "respond", message: responseMessage };
|
|
597
|
-
}
|
|
598
|
-
case "invalid": {
|
|
599
|
-
const fallbackMessage = parsed.fallbackMessage.trim();
|
|
600
|
-
const responseMessage = fallbackMessage.length > 0
|
|
601
|
-
? fallbackMessage.slice(0, 2000)
|
|
602
|
-
: buildOperatorFailureFallbackMessage("operator_invalid_command_directive");
|
|
603
|
-
await this.#auditTurn(input, {
|
|
604
|
-
outcome: "invalid_directive",
|
|
605
|
-
reason: parsed.reason,
|
|
606
|
-
messagePreview: message,
|
|
607
|
-
});
|
|
608
|
-
return { kind: "respond", message: responseMessage };
|
|
609
|
-
}
|
|
610
|
-
case "none":
|
|
611
|
-
default: {
|
|
612
|
-
const responseMessage = message.slice(0, 2000);
|
|
613
|
-
await this.#auditTurn(input, {
|
|
614
|
-
outcome: "respond",
|
|
615
|
-
messagePreview: responseMessage,
|
|
616
|
-
});
|
|
617
|
-
return { kind: "respond", message: responseMessage };
|
|
618
|
-
}
|
|
619
|
-
}
|
|
484
|
+
const responseMessage = message.slice(0, 2000);
|
|
485
|
+
await this.#auditTurn(input, {
|
|
486
|
+
outcome: "respond",
|
|
487
|
+
messagePreview: responseMessage,
|
|
488
|
+
});
|
|
489
|
+
return { kind: "respond", message: responseMessage };
|
|
620
490
|
}
|
|
621
491
|
dispose() {
|
|
622
492
|
for (const sessionId of [...this.#sessions.keys()]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-agent",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.45",
|
|
4
4
|
"description": "Shared agent runtime for mu chat, orchestration roles, and serve extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mu",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"prompts/**"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@femtomc/mu-core": "26.2.
|
|
26
|
+
"@femtomc/mu-core": "26.2.45",
|
|
27
27
|
"@mariozechner/pi-agent-core": "^0.52.12",
|
|
28
28
|
"@mariozechner/pi-ai": "^0.52.12",
|
|
29
29
|
"@mariozechner/pi-coding-agent": "^0.52.12",
|
package/prompts/operator.md
CHANGED
|
@@ -5,7 +5,7 @@ Mission:
|
|
|
5
5
|
- Help users with any coding tasks they ask you to handle directly.
|
|
6
6
|
- Help users inspect repository/control-plane state.
|
|
7
7
|
- Help users choose safe next actions.
|
|
8
|
-
- When needed, propose approved
|
|
8
|
+
- When needed, propose approved commands using the mu_command tool.
|
|
9
9
|
|
|
10
10
|
Available tools:
|
|
11
11
|
- read: Read file contents
|
|
@@ -25,13 +25,15 @@ You also have access to specialized read/diagnostic tools:
|
|
|
25
25
|
- `mu_messaging_setup`
|
|
26
26
|
|
|
27
27
|
Hard Constraints:
|
|
28
|
-
- Never perform mutations directly through tools
|
|
29
|
-
- Mutating actions must flow through
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
- Never perform mutations directly through read/write tools.
|
|
29
|
+
- Mutating actions must flow through the `mu_command` tool.
|
|
30
|
+
- Use the `mu_command` tool to propose commands. It accepts structured parameters — do NOT emit raw JSON directives in your text output.
|
|
31
|
+
|
|
32
|
+
mu_command tool usage:
|
|
33
|
+
- Call `mu_command` with `kind` set to the command type and relevant parameters.
|
|
34
|
+
- Example: `mu_command({ kind: "run_start", prompt: "ship release" })`
|
|
35
|
+
- Example: `mu_command({ kind: "status" })`
|
|
36
|
+
- Example: `mu_command({ kind: "issue_get", issue_id: "mu-abc123" })`
|
|
35
37
|
|
|
36
38
|
Allowed command kinds:
|
|
37
39
|
- `status`
|