@femtomc/mu-agent 26.2.101 → 26.2.103
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 +5 -4
- package/dist/extensions/hud.d.ts.map +1 -1
- package/dist/extensions/hud.js +216 -15
- package/dist/operator.d.ts +176 -0
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +2 -169
- package/dist/session_factory.d.ts.map +1 -1
- package/dist/session_factory.js +21 -8
- package/dist/session_turn.d.ts.map +1 -1
- package/dist/session_turn.js +71 -16
- package/package.json +5 -5
- package/prompts/roles/operator.md +1 -0
- package/prompts/skills/hud/SKILL.md +12 -10
- package/prompts/skills/memory/SKILL.md +0 -3
- package/prompts/skills/mu/SKILL.md +87 -18
- package/prompts/skills/planning/SKILL.md +2 -1
- package/prompts/skills/setup-telegram/SKILL.md +5 -19
- package/prompts/skills/subagents/SKILL.md +2 -1
- package/prompts/skills/writing/SKILL.md +179 -0
package/dist/operator.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HudDocSchema, normalizeHudDocs } from "@femtomc/mu-core";
|
|
2
|
-
import { appendJsonl, getStorePaths
|
|
2
|
+
import { appendJsonl, getStorePaths } from "@femtomc/mu-core/node";
|
|
3
3
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -292,93 +292,6 @@ function collectHudDocsFromToolExecutionEvent(event) {
|
|
|
292
292
|
}
|
|
293
293
|
return extractHudDocsFromToolResult(rec.result);
|
|
294
294
|
}
|
|
295
|
-
function finiteInt(value) {
|
|
296
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
297
|
-
return null;
|
|
298
|
-
}
|
|
299
|
-
return Math.trunc(value);
|
|
300
|
-
}
|
|
301
|
-
function stringList(value) {
|
|
302
|
-
if (!Array.isArray(value)) {
|
|
303
|
-
return [];
|
|
304
|
-
}
|
|
305
|
-
const out = [];
|
|
306
|
-
for (const item of value) {
|
|
307
|
-
const parsed = nonEmptyString(item);
|
|
308
|
-
if (parsed) {
|
|
309
|
-
out.push(parsed);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return out;
|
|
313
|
-
}
|
|
314
|
-
function sessionFlashPath(repoRoot) {
|
|
315
|
-
return join(getStorePaths(repoRoot).storeDir, "control-plane", "session_flash.jsonl");
|
|
316
|
-
}
|
|
317
|
-
async function loadPendingSessionFlashes(opts) {
|
|
318
|
-
const rows = await readJsonl(sessionFlashPath(opts.repoRoot));
|
|
319
|
-
const created = new Map();
|
|
320
|
-
const delivered = new Set();
|
|
321
|
-
for (const row of rows) {
|
|
322
|
-
const rec = asRecord(row);
|
|
323
|
-
if (!rec) {
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
const kind = nonEmptyString(rec.kind);
|
|
327
|
-
if (kind === "session_flash.create") {
|
|
328
|
-
const tsMs = finiteInt(rec.ts_ms) ?? Date.now();
|
|
329
|
-
const flashId = nonEmptyString(rec.flash_id);
|
|
330
|
-
const sessionId = nonEmptyString(rec.session_id);
|
|
331
|
-
const body = nonEmptyString(rec.body);
|
|
332
|
-
if (!flashId || !sessionId || !body || sessionId !== opts.sessionId) {
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
created.set(flashId, {
|
|
336
|
-
flash_id: flashId,
|
|
337
|
-
created_at_ms: tsMs,
|
|
338
|
-
session_id: sessionId,
|
|
339
|
-
session_kind: nonEmptyString(rec.session_kind),
|
|
340
|
-
body,
|
|
341
|
-
context_ids: stringList(rec.context_ids),
|
|
342
|
-
source: nonEmptyString(rec.source),
|
|
343
|
-
metadata: asRecord(rec.metadata) ?? {},
|
|
344
|
-
});
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (kind === "session_flash.delivery") {
|
|
348
|
-
const flashId = nonEmptyString(rec.flash_id);
|
|
349
|
-
const sessionId = nonEmptyString(rec.session_id);
|
|
350
|
-
if (!flashId || !sessionId || sessionId !== opts.sessionId) {
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
delivered.add(flashId);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
const pending = [...created.values()]
|
|
357
|
-
.filter((row) => !delivered.has(row.flash_id))
|
|
358
|
-
.sort((a, b) => a.created_at_ms - b.created_at_ms);
|
|
359
|
-
const limit = Math.max(1, Math.trunc(opts.limit ?? 16));
|
|
360
|
-
if (pending.length <= limit) {
|
|
361
|
-
return pending;
|
|
362
|
-
}
|
|
363
|
-
return pending.slice(pending.length - limit);
|
|
364
|
-
}
|
|
365
|
-
async function markSessionFlashesDelivered(opts) {
|
|
366
|
-
if (opts.flashIds.length === 0) {
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
const deduped = [...new Set(opts.flashIds.filter((id) => id.trim().length > 0))];
|
|
370
|
-
for (const flashId of deduped) {
|
|
371
|
-
const row = {
|
|
372
|
-
kind: "session_flash.delivery",
|
|
373
|
-
ts_ms: opts.nowMs,
|
|
374
|
-
flash_id: flashId,
|
|
375
|
-
session_id: opts.sessionId,
|
|
376
|
-
delivered_by: "messaging_operator_runtime",
|
|
377
|
-
note: null,
|
|
378
|
-
};
|
|
379
|
-
await appendJsonl(sessionFlashPath(opts.repoRoot), row);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
295
|
export class JsonFileConversationSessionStore {
|
|
383
296
|
#path;
|
|
384
297
|
#loaded = false;
|
|
@@ -551,46 +464,14 @@ export class MessagingOperatorRuntime {
|
|
|
551
464
|
operatorTurnId: turnId,
|
|
552
465
|
};
|
|
553
466
|
}
|
|
554
|
-
let pendingFlashes = [];
|
|
555
|
-
try {
|
|
556
|
-
pendingFlashes = await loadPendingSessionFlashes({
|
|
557
|
-
repoRoot: opts.inbound.repo_root,
|
|
558
|
-
sessionId,
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
catch {
|
|
562
|
-
pendingFlashes = [];
|
|
563
|
-
}
|
|
564
|
-
const inboundForBackend = pendingFlashes.length > 0
|
|
565
|
-
? {
|
|
566
|
-
...opts.inbound,
|
|
567
|
-
metadata: {
|
|
568
|
-
...opts.inbound.metadata,
|
|
569
|
-
session_flash_messages: pendingFlashes,
|
|
570
|
-
},
|
|
571
|
-
}
|
|
572
|
-
: opts.inbound;
|
|
573
467
|
let backendResult;
|
|
574
468
|
try {
|
|
575
469
|
backendResult = OperatorBackendTurnResultSchema.parse(await this.#backend.runTurn({
|
|
576
470
|
sessionId,
|
|
577
471
|
turnId,
|
|
578
|
-
inbound:
|
|
472
|
+
inbound: opts.inbound,
|
|
579
473
|
binding: opts.binding,
|
|
580
474
|
}));
|
|
581
|
-
if (pendingFlashes.length > 0) {
|
|
582
|
-
try {
|
|
583
|
-
await markSessionFlashesDelivered({
|
|
584
|
-
repoRoot: opts.inbound.repo_root,
|
|
585
|
-
sessionId,
|
|
586
|
-
flashIds: pendingFlashes.map((row) => row.flash_id),
|
|
587
|
-
nowMs: Date.now(),
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
catch {
|
|
591
|
-
// Best-effort delivery bookkeeping; do not fail the operator turn.
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
475
|
}
|
|
595
476
|
catch (err) {
|
|
596
477
|
const failureCode = classifyBackendFailureCode(err);
|
|
@@ -707,53 +588,6 @@ function buildOperatorPromptContextBlock(metadata) {
|
|
|
707
588
|
}
|
|
708
589
|
return ["", "Client context (structured preview):", preview];
|
|
709
590
|
}
|
|
710
|
-
function extractSessionFlashPromptMessages(metadata) {
|
|
711
|
-
const raw = metadata.session_flash_messages;
|
|
712
|
-
if (!Array.isArray(raw)) {
|
|
713
|
-
return [];
|
|
714
|
-
}
|
|
715
|
-
const out = [];
|
|
716
|
-
for (const value of raw) {
|
|
717
|
-
const rec = asRecord(value);
|
|
718
|
-
if (!rec) {
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
const flashId = nonEmptyString(rec.flash_id);
|
|
722
|
-
const body = nonEmptyString(rec.body);
|
|
723
|
-
const sessionId = nonEmptyString(rec.session_id);
|
|
724
|
-
if (!flashId || !body || !sessionId) {
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
727
|
-
out.push({
|
|
728
|
-
flash_id: flashId,
|
|
729
|
-
created_at_ms: finiteInt(rec.created_at_ms) ?? 0,
|
|
730
|
-
session_id: sessionId,
|
|
731
|
-
session_kind: nonEmptyString(rec.session_kind),
|
|
732
|
-
body,
|
|
733
|
-
context_ids: stringList(rec.context_ids),
|
|
734
|
-
source: nonEmptyString(rec.source),
|
|
735
|
-
metadata: asRecord(rec.metadata) ?? {},
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
out.sort((a, b) => a.created_at_ms - b.created_at_ms);
|
|
739
|
-
return out;
|
|
740
|
-
}
|
|
741
|
-
function buildOperatorPromptFlashBlock(metadata) {
|
|
742
|
-
const flashes = extractSessionFlashPromptMessages(metadata);
|
|
743
|
-
if (flashes.length === 0) {
|
|
744
|
-
return [];
|
|
745
|
-
}
|
|
746
|
-
const lines = ["", `Session flash messages (${flashes.length}):`];
|
|
747
|
-
for (const flash of flashes) {
|
|
748
|
-
const source = flash.source ?? "unknown";
|
|
749
|
-
const contextIds = flash.context_ids.length > 0 ? ` | context_ids=${flash.context_ids.join(",")}` : "";
|
|
750
|
-
const bodyPreview = compactJsonPreview(flash.body, 400) ?? flash.body;
|
|
751
|
-
lines.push(`- [${flash.flash_id}] source=${source}${contextIds}`);
|
|
752
|
-
lines.push(` ${bodyPreview}`);
|
|
753
|
-
}
|
|
754
|
-
lines.push("Treat these as high-priority user-provided context for this session.");
|
|
755
|
-
return lines;
|
|
756
|
-
}
|
|
757
591
|
function buildOperatorPrompt(input) {
|
|
758
592
|
const lines = [
|
|
759
593
|
`[Messaging context]`,
|
|
@@ -763,7 +597,6 @@ function buildOperatorPrompt(input) {
|
|
|
763
597
|
``,
|
|
764
598
|
`User message: ${input.inbound.command_text}`,
|
|
765
599
|
...buildOperatorPromptContextBlock(input.inbound.metadata),
|
|
766
|
-
...buildOperatorPromptFlashBlock(input.inbound.metadata),
|
|
767
600
|
];
|
|
768
601
|
return lines.join("\n");
|
|
769
602
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session_factory.d.ts","sourceRoot":"","sources":["../src/session_factory.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,iBAAiB,GAAG,KAAK,GAAG,MAAM,CAAC;AAExF,MAAM,MAAM,wBAAwB,GAAG;IACtC,IAAI,CAAC,EAAE,wBAAwB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE;QAChB,SAAS,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACF,CAAC;
|
|
1
|
+
{"version":3,"file":"session_factory.d.ts","sourceRoot":"","sources":["../src/session_factory.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,iBAAiB,GAAG,KAAK,GAAG,MAAM,CAAC;AAExF,MAAM,MAAM,wBAAwB,GAAG;IACtC,IAAI,CAAC,EAAE,wBAAwB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE;QAChB,SAAS,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACF,CAAC;AAsCF,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAuDnF"}
|
package/dist/session_factory.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { createMuResourceLoader, resolveModel } from "./backend.js";
|
|
3
3
|
import { MU_DEFAULT_THEME_NAME, MU_DEFAULT_THEME_PATH } from "./ui_defaults.js";
|
|
4
|
+
function resolveSessionPersistenceMode(sessionOpts) {
|
|
5
|
+
return sessionOpts?.mode ?? (sessionOpts?.sessionFile ? "open" : "continue-recent");
|
|
6
|
+
}
|
|
4
7
|
function createSessionManager(SessionManager, cwd, sessionOpts) {
|
|
5
|
-
const mode =
|
|
8
|
+
const mode = resolveSessionPersistenceMode(sessionOpts);
|
|
6
9
|
const sessionDir = sessionOpts?.sessionDir;
|
|
7
10
|
switch (mode) {
|
|
8
11
|
case "continue-recent":
|
|
@@ -23,10 +26,13 @@ function createSessionManager(SessionManager, cwd, sessionOpts) {
|
|
|
23
26
|
export async function createMuSession(opts) {
|
|
24
27
|
const { AuthStorage, createAgentSession, SessionManager, SettingsManager } = await import("@mariozechner/pi-coding-agent");
|
|
25
28
|
const authStorage = AuthStorage.create();
|
|
29
|
+
const sessionMode = resolveSessionPersistenceMode(opts.session);
|
|
30
|
+
const shouldRestoreSessionConfig = sessionMode === "open" && !opts.provider && !opts.model;
|
|
26
31
|
const defaultModel = "gpt-5.3-codex";
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
32
|
+
const requestedModelId = opts.model?.trim();
|
|
33
|
+
const modelId = requestedModelId && requestedModelId.length > 0 ? requestedModelId : shouldRestoreSessionConfig ? null : defaultModel;
|
|
34
|
+
const model = modelId ? resolveModel(modelId, authStorage, opts.provider) : undefined;
|
|
35
|
+
if (modelId && !model) {
|
|
30
36
|
const scope = opts.provider ? ` in provider "${opts.provider}"` : "";
|
|
31
37
|
throw new Error(`Model "${modelId}" not found${scope} in pi-ai registry.`);
|
|
32
38
|
}
|
|
@@ -45,15 +51,22 @@ export async function createMuSession(opts) {
|
|
|
45
51
|
createWriteTool(opts.cwd),
|
|
46
52
|
createEditTool(opts.cwd),
|
|
47
53
|
];
|
|
48
|
-
const
|
|
54
|
+
const requestedThinking = opts.thinking?.trim();
|
|
55
|
+
const thinkingLevel = requestedThinking && requestedThinking.length > 0
|
|
56
|
+
? requestedThinking
|
|
57
|
+
: shouldRestoreSessionConfig
|
|
58
|
+
? undefined
|
|
59
|
+
: "minimal";
|
|
60
|
+
const createOpts = {
|
|
49
61
|
cwd: opts.cwd,
|
|
50
|
-
model,
|
|
62
|
+
...(model ? { model } : {}),
|
|
51
63
|
tools,
|
|
52
|
-
thinkingLevel:
|
|
64
|
+
...(thinkingLevel ? { thinkingLevel: thinkingLevel } : {}),
|
|
53
65
|
sessionManager: createSessionManager(SessionManager, opts.cwd, opts.session),
|
|
54
66
|
settingsManager,
|
|
55
67
|
resourceLoader,
|
|
56
68
|
authStorage,
|
|
57
|
-
}
|
|
69
|
+
};
|
|
70
|
+
const { session } = await createAgentSession(createOpts);
|
|
58
71
|
return session;
|
|
59
72
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session_turn.d.ts","sourceRoot":"","sources":["../src/session_turn.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjG,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AACrD,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEL,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAIlD;
|
|
1
|
+
{"version":3,"file":"session_turn.d.ts","sourceRoot":"","sources":["../src/session_turn.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjG,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AACrD,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEL,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAIlD;AAuUD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACvE,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAyCA;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgG7B"}
|
package/dist/session_turn.js
CHANGED
|
@@ -65,6 +65,29 @@ function defaultSessionDirForKind(repoRoot, sessionKind) {
|
|
|
65
65
|
return join(storeDir, "control-plane", "operator-sessions");
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
function normalizePathForPrefixMatch(path) {
|
|
69
|
+
return resolve(path).replaceAll("\\", "/");
|
|
70
|
+
}
|
|
71
|
+
function inferSessionKindFromSessionDir(repoRoot, sessionDir) {
|
|
72
|
+
const storeDir = getStorePaths(repoRoot).storeDir;
|
|
73
|
+
const normalizedSessionDir = normalizePathForPrefixMatch(sessionDir);
|
|
74
|
+
const operatorDir = normalizePathForPrefixMatch(join(storeDir, "operator", "sessions"));
|
|
75
|
+
if (normalizedSessionDir === operatorDir || normalizedSessionDir.startsWith(`${operatorDir}/`)) {
|
|
76
|
+
return "operator";
|
|
77
|
+
}
|
|
78
|
+
const cpOperatorDir = normalizePathForPrefixMatch(join(storeDir, "control-plane", "operator-sessions"));
|
|
79
|
+
if (normalizedSessionDir === cpOperatorDir || normalizedSessionDir.startsWith(`${cpOperatorDir}/`)) {
|
|
80
|
+
return "cp_operator";
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function defaultSessionLookupTargets(repoRoot) {
|
|
85
|
+
const storeDir = getStorePaths(repoRoot).storeDir;
|
|
86
|
+
return [
|
|
87
|
+
{ sessionKind: "operator", sessionDir: join(storeDir, "operator", "sessions") },
|
|
88
|
+
{ sessionKind: "cp_operator", sessionDir: join(storeDir, "control-plane", "operator-sessions") },
|
|
89
|
+
];
|
|
90
|
+
}
|
|
68
91
|
async function pathExists(path) {
|
|
69
92
|
try {
|
|
70
93
|
await stat(path);
|
|
@@ -208,9 +231,7 @@ function safeSessionFile(session) {
|
|
|
208
231
|
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
209
232
|
}
|
|
210
233
|
async function resolveSessionTarget(opts) {
|
|
211
|
-
const
|
|
212
|
-
? resolveRepoPath(opts.repoRoot, opts.request.session_dir)
|
|
213
|
-
: defaultSessionDirForKind(opts.repoRoot, opts.normalizedSessionKind);
|
|
234
|
+
const explicitSessionDir = opts.request.session_dir ? resolveRepoPath(opts.repoRoot, opts.request.session_dir) : null;
|
|
214
235
|
if (opts.request.session_file) {
|
|
215
236
|
const sessionFile = resolveRepoPath(opts.repoRoot, opts.request.session_file);
|
|
216
237
|
if (!(await pathExists(sessionFile))) {
|
|
@@ -223,22 +244,55 @@ async function resolveSessionTarget(opts) {
|
|
|
223
244
|
if (headerId !== opts.request.session_id) {
|
|
224
245
|
throw new SessionTurnError(409, `session_file header id mismatch (expected ${opts.request.session_id}, found ${headerId})`);
|
|
225
246
|
}
|
|
247
|
+
const sessionDir = explicitSessionDir ?? dirname(sessionFile);
|
|
248
|
+
return {
|
|
249
|
+
sessionFile,
|
|
250
|
+
sessionDir,
|
|
251
|
+
sessionKind: opts.normalizedSessionKind ?? inferSessionKindFromSessionDir(opts.repoRoot, sessionDir),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (explicitSessionDir || opts.normalizedSessionKind) {
|
|
255
|
+
const sessionDir = explicitSessionDir ?? defaultSessionDirForKind(opts.repoRoot, opts.normalizedSessionKind);
|
|
256
|
+
if (!(await directoryExists(sessionDir))) {
|
|
257
|
+
throw new SessionTurnError(404, `session directory not found: ${sessionDir}`);
|
|
258
|
+
}
|
|
259
|
+
const sessionFile = await resolveSessionFileById({
|
|
260
|
+
sessionDir,
|
|
261
|
+
sessionId: opts.request.session_id,
|
|
262
|
+
});
|
|
263
|
+
if (!sessionFile) {
|
|
264
|
+
throw new SessionTurnError(404, `session_id not found in ${sessionDir}: ${opts.request.session_id}`);
|
|
265
|
+
}
|
|
226
266
|
return {
|
|
227
267
|
sessionFile,
|
|
228
|
-
sessionDir
|
|
268
|
+
sessionDir,
|
|
269
|
+
sessionKind: opts.normalizedSessionKind ?? inferSessionKindFromSessionDir(opts.repoRoot, sessionDir),
|
|
229
270
|
};
|
|
230
271
|
}
|
|
231
|
-
|
|
232
|
-
|
|
272
|
+
const matches = [];
|
|
273
|
+
for (const target of defaultSessionLookupTargets(opts.repoRoot)) {
|
|
274
|
+
if (!(await directoryExists(target.sessionDir))) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const sessionFile = await resolveSessionFileById({
|
|
278
|
+
sessionDir: target.sessionDir,
|
|
279
|
+
sessionId: opts.request.session_id,
|
|
280
|
+
});
|
|
281
|
+
if (sessionFile) {
|
|
282
|
+
matches.push({
|
|
283
|
+
sessionFile,
|
|
284
|
+
sessionDir: target.sessionDir,
|
|
285
|
+
sessionKind: target.sessionKind,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (matches.length === 1) {
|
|
290
|
+
return matches[0];
|
|
233
291
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
sessionId: opts.request.session_id,
|
|
237
|
-
});
|
|
238
|
-
if (!sessionFile) {
|
|
239
|
-
throw new SessionTurnError(404, `session_id not found in ${sessionDir}: ${opts.request.session_id}`);
|
|
292
|
+
if (matches.length > 1) {
|
|
293
|
+
throw new SessionTurnError(409, `session_id is ambiguous across operator/cp_operator stores: ${opts.request.session_id} (pass --session-kind or --session-dir)`);
|
|
240
294
|
}
|
|
241
|
-
|
|
295
|
+
throw new SessionTurnError(404, `session_id not found in operator/cp_operator stores: ${opts.request.session_id}`);
|
|
242
296
|
}
|
|
243
297
|
export function parseSessionTurnRequest(body) {
|
|
244
298
|
const sessionId = nonEmptyString(body.session_id);
|
|
@@ -289,18 +343,19 @@ export async function executeSessionTurn(opts) {
|
|
|
289
343
|
request: opts.request,
|
|
290
344
|
normalizedSessionKind,
|
|
291
345
|
});
|
|
346
|
+
const effectiveSessionKind = target.sessionKind ?? normalizedSessionKind;
|
|
292
347
|
const sessionFactory = opts.sessionFactory ?? createMuSession;
|
|
293
348
|
const session = await sessionFactory({
|
|
294
349
|
cwd: opts.repoRoot,
|
|
295
350
|
systemPrompt: systemPromptForTurn({
|
|
296
|
-
sessionKind:
|
|
351
|
+
sessionKind: effectiveSessionKind,
|
|
297
352
|
extensionProfile,
|
|
298
353
|
}),
|
|
299
354
|
provider: opts.request.provider ?? undefined,
|
|
300
355
|
model: opts.request.model ?? undefined,
|
|
301
356
|
thinking: opts.request.thinking ?? undefined,
|
|
302
357
|
extensionPaths: extensionPathsForTurn({
|
|
303
|
-
sessionKind:
|
|
358
|
+
sessionKind: effectiveSessionKind,
|
|
304
359
|
extensionProfile,
|
|
305
360
|
}),
|
|
306
361
|
session: {
|
|
@@ -365,7 +420,7 @@ export async function executeSessionTurn(opts) {
|
|
|
365
420
|
}
|
|
366
421
|
return {
|
|
367
422
|
session_id: resolvedSessionId ?? opts.request.session_id,
|
|
368
|
-
session_kind:
|
|
423
|
+
session_kind: effectiveSessionKind,
|
|
369
424
|
session_file: resolvedSessionFile ?? target.sessionFile,
|
|
370
425
|
context_entry_id: contextEntryId,
|
|
371
426
|
reply,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-agent",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.103",
|
|
4
4
|
"description": "Shared operator runtime for mu assistant sessions and serve extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mu",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"themes/**"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@femtomc/mu-core": "26.2.
|
|
28
|
-
"@mariozechner/pi-agent-core": "^0.
|
|
29
|
-
"@mariozechner/pi-ai": "^0.
|
|
30
|
-
"@mariozechner/pi-coding-agent": "^0.
|
|
27
|
+
"@femtomc/mu-core": "26.2.103",
|
|
28
|
+
"@mariozechner/pi-agent-core": "^0.54.2",
|
|
29
|
+
"@mariozechner/pi-ai": "^0.54.2",
|
|
30
|
+
"@mariozechner/pi-coding-agent": "^0.54.2",
|
|
31
31
|
"zod": "^4.1.9"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -19,5 +19,6 @@ Using `mu`:
|
|
|
19
19
|
- The `mu` CLI is self-explanatory: poke around with `mu --help` to understand.
|
|
20
20
|
- Use `mu memory search|timeline|stats` to access contextual memory from past interactions.
|
|
21
21
|
- Use `mu memory index status|rebuild` to inspect/refresh local memory index health when needed.
|
|
22
|
+
- Use `mu control harness [provider] --json --pretty` to inspect harness adapter/provider/model configuration and model capability vectors.
|
|
22
23
|
- Use `mu heartbeats` and `mu cron` to access persistent scheduled processes that broadcast to the user.
|
|
23
24
|
|
|
@@ -44,6 +44,7 @@ Notes:
|
|
|
44
44
|
- `set` and `update` are both upsert-style single-doc writes.
|
|
45
45
|
- `replace` is whole-inventory replacement.
|
|
46
46
|
- Tool results include normalized `hud_docs` for downstream transport/rendering.
|
|
47
|
+
- Advisory preset-shape warnings are surfaced in tool `details.preset_warnings` when `metadata.style_preset` and doc shape diverge (non-blocking).
|
|
47
48
|
|
|
48
49
|
### Command (`/mu hud ...`)
|
|
49
50
|
|
|
@@ -72,14 +73,15 @@ Minimum practical fields:
|
|
|
72
73
|
Common optional fields:
|
|
73
74
|
|
|
74
75
|
- `scope` (for root/session/issue scoping)
|
|
75
|
-
- `
|
|
76
|
+
- `title_style` / `snapshot_style` (`{weight?:"normal|strong", italic?:boolean, code?:boolean}`)
|
|
77
|
+
- `chips` (`[{key,label,tone?,style?}]`)
|
|
76
78
|
- `sections`:
|
|
77
|
-
- `kv` (key/value)
|
|
78
|
-
- `checklist` (checkbox-style progress)
|
|
79
|
-
- `activity` (recent lines)
|
|
80
|
-
- `text` (free text)
|
|
81
|
-
- `actions` (`[{id,label,command_text,kind?}]`)
|
|
82
|
-
- `metadata` (machine-readable extras)
|
|
79
|
+
- `kv` (key/value; supports `title_style` + item `value_style`)
|
|
80
|
+
- `checklist` (checkbox-style progress; supports `title_style` + item `style`)
|
|
81
|
+
- `activity` (recent lines; supports `title_style`)
|
|
82
|
+
- `text` (free text; supports `title_style` + `style`)
|
|
83
|
+
- `actions` (`[{id,label,command_text,kind?,style?}]`)
|
|
84
|
+
- `metadata` (machine-readable extras; optional `style_preset` convention: `planning|subagents`)
|
|
83
85
|
|
|
84
86
|
Example checklist doc:
|
|
85
87
|
|
|
@@ -92,7 +94,7 @@ Example checklist doc:
|
|
|
92
94
|
"title": "Planning HUD",
|
|
93
95
|
"scope": "mu-root-123",
|
|
94
96
|
"chips": [
|
|
95
|
-
{ "key": "phase", "label": "phase:drafting", "tone": "accent" },
|
|
97
|
+
{ "key": "phase", "label": "phase:drafting", "tone": "accent", "style": { "weight": "strong" } },
|
|
96
98
|
{ "key": "steps", "label": "steps:2/5", "tone": "dim" }
|
|
97
99
|
],
|
|
98
100
|
"sections": [
|
|
@@ -107,11 +109,11 @@ Example checklist doc:
|
|
|
107
109
|
}
|
|
108
110
|
],
|
|
109
111
|
"actions": [
|
|
110
|
-
{ "id": "snapshot", "label": "Snapshot", "command_text": "/mu hud snapshot", "kind": "secondary" }
|
|
112
|
+
{ "id": "snapshot", "label": "Snapshot", "command_text": "/mu hud snapshot", "kind": "secondary", "style": { "italic": true } }
|
|
111
113
|
],
|
|
112
114
|
"snapshot_compact": "HUD(plan) · phase=drafting · steps=2/5",
|
|
113
115
|
"updated_at_ms": 1771853115000,
|
|
114
|
-
"metadata": { "phase": "drafting" }
|
|
116
|
+
"metadata": { "style_preset": "planning", "phase": "drafting" }
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
```
|
|
@@ -136,9 +136,6 @@ mu memory stats --pretty
|
|
|
136
136
|
- adjust anchors (`issue_id`, `run_id`, `session_id`, `topic`, channel metadata)
|
|
137
137
|
- rebuild selected sources only
|
|
138
138
|
|
|
139
|
-
Compatibility note:
|
|
140
|
-
- `mu context ...` remains an alias to `mu memory ...`.
|
|
141
|
-
|
|
142
139
|
## Evaluation scenarios
|
|
143
140
|
|
|
144
141
|
1. **Issue-scoped context retrieval**
|