@femtomc/mu-agent 26.2.98 → 26.2.99
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 +17 -0
- package/dist/operator.d.ts +3 -1
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +141 -6
- package/dist/session_factory.d.ts +1 -0
- package/dist/session_factory.d.ts.map +1 -1
- package/package.json +2 -2
- package/prompts/skills/crons/SKILL.md +204 -0
- package/prompts/skills/heartbeats/SKILL.md +172 -0
- package/prompts/skills/hierarchical-work-protocol/SKILL.md +233 -0
- package/prompts/skills/memory/SKILL.md +154 -0
- package/prompts/skills/mu/SKILL.md +144 -0
- package/prompts/skills/planning/SKILL.md +75 -14
- package/prompts/skills/setup-discord/SKILL.md +141 -0
- package/prompts/skills/setup-neovim/SKILL.md +141 -0
- package/prompts/skills/setup-slack/SKILL.md +159 -0
- package/prompts/skills/setup-telegram/SKILL.md +171 -0
- package/prompts/skills/subagents/SKILL.md +92 -165
package/README.md
CHANGED
|
@@ -19,6 +19,23 @@ Bundled defaults now live as markdown files under `packages/agent/prompts/`:
|
|
|
19
19
|
|
|
20
20
|
These are loaded by runtime code and are the single source of truth for default system prompts.
|
|
21
21
|
|
|
22
|
+
## Bundled starter skills
|
|
23
|
+
|
|
24
|
+
Bundled starter skills live under `packages/agent/prompts/skills/` and are bootstrapped
|
|
25
|
+
into `~/.mu/skills/` (or `$MU_HOME/skills/`) when missing:
|
|
26
|
+
|
|
27
|
+
- `mu`
|
|
28
|
+
- `memory`
|
|
29
|
+
- `planning`
|
|
30
|
+
- `hierarchical-work-protocol`
|
|
31
|
+
- `subagents`
|
|
32
|
+
- `heartbeats`
|
|
33
|
+
- `crons`
|
|
34
|
+
- `setup-slack`
|
|
35
|
+
- `setup-discord`
|
|
36
|
+
- `setup-telegram`
|
|
37
|
+
- `setup-neovim`
|
|
38
|
+
|
|
22
39
|
## Install
|
|
23
40
|
|
|
24
41
|
```bash
|
package/dist/operator.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ export type OperatorBackendTurnInput = {
|
|
|
105
105
|
};
|
|
106
106
|
export interface MessagingOperatorBackend {
|
|
107
107
|
runTurn(input: OperatorBackendTurnInput): Promise<OperatorBackendTurnResult>;
|
|
108
|
+
abortSession?(sessionId: string): Promise<boolean> | boolean;
|
|
108
109
|
dispose?(): void | Promise<void>;
|
|
109
110
|
}
|
|
110
111
|
export type OperatorDecision = {
|
|
@@ -119,7 +120,7 @@ export type OperatorDecision = {
|
|
|
119
120
|
operatorTurnId: string;
|
|
120
121
|
} | {
|
|
121
122
|
kind: "reject";
|
|
122
|
-
reason: "operator_disabled" | "operator_action_disallowed" | "operator_invalid_output" | "context_missing" | "context_ambiguous" | "context_unauthorized" | "cli_validation_failed";
|
|
123
|
+
reason: "operator_disabled" | "operator_action_disallowed" | "operator_invalid_output" | "operator_cancelled" | "context_missing" | "context_ambiguous" | "context_unauthorized" | "cli_validation_failed";
|
|
123
124
|
details?: string;
|
|
124
125
|
operatorSessionId: string;
|
|
125
126
|
operatorTurnId: string;
|
|
@@ -191,6 +192,7 @@ export { DEFAULT_OPERATOR_SYSTEM_PROMPT };
|
|
|
191
192
|
export declare class PiMessagingOperatorBackend implements MessagingOperatorBackend {
|
|
192
193
|
#private;
|
|
193
194
|
constructor(opts?: PiMessagingOperatorBackendOpts);
|
|
195
|
+
abortSession(sessionId: string): Promise<boolean>;
|
|
194
196
|
runTurn(input: OperatorBackendTurnInput): Promise<OperatorBackendTurnResult>;
|
|
195
197
|
dispose(): void;
|
|
196
198
|
}
|
package/dist/operator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"AAGA,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;
|
|
1
|
+
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
|
package/dist/operator.js
CHANGED
|
@@ -7,6 +7,8 @@ import { createMuSession } from "./session_factory.js";
|
|
|
7
7
|
import { DEFAULT_OPERATOR_SYSTEM_PROMPT } from "./default_prompts.js";
|
|
8
8
|
const OPERATOR_RESPONSE_MAX_CHARS = 12_000;
|
|
9
9
|
const SAFE_RESPONSE_RE = new RegExp(`^[\\s\\S]{1,${OPERATOR_RESPONSE_MAX_CHARS}}$`);
|
|
10
|
+
const OPERATOR_TIMEOUT_MIN_MS = 1_000;
|
|
11
|
+
const OPERATOR_TIMEOUT_HARD_CAP_MS = 10 * 60 * 1_000;
|
|
10
12
|
export const OperatorApprovedCommandSchema = z.discriminatedUnion("kind", [
|
|
11
13
|
z.object({ kind: z.literal("status") }),
|
|
12
14
|
z.object({ kind: z.literal("ready") }),
|
|
@@ -161,12 +163,48 @@ function defaultTurnId() {
|
|
|
161
163
|
return `turn-${crypto.randomUUID()}`;
|
|
162
164
|
}
|
|
163
165
|
function buildOperatorFailureFallbackMessage(code) {
|
|
166
|
+
const reasonLine = code === "operator_timeout"
|
|
167
|
+
? "The operator turn exceeded the messaging timeout before a safe response was produced."
|
|
168
|
+
: code === "operator_busy"
|
|
169
|
+
? "Another operator turn is already in progress for this conversation."
|
|
170
|
+
: code === "operator_empty_response"
|
|
171
|
+
? "The operator completed without a usable response payload."
|
|
172
|
+
: code === "operator_cancelled"
|
|
173
|
+
? "The operator turn was cancelled before completion."
|
|
174
|
+
: "An internal operator runtime/formatting error interrupted the turn.";
|
|
164
175
|
return [
|
|
165
|
-
"I
|
|
176
|
+
"I could not complete that turn safely.",
|
|
166
177
|
`Code: ${code}`,
|
|
167
|
-
|
|
178
|
+
reasonLine,
|
|
179
|
+
"You can retry this request in plain conversational text.",
|
|
168
180
|
].join("\n");
|
|
169
181
|
}
|
|
182
|
+
function classifyBackendFailureCode(err) {
|
|
183
|
+
if (!(err instanceof Error)) {
|
|
184
|
+
return "operator_backend_error";
|
|
185
|
+
}
|
|
186
|
+
const message = err.message.trim().toLowerCase();
|
|
187
|
+
if (message.includes("pi operator timeout") || message.includes("operator timeout")) {
|
|
188
|
+
return "operator_timeout";
|
|
189
|
+
}
|
|
190
|
+
if (message.includes("operator_empty_response")) {
|
|
191
|
+
return "operator_empty_response";
|
|
192
|
+
}
|
|
193
|
+
if (message.includes("agent is already processing")) {
|
|
194
|
+
return "operator_busy";
|
|
195
|
+
}
|
|
196
|
+
if (message.includes("aborted") || message.includes("cancelled")) {
|
|
197
|
+
return "operator_cancelled";
|
|
198
|
+
}
|
|
199
|
+
return "operator_backend_error";
|
|
200
|
+
}
|
|
201
|
+
function parseOperatorControlDirective(commandText) {
|
|
202
|
+
const normalized = commandText.trim().toLowerCase();
|
|
203
|
+
if (normalized === "cancel" || normalized === "abort" || normalized === "/mu cancel" || normalized === "/mu abort") {
|
|
204
|
+
return "cancel";
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
170
208
|
function isAgentBusyError(err) {
|
|
171
209
|
if (!(err instanceof Error)) {
|
|
172
210
|
return false;
|
|
@@ -375,6 +413,7 @@ export class MessagingOperatorRuntime {
|
|
|
375
413
|
#turnIdFactory;
|
|
376
414
|
#conversationSessionStore;
|
|
377
415
|
#sessionByConversation = new Map();
|
|
416
|
+
#suppressedCancelledTurnsBySession = new Map();
|
|
378
417
|
constructor(opts) {
|
|
379
418
|
this.#backend = opts.backend;
|
|
380
419
|
this.#broker = opts.broker ?? new ApprovedCommandBroker();
|
|
@@ -414,6 +453,22 @@ export class MessagingOperatorRuntime {
|
|
|
414
453
|
}
|
|
415
454
|
return created;
|
|
416
455
|
}
|
|
456
|
+
#markSuppressedCancelledTurn(sessionId) {
|
|
457
|
+
const next = (this.#suppressedCancelledTurnsBySession.get(sessionId) ?? 0) + 1;
|
|
458
|
+
this.#suppressedCancelledTurnsBySession.set(sessionId, next);
|
|
459
|
+
}
|
|
460
|
+
#consumeSuppressedCancelledTurn(sessionId) {
|
|
461
|
+
const current = this.#suppressedCancelledTurnsBySession.get(sessionId) ?? 0;
|
|
462
|
+
if (current <= 0) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
if (current === 1) {
|
|
466
|
+
this.#suppressedCancelledTurnsBySession.delete(sessionId);
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
this.#suppressedCancelledTurnsBySession.set(sessionId, current - 1);
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
417
472
|
async handleInbound(opts) {
|
|
418
473
|
const sessionId = await this.#resolveSessionId(opts.inbound, opts.binding);
|
|
419
474
|
const turnId = this.#turnIdFactory();
|
|
@@ -433,6 +488,21 @@ export class MessagingOperatorRuntime {
|
|
|
433
488
|
operatorTurnId: turnId,
|
|
434
489
|
};
|
|
435
490
|
}
|
|
491
|
+
const controlDirective = parseOperatorControlDirective(opts.inbound.command_text);
|
|
492
|
+
if (controlDirective === "cancel") {
|
|
493
|
+
const aborted = (await this.#backend.abortSession?.(sessionId)) ?? false;
|
|
494
|
+
if (aborted) {
|
|
495
|
+
this.#markSuppressedCancelledTurn(sessionId);
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
kind: "response",
|
|
499
|
+
message: aborted
|
|
500
|
+
? "Cancelled the in-flight operator turn for this conversation."
|
|
501
|
+
: "No in-flight operator turn is active for this conversation.",
|
|
502
|
+
operatorSessionId: sessionId,
|
|
503
|
+
operatorTurnId: turnId,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
436
506
|
let pendingFlashes = [];
|
|
437
507
|
try {
|
|
438
508
|
pendingFlashes = await loadPendingSessionFlashes({
|
|
@@ -475,9 +545,18 @@ export class MessagingOperatorRuntime {
|
|
|
475
545
|
}
|
|
476
546
|
}
|
|
477
547
|
catch (err) {
|
|
548
|
+
const failureCode = classifyBackendFailureCode(err);
|
|
549
|
+
if (failureCode === "operator_cancelled" && this.#consumeSuppressedCancelledTurn(sessionId)) {
|
|
550
|
+
return {
|
|
551
|
+
kind: "reject",
|
|
552
|
+
reason: "operator_cancelled",
|
|
553
|
+
operatorSessionId: sessionId,
|
|
554
|
+
operatorTurnId: turnId,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
478
557
|
return {
|
|
479
558
|
kind: "response",
|
|
480
|
-
message: buildOperatorFailureFallbackMessage(
|
|
559
|
+
message: buildOperatorFailureFallbackMessage(failureCode),
|
|
481
560
|
operatorSessionId: sessionId,
|
|
482
561
|
operatorTurnId: turnId,
|
|
483
562
|
};
|
|
@@ -521,6 +600,7 @@ export class MessagingOperatorRuntime {
|
|
|
521
600
|
}
|
|
522
601
|
async stop() {
|
|
523
602
|
this.#sessionByConversation.clear();
|
|
603
|
+
this.#suppressedCancelledTurnsBySession.clear();
|
|
524
604
|
await this.#backend.dispose?.();
|
|
525
605
|
await this.#conversationSessionStore?.stop?.();
|
|
526
606
|
}
|
|
@@ -657,12 +737,14 @@ export class PiMessagingOperatorBackend {
|
|
|
657
737
|
#persistSessions;
|
|
658
738
|
#sessionDirForRepoRoot;
|
|
659
739
|
#sessions = new Map();
|
|
740
|
+
#activePromptCountsBySession = new Map();
|
|
660
741
|
constructor(opts = {}) {
|
|
661
742
|
this.#provider = opts.provider;
|
|
662
743
|
this.#model = opts.model;
|
|
663
744
|
this.#thinking = opts.thinking ?? "minimal";
|
|
664
745
|
this.#systemPrompt = opts.systemPrompt ?? DEFAULT_OPERATOR_SYSTEM_PROMPT;
|
|
665
|
-
|
|
746
|
+
const requestedTimeoutMs = Math.max(OPERATOR_TIMEOUT_MIN_MS, Math.trunc(opts.timeoutMs ?? 90_000));
|
|
747
|
+
this.#timeoutMs = Math.min(requestedTimeoutMs, OPERATOR_TIMEOUT_HARD_CAP_MS);
|
|
666
748
|
this.#extensionPaths = opts.extensionPaths ?? [];
|
|
667
749
|
this.#sessionFactory = opts.sessionFactory ?? createMuSession;
|
|
668
750
|
this.#nowMs = opts.nowMs ?? Date.now;
|
|
@@ -681,6 +763,7 @@ export class PiMessagingOperatorBackend {
|
|
|
681
763
|
return;
|
|
682
764
|
}
|
|
683
765
|
this.#sessions.delete(sessionId);
|
|
766
|
+
this.#activePromptCountsBySession.delete(sessionId);
|
|
684
767
|
try {
|
|
685
768
|
entry.session.dispose();
|
|
686
769
|
}
|
|
@@ -703,6 +786,18 @@ export class PiMessagingOperatorBackend {
|
|
|
703
786
|
this.#disposeSession(sessionId);
|
|
704
787
|
}
|
|
705
788
|
}
|
|
789
|
+
#incrementActivePrompt(sessionId) {
|
|
790
|
+
const next = (this.#activePromptCountsBySession.get(sessionId) ?? 0) + 1;
|
|
791
|
+
this.#activePromptCountsBySession.set(sessionId, next);
|
|
792
|
+
}
|
|
793
|
+
#decrementActivePrompt(sessionId) {
|
|
794
|
+
const current = this.#activePromptCountsBySession.get(sessionId) ?? 0;
|
|
795
|
+
if (current <= 1) {
|
|
796
|
+
this.#activePromptCountsBySession.delete(sessionId);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
this.#activePromptCountsBySession.set(sessionId, current - 1);
|
|
800
|
+
}
|
|
706
801
|
#sessionPersistence(repoRoot, sessionId) {
|
|
707
802
|
if (!this.#persistSessions) {
|
|
708
803
|
return undefined;
|
|
@@ -784,6 +879,27 @@ export class PiMessagingOperatorBackend {
|
|
|
784
879
|
// best effort audit
|
|
785
880
|
}
|
|
786
881
|
}
|
|
882
|
+
async abortSession(sessionId) {
|
|
883
|
+
const activeCount = this.#activePromptCountsBySession.get(sessionId) ?? 0;
|
|
884
|
+
if (activeCount <= 0) {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
const record = this.#sessions.get(sessionId);
|
|
888
|
+
if (!record) {
|
|
889
|
+
return false;
|
|
890
|
+
}
|
|
891
|
+
const abortFn = record.session.abort;
|
|
892
|
+
if (!abortFn) {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
try {
|
|
896
|
+
await abortFn.call(record.session);
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
899
|
+
catch {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
787
903
|
async runTurn(input) {
|
|
788
904
|
const sessionRecord = await this.#resolveSession(input.sessionId, input.inbound.repo_root);
|
|
789
905
|
const session = sessionRecord.session;
|
|
@@ -820,11 +936,29 @@ export class PiMessagingOperatorBackend {
|
|
|
820
936
|
});
|
|
821
937
|
const promptText = buildOperatorPrompt(input);
|
|
822
938
|
const promptOnce = async () => {
|
|
939
|
+
let timeoutHandle = null;
|
|
823
940
|
const timeoutPromise = new Promise((_, reject) => {
|
|
824
|
-
setTimeout(() =>
|
|
941
|
+
timeoutHandle = setTimeout(() => {
|
|
942
|
+
const abortFn = session.abort;
|
|
943
|
+
if (abortFn) {
|
|
944
|
+
void abortFn.call(session).catch(() => {
|
|
945
|
+
// Best effort abort on timeout.
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
reject(new Error("pi operator timeout"));
|
|
949
|
+
}, this.#timeoutMs);
|
|
825
950
|
});
|
|
826
|
-
|
|
951
|
+
try {
|
|
952
|
+
await Promise.race([session.prompt(promptText, { expandPromptTemplates: false }), timeoutPromise]);
|
|
953
|
+
}
|
|
954
|
+
finally {
|
|
955
|
+
if (timeoutHandle) {
|
|
956
|
+
clearTimeout(timeoutHandle);
|
|
957
|
+
timeoutHandle = null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
827
960
|
};
|
|
961
|
+
this.#incrementActivePrompt(input.sessionId);
|
|
828
962
|
try {
|
|
829
963
|
try {
|
|
830
964
|
await promptOnce();
|
|
@@ -848,6 +982,7 @@ export class PiMessagingOperatorBackend {
|
|
|
848
982
|
throw err;
|
|
849
983
|
}
|
|
850
984
|
finally {
|
|
985
|
+
this.#decrementActivePrompt(input.sessionId);
|
|
851
986
|
unsub();
|
|
852
987
|
sessionRecord.lastUsedAtMs = Math.trunc(this.#nowMs());
|
|
853
988
|
}
|
|
@@ -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,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;AAkCF,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CnF"}
|
|
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;AAkCF,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CnF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-agent",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.99",
|
|
4
4
|
"description": "Shared operator runtime for mu assistant sessions and serve extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mu",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"themes/**"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@femtomc/mu-core": "26.2.
|
|
27
|
+
"@femtomc/mu-core": "26.2.99",
|
|
28
28
|
"@mariozechner/pi-agent-core": "^0.53.0",
|
|
29
29
|
"@mariozechner/pi-ai": "^0.53.0",
|
|
30
30
|
"@mariozechner/pi-coding-agent": "^0.53.0",
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crons
|
|
3
|
+
description: "Runs cron-program lifecycle workflows for wall-clock scheduling, recurring automation, and scheduler diagnostics. Use when creating, tuning, or repairing time-based automation."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# crons
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to schedule, inspect, tune, or debug `mu cron` automation.
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Core contract](#core-contract)
|
|
13
|
+
- [Preflight checks](#preflight-checks)
|
|
14
|
+
- [Schedule kinds](#schedule-kinds)
|
|
15
|
+
- [Cron lifecycle workflow](#cron-lifecycle-workflow)
|
|
16
|
+
- [Prompt design for scheduled runs](#prompt-design-for-scheduled-runs)
|
|
17
|
+
- [Diagnostics and recovery](#diagnostics-and-recovery)
|
|
18
|
+
- [Evaluation scenarios](#evaluation-scenarios)
|
|
19
|
+
|
|
20
|
+
## Core contract
|
|
21
|
+
|
|
22
|
+
1. **Use explicit schedule semantics**
|
|
23
|
+
- Choose the right schedule kind (`at`, `every`, or `cron`) instead of overloading one pattern.
|
|
24
|
+
- Keep schedule intent readable in `title` and `reason` fields.
|
|
25
|
+
|
|
26
|
+
2. **CLI-first lifecycle control**
|
|
27
|
+
- Create/update/trigger/enable/disable/delete via `mu cron ...` commands.
|
|
28
|
+
- Do not hand-edit `cron.jsonl`.
|
|
29
|
+
|
|
30
|
+
3. **Bounded execution prompts**
|
|
31
|
+
- Scheduled runs should execute one bounded control-loop pass and exit.
|
|
32
|
+
- Avoid prompts that imply unbounded autonomous execution.
|
|
33
|
+
|
|
34
|
+
4. **Read -> mutate -> verify**
|
|
35
|
+
- Inspect scheduler/program state first.
|
|
36
|
+
- After changes, re-read and trigger a smoke run.
|
|
37
|
+
|
|
38
|
+
## Preflight checks
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mu status --pretty
|
|
42
|
+
mu control status --pretty
|
|
43
|
+
mu control identities --all --pretty
|
|
44
|
+
mu cron stats --pretty
|
|
45
|
+
mu cron list --limit 20 --pretty
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If user expects channel delivery, verify linked operator identity for that channel
|
|
49
|
+
before diagnosing cron execution as broken.
|
|
50
|
+
|
|
51
|
+
## Schedule kinds
|
|
52
|
+
|
|
53
|
+
1. **One-shot (`at`)**
|
|
54
|
+
- Run once at an absolute time.
|
|
55
|
+
- Flags: `--schedule-kind at --at <iso8601>` (or `--at-ms <epoch-ms>`)
|
|
56
|
+
|
|
57
|
+
2. **Fixed interval (`every`)**
|
|
58
|
+
- Run repeatedly on fixed millisecond cadence.
|
|
59
|
+
- Flags: `--schedule-kind every --every-ms <ms>`
|
|
60
|
+
- Optional alignment: `--anchor-ms <epoch-ms>`
|
|
61
|
+
|
|
62
|
+
3. **Cron expression (`cron`)**
|
|
63
|
+
- Run by cron expression.
|
|
64
|
+
- Flags: `--schedule-kind cron --expr "<cron-expr>" --tz <timezone>`
|
|
65
|
+
|
|
66
|
+
## Cron lifecycle workflow
|
|
67
|
+
|
|
68
|
+
### 1) Inspect scheduler and programs
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
mu cron stats --pretty
|
|
72
|
+
mu cron list --limit 20 --pretty
|
|
73
|
+
mu cron get <program-id> --pretty
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Use `--json --pretty` for full records.
|
|
77
|
+
|
|
78
|
+
### 2) Create a cron program
|
|
79
|
+
|
|
80
|
+
One-shot example:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
mu cron create \
|
|
84
|
+
--title "One-shot audit" \
|
|
85
|
+
--schedule-kind at \
|
|
86
|
+
--at 2026-02-22T02:00:00Z \
|
|
87
|
+
--reason oneshot_audit
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Fixed interval example:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
mu cron create \
|
|
94
|
+
--title "Every 10m check" \
|
|
95
|
+
--schedule-kind every \
|
|
96
|
+
--every-ms 600000 \
|
|
97
|
+
--reason periodic_check
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Cron-expression example:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
mu cron create \
|
|
104
|
+
--title "Nightly maintenance" \
|
|
105
|
+
--schedule-kind cron \
|
|
106
|
+
--expr "0 2 * * *" \
|
|
107
|
+
--tz UTC \
|
|
108
|
+
--reason nightly_maintenance
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 3) Update schedule or enablement
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
mu cron update <program-id> --enabled false
|
|
115
|
+
mu cron update <program-id> --schedule-kind every --every-ms 300000
|
|
116
|
+
mu cron update <program-id> --schedule-kind cron --expr "0 3 * * *" --tz UTC
|
|
117
|
+
mu cron enable <program-id>
|
|
118
|
+
mu cron disable <program-id>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 4) Trigger smoke run
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
mu cron trigger <program-id> --reason smoke_test
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Then verify:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
mu cron get <program-id> --pretty
|
|
131
|
+
mu store tail events --limit 40 --pretty
|
|
132
|
+
mu store tail cp_operator_turns --limit 40 --pretty
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5) Delete obsolete programs
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
mu cron delete <program-id>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Prompt design for scheduled runs
|
|
142
|
+
|
|
143
|
+
Use concise prompts with explicit bounded-pass behavior.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
Review open issues for root <root-id>. Perform exactly one bounded step:
|
|
149
|
+
select one ready task (or report blocked), apply one action, verify state,
|
|
150
|
+
post concise summary, then exit.
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
For DAG execution workloads, combine with:
|
|
154
|
+
- `planning`
|
|
155
|
+
- `hierarchical-work-protocol`
|
|
156
|
+
- `subagents`
|
|
157
|
+
- `heartbeats` (for short-cadence wake loops)
|
|
158
|
+
|
|
159
|
+
## Diagnostics and recovery
|
|
160
|
+
|
|
161
|
+
When cron automation appears stalled or misfiring:
|
|
162
|
+
|
|
163
|
+
1. Confirm scheduler + program state:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
mu cron stats --pretty
|
|
167
|
+
mu cron list --enabled true --limit 50 --pretty
|
|
168
|
+
mu cron get <program-id> --pretty
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
2. Trigger manually to separate scheduler timing issues from execution issues:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
mu cron trigger <program-id> --reason manual_recovery_test
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
3. Inspect runtime evidence:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
mu store tail events --limit 60 --pretty
|
|
181
|
+
mu store tail cp_operator_turns --limit 60 --pretty
|
|
182
|
+
mu store tail cp_outbox --limit 40 --pretty
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
4. Apply smallest recovery step:
|
|
186
|
+
- fix schedule-kind/flags mismatch
|
|
187
|
+
- correct timezone (`--tz`)
|
|
188
|
+
- simplify prompt scope
|
|
189
|
+
- toggle enablement (`disable` -> `enable`)
|
|
190
|
+
- relink channel identity if delivery is absent
|
|
191
|
+
|
|
192
|
+
## Evaluation scenarios
|
|
193
|
+
|
|
194
|
+
1. **One-shot schedule execution (`at`)**
|
|
195
|
+
- Setup: `mu cron create --schedule-kind at --at <future-iso>`.
|
|
196
|
+
- Expected: program executes once, records deterministic status, then does not re-fire.
|
|
197
|
+
|
|
198
|
+
2. **Interval schedule retune (`every`)**
|
|
199
|
+
- Setup: active interval schedule at 10m.
|
|
200
|
+
- Expected: `mu cron update ... --every-ms 300000` changes cadence to 5m without recreating program ID.
|
|
201
|
+
|
|
202
|
+
3. **Cron expression + timezone correctness**
|
|
203
|
+
- Setup: cron expression schedule with explicit timezone.
|
|
204
|
+
- Expected: next-run alignment matches timezone expectations; manual trigger succeeds and logs are auditable.
|