@llblab/pi-actors 0.42.3 → 0.43.1
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/AGENTS.md +127 -175
- package/BACKLOG.md +189 -1
- package/CHANGELOG.md +183 -292
- package/README.md +115 -276
- package/dist/fixtures/protocol/control-endpoint.json +6 -0
- package/dist/fixtures/protocol/control-record.json +9 -0
- package/dist/fixtures/protocol/recipe-summary.json +4 -12
- package/dist/fixtures/protocol/trace-event.json +9 -0
- package/dist/index.js +1 -1
- package/dist/lib/async-runs.d.ts +15 -38
- package/dist/lib/async-runs.js +173 -111
- package/dist/lib/automatic-review-runtime.d.ts +1 -1
- package/dist/lib/automatic-review-runtime.js +5 -5
- package/dist/lib/command-templates.d.ts +2 -0
- package/dist/lib/command-templates.js +38 -4
- package/dist/lib/control-projection.d.ts +20 -0
- package/dist/lib/control-projection.js +66 -0
- package/dist/lib/control.d.ts +15 -0
- package/dist/lib/control.js +97 -0
- package/dist/lib/draft-sleep.js +3 -3
- package/dist/lib/execution-sessions.d.ts +17 -0
- package/dist/lib/execution-sessions.js +85 -0
- package/dist/lib/file-state.d.ts +2 -0
- package/dist/lib/file-state.js +114 -46
- package/dist/lib/inspector-actions.d.ts +2 -2
- package/dist/lib/inspector-actions.js +2 -2
- package/dist/lib/inspector-command.js +3 -3
- package/dist/lib/inspector-overlay.d.ts +54 -70
- package/dist/lib/inspector-overlay.js +576 -910
- package/dist/lib/inspector.d.ts +3 -71
- package/dist/lib/inspector.js +19 -665
- package/dist/lib/limits.d.ts +7 -3
- package/dist/lib/limits.js +7 -3
- package/dist/lib/observability.d.ts +16 -16
- package/dist/lib/observability.js +43 -82
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +3 -3
- package/dist/lib/recipe-control.d.ts +7 -0
- package/dist/lib/recipe-control.js +43 -0
- package/dist/lib/recipes-discovery.js +2 -0
- package/dist/lib/recipes-references.d.ts +1 -14
- package/dist/lib/recipes-references.js +6 -21
- package/dist/lib/review-control.d.ts +1 -1
- package/dist/lib/review-control.js +4 -5
- package/dist/lib/review-projection.js +1 -5
- package/dist/lib/run-ui-runtime.js +2 -2
- package/dist/lib/runs-control-delivery.d.ts +28 -0
- package/dist/lib/runs-control-delivery.js +150 -0
- package/dist/lib/runs-controls.d.ts +37 -0
- package/dist/lib/runs-controls.js +146 -0
- package/dist/lib/runs-retention.d.ts +7 -0
- package/dist/lib/runs-retention.js +27 -3
- package/dist/lib/runs-start.js +4 -2
- package/dist/lib/runs-status.js +11 -6
- package/dist/lib/runs-trace.d.ts +24 -0
- package/dist/lib/runs-trace.js +102 -0
- package/dist/lib/runtime-identity.d.ts +7 -0
- package/dist/lib/runtime-identity.js +35 -0
- package/dist/lib/runtime-notifier.d.ts +1 -1
- package/dist/lib/runtime-notifier.js +1 -1
- package/dist/lib/runtime-triage.d.ts +29 -0
- package/dist/lib/runtime-triage.js +76 -0
- package/dist/lib/tool-review-scheduler.js +7 -7
- package/dist/lib/tools-inspect.d.ts +3 -3
- package/dist/lib/tools-inspect.js +241 -707
- package/dist/lib/tools-local.js +2 -10
- package/dist/lib/tools-message.d.ts +6 -7
- package/dist/lib/tools-message.js +95 -396
- package/dist/lib/tools-response.d.ts +1 -5
- package/dist/lib/tools-response.js +5 -48
- package/dist/lib/tools-spawn.js +16 -28
- package/dist/lib/tools.d.ts +1 -1
- package/dist/lib/tools.js +2 -3
- package/dist/lib/trace-projection.d.ts +22 -0
- package/dist/lib/trace-projection.js +185 -0
- package/dist/recipes/draft-review.json +0 -10
- package/dist/recipes/lens-swarm.json +0 -14
- package/dist/recipes/music-player.json +10 -19
- package/dist/recipes/pipeline-architect-coordinator.json +0 -11
- package/dist/recipes/pipeline-artifact-bundle.json +1 -22
- package/dist/recipes/pipeline-artifact-report.json +1 -18
- package/dist/recipes/pipeline-artifact-write.json +1 -18
- package/dist/recipes/pipeline-async-run-ops.json +0 -12
- package/dist/recipes/pipeline-checkpoint-continuation.json +0 -14
- package/dist/recipes/pipeline-development-tasking.json +0 -12
- package/dist/recipes/pipeline-docs-maintenance.json +0 -12
- package/dist/recipes/pipeline-media-library.json +0 -12
- package/dist/recipes/pipeline-quorum-review.json +0 -12
- package/dist/recipes/pipeline-release-readiness.json +0 -12
- package/dist/recipes/pipeline-release-summary.json +0 -12
- package/dist/recipes/pipeline-repo-health.json +0 -12
- package/dist/recipes/pipeline-research-synthesis.json +0 -11
- package/dist/recipes/pipeline-review-readiness.json +0 -12
- package/dist/recipes/resource-locker.json +27 -0
- package/dist/recipes/subagent-artifact.json +0 -9
- package/dist/recipes/subagent-checkpoint.json +0 -10
- package/dist/recipes/subagent-conflict-report.json +0 -11
- package/dist/recipes/subagent-contradiction-map.json +0 -11
- package/dist/recipes/subagent-critic.json +0 -11
- package/dist/recipes/subagent-evidence-map.json +0 -11
- package/dist/recipes/subagent-followup.json +0 -10
- package/dist/recipes/subagent-judge.json +0 -11
- package/dist/recipes/subagent-merge.json +0 -11
- package/dist/recipes/subagent-normalize.json +0 -11
- package/dist/recipes/subagent-plan.json +0 -11
- package/dist/recipes/subagent-preflight.json +0 -11
- package/dist/recipes/subagent-prompt.json +0 -10
- package/dist/recipes/subagent-quorum.json +0 -10
- package/dist/recipes/subagent-review-coordinator.json +0 -14
- package/dist/recipes/subagent-review.json +0 -11
- package/dist/recipes/subagent-task-card.json +0 -11
- package/dist/recipes/subagent-tools.json +0 -10
- package/dist/recipes/subagent-verify.json +0 -11
- package/dist/recipes/subagents-prompts.json +0 -10
- package/dist/recipes/tool-review.json +0 -10
- package/dist/scripts/async-runner.mjs +25 -25
- package/dist/scripts/conformance.mjs +4 -2
- package/dist/scripts/locker.mjs +196 -69
- package/dist/scripts/music-player.mjs +162 -159
- package/dist/scripts/recipe-utils.mjs +6 -96
- package/dist/scripts/release-gates.mjs +91 -0
- package/dist/scripts/validate-recipe.mjs +8 -57
- package/dist/skills/actors/SKILL.md +57 -265
- package/dist/skills/swarm/SKILL.md +10 -34
- package/docs/0.43-baseline.md +39 -0
- package/docs/README.md +4 -6
- package/docs/actor-inspector.md +26 -64
- package/docs/async-runs.md +81 -328
- package/docs/command-templates.md +8 -118
- package/docs/recipe-library.md +55 -182
- package/docs/releasing.md +28 -0
- package/docs/template-recipes.md +76 -289
- package/docs/tool-registry.md +41 -161
- package/fixtures/protocol/control-endpoint.json +6 -0
- package/fixtures/protocol/control-record.json +9 -0
- package/fixtures/protocol/recipe-summary.json +4 -12
- package/fixtures/protocol/trace-event.json +9 -0
- package/index.ts +1 -1
- package/lib/async-runs.ts +218 -204
- package/lib/automatic-review-runtime.ts +7 -7
- package/lib/command-templates.ts +44 -4
- package/lib/control-projection.ts +105 -0
- package/lib/control.ts +117 -0
- package/lib/draft-sleep.ts +3 -3
- package/lib/execution-sessions.ts +111 -0
- package/lib/file-state.ts +84 -64
- package/lib/inspector-actions.ts +2 -2
- package/lib/inspector-command.ts +3 -3
- package/lib/inspector-overlay.ts +617 -1126
- package/lib/inspector.ts +46 -979
- package/lib/limits.ts +7 -3
- package/lib/observability.ts +60 -101
- package/lib/prompts.ts +3 -3
- package/lib/recipe-control.ts +52 -0
- package/lib/recipes-discovery.ts +2 -0
- package/lib/recipes-references.ts +9 -45
- package/lib/review-control.ts +4 -5
- package/lib/review-projection.ts +1 -5
- package/lib/run-ui-runtime.ts +2 -2
- package/lib/runs-control-delivery.ts +209 -0
- package/lib/runs-controls.ts +213 -0
- package/lib/runs-retention.ts +38 -3
- package/lib/runs-start.ts +4 -2
- package/lib/runs-status.ts +11 -6
- package/lib/runs-trace.ts +136 -0
- package/lib/runtime-identity.ts +39 -0
- package/lib/runtime-notifier.ts +1 -1
- package/lib/runtime-triage.ts +120 -0
- package/lib/tool-review-scheduler.ts +7 -7
- package/lib/tools-inspect.ts +283 -900
- package/lib/tools-local.ts +2 -12
- package/lib/tools-message.ts +112 -520
- package/lib/tools-response.ts +5 -64
- package/lib/tools-spawn.ts +16 -32
- package/lib/tools.ts +5 -6
- package/lib/trace-projection.ts +244 -0
- package/package.json +2 -1
- package/recipes/draft-review.json +0 -10
- package/recipes/lens-swarm.json +0 -14
- package/recipes/music-player.json +10 -19
- package/recipes/pipeline-architect-coordinator.json +0 -11
- package/recipes/pipeline-artifact-bundle.json +1 -22
- package/recipes/pipeline-artifact-report.json +1 -18
- package/recipes/pipeline-artifact-write.json +1 -18
- package/recipes/pipeline-async-run-ops.json +0 -12
- package/recipes/pipeline-checkpoint-continuation.json +0 -14
- package/recipes/pipeline-development-tasking.json +0 -12
- package/recipes/pipeline-docs-maintenance.json +0 -12
- package/recipes/pipeline-media-library.json +0 -12
- package/recipes/pipeline-quorum-review.json +0 -12
- package/recipes/pipeline-release-readiness.json +0 -12
- package/recipes/pipeline-release-summary.json +0 -12
- package/recipes/pipeline-repo-health.json +0 -12
- package/recipes/pipeline-research-synthesis.json +0 -11
- package/recipes/pipeline-review-readiness.json +0 -12
- package/recipes/resource-locker.json +27 -0
- package/recipes/subagent-artifact.json +0 -9
- package/recipes/subagent-checkpoint.json +0 -10
- package/recipes/subagent-conflict-report.json +0 -11
- package/recipes/subagent-contradiction-map.json +0 -11
- package/recipes/subagent-critic.json +0 -11
- package/recipes/subagent-evidence-map.json +0 -11
- package/recipes/subagent-followup.json +0 -10
- package/recipes/subagent-judge.json +0 -11
- package/recipes/subagent-merge.json +0 -11
- package/recipes/subagent-normalize.json +0 -11
- package/recipes/subagent-plan.json +0 -11
- package/recipes/subagent-preflight.json +0 -11
- package/recipes/subagent-prompt.json +0 -10
- package/recipes/subagent-quorum.json +0 -10
- package/recipes/subagent-review-coordinator.json +0 -14
- package/recipes/subagent-review.json +0 -11
- package/recipes/subagent-task-card.json +0 -11
- package/recipes/subagent-tools.json +0 -10
- package/recipes/subagent-verify.json +0 -11
- package/recipes/subagents-prompts.json +0 -10
- package/recipes/tool-review.json +0 -10
- package/scripts/async-runner.mjs +25 -25
- package/scripts/conformance.mjs +4 -2
- package/scripts/locker.mjs +196 -69
- package/scripts/music-player.mjs +162 -159
- package/scripts/recipe-utils.mjs +6 -96
- package/scripts/release-gates.mjs +91 -0
- package/scripts/validate-recipe.mjs +8 -57
- package/skills/actors/SKILL.md +57 -265
- package/skills/swarm/SKILL.md +10 -34
- package/dist/fixtures/protocol/actor-message-branch.json +0 -13
- package/dist/fixtures/protocol/mailbox-contract.json +0 -15
- package/dist/fixtures/protocol/room-message.json +0 -11
- package/dist/fixtures/protocol/room-roster.json +0 -11
- package/dist/fixtures/protocol/run-inbox-message.json +0 -9
- package/dist/fixtures/protocol/run-outbox-event.json +0 -9
- package/dist/lib/mailbox-loop.d.ts +0 -41
- package/dist/lib/mailbox-loop.js +0 -60
- package/dist/lib/messages.d.ts +0 -25
- package/dist/lib/messages.js +0 -122
- package/dist/lib/rooms.d.ts +0 -104
- package/dist/lib/rooms.js +0 -647
- package/dist/lib/runs-mailbox.d.ts +0 -25
- package/dist/lib/runs-mailbox.js +0 -146
- package/dist/lib/runs-messages.d.ts +0 -15
- package/dist/lib/runs-messages.js +0 -179
- package/dist/lib/runs-outbox.d.ts +0 -41
- package/dist/lib/runs-outbox.js +0 -87
- package/dist/lib/tools-mailbox.d.ts +0 -8
- package/dist/lib/tools-mailbox.js +0 -48
- package/dist/recipes/actor-worker.json +0 -39
- package/dist/recipes/coordinator-locker.json +0 -45
- package/dist/recipes/locker.json +0 -45
- package/dist/recipes/pipeline-room-swarm.json +0 -50
- package/dist/recipes/subagent-message.json +0 -32
- package/dist/recipes/utility-actor-message.json +0 -23
- package/dist/scripts/actor-worker.mjs +0 -214
- package/dist/scripts/coordinator.mjs +0 -799
- package/docs/actor-messages.md +0 -225
- package/docs/actors-deep-reference.md +0 -66
- package/docs/component-recipes.md +0 -148
- package/docs/task-first-recipes.md +0 -263
- package/fixtures/protocol/actor-message-branch.json +0 -13
- package/fixtures/protocol/mailbox-contract.json +0 -15
- package/fixtures/protocol/room-message.json +0 -11
- package/fixtures/protocol/room-roster.json +0 -11
- package/fixtures/protocol/run-inbox-message.json +0 -9
- package/fixtures/protocol/run-outbox-event.json +0 -9
- package/lib/mailbox-loop.ts +0 -144
- package/lib/messages.ts +0 -151
- package/lib/rooms.ts +0 -939
- package/lib/runs-mailbox.ts +0 -208
- package/lib/runs-messages.ts +0 -252
- package/lib/runs-outbox.ts +0 -144
- package/lib/tools-mailbox.ts +0 -56
- package/recipes/actor-worker.json +0 -39
- package/recipes/coordinator-locker.json +0 -45
- package/recipes/locker.json +0 -45
- package/recipes/pipeline-room-swarm.json +0 -50
- package/recipes/subagent-message.json +0 -32
- package/recipes/utility-actor-message.json +0 -23
- package/scripts/actor-worker.mjs +0 -214
- package/scripts/coordinator.mjs +0 -799
package/dist/lib/limits.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared output and
|
|
3
|
-
* Zones: output governance,
|
|
2
|
+
* Shared output, preview, and Control envelope limits.
|
|
3
|
+
* Zones: output governance, Trace previews, Inspect defaults, Control portability
|
|
4
4
|
*/
|
|
5
5
|
export declare const DEFAULT_INSPECT_LINES = 40;
|
|
6
6
|
export declare const TOOL_OUTPUT_MAX_BYTES: number;
|
|
7
7
|
export declare const TOOL_OUTPUT_MAX_LINES = 2000;
|
|
8
8
|
export declare const COMPACT_PREVIEW_CHARS = 160;
|
|
9
|
+
export declare const CONTROL_ACTION_MAX_LENGTH = 64;
|
|
10
|
+
export declare const CONTROL_INPUT_MAX_BYTES = 380;
|
|
11
|
+
export declare const CONTROL_WIRE_MAX_BYTES = 512;
|
|
9
12
|
export declare const INSPECTOR_BODY_PREVIEW_CHARS = 320;
|
|
10
|
-
export declare const ROOM_MESSAGE_PREVIEW_CHARS = 320;
|
|
11
13
|
export declare const DOCTOR_ACTION_PREVIEW_CHARS = 72;
|
|
12
14
|
export declare const SESSION_EVIDENCE_MAX_TURNS = 100;
|
|
13
15
|
export declare const SESSION_EVIDENCE_TEXT_CHARS = 4000;
|
|
14
16
|
export declare const SESSION_EVIDENCE_MAX_TOOL_CALLS = 100;
|
|
17
|
+
export declare const TRACE_EVENT_MAX_BYTES: number;
|
|
18
|
+
export declare const TRACE_EVENT_MAX_READ = 200;
|
package/dist/lib/limits.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared output and
|
|
3
|
-
* Zones: output governance,
|
|
2
|
+
* Shared output, preview, and Control envelope limits.
|
|
3
|
+
* Zones: output governance, Trace previews, Inspect defaults, Control portability
|
|
4
4
|
*/
|
|
5
5
|
export const DEFAULT_INSPECT_LINES = 40;
|
|
6
6
|
export const TOOL_OUTPUT_MAX_BYTES = 50 * 1024;
|
|
7
7
|
export const TOOL_OUTPUT_MAX_LINES = 2_000;
|
|
8
8
|
export const COMPACT_PREVIEW_CHARS = 160;
|
|
9
|
+
export const CONTROL_ACTION_MAX_LENGTH = 64;
|
|
10
|
+
export const CONTROL_INPUT_MAX_BYTES = 380;
|
|
11
|
+
export const CONTROL_WIRE_MAX_BYTES = 512;
|
|
9
12
|
export const INSPECTOR_BODY_PREVIEW_CHARS = 320;
|
|
10
|
-
export const ROOM_MESSAGE_PREVIEW_CHARS = 320;
|
|
11
13
|
export const DOCTOR_ACTION_PREVIEW_CHARS = 72;
|
|
12
14
|
export const SESSION_EVIDENCE_MAX_TURNS = 100;
|
|
13
15
|
export const SESSION_EVIDENCE_TEXT_CHARS = 4_000;
|
|
14
16
|
export const SESSION_EVIDENCE_MAX_TOOL_CALLS = 100;
|
|
17
|
+
export const TRACE_EVENT_MAX_BYTES = 64 * 1024;
|
|
18
|
+
export const TRACE_EVENT_MAX_READ = 200;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Async run observability helpers
|
|
3
3
|
* Zones: async runtime, ambient UI, diagnostics
|
|
4
|
-
* Owns ambient summaries, terminal events, and
|
|
4
|
+
* Owns ambient summaries, terminal events, and Trace-attention delivery for detached Runs
|
|
5
5
|
*/
|
|
6
6
|
import { type FSWatcher } from "node:fs";
|
|
7
7
|
import * as AsyncRuns from "./async-runs.ts";
|
|
8
8
|
export type RunObservedStatus = "running" | "done" | "failed" | "exited" | "cancelled" | "killed";
|
|
9
|
-
export type
|
|
10
|
-
export type
|
|
9
|
+
export type RunTraceAttention = "log" | "notify" | "followup";
|
|
10
|
+
export type RunTraceLevel = "info" | "warning" | "error";
|
|
11
11
|
export interface RunObservation {
|
|
12
12
|
activeSubagents?: number;
|
|
13
13
|
completed?: number;
|
|
@@ -41,13 +41,13 @@ export interface RunSummary {
|
|
|
41
41
|
total: number;
|
|
42
42
|
}
|
|
43
43
|
export interface RunUiObservationState {
|
|
44
|
+
attentionEventIds: Map<string, Set<string>>;
|
|
44
45
|
eventLines: Map<string, number>;
|
|
45
46
|
frame: number;
|
|
46
47
|
observed: Map<string, RunObservedStatus>;
|
|
47
|
-
outboxEventIds: Map<string, Set<string>>;
|
|
48
48
|
}
|
|
49
49
|
export interface RunUiSnapshot {
|
|
50
|
-
|
|
50
|
+
attentionEvents: RunAttentionEvent[];
|
|
51
51
|
status: string | undefined;
|
|
52
52
|
summary: RunSummary;
|
|
53
53
|
transitions: RunTransition[];
|
|
@@ -63,7 +63,7 @@ export interface RunUiNotificationSink {
|
|
|
63
63
|
}
|
|
64
64
|
export declare function createRunUiObservationState(): RunUiObservationState;
|
|
65
65
|
export declare function readRunUiSnapshot(state: RunUiObservationState, ownerId: string, options?: {
|
|
66
|
-
|
|
66
|
+
includeAttention?: boolean;
|
|
67
67
|
stateRoot?: string;
|
|
68
68
|
}): RunUiSnapshot;
|
|
69
69
|
export declare function pruneRunUiObservationState(state: RunUiObservationState, snapshot: Pick<RunUiSnapshot, "summary" | "transitions">): void;
|
|
@@ -75,7 +75,7 @@ export declare function reconcileRunTerminalNotifications(input: {
|
|
|
75
75
|
state: RunUiObservationState;
|
|
76
76
|
stateRoot?: string;
|
|
77
77
|
}): RunUiSnapshot;
|
|
78
|
-
export declare function
|
|
78
|
+
export declare function deliverRunAttentionNotifications(events: RunAttentionEvent[], sink: RunUiNotificationSink): void;
|
|
79
79
|
export interface RunRetirementCandidate {
|
|
80
80
|
activeSubagents: number;
|
|
81
81
|
childRuns: number;
|
|
@@ -150,13 +150,13 @@ export interface RunTerminalSemanticResult {
|
|
|
150
150
|
synthesized: boolean;
|
|
151
151
|
type: string;
|
|
152
152
|
}
|
|
153
|
-
export interface
|
|
153
|
+
export interface RunAttentionEvent {
|
|
154
154
|
body?: unknown;
|
|
155
155
|
data?: unknown;
|
|
156
|
-
|
|
157
|
-
event: string;
|
|
156
|
+
attention: RunTraceAttention;
|
|
158
157
|
id: string;
|
|
159
|
-
|
|
158
|
+
kind: string;
|
|
159
|
+
level: RunTraceLevel;
|
|
160
160
|
metadata?: Record<string, unknown>;
|
|
161
161
|
run: string;
|
|
162
162
|
stateDir: string;
|
|
@@ -173,11 +173,11 @@ export declare function findRunRetirementCandidates(summary: RunSummary): RunRet
|
|
|
173
173
|
export declare function executeRunRetirements(summary: RunSummary, options: RunRetirementExecutorOptions): Promise<RunRetirementExecution[]>;
|
|
174
174
|
export declare function detectRunTransitions(previous: Map<string, RunObservedStatus>, summary: RunSummary): RunTransition[];
|
|
175
175
|
export declare function pruneRunObservationState(previousStatuses: Map<string, RunObservedStatus>, previousLineCounts: Map<string, number>, summary: RunSummary, terminalRuns?: Iterable<string>, seenEventIds?: Map<string, Set<string>>): void;
|
|
176
|
-
export declare function
|
|
177
|
-
export declare function
|
|
178
|
-
export declare function
|
|
179
|
-
export declare function
|
|
180
|
-
export declare function
|
|
176
|
+
export declare function detectRunAttentionEvents(previousLineCounts: Map<string, number>, summary: RunSummary, seenEventIds?: Map<string, Set<string>>): RunAttentionEvent[];
|
|
177
|
+
export declare function getRunAttentionNotificationType(event: RunAttentionEvent): RunTransitionNotificationType;
|
|
178
|
+
export declare function shouldNotifyRunAttentionEvent(event: RunAttentionEvent): boolean;
|
|
179
|
+
export declare function shouldSendRunAttentionFollowUp(event: RunAttentionEvent): boolean;
|
|
180
|
+
export declare function formatRunAttentionMessage(event: RunAttentionEvent): string;
|
|
181
181
|
export declare function getRunTransitionNotificationType(transition: RunTransition): RunTransitionNotificationType;
|
|
182
182
|
export declare function shouldNotifyRunTransition(transition: RunTransition): boolean;
|
|
183
183
|
export declare function shouldSendRunTransitionFollowUp(transition: RunTransition): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Async run observability helpers
|
|
3
3
|
* Zones: async runtime, ambient UI, diagnostics
|
|
4
|
-
* Owns ambient summaries, terminal events, and
|
|
4
|
+
* Owns ambient summaries, terminal events, and Trace-attention delivery for detached Runs
|
|
5
5
|
*/
|
|
6
6
|
import { closeSync, existsSync, fstatSync, openSync, readdirSync, readFileSync, readSync, watch, } from "node:fs";
|
|
7
7
|
import { basename, dirname, isAbsolute, join, relative, } from "node:path";
|
|
@@ -10,26 +10,26 @@ import * as Paths from "./paths.js";
|
|
|
10
10
|
import { readJsonlFileResilient } from "./state-readers.js";
|
|
11
11
|
export function createRunUiObservationState() {
|
|
12
12
|
return {
|
|
13
|
+
attentionEventIds: new Map(),
|
|
13
14
|
eventLines: new Map(),
|
|
14
15
|
frame: 0,
|
|
15
16
|
observed: new Map(),
|
|
16
|
-
outboxEventIds: new Map(),
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
export function readRunUiSnapshot(state, ownerId, options = {}) {
|
|
20
20
|
const summary = summarizeRuns(options.stateRoot, ownerId);
|
|
21
21
|
const status = renderRunStatus(summary, state.frame++);
|
|
22
22
|
return {
|
|
23
|
-
|
|
23
|
+
attentionEvents: options.includeAttention === false
|
|
24
24
|
? []
|
|
25
|
-
:
|
|
25
|
+
: detectRunAttentionEvents(state.eventLines, summary, state.attentionEventIds),
|
|
26
26
|
status,
|
|
27
27
|
summary,
|
|
28
28
|
transitions: detectRunTransitions(state.observed, summary),
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
export function pruneRunUiObservationState(state, snapshot) {
|
|
32
|
-
pruneRunObservationState(state.observed, state.eventLines, snapshot.summary, snapshot.transitions.map((transition) => transition.stateDir ?? transition.run), state.
|
|
32
|
+
pruneRunObservationState(state.observed, state.eventLines, snapshot.summary, snapshot.transitions.map((transition) => transition.stateDir ?? transition.run), state.attentionEventIds);
|
|
33
33
|
}
|
|
34
34
|
export function deliverRunTransitionNotifications(transitions, sink, inFlight = new Set()) {
|
|
35
35
|
for (const transition of transitions) {
|
|
@@ -68,23 +68,23 @@ export function deliverRunTransitionNotifications(transitions, sink, inFlight =
|
|
|
68
68
|
}
|
|
69
69
|
export function reconcileRunTerminalNotifications(input) {
|
|
70
70
|
const snapshot = readRunUiSnapshot(input.state, input.ownerId, {
|
|
71
|
-
|
|
71
|
+
includeAttention: false,
|
|
72
72
|
stateRoot: input.stateRoot,
|
|
73
73
|
});
|
|
74
74
|
deliverRunTransitionNotifications(snapshot.transitions, input.sink, input.inFlight);
|
|
75
75
|
pruneRunUiObservationState(input.state, snapshot);
|
|
76
76
|
return snapshot;
|
|
77
77
|
}
|
|
78
|
-
export function
|
|
78
|
+
export function deliverRunAttentionNotifications(events, sink) {
|
|
79
79
|
for (const event of events) {
|
|
80
|
-
if (!
|
|
80
|
+
if (!shouldNotifyRunAttentionEvent(event))
|
|
81
81
|
continue;
|
|
82
|
-
const text =
|
|
83
|
-
sink.notify(text,
|
|
84
|
-
if (!
|
|
82
|
+
const text = formatRunAttentionMessage(event);
|
|
83
|
+
sink.notify(text, getRunAttentionNotificationType(event));
|
|
84
|
+
if (!shouldSendRunAttentionFollowUp(event))
|
|
85
85
|
continue;
|
|
86
86
|
sink.sendFollowUp({
|
|
87
|
-
customType: "pi-actors-run-
|
|
87
|
+
customType: "pi-actors-run-trace",
|
|
88
88
|
content: text,
|
|
89
89
|
display: false,
|
|
90
90
|
details: event,
|
|
@@ -326,46 +326,7 @@ function terminalSemanticResult(status, stateDir, observedStatus) {
|
|
|
326
326
|
: typeof correlation.tool_call_id === "string"
|
|
327
327
|
? correlation.tool_call_id
|
|
328
328
|
: undefined;
|
|
329
|
-
const
|
|
330
|
-
typeof status.mailbox === "object" &&
|
|
331
|
-
!Array.isArray(status.mailbox)
|
|
332
|
-
? status.mailbox
|
|
333
|
-
: {};
|
|
334
|
-
const emits = Array.isArray(mailbox.emits)
|
|
335
|
-
? mailbox.emits.filter((item) => typeof item === "string")
|
|
336
|
-
: [];
|
|
337
|
-
const outbox = readJsonlFileResilient(join(stateDir, "outbox.jsonl")).records;
|
|
338
|
-
const explicit = outbox.findLast((record) => {
|
|
339
|
-
const type = String(record.type ?? record.event ?? "");
|
|
340
|
-
return emits.includes(type) && !["command.done", "run.done", "run.failed"].includes(type);
|
|
341
|
-
});
|
|
342
|
-
if (explicit) {
|
|
343
|
-
const type = String(explicit.type ?? explicit.event);
|
|
344
|
-
return {
|
|
345
|
-
...(explicit.body === undefined
|
|
346
|
-
? {} : { body: formatSemanticBody(explicit.body) }),
|
|
347
|
-
...(typeof explicit.correlation_id === "string"
|
|
348
|
-
? { correlationId: explicit.correlation_id } : correlationId ? { correlationId } : {}),
|
|
349
|
-
metadata: {
|
|
350
|
-
...(explicit.metadata &&
|
|
351
|
-
typeof explicit.metadata === "object" &&
|
|
352
|
-
!Array.isArray(explicit.metadata)
|
|
353
|
-
? explicit.metadata : {}),
|
|
354
|
-
...(status.transport_context &&
|
|
355
|
-
typeof status.transport_context === "object" &&
|
|
356
|
-
!Array.isArray(status.transport_context)
|
|
357
|
-
? {
|
|
358
|
-
transport_context: status.transport_context,
|
|
359
|
-
} : {}),
|
|
360
|
-
run: String(status.run ?? ""),
|
|
361
|
-
status: observedStatus,
|
|
362
|
-
},
|
|
363
|
-
summary: String(explicit.summary ?? type),
|
|
364
|
-
synthesized: false,
|
|
365
|
-
type,
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
const reviewCompleted = observedStatus === "done" && emits.includes("review.completed");
|
|
329
|
+
const reviewCompleted = false;
|
|
369
330
|
const result = status.result &&
|
|
370
331
|
typeof status.result === "object" &&
|
|
371
332
|
!Array.isArray(status.result)
|
|
@@ -396,14 +357,6 @@ function terminalSemanticResult(status, stateDir, observedStatus) {
|
|
|
396
357
|
type,
|
|
397
358
|
};
|
|
398
359
|
}
|
|
399
|
-
function formatSemanticBody(body) {
|
|
400
|
-
const rendered = typeof body === "string" ? body : JSON.stringify(body);
|
|
401
|
-
const text = typeof rendered === "string" ? rendered : String(body);
|
|
402
|
-
const compact = text.trim();
|
|
403
|
-
return compact.length > TERMINAL_RESULT_CHARS
|
|
404
|
-
? `${compact.slice(0, TERMINAL_RESULT_CHARS - 1)}…`
|
|
405
|
-
: compact;
|
|
406
|
-
}
|
|
407
360
|
function observeRun(stateDir) {
|
|
408
361
|
try {
|
|
409
362
|
const status = AsyncRuns.getRunStatus(stateDir);
|
|
@@ -771,18 +724,20 @@ export function detectRunTransitions(previous, summary) {
|
|
|
771
724
|
}
|
|
772
725
|
return transitions;
|
|
773
726
|
}
|
|
774
|
-
function
|
|
727
|
+
function normalizeTraceAttention(value) {
|
|
775
728
|
return value === "notify" || value === "followup" ? value : "log";
|
|
776
729
|
}
|
|
777
|
-
function
|
|
730
|
+
function normalizeTraceLevel(value) {
|
|
778
731
|
return value === "warning" || value === "error" ? value : "info";
|
|
779
732
|
}
|
|
780
|
-
function
|
|
733
|
+
function parseAttentionRecord(raw, run, index) {
|
|
781
734
|
if (!run.stateDir)
|
|
782
735
|
return undefined;
|
|
783
|
-
const event = typeof raw.
|
|
784
|
-
? raw.
|
|
785
|
-
: "
|
|
736
|
+
const event = typeof raw.kind === "string" && raw.kind.trim()
|
|
737
|
+
? raw.kind.trim()
|
|
738
|
+
: typeof raw.event === "string" && raw.event.trim()
|
|
739
|
+
? raw.event.trim()
|
|
740
|
+
: "run.event";
|
|
786
741
|
const summary = typeof raw.summary === "string" && raw.summary.trim()
|
|
787
742
|
? raw.summary.trim()
|
|
788
743
|
: event;
|
|
@@ -795,10 +750,12 @@ function parseOutboxRecord(raw, run, index) {
|
|
|
795
750
|
return {
|
|
796
751
|
...(raw.body !== undefined ? { body: raw.body } : {}),
|
|
797
752
|
...(raw.data !== undefined ? { data: raw.data } : {}),
|
|
798
|
-
|
|
799
|
-
|
|
753
|
+
attention: raw.attention === "notify" || raw.attention === "followup"
|
|
754
|
+
? raw.attention
|
|
755
|
+
: normalizeTraceAttention(raw.delivery),
|
|
800
756
|
id,
|
|
801
|
-
|
|
757
|
+
kind: event,
|
|
758
|
+
level: normalizeTraceLevel(raw.level),
|
|
802
759
|
...(raw.metadata &&
|
|
803
760
|
typeof raw.metadata === "object" &&
|
|
804
761
|
!Array.isArray(raw.metadata)
|
|
@@ -810,9 +767,13 @@ function parseOutboxRecord(raw, run, index) {
|
|
|
810
767
|
ts,
|
|
811
768
|
};
|
|
812
769
|
}
|
|
813
|
-
function
|
|
770
|
+
function readTraceAttentionRecords(run) {
|
|
814
771
|
if (!run.stateDir)
|
|
815
772
|
return [];
|
|
773
|
+
const tracePath = join(run.stateDir, "trace.jsonl");
|
|
774
|
+
if (existsSync(tracePath)) {
|
|
775
|
+
return readJsonlFileResilient(tracePath).records;
|
|
776
|
+
}
|
|
816
777
|
return readJsonlFileResilient(join(run.stateDir, "outbox.jsonl")).records;
|
|
817
778
|
}
|
|
818
779
|
export function pruneRunObservationState(previousStatuses, previousLineCounts, summary, terminalRuns = [], seenEventIds = new Map()) {
|
|
@@ -839,11 +800,11 @@ export function pruneRunObservationState(previousStatuses, previousLineCounts, s
|
|
|
839
800
|
}
|
|
840
801
|
}
|
|
841
802
|
}
|
|
842
|
-
export function
|
|
803
|
+
export function detectRunAttentionEvents(previousLineCounts, summary, seenEventIds = new Map()) {
|
|
843
804
|
const events = [];
|
|
844
805
|
for (const run of summary.runs) {
|
|
845
806
|
const key = run.stateDir ?? run.run;
|
|
846
|
-
const records =
|
|
807
|
+
const records = readTraceAttentionRecords(run);
|
|
847
808
|
const previousCount = previousLineCounts.get(key) ?? 0;
|
|
848
809
|
if (run.notificationPolicy === "silent") {
|
|
849
810
|
previousLineCounts.set(key, records.length);
|
|
@@ -853,7 +814,7 @@ export function detectRunOutboxEvents(previousLineCounts, summary, seenEventIds
|
|
|
853
814
|
const start = Math.min(previousCount, records.length);
|
|
854
815
|
const seen = seenEventIds.get(key) ?? new Set();
|
|
855
816
|
for (let index = start; index < records.length; index += 1) {
|
|
856
|
-
const event =
|
|
817
|
+
const event = parseAttentionRecord(records[index], run, index);
|
|
857
818
|
if (!event || seen.has(event.id))
|
|
858
819
|
continue;
|
|
859
820
|
events.push(event);
|
|
@@ -864,14 +825,14 @@ export function detectRunOutboxEvents(previousLineCounts, summary, seenEventIds
|
|
|
864
825
|
}
|
|
865
826
|
return events;
|
|
866
827
|
}
|
|
867
|
-
export function
|
|
828
|
+
export function getRunAttentionNotificationType(event) {
|
|
868
829
|
return event.level;
|
|
869
830
|
}
|
|
870
|
-
export function
|
|
871
|
-
return event.
|
|
831
|
+
export function shouldNotifyRunAttentionEvent(event) {
|
|
832
|
+
return event.attention === "notify" || event.attention === "followup";
|
|
872
833
|
}
|
|
873
|
-
export function
|
|
874
|
-
return event.
|
|
834
|
+
export function shouldSendRunAttentionFollowUp(event) {
|
|
835
|
+
return event.attention === "followup";
|
|
875
836
|
}
|
|
876
837
|
function commonDirectory(paths) {
|
|
877
838
|
if (paths.length === 0)
|
|
@@ -919,7 +880,7 @@ function formatNamedArtifacts(artifacts) {
|
|
|
919
880
|
return "";
|
|
920
881
|
return formatPathGroup("Artifacts", Object.values(artifacts).filter((path) => typeof path === "string"));
|
|
921
882
|
}
|
|
922
|
-
function
|
|
883
|
+
function getAttentionDataField(event, key) {
|
|
923
884
|
return event.data &&
|
|
924
885
|
typeof event.data === "object" &&
|
|
925
886
|
!Array.isArray(event.data)
|
|
@@ -935,10 +896,10 @@ function formatBodyPreview(body) {
|
|
|
935
896
|
return "";
|
|
936
897
|
return `\nBody: ${compact.length > 500 ? `${compact.slice(0, 500)}…` : compact}`;
|
|
937
898
|
}
|
|
938
|
-
export function
|
|
939
|
-
if (event.
|
|
899
|
+
export function formatRunAttentionMessage(event) {
|
|
900
|
+
if (event.kind === "command.done")
|
|
940
901
|
return `Run ${event.run}: ${event.summary}`;
|
|
941
|
-
return `Run ${event.run}: ${event.summary}${formatBodyPreview(event.body)}${formatNamedArtifacts(
|
|
902
|
+
return `Run ${event.run}: ${event.summary}${formatBodyPreview(event.body)}${formatNamedArtifacts(getAttentionDataField(event, "artifacts"))}${formatRunFileList(getAttentionDataField(event, "run_files"))}`;
|
|
942
903
|
}
|
|
943
904
|
export function getRunTransitionNotificationType(transition) {
|
|
944
905
|
if (transition.to === "done" || transition.to === "cancelled")
|
package/dist/lib/prompts.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export declare const REGISTER_TOOL_DESCRIPTION: string;
|
|
7
7
|
export declare const REGISTER_TOOL_PROMPT_SNIPPET = "Register persistent command templates as agent-callable tools";
|
|
8
8
|
export declare const REGISTER_TOOL_GUIDELINES: string[];
|
|
9
|
-
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync and shell-free: string leaves split into executable + argv,
|
|
9
|
+
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node\u2192bun\u2192deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.\n- Recipes own template directly and may declare metadata/defaults/imports/control/artifacts; files >1 MiB or import depth >32 fail closed.\n- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.\n- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.\n- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid internal transport vocabulary in public guidance.\n- Run state lives under ~/.pi/agent/tmp/pi-actors/runs. Inspect intentionally and avoid busy-polling. Terminal and coordinator-bound notifications queue as Pi follow-ups so concurrently completed actors can reach the coordinator after current work instead of steering between tool calls. Terminal follow-up content stays minimal: run, status, one base path, and relative artifact names only; semantic output stays in non-LLM details and run state. When a deferred actor result gates the next step, wait for its terminal follow-up; do not schedule continuation loops, repeatedly inspect, or mutate its reviewed scope while it runs. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue/stuck run.\n- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; packaged/ad hoc recipes are lower-priority components; offer to save successful recurring patterns only after confirmation.\n- Prefer maintained packaged recipes/pipelines with spawn file=<recipe> before ad hoc scripts/wrappers; review swarms inherit current model/thinking, preflight before fanout, and expose quorum/concurrency/TTL knobs unless explicit args are passed.\n- For any non-trivial actor use or pi-actors change, read the bundled actors skill first. Before launching multiple actors/subagents for parallel implementation, independent artifact generation, delegated audit, or review, also read the bundled swarm skill; the coordinator owns decomposition, disjoint scopes, launch correctness, integration, and final validation. For deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.";
|
|
10
10
|
export declare const REGISTER_TOOL_PARAM_DESCRIPTIONS: {
|
|
11
11
|
readonly name: "Tool name in snake_case (e.g., 'transcribe')";
|
|
12
12
|
readonly description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.";
|
package/dist/lib/prompts.js
CHANGED
|
@@ -16,13 +16,13 @@ export const REGISTER_TOOL_GUIDELINES = [
|
|
|
16
16
|
export const ONBOARDING_SYSTEM_PROMPT = `pi-actors quick model:
|
|
17
17
|
- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.
|
|
18
18
|
- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.
|
|
19
|
-
- Command templates stay sync and shell-free: string leaves split into executable + argv,
|
|
19
|
+
- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node→bun→deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.
|
|
20
20
|
- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.
|
|
21
21
|
- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.
|
|
22
|
-
- Recipes own template directly and may declare metadata/defaults/imports/
|
|
22
|
+
- Recipes own template directly and may declare metadata/defaults/imports/control/artifacts; files >1 MiB or import depth >32 fail closed.
|
|
23
23
|
- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.
|
|
24
24
|
- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.
|
|
25
|
-
- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid
|
|
25
|
+
- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid internal transport vocabulary in public guidance.
|
|
26
26
|
- Run state lives under ~/.pi/agent/tmp/pi-actors/runs. Inspect intentionally and avoid busy-polling. Terminal and coordinator-bound notifications queue as Pi follow-ups so concurrently completed actors can reach the coordinator after current work instead of steering between tool calls. Terminal follow-up content stays minimal: run, status, one base path, and relative artifact names only; semantic output stays in non-LLM details and run state. When a deferred actor result gates the next step, wait for its terminal follow-up; do not schedule continuation loops, repeatedly inspect, or mutate its reviewed scope while it runs. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue/stuck run.
|
|
27
27
|
- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; packaged/ad hoc recipes are lower-priority components; offer to save successful recurring patterns only after confirmation.
|
|
28
28
|
- Prefer maintained packaged recipes/pipelines with spawn file=<recipe> before ad hoc scripts/wrappers; review swarms inherit current model/thinking, preflight before fanout, and expose quorum/concurrency/TTL knobs unless explicit args are passed.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recipe Control contract.
|
|
3
|
+
* Zones: actor-local action declarations, normalization, reserved-action fencing
|
|
4
|
+
* Owns pure Recipe control validation; Recipe loading and Run capture stay in recipe/run domains.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeRecipeControl(value: unknown): string[] | undefined;
|
|
7
|
+
export declare function assertRecipeHasNoMailbox(recipe: Record<string, unknown>): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recipe Control contract.
|
|
3
|
+
* Zones: actor-local action declarations, normalization, reserved-action fencing
|
|
4
|
+
* Owns pure Recipe control validation; Recipe loading and Run capture stay in recipe/run domains.
|
|
5
|
+
*/
|
|
6
|
+
import * as Control from "./control.js";
|
|
7
|
+
import * as Limits from "./limits.js";
|
|
8
|
+
const RUNTIME_RUN_ACTIONS = new Set(["archive", "kill", "prune"]);
|
|
9
|
+
export function normalizeRecipeControl(value) {
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (!Array.isArray(value)) {
|
|
13
|
+
throw new Error("recipe.control must be an array of action strings");
|
|
14
|
+
}
|
|
15
|
+
const actions = [];
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
for (const raw of value) {
|
|
18
|
+
if (typeof raw !== "string" || !raw.trim()) {
|
|
19
|
+
throw new Error("recipe.control actions must be non-empty strings");
|
|
20
|
+
}
|
|
21
|
+
const action = raw.trim();
|
|
22
|
+
if (!Control.isControlAction(action)) {
|
|
23
|
+
throw new Error(`invalid recipe.control action: ${action}`);
|
|
24
|
+
}
|
|
25
|
+
if (action.length > Limits.CONTROL_ACTION_MAX_LENGTH) {
|
|
26
|
+
throw new Error(`recipe.control action exceeds ${Limits.CONTROL_ACTION_MAX_LENGTH} ASCII characters: ${action}`);
|
|
27
|
+
}
|
|
28
|
+
if (RUNTIME_RUN_ACTIONS.has(action)) {
|
|
29
|
+
throw new Error(`recipe.control action is runtime-reserved and must not be declared: ${action}`);
|
|
30
|
+
}
|
|
31
|
+
if (seen.has(action)) {
|
|
32
|
+
throw new Error(`duplicate recipe.control action: ${action}`);
|
|
33
|
+
}
|
|
34
|
+
seen.add(action);
|
|
35
|
+
actions.push(action);
|
|
36
|
+
}
|
|
37
|
+
return actions;
|
|
38
|
+
}
|
|
39
|
+
export function assertRecipeHasNoMailbox(recipe) {
|
|
40
|
+
if (Object.hasOwn(recipe, "mailbox")) {
|
|
41
|
+
throw new Error("recipe.mailbox was removed; replace it with control: [\"action\"] for actor-local inputs and emit Trace events for outputs");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -356,6 +356,8 @@ function diagnosticSuggestedAction(message) {
|
|
|
356
356
|
return "break the import cycle";
|
|
357
357
|
if (/exceeds.*size/i.test(message))
|
|
358
358
|
return "split large prompt or data into separate files";
|
|
359
|
+
if (/recipe\.mailbox was removed/i.test(message))
|
|
360
|
+
return "replace mailbox.accepts with control actions and route outputs to Trace events";
|
|
359
361
|
if (/repeat must/i.test(message))
|
|
360
362
|
return "use a positive repeat count or an array-typed repeat source";
|
|
361
363
|
if (/shadows/i.test(message))
|
|
@@ -11,19 +11,6 @@ export interface TemplateRecipeImportBinding {
|
|
|
11
11
|
values?: Record<string, unknown>;
|
|
12
12
|
}
|
|
13
13
|
export type TemplateRecipeImport = string | TemplateRecipeImportBinding;
|
|
14
|
-
export type TemplateRecipeMailboxEntry = string | {
|
|
15
|
-
type: string;
|
|
16
|
-
body_schema?: unknown;
|
|
17
|
-
ack?: boolean | string;
|
|
18
|
-
idempotency?: string;
|
|
19
|
-
requires_response?: boolean;
|
|
20
|
-
level?: string;
|
|
21
|
-
summary?: string;
|
|
22
|
-
};
|
|
23
|
-
export interface TemplateRecipeMailbox {
|
|
24
|
-
accepts?: TemplateRecipeMailboxEntry[];
|
|
25
|
-
emits?: TemplateRecipeMailboxEntry[];
|
|
26
|
-
}
|
|
27
14
|
export interface TemplateRecipeDefinition {
|
|
28
15
|
name?: string;
|
|
29
16
|
description?: string;
|
|
@@ -42,7 +29,7 @@ export interface TemplateRecipeDefinition {
|
|
|
42
29
|
accept_output?: "review_evidence";
|
|
43
30
|
output?: string;
|
|
44
31
|
artifacts?: Record<string, string>;
|
|
45
|
-
|
|
32
|
+
control?: string[];
|
|
46
33
|
retire_when?: "children_terminal";
|
|
47
34
|
retry?: number | string;
|
|
48
35
|
failure?: CommandTemplates.CommandTemplateFailureScope;
|
|
@@ -8,6 +8,7 @@ import { homedir } from "node:os";
|
|
|
8
8
|
import { basename, dirname, extname, resolve } from "node:path";
|
|
9
9
|
import * as CommandTemplates from "./command-templates.js";
|
|
10
10
|
import * as Paths from "./paths.js";
|
|
11
|
+
import * as RecipeControl from "./recipe-control.js";
|
|
11
12
|
const MAX_RECIPE_FILE_BYTES = 1024 * 1024;
|
|
12
13
|
const MAX_RECIPE_IMPORT_DEPTH = 32;
|
|
13
14
|
function hasWhitespace(value) {
|
|
@@ -629,6 +630,7 @@ export function readResolvedRecipeConfig(file, stack = [], options = {}) {
|
|
|
629
630
|
const raw = readRawRecipeConfig(path);
|
|
630
631
|
if (!raw || !Object.hasOwn(raw, "template"))
|
|
631
632
|
return undefined;
|
|
633
|
+
RecipeControl.assertRecipeHasNoMailbox(raw);
|
|
632
634
|
const imports = {};
|
|
633
635
|
for (const [alias, binding] of Object.entries(getRecipeImports(raw))) {
|
|
634
636
|
const importPath = resolveRecipeImportPath(getImportFrom(binding), dirname(path));
|
|
@@ -671,9 +673,9 @@ export function readResolvedRecipeConfig(file, stack = [], options = {}) {
|
|
|
671
673
|
const artifactSource = isRecord(substituted.artifacts)
|
|
672
674
|
? substituted.artifacts
|
|
673
675
|
: delegated?.artifacts;
|
|
674
|
-
const
|
|
675
|
-
? substituted.
|
|
676
|
-
: delegated?.
|
|
676
|
+
const control = RecipeControl.normalizeRecipeControl(Object.hasOwn(substituted, "control")
|
|
677
|
+
? substituted.control
|
|
678
|
+
: delegated?.control);
|
|
677
679
|
return {
|
|
678
680
|
name: recipeName,
|
|
679
681
|
...(typeof substituted.description === "string" &&
|
|
@@ -743,24 +745,7 @@ export function readResolvedRecipeConfig(file, stack = [], options = {}) {
|
|
|
743
745
|
artifacts: Object.fromEntries(Object.entries(artifactSource).filter((entry) => typeof entry[1] === "string")),
|
|
744
746
|
}
|
|
745
747
|
: {}),
|
|
746
|
-
...(
|
|
747
|
-
? {
|
|
748
|
-
mailbox: {
|
|
749
|
-
...(Array.isArray(mailboxSource.accepts)
|
|
750
|
-
? {
|
|
751
|
-
accepts: mailboxSource.accepts.filter((value) => typeof value === "string" ||
|
|
752
|
-
(isRecord(value) && typeof value.type === "string")),
|
|
753
|
-
}
|
|
754
|
-
: {}),
|
|
755
|
-
...(Array.isArray(mailboxSource.emits)
|
|
756
|
-
? {
|
|
757
|
-
emits: mailboxSource.emits.filter((value) => typeof value === "string" ||
|
|
758
|
-
(isRecord(value) && typeof value.type === "string")),
|
|
759
|
-
}
|
|
760
|
-
: {}),
|
|
761
|
-
},
|
|
762
|
-
}
|
|
763
|
-
: {}),
|
|
748
|
+
...(control !== undefined ? { control } : {}),
|
|
764
749
|
...(substituted.retire_when === "children_terminal" ||
|
|
765
750
|
delegated?.retire_when === "children_terminal"
|
|
766
751
|
? { retire_when: "children_terminal" }
|
|
@@ -11,4 +11,4 @@ export interface AutomaticReviewControlOptions {
|
|
|
11
11
|
toolStatePath?: string;
|
|
12
12
|
}
|
|
13
13
|
export declare function controlAutomaticReview(action: AutomaticReviewControlAction, scope: AutomaticReviewScope, options?: AutomaticReviewControlOptions): Record<string, unknown>;
|
|
14
|
-
export declare function parseAutomaticReviewScope(
|
|
14
|
+
export declare function parseAutomaticReviewScope(input: unknown): AutomaticReviewScope;
|
|
@@ -98,14 +98,13 @@ export function controlAutomaticReview(action, scope, options = {}) {
|
|
|
98
98
|
return {
|
|
99
99
|
...result,
|
|
100
100
|
next_actions: nextActions,
|
|
101
|
-
sent: result.changed === true,
|
|
102
101
|
};
|
|
103
102
|
}
|
|
104
|
-
export function parseAutomaticReviewScope(
|
|
105
|
-
const record =
|
|
106
|
-
?
|
|
103
|
+
export function parseAutomaticReviewScope(input) {
|
|
104
|
+
const record = input && typeof input === "object" && !Array.isArray(input)
|
|
105
|
+
? input
|
|
107
106
|
: {};
|
|
108
107
|
if (record.scope === "draft" || record.scope === "tool")
|
|
109
108
|
return record.scope;
|
|
110
|
-
throw new Error("review
|
|
109
|
+
throw new Error("review Control requires input.scope=draft or input.scope=tool.");
|
|
111
110
|
}
|
|
@@ -68,17 +68,13 @@ function templateShape(value) {
|
|
|
68
68
|
function contractShape(recipe) {
|
|
69
69
|
if (!recipe)
|
|
70
70
|
return undefined;
|
|
71
|
-
const mailbox = recipe.mailbox && typeof recipe.mailbox === "object" && !Array.isArray(recipe.mailbox)
|
|
72
|
-
? recipe.mailbox
|
|
73
|
-
: {};
|
|
74
71
|
return {
|
|
75
72
|
argument_count: countArray(recipe.args),
|
|
76
73
|
artifact_count: countRecord(recipe.artifacts),
|
|
77
74
|
async: recipe.async === true,
|
|
78
75
|
default_count: countRecord(recipe.defaults),
|
|
79
76
|
import_count: countRecord(recipe.imports),
|
|
80
|
-
|
|
81
|
-
mailbox_emits_count: countArray(mailbox.emits),
|
|
77
|
+
control_action_count: countArray(recipe.control),
|
|
82
78
|
template_shape: templateShape(recipe.template),
|
|
83
79
|
};
|
|
84
80
|
}
|
|
@@ -19,7 +19,7 @@ export function createRunUiRuntime(deps) {
|
|
|
19
19
|
attempted: retirementAttempts,
|
|
20
20
|
cancelRun: (candidate) => AsyncRuns.cancelRun(candidate.stateDir),
|
|
21
21
|
notify: (message, level) => ctx.ui.notify(message, level),
|
|
22
|
-
sendStop: (candidate) => AsyncRuns.
|
|
22
|
+
sendStop: async (candidate) => AsyncRuns.cancelRun(candidate.stateDir),
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
const update = (ctx, notify = false, terminalOnly = false) => {
|
|
@@ -33,7 +33,7 @@ export function createRunUiRuntime(deps) {
|
|
|
33
33
|
Observability.deliverRunTransitionNotifications(snapshot.transitions, sink, terminalNotificationsInFlight);
|
|
34
34
|
Observability.pruneRunUiObservationState(observation, snapshot);
|
|
35
35
|
if (!terminalOnly) {
|
|
36
|
-
Observability.
|
|
36
|
+
Observability.deliverRunAttentionNotifications(snapshot.attentionEvents, sink);
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
const reportDiagnostics = (ctx) => {
|