pi-famulus 0.1.3 → 0.1.4
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 +3 -1
- package/package.json +8 -6
- package/src/agents/builtins.ts +5 -22
- package/src/agents/definition.ts +4 -6
- package/src/bash-override.ts +8 -21
- package/src/behavior-guidelines.ts +40 -16
- package/src/comms/comms.ts +7 -3
- package/src/comms/tools.ts +11 -36
- package/src/format.ts +31 -14
- package/src/index.ts +123 -33
- package/src/manager-client.ts +265 -114
- package/src/monitor.ts +14 -33
- package/src/prompts.generated.ts +183 -0
- package/src/shell-exec.ts +6 -2
- package/src/subagent/admission.ts +224 -0
- package/src/subagent/agent-records.ts +66 -3
- package/src/subagent/child-bash.ts +6 -14
- package/src/subagent/overrun.ts +36 -21
- package/src/subagent/pi-runtime.ts +116 -31
- package/src/subagent/registry.ts +165 -42
- package/src/subagent/runner.ts +335 -60
- package/src/subagent/tool.ts +49 -72
- package/src/subagent/types.ts +60 -5
- package/src/subagent/usage.ts +48 -0
- package/src/task-tools.ts +15 -25
- package/src/tui/message-renderers.ts +17 -1
- package/src/wake.ts +66 -37
- package/src/work-index.ts +4 -0
package/src/wake.ts
CHANGED
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* discriminator the renderer and the eval adapter both read.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// .ts suffix: eval/ loads this module with Node's own TypeScript support, which
|
|
9
|
+
// needs explicit extensions (it imports wake.ts and behavior-guidelines.ts).
|
|
10
|
+
import { fill, PROMPTS } from "./prompts.generated.ts";
|
|
11
|
+
|
|
8
12
|
export const FAMULUS_WAKE_CUSTOM_TYPE = "pi-famulus-wake";
|
|
9
13
|
|
|
10
|
-
export const FAMULUS_WAKE_LEAD_IN =
|
|
11
|
-
"System wake — not a new user message. Handle this <pi-famulus-wake> before other work.";
|
|
14
|
+
export const FAMULUS_WAKE_LEAD_IN = PROMPTS["wakes.lead-in"];
|
|
12
15
|
|
|
13
16
|
export interface WakeItem {
|
|
14
17
|
id: string;
|
|
@@ -60,6 +63,21 @@ export interface OverrunShell {
|
|
|
60
63
|
growing: boolean | null;
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
/** Context retained for every secondary child in a batched overrun wake. */
|
|
67
|
+
export interface SubagentOverrunChild {
|
|
68
|
+
runId: string;
|
|
69
|
+
childId: string;
|
|
70
|
+
name: string;
|
|
71
|
+
elapsedMs: number;
|
|
72
|
+
budgetMs: number;
|
|
73
|
+
reminder: number;
|
|
74
|
+
nextReminderMs: number;
|
|
75
|
+
/** Time left until the opt-in hard ceiling aborts this child. */
|
|
76
|
+
hardCeilingMs?: number;
|
|
77
|
+
lastActivity: { agoMs: number; text: string };
|
|
78
|
+
shell?: OverrunShell;
|
|
79
|
+
}
|
|
80
|
+
|
|
63
81
|
/**
|
|
64
82
|
* When the wake's content was generated (design.md §4.5): stamped by the
|
|
65
83
|
* NotifyCenter when it receives or builds the wake. A steered wake can wait
|
|
@@ -133,6 +151,7 @@ type FamulusWakeBody =
|
|
|
133
151
|
summary: string;
|
|
134
152
|
lastActivity: { agoMs: number; text: string };
|
|
135
153
|
shell?: OverrunShell;
|
|
154
|
+
additional?: SubagentOverrunChild[];
|
|
136
155
|
}
|
|
137
156
|
| { kind: "supervisor-request"; from: string; name: string; message: string }
|
|
138
157
|
| { kind: "supervisor-update"; from: string; name: string; message: string };
|
|
@@ -177,6 +196,7 @@ export function wakeIds(details: { kind?: string }): string[] {
|
|
|
177
196
|
};
|
|
178
197
|
if (d.kind === "task") return (d.tasks ?? []).map((task) => task.id);
|
|
179
198
|
if (d.kind === "subagent-done") return (d.children ?? []).map((child) => child.childId);
|
|
199
|
+
if (d.kind === "subagent-overrun") return [d.childId, ...((d as { additional?: { childId: string }[] }).additional ?? []).map((child) => child.childId)].filter((id): id is string => Boolean(id));
|
|
180
200
|
return [d.id ?? d.taskId ?? d.childId ?? d.from].filter((id): id is string => Boolean(id));
|
|
181
201
|
}
|
|
182
202
|
|
|
@@ -317,7 +337,7 @@ function renderHandover(details: Extract<FamulusWake, { kind: "subagent-handover
|
|
|
317
337
|
parts.push(
|
|
318
338
|
` <changed-since-as-of>${escapeXml(
|
|
319
339
|
`${details.name} (${details.childId}): ${details.statusAsOf} → ${details.status}.` +
|
|
320
|
-
(active ? "
|
|
340
|
+
(active ? ` ${PROMPTS["wakes.handover.changed-active"]}` : ""),
|
|
321
341
|
)}</changed-since-as-of>`,
|
|
322
342
|
);
|
|
323
343
|
}
|
|
@@ -344,7 +364,7 @@ function renderDone(details: Extract<FamulusWake, { kind: "subagent-done" }>): s
|
|
|
344
364
|
const active = details.children.some((c) => c.status === "pending" || c.status === "running");
|
|
345
365
|
parts.push(
|
|
346
366
|
` <changed-since-as-of>${escapeXml(
|
|
347
|
-
`${list}.` + (active ? "
|
|
367
|
+
`${list}.` + (active ? ` ${PROMPTS["wakes.done.changed-active"]}` : ""),
|
|
348
368
|
)}</changed-since-as-of>`,
|
|
349
369
|
);
|
|
350
370
|
}
|
|
@@ -387,17 +407,29 @@ function renderOverrun(details: Extract<FamulusWake, { kind: "subagent-overrun"
|
|
|
387
407
|
parts.push(
|
|
388
408
|
` <last-activity ago-ms="${Math.round(details.lastActivity.agoMs)}">${escapeXml(details.lastActivity.text)}</last-activity>`,
|
|
389
409
|
);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
410
|
+
if (details.additional?.length) {
|
|
411
|
+
parts.push(" <also-overdue>");
|
|
412
|
+
for (const child of details.additional) {
|
|
413
|
+
const childAttrs = [
|
|
414
|
+
`run-id="${escapeXmlAttr(child.runId)}"`,
|
|
415
|
+
`child-id="${escapeXmlAttr(child.childId)}"`,
|
|
416
|
+
`name="${escapeXmlAttr(child.name)}"`,
|
|
417
|
+
`elapsed-ms="${Math.round(child.elapsedMs)}"`,
|
|
418
|
+
`budget-ms="${Math.round(child.budgetMs)}"`,
|
|
419
|
+
`reminder="${child.reminder}"`,
|
|
420
|
+
`next-reminder-ms="${Math.round(child.nextReminderMs)}"`,
|
|
421
|
+
];
|
|
422
|
+
if (child.hardCeilingMs !== undefined) childAttrs.push(`hard-ceiling-ms="${Math.round(child.hardCeilingMs)}"`);
|
|
423
|
+
parts.push(` <child ${childAttrs.join(" ")}>`);
|
|
424
|
+
parts.push(
|
|
425
|
+
` <last-activity ago-ms="${Math.round(child.lastActivity.agoMs)}">${escapeXml(child.lastActivity.text)}</last-activity>`,
|
|
426
|
+
);
|
|
427
|
+
if (child.shell) parts.push(...renderOverrunShell(child.shell, " "));
|
|
428
|
+
parts.push(" </child>");
|
|
429
|
+
}
|
|
430
|
+
parts.push(" </also-overdue>");
|
|
400
431
|
}
|
|
432
|
+
if (details.shell) parts.push(...renderOverrunShell(details.shell, " "));
|
|
401
433
|
parts.push(` <options>${escapeXml(overrunOptions(details))}</options>`);
|
|
402
434
|
parts.push("</pi-famulus-wake>");
|
|
403
435
|
return parts.join("\n");
|
|
@@ -405,37 +437,34 @@ function renderOverrun(details: Extract<FamulusWake, { kind: "subagent-overrun"
|
|
|
405
437
|
|
|
406
438
|
/** The parent's three actions and what happens if it takes none. */
|
|
407
439
|
function overrunOptions(details: Extract<FamulusWake, { kind: "subagent-overrun" }>): string {
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
`give it more time with subagent({ action: "extend", ${ids}, timeout_ms: <ms from now> }); ` +
|
|
411
|
-
`redirect it with agent_message({ action: "send", to: "${details.childId}", message: "<instruction>" }); ` +
|
|
412
|
-
`or stop it with subagent({ action: "interrupt", ${ids} }). `;
|
|
440
|
+
const actions = fill("wakes.overrun.actions", { runId: details.runId, childId: details.childId });
|
|
441
|
+
const next = wakeDuration(details.nextReminderMs);
|
|
413
442
|
const hard = details.hardCeilingMs;
|
|
414
|
-
if (hard === undefined) {
|
|
415
|
-
return (
|
|
416
|
-
"It has not been stopped. Choose one: " +
|
|
417
|
-
actions +
|
|
418
|
-
`If you do none of these, it keeps running and the next reminder is scheduled in ${wakeDuration(details.nextReminderMs)}; ` +
|
|
419
|
-
"its result arrives as usual when it finishes."
|
|
420
|
-
);
|
|
421
|
-
}
|
|
443
|
+
if (hard === undefined) return fill("wakes.overrun.options", { actions, next });
|
|
422
444
|
// Opt-in hard ceiling: never promise "keeps running" past it.
|
|
423
445
|
const ceiling = wakeDuration(hard);
|
|
424
446
|
const noAction =
|
|
425
447
|
details.nextReminderMs < hard
|
|
426
|
-
?
|
|
427
|
-
: "
|
|
428
|
-
return (
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
);
|
|
448
|
+
? fill("wakes.overrun.ceiling-no-action-reminder", { next, ceiling })
|
|
449
|
+
: PROMPTS["wakes.overrun.ceiling-no-action"];
|
|
450
|
+
return fill("wakes.overrun.options-ceiling", { ceiling, actions, noAction });
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function renderOverrunShell(shell: OverrunShell, indent: string): string[] {
|
|
454
|
+
const shellAttrs = [`task-id="${escapeXmlAttr(shell.taskId)}"`, `elapsed-ms="${Math.round(shell.elapsedMs)}"`];
|
|
455
|
+
if (shell.outputBytes !== null) shellAttrs.push(`output-bytes="${shell.outputBytes}"`);
|
|
456
|
+
if (shell.outputIdleMs !== null) shellAttrs.push(`output-idle-ms="${Math.round(shell.outputIdleMs)}"`);
|
|
457
|
+
if (shell.growing !== null) shellAttrs.push(`growing="${shell.growing ? "yes" : "no"}"`);
|
|
458
|
+
return [
|
|
459
|
+
`${indent}<shell ${shellAttrs.join(" ")}>`,
|
|
460
|
+
`${indent} <command>${escapeXml(shell.command)}</command>`,
|
|
461
|
+
`${indent} <output-file>${escapeXml(shell.outputPath)}</output-file>`,
|
|
462
|
+
`${indent}</shell>`,
|
|
463
|
+
];
|
|
435
464
|
}
|
|
436
465
|
|
|
437
466
|
function renderRequest(details: Extract<FamulusWake, { kind: "supervisor-request" }>): string {
|
|
438
|
-
const recipe =
|
|
467
|
+
const recipe = fill("wakes.request.reply-with", { from: details.from });
|
|
439
468
|
return [
|
|
440
469
|
`<pi-famulus-wake kind="supervisor-request" from="${escapeXmlAttr(details.from)}" name="${escapeXmlAttr(details.name)}">`,
|
|
441
470
|
` <message>${escapeXml(details.message)}</message>`,
|
package/src/work-index.ts
CHANGED
|
@@ -14,6 +14,10 @@ export const WORK_FINISHED_CAP = 50;
|
|
|
14
14
|
export interface WorkItem {
|
|
15
15
|
id: string;
|
|
16
16
|
kind: WorkKind;
|
|
17
|
+
/** Daemon work-kind label for agents; shell commands are classified separately. */
|
|
18
|
+
workKind?: string;
|
|
19
|
+
/** Time queued for local/machine admission before the agent started. */
|
|
20
|
+
queueMs?: number;
|
|
17
21
|
status: string;
|
|
18
22
|
title: string;
|
|
19
23
|
command?: string;
|