agents-pod 0.3.5 → 0.3.7
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/CHANGELOG.md +154 -0
- package/README.md +141 -23
- package/examples/feishu/README.md +65 -0
- package/package.json +1 -1
- package/src/admission.ts +102 -0
- package/src/cadence.ts +67 -0
- package/src/cli.ts +885 -94
- package/src/doctor.ts +145 -3
- package/src/extension/handoff.ts +17 -6
- package/src/extension/management.ts +33 -6
- package/src/extension/self.ts +8 -5
- package/src/listeners.ts +14 -2
- package/src/registry.ts +57 -1
- package/src/sources.ts +40 -19
- package/src/wrapper.ts +35 -11
package/src/wrapper.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
withRegistryLock,
|
|
29
29
|
writePods,
|
|
30
30
|
type PodRecord,
|
|
31
|
+
type WakeUpEntry,
|
|
31
32
|
type WakeUpOutcome,
|
|
32
33
|
} from "./registry.ts";
|
|
33
34
|
import { snapshotDocs } from "./workspace.ts";
|
|
@@ -70,7 +71,7 @@ export function composePrompt(goal: string, docsDir: string, roleInstructions =
|
|
|
70
71
|
`1. ${join(docsDir, "HANDOFF.md")} — your predecessor's handoff; start where it ended.`,
|
|
71
72
|
`2. ${join(docsDir, "TASKS.md")} — the plan and its checkboxes.`,
|
|
72
73
|
`3. ${join(docsDir, "DECISIONS.md")} — the why behind the plan.`,
|
|
73
|
-
"When this window's work is done, end with pod_handoff
|
|
74
|
+
"When this window's work is done, end with pod_handoff so your successor starts clean: status rotating (the goal is open and the next wake-up continues), quiet (this window is satisfied, nothing is scheduled — wake me when work arrives), or done (the goal is reached and the pod ends). Write-then-exit is the protocol: an exit without a fresh handoff counts as a failure.",
|
|
74
75
|
].join("\n");
|
|
75
76
|
const goalBlock = `Goal: ${goal}`;
|
|
76
77
|
const sections = [preamble, roleInstructions.trim(), goalBlock].filter(
|
|
@@ -170,14 +171,19 @@ export interface Adjudication {
|
|
|
170
171
|
async function freshDeclaration(
|
|
171
172
|
docsDir: string,
|
|
172
173
|
wakeStart: number,
|
|
173
|
-
): Promise<"more" | "done" | undefined> {
|
|
174
|
+
): Promise<"more" | "quiet" | "done" | undefined> {
|
|
174
175
|
const handoff = join(docsDir, "HANDOFF.md");
|
|
175
176
|
const info = await stat(handoff).catch(() => undefined);
|
|
176
177
|
if (!info || info.mtimeMs <= wakeStart) return undefined;
|
|
177
178
|
const firstLine = (await readFile(handoff, "utf8")).split("\n", 1)[0]!.trim();
|
|
178
|
-
|
|
179
|
+
// Three words, and no fourth (ADR 0009 amendment, #36): `rotating` — another
|
|
180
|
+
// wake-up is the work; `quiet` — this window is satisfied and nothing is
|
|
181
|
+
// scheduled; `done` — terminal. Anything else is not a declaration.
|
|
182
|
+
const declared = /^status:\s*(rotating|quiet|done)\s*$/.exec(firstLine);
|
|
179
183
|
if (!declared) return undefined;
|
|
180
|
-
|
|
184
|
+
const word = declared[1];
|
|
185
|
+
if (word === "done") return "done";
|
|
186
|
+
return word === "quiet" ? "quiet" : "more";
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
/**
|
|
@@ -265,7 +271,13 @@ async function recordUnresolved(
|
|
|
265
271
|
what: string,
|
|
266
272
|
): Promise<boolean> {
|
|
267
273
|
return updatePodRecord(podId, podRoot, n, (record) => {
|
|
268
|
-
record.wakeUps.push({
|
|
274
|
+
record.wakeUps.push({
|
|
275
|
+
n,
|
|
276
|
+
outcome: "failed",
|
|
277
|
+
detail: what,
|
|
278
|
+
at: Date.now(),
|
|
279
|
+
...(record.admitted === undefined ? {} : { trigger: record.admitted }),
|
|
280
|
+
});
|
|
269
281
|
record.state = "idle";
|
|
270
282
|
});
|
|
271
283
|
}
|
|
@@ -493,11 +505,13 @@ export async function runWrapper(argv: string[]): Promise<number> {
|
|
|
493
505
|
// a wedged kill. A human cancel has no intent and records exactly as
|
|
494
506
|
// before.
|
|
495
507
|
if (record.killIntent?.n !== n) {
|
|
496
|
-
record.wakeUps.push(
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
508
|
+
record.wakeUps.push({
|
|
509
|
+
n,
|
|
510
|
+
outcome: "cancelled",
|
|
511
|
+
...(killedAfterGrace ? { detail: "killed after grace" } : {}),
|
|
512
|
+
at: Date.now(),
|
|
513
|
+
...(record.admitted === undefined ? {} : { trigger: record.admitted }),
|
|
514
|
+
});
|
|
501
515
|
record.state = "cancelled";
|
|
502
516
|
}
|
|
503
517
|
});
|
|
@@ -518,7 +532,17 @@ export async function runWrapper(argv: string[]): Promise<number> {
|
|
|
518
532
|
// (ADR 0025). Failures are logged inside the flush and never change outcome.
|
|
519
533
|
await flushOutbox(podId, pod.project, n);
|
|
520
534
|
const recorded = await updatePodRecord(podId, podRoot, n, (record) => {
|
|
521
|
-
|
|
535
|
+
// Provenance (ADR 0032): when, and what admitted the window. `admitted` is
|
|
536
|
+
// written by the launcher under the registry lock, so a wake-up the tick
|
|
537
|
+
// started and one a human started stop looking identical after the fact.
|
|
538
|
+
const entry: WakeUpEntry = {
|
|
539
|
+
n,
|
|
540
|
+
outcome,
|
|
541
|
+
...(detail === undefined ? {} : { detail }),
|
|
542
|
+
at: Date.now(),
|
|
543
|
+
...(record.admitted === undefined ? {} : { trigger: record.admitted }),
|
|
544
|
+
};
|
|
545
|
+
record.wakeUps.push(entry);
|
|
522
546
|
record.state = outcome === "done" ? "done" : "idle";
|
|
523
547
|
});
|
|
524
548
|
if (!recorded) return 1;
|