@llblab/pi-actors 0.43.0 → 0.44.0
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 +16 -10
- package/CHANGELOG.md +425 -536
- package/README.md +13 -11
- package/dist/index.js +1 -1
- package/dist/lib/async-runs.d.ts +2 -1
- package/dist/lib/async-runs.js +24 -34
- 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 +3 -0
- package/dist/lib/control.js +27 -14
- package/dist/lib/draft-sleep.js +3 -3
- package/dist/lib/file-state.d.ts +4 -1
- package/dist/lib/file-state.js +118 -44
- package/dist/lib/inspector-overlay.d.ts +2 -0
- package/dist/lib/inspector-overlay.js +124 -69
- package/dist/lib/limits.d.ts +15 -3
- package/dist/lib/limits.js +15 -3
- package/dist/lib/observability.d.ts +4 -2
- package/dist/lib/observability.js +43 -36
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +1 -1
- package/dist/lib/recipe-control.js +6 -2
- package/dist/lib/review-control.d.ts +1 -1
- package/dist/lib/review-control.js +4 -5
- package/dist/lib/run-evidence-policy.d.ts +95 -0
- package/dist/lib/run-evidence-policy.js +177 -0
- package/dist/lib/run-ui-runtime.js +2 -0
- package/dist/lib/runs-control-delivery.d.ts +8 -1
- package/dist/lib/runs-control-delivery.js +38 -15
- package/dist/lib/runs-controls.d.ts +8 -4
- package/dist/lib/runs-controls.js +189 -50
- package/dist/lib/runs-retention.js +27 -14
- package/dist/lib/runs-trace.d.ts +26 -2
- package/dist/lib/runs-trace.js +411 -18
- package/dist/lib/runtime-identity.d.ts +7 -0
- package/dist/lib/runtime-identity.js +35 -0
- package/dist/lib/runtime-triage.d.ts +29 -0
- package/dist/lib/runtime-triage.js +60 -0
- package/dist/lib/tool-review-scheduler.js +7 -7
- package/dist/lib/tools-inspect.js +91 -18
- package/dist/lib/tools-message.d.ts +1 -2
- package/dist/lib/tools-message.js +6 -6
- package/dist/lib/tools-response.d.ts +0 -1
- package/dist/lib/tools-response.js +0 -9
- package/dist/lib/tools.d.ts +1 -1
- package/dist/lib/tools.js +1 -1
- package/dist/lib/trace-projection.js +107 -41
- package/dist/scripts/conformance.mjs +5 -0
- package/dist/scripts/locker.mjs +40 -90
- package/dist/scripts/music-player.mjs +48 -142
- package/dist/scripts/release-gates.mjs +56 -3
- package/dist/scripts/validate-recipe.mjs +5 -4
- package/dist/skills/actors/SKILL.md +17 -11
- package/dist/skills/swarm/SKILL.md +2 -4
- package/docs/README.md +1 -4
- package/docs/actor-inspector.md +6 -5
- package/docs/async-runs.md +11 -9
- package/docs/command-templates.md +6 -116
- package/docs/recipe-library.md +4 -6
- package/docs/releasing.md +28 -0
- package/docs/template-recipes.md +1 -1
- package/docs/tool-registry.md +2 -2
- package/index.ts +1 -1
- package/lib/async-runs.ts +26 -50
- 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 +33 -18
- package/lib/draft-sleep.ts +3 -3
- package/lib/file-state.ts +91 -63
- package/lib/inspector-overlay.ts +108 -61
- package/lib/limits.ts +15 -3
- package/lib/observability.ts +55 -57
- package/lib/prompts.ts +1 -1
- package/lib/recipe-control.ts +9 -2
- package/lib/review-control.ts +4 -5
- package/lib/run-evidence-policy.ts +242 -0
- package/lib/run-ui-runtime.ts +2 -0
- package/lib/runs-control-delivery.ts +45 -17
- package/lib/runs-controls.ts +180 -102
- package/lib/runs-retention.ts +28 -20
- package/lib/runs-trace.ts +499 -20
- package/lib/runtime-identity.ts +39 -0
- package/lib/runtime-triage.ts +106 -0
- package/lib/tool-review-scheduler.ts +7 -7
- package/lib/tools-inspect.ts +94 -20
- package/lib/tools-message.ts +7 -8
- package/lib/tools-response.ts +0 -12
- package/lib/tools.ts +4 -4
- package/lib/trace-projection.ts +156 -71
- package/package.json +1 -1
- package/scripts/conformance.mjs +5 -0
- package/scripts/locker.mjs +40 -90
- package/scripts/music-player.mjs +48 -142
- package/scripts/release-gates.mjs +56 -3
- package/scripts/validate-recipe.mjs +5 -4
- package/skills/actors/SKILL.md +17 -11
- package/skills/swarm/SKILL.md +2 -4
- package/dist/lib/runtime-notifier.d.ts +0 -48
- package/dist/lib/runtime-notifier.js +0 -138
- package/docs/0.43-baseline.md +0 -44
- package/docs/actors-deep-reference.md +0 -108
- package/docs/component-recipes.md +0 -45
- package/docs/task-first-recipes.md +0 -261
- package/lib/runtime-notifier.ts +0 -211
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure runtime Control triage policy.
|
|
3
|
+
* Zones: pending admission, status-age classification, terminal/replacement staleness
|
|
4
|
+
* Owns Control triage decisions; journal reads and owner filtering stay in adapters, while safe fields come from control-projection.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as ControlProjection from "./control-projection.ts";
|
|
8
|
+
import {
|
|
9
|
+
classifyRunControlRecord,
|
|
10
|
+
runControlStatusTimestamp,
|
|
11
|
+
type RunControlStatus,
|
|
12
|
+
} from "./run-evidence-policy.ts";
|
|
13
|
+
import type { RunControlRecord } from "./runs-controls.ts";
|
|
14
|
+
|
|
15
|
+
export const RUNTIME_CONTROL_STALE_AFTER_MS = 5 * 60 * 1_000;
|
|
16
|
+
|
|
17
|
+
export interface RuntimeTriageRun {
|
|
18
|
+
run: string;
|
|
19
|
+
runInstanceId?: string;
|
|
20
|
+
status: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RuntimePendingControl
|
|
24
|
+
extends ControlProjection.ProjectedRunControl {
|
|
25
|
+
age_ms: number;
|
|
26
|
+
reason: "age_threshold_reached" | "run_generation_replaced" | "run_generation_unavailable" | "run_terminal" | "within_age_threshold";
|
|
27
|
+
run: string;
|
|
28
|
+
status: "queued" | "delivered" | "claimed";
|
|
29
|
+
status_at: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RuntimeControlDiagnostic {
|
|
33
|
+
reason: "invalid_action" | "invalid_control_id" | "invalid_generation" | "invalid_record" | "invalid_status" | "invalid_status_timestamp";
|
|
34
|
+
run: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RuntimeControlClassification {
|
|
38
|
+
diagnostic?: RuntimeControlDiagnostic;
|
|
39
|
+
pending?: RuntimePendingControl;
|
|
40
|
+
stale?: RuntimePendingControl;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function diagnostic(
|
|
44
|
+
run: string,
|
|
45
|
+
reason: RuntimeControlDiagnostic["reason"],
|
|
46
|
+
): RuntimeControlClassification {
|
|
47
|
+
return { diagnostic: { reason, run } };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function classifyRuntimeControl(
|
|
51
|
+
run: RuntimeTriageRun,
|
|
52
|
+
value: unknown,
|
|
53
|
+
nowMs: number,
|
|
54
|
+
): RuntimeControlClassification {
|
|
55
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
56
|
+
return diagnostic(run.run, "invalid_record");
|
|
57
|
+
}
|
|
58
|
+
const record = value as Record<string, unknown>;
|
|
59
|
+
const status = record.status as RunControlStatus;
|
|
60
|
+
const recordClass = classifyRunControlRecord(record);
|
|
61
|
+
if (!recordClass) return diagnostic(run.run, "invalid_status");
|
|
62
|
+
if (typeof record.id !== "string" || !record.id) {
|
|
63
|
+
return diagnostic(run.run, "invalid_control_id");
|
|
64
|
+
}
|
|
65
|
+
if (typeof record.action !== "string" || !record.action) {
|
|
66
|
+
return diagnostic(run.run, "invalid_action");
|
|
67
|
+
}
|
|
68
|
+
if (typeof record.run_instance_id !== "string" || !record.run_instance_id) {
|
|
69
|
+
return diagnostic(run.run, "invalid_generation");
|
|
70
|
+
}
|
|
71
|
+
const timestamp = runControlStatusTimestamp(record, status);
|
|
72
|
+
const timestampMs = typeof timestamp === "string" ? Date.parse(timestamp) : Number.NaN;
|
|
73
|
+
if (!Number.isFinite(timestampMs)) {
|
|
74
|
+
return diagnostic(run.run, "invalid_status_timestamp");
|
|
75
|
+
}
|
|
76
|
+
if (recordClass === "terminal") return {};
|
|
77
|
+
const ageMs = Math.max(0, Math.floor(nowMs - timestampMs));
|
|
78
|
+
const staleReason: RuntimePendingControl["reason"] | undefined =
|
|
79
|
+
!run.runInstanceId
|
|
80
|
+
? "run_generation_unavailable"
|
|
81
|
+
: record.run_instance_id !== run.runInstanceId
|
|
82
|
+
? "run_generation_replaced"
|
|
83
|
+
: run.status !== "running"
|
|
84
|
+
? "run_terminal"
|
|
85
|
+
: ageMs >= RUNTIME_CONTROL_STALE_AFTER_MS
|
|
86
|
+
? "age_threshold_reached"
|
|
87
|
+
: undefined;
|
|
88
|
+
const projected = ControlProjection.projectRunControl(
|
|
89
|
+
record as unknown as RunControlRecord,
|
|
90
|
+
);
|
|
91
|
+
const pending: RuntimePendingControl = {
|
|
92
|
+
...projected,
|
|
93
|
+
age_ms: ageMs,
|
|
94
|
+
reason: staleReason ?? "within_age_threshold",
|
|
95
|
+
run: run.run,
|
|
96
|
+
status: status as RuntimePendingControl["status"],
|
|
97
|
+
status_at: runControlStatusTimestamp(
|
|
98
|
+
projected as unknown as Record<string, unknown>,
|
|
99
|
+
status,
|
|
100
|
+
) as string,
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
pending,
|
|
104
|
+
...(staleReason ? { stale: pending } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -404,7 +404,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
404
404
|
...state,
|
|
405
405
|
failedStage: "approval_state",
|
|
406
406
|
lastError: "approved review path is unavailable",
|
|
407
|
-
nextAction: "message
|
|
407
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
408
408
|
phase: "processing_failed" as const,
|
|
409
409
|
updatedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
410
410
|
};
|
|
@@ -427,7 +427,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
427
427
|
...state,
|
|
428
428
|
failedStage: "transaction",
|
|
429
429
|
lastError: `tool review transaction remained ${transaction.phase}`,
|
|
430
|
-
nextAction: "message
|
|
430
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
431
431
|
phase: "processing_failed" as const,
|
|
432
432
|
updatedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
433
433
|
};
|
|
@@ -458,7 +458,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
458
458
|
...pending,
|
|
459
459
|
failedStage: "lineage",
|
|
460
460
|
lastError: error instanceof Error ? error.message : String(error),
|
|
461
|
-
nextAction: "message
|
|
461
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
462
462
|
};
|
|
463
463
|
writeJsonAtomic(statePath, failedPending);
|
|
464
464
|
return { outcome: "lineage_pending", state: failedPending };
|
|
@@ -478,7 +478,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
478
478
|
...state,
|
|
479
479
|
failedStage: "transaction",
|
|
480
480
|
lastError: error instanceof Error ? error.message : String(error),
|
|
481
|
-
nextAction: "message
|
|
481
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
482
482
|
phase: "processing_failed" as const,
|
|
483
483
|
updatedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
484
484
|
};
|
|
@@ -541,7 +541,7 @@ export function createToolReviewScheduler(
|
|
|
541
541
|
...existing,
|
|
542
542
|
failedStage: processed.stage ?? "review_result",
|
|
543
543
|
lastError: processed.error ?? "automatic tool reviewer failed",
|
|
544
|
-
nextAction: "message
|
|
544
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
545
545
|
phase: processed.outcome === "processing_failed"
|
|
546
546
|
? "processing_failed"
|
|
547
547
|
: "failed",
|
|
@@ -577,7 +577,7 @@ export function createToolReviewScheduler(
|
|
|
577
577
|
attempts: existing.attempts + 1,
|
|
578
578
|
failedStage: "review_launch",
|
|
579
579
|
lastError: error instanceof Error ? error.message : String(error),
|
|
580
|
-
nextAction: "message
|
|
580
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
581
581
|
phase: "failed",
|
|
582
582
|
updatedAt: now().toISOString(),
|
|
583
583
|
} satisfies ToolReviewAdmissionState);
|
|
@@ -612,7 +612,7 @@ export function createToolReviewScheduler(
|
|
|
612
612
|
failedStage: "review_launch",
|
|
613
613
|
inputPath: batch.inputPath,
|
|
614
614
|
lastError: error instanceof Error ? error.message : String(error),
|
|
615
|
-
nextAction: "message
|
|
615
|
+
nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
616
616
|
phase: "failed",
|
|
617
617
|
reviewerInputPath: batch.reviewerInputPath,
|
|
618
618
|
reviewId: batch.reviewId,
|
package/lib/tools-inspect.ts
CHANGED
|
@@ -4,17 +4,22 @@
|
|
|
4
4
|
* Owns exact inspect target/view dispatch; source projection stays in domain modules.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { statSync } from "node:fs";
|
|
7
8
|
import { join } from "node:path";
|
|
8
9
|
|
|
9
10
|
import * as AsyncRuns from "./async-runs.ts";
|
|
11
|
+
import * as ControlProjection from "./control-projection.ts";
|
|
10
12
|
import * as Inspector from "./inspector.ts";
|
|
11
13
|
import * as Limits from "./limits.ts";
|
|
14
|
+
import { computeRunControlCapacity } from "./run-evidence-policy.ts";
|
|
12
15
|
import * as Paths from "./paths.ts";
|
|
13
16
|
import * as RecipesDiscovery from "./recipes-discovery.ts";
|
|
14
17
|
import * as ReviewDiagnostics from "./review-diagnostics.ts";
|
|
15
18
|
import * as RunsControls from "./runs-controls.ts";
|
|
16
19
|
import * as RunControlDelivery from "./runs-control-delivery.ts";
|
|
17
20
|
import * as RunsTrace from "./runs-trace.ts";
|
|
21
|
+
import * as RuntimeIdentity from "./runtime-identity.ts";
|
|
22
|
+
import * as RuntimeTriage from "./runtime-triage.ts";
|
|
18
23
|
import * as Schema from "./schema.ts";
|
|
19
24
|
import * as ToolsAccess from "./tools-access.ts";
|
|
20
25
|
import * as ToolsResponse from "./tools-response.ts";
|
|
@@ -35,7 +40,8 @@ function runtimeStatus(): Record<string, unknown> {
|
|
|
35
40
|
return {
|
|
36
41
|
automatic_review: process.env.PI_ACTORS_AUTOMATIC_REVIEW !== "off",
|
|
37
42
|
run_root: Paths.getRunStateRoot(),
|
|
38
|
-
|
|
43
|
+
state_schema: RuntimeIdentity.RUN_STATE_SCHEMA,
|
|
44
|
+
version: RuntimeIdentity.getPackageVersion(),
|
|
39
45
|
};
|
|
40
46
|
}
|
|
41
47
|
|
|
@@ -69,13 +75,45 @@ function runtimeTriage(
|
|
|
69
75
|
const inventory = runtimeRuns(ctx, deps, undefined);
|
|
70
76
|
const runs = inventory.runs as Array<Record<string, unknown>>;
|
|
71
77
|
const failed = runs.filter((run) => run.status === "failed");
|
|
72
|
-
const
|
|
78
|
+
const pendingControls: RuntimeTriage.RuntimePendingControl[] = [];
|
|
79
|
+
const staleControls: RuntimeTriage.RuntimePendingControl[] = [];
|
|
80
|
+
const backpressuredRuns: Array<Record<string, unknown>> = [];
|
|
81
|
+
const controlDiagnostics: Array<Record<string, unknown>> = [];
|
|
82
|
+
const traceDiagnostics: Array<Record<string, unknown>> = [];
|
|
83
|
+
const nowMs = Date.now();
|
|
84
|
+
for (const run of runs) {
|
|
73
85
|
const stateDir = typeof run.state_dir === "string" ? run.state_dir : undefined;
|
|
74
|
-
if (!stateDir)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
if (!stateDir) continue;
|
|
87
|
+
const runId = String(run.run ?? "unknown");
|
|
88
|
+
const journal = RunsControls.readRunControlJournalFromStateDir(stateDir);
|
|
89
|
+
const capacity = computeRunControlCapacity(journal.records);
|
|
90
|
+
controlDiagnostics.push(...journal.diagnostics.map((diagnostic) => ({
|
|
91
|
+
...(diagnostic.line === undefined ? {} : { line: diagnostic.line }),
|
|
92
|
+
reason: diagnostic.line === undefined ? "unreadable_control_journal" : "invalid_control_json", run: runId,
|
|
93
|
+
})));
|
|
94
|
+
if (capacity.backpressured) backpressuredRuns.push({
|
|
95
|
+
run: runId, pending: capacity.pending, pending_limit: capacity.limit,
|
|
96
|
+
journal_bytes: statSync(RunsControls.runControlsFile(stateDir)).size,
|
|
97
|
+
});
|
|
98
|
+
const traceSummary = RunsTrace.summarizeRunTraceJournal(RunsTrace.readRunTraceJournal(stateDir));
|
|
99
|
+
if (!traceSummary.history_complete) traceDiagnostics.push({ run: runId, ...traceSummary });
|
|
100
|
+
for (const control of journal.records) {
|
|
101
|
+
const classified = RuntimeTriage.classifyRuntimeControl(
|
|
102
|
+
{
|
|
103
|
+
run: runId,
|
|
104
|
+
...(typeof run.run_instance_id === "string"
|
|
105
|
+
? { runInstanceId: run.run_instance_id }
|
|
106
|
+
: {}),
|
|
107
|
+
status: String(run.status ?? "unknown"),
|
|
108
|
+
},
|
|
109
|
+
control,
|
|
110
|
+
nowMs,
|
|
111
|
+
);
|
|
112
|
+
if (classified.pending) pendingControls.push(classified.pending);
|
|
113
|
+
if (classified.stale) staleControls.push(classified.stale);
|
|
114
|
+
if (classified.diagnostic) controlDiagnostics.push({ ...classified.diagnostic });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
79
117
|
const attentionEvents = runs.flatMap((run) => {
|
|
80
118
|
const stateDir = typeof run.state_dir === "string" ? run.state_dir : undefined;
|
|
81
119
|
if (!stateDir) return [];
|
|
@@ -90,18 +128,32 @@ function runtimeTriage(
|
|
|
90
128
|
ts: event.ts,
|
|
91
129
|
}));
|
|
92
130
|
});
|
|
131
|
+
const boundedStaleControls = staleControls.slice(0, 40);
|
|
132
|
+
const staleKeys = new Set(boundedStaleControls.map((control) =>
|
|
133
|
+
`${control.run}\0${control.run_instance_id}\0${control.id}`,
|
|
134
|
+
));
|
|
135
|
+
const boundedPendingControls = [
|
|
136
|
+
...boundedStaleControls,
|
|
137
|
+
...pendingControls.filter((control) =>
|
|
138
|
+
!staleKeys.has(`${control.run}\0${control.run_instance_id}\0${control.id}`),
|
|
139
|
+
),
|
|
140
|
+
].slice(0, 40);
|
|
93
141
|
return {
|
|
94
142
|
...inventory,
|
|
95
143
|
failed_runs: failed,
|
|
96
|
-
|
|
144
|
+
pending_control_count: pendingControls.length,
|
|
145
|
+
pending_controls: boundedPendingControls,
|
|
146
|
+
stale_control_count: staleControls.length,
|
|
147
|
+
stale_controls: boundedStaleControls,
|
|
148
|
+
backpressured_run_count: backpressuredRuns.length, backpressured_runs: backpressuredRuns.slice(0, 40),
|
|
149
|
+
control_diagnostic_count: controlDiagnostics.length, control_diagnostics: controlDiagnostics.slice(0, 40),
|
|
150
|
+
trace_diagnostic_count: traceDiagnostics.length, trace_diagnostics: traceDiagnostics.slice(0, 40),
|
|
97
151
|
attention_events: attentionEvents.slice(-40).reverse(),
|
|
98
152
|
next_actions: [
|
|
99
153
|
...(failed.length ? ["inspect target=runtime view=runs status=failed"] : []),
|
|
100
|
-
...(staleControls.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
: []),
|
|
154
|
+
...new Set([...staleControls, ...backpressuredRuns].slice(0, 3).map((entry) =>
|
|
155
|
+
`inspect target=run:${entry.run} view=control`,
|
|
156
|
+
)),
|
|
105
157
|
...(attentionEvents.length
|
|
106
158
|
? attentionEvents.slice(-3).map((entry) =>
|
|
107
159
|
`inspect target=run:${String(entry.run)} view=trace source=runtime`,
|
|
@@ -116,9 +168,9 @@ function compactRuntime(view: string, details: Record<string, unknown>): string
|
|
|
116
168
|
return `\nruntime runs=${(details.runs as unknown[] | undefined)?.length ?? 0}`;
|
|
117
169
|
}
|
|
118
170
|
if (view === "triage") {
|
|
119
|
-
return `\nruntime failed=${(details.failed_runs as unknown[] | undefined)?.length ?? 0} stale_controls=${(details.
|
|
171
|
+
return `\nruntime failed=${(details.failed_runs as unknown[] | undefined)?.length ?? 0} pending_controls=${Number(details.pending_control_count ?? 0)} stale_controls=${Number(details.stale_control_count ?? 0)} backpressured=${Number(details.backpressured_run_count ?? 0)} incomplete_trace=${Number(details.trace_diagnostic_count ?? 0)} attention=${(details.attention_events as unknown[] | undefined)?.length ?? 0}`;
|
|
120
172
|
}
|
|
121
|
-
return `\nruntime version=${String(details.version
|
|
173
|
+
return `\nruntime version=${String(details.version)} state_schema=${String(details.state_schema)} automatic_review=${String(details.automatic_review)}`;
|
|
122
174
|
}
|
|
123
175
|
|
|
124
176
|
function inspectRuntime(
|
|
@@ -209,8 +261,8 @@ function inspectRun(
|
|
|
209
261
|
throw new Error(`unsupported Trace source: ${source}`);
|
|
210
262
|
}
|
|
211
263
|
return {
|
|
212
|
-
run,
|
|
213
|
-
|
|
264
|
+
run, source,
|
|
265
|
+
summary: RunsTrace.summarizeRunTraceJournal(RunsTrace.readRunTraceJournal(stateDir)),
|
|
214
266
|
items: TraceProjection.projectRunTrace(stateDir, {
|
|
215
267
|
artifacts: status.artifacts as
|
|
216
268
|
| Record<string, AsyncRuns.RunArtifactDeclaration>
|
|
@@ -221,18 +273,40 @@ function inspectRun(
|
|
|
221
273
|
};
|
|
222
274
|
}
|
|
223
275
|
const runInstanceId = String(status.run_instance_id ?? "");
|
|
276
|
+
const journal = RunsControls.readRunControlJournalFromStateDir(stateDir);
|
|
277
|
+
const capacity = computeRunControlCapacity(journal.records);
|
|
278
|
+
const structuralDiagnostics = journal.records.flatMap((value) => {
|
|
279
|
+
const classified = RuntimeTriage.classifyRuntimeControl({ run,
|
|
280
|
+
runInstanceId: runInstanceId || undefined, status: String(status.status ?? "unknown") }, value, Date.now());
|
|
281
|
+
return classified.diagnostic ? [classified.diagnostic.reason] : [];
|
|
282
|
+
});
|
|
283
|
+
let journalBytes = 0;
|
|
284
|
+
try { journalBytes = statSync(RunsControls.runControlsFile(stateDir)).size; } catch {}
|
|
285
|
+
const diagnostics = [...journal.diagnostics.map((item) => ({
|
|
286
|
+
...(item.line === undefined ? {} : { line: item.line }),
|
|
287
|
+
reason: item.line === undefined ? "unreadable_control_journal" : "invalid_control_json",
|
|
288
|
+
})), ...structuralDiagnostics.map((reason) => ({ reason }))].slice(0, 40);
|
|
289
|
+
const nowMs = Date.now();
|
|
290
|
+
const stalePending = journal.records.reduce<number>((count, control) => count + Number(Boolean(
|
|
291
|
+
RuntimeTriage.classifyRuntimeControl({ run, runInstanceId: runInstanceId || undefined,
|
|
292
|
+
status: String(status.status ?? "unknown") }, control, nowMs).stale)), 0);
|
|
224
293
|
return {
|
|
225
294
|
run,
|
|
226
295
|
run_instance_id: runInstanceId,
|
|
227
296
|
status: status.status,
|
|
297
|
+
pending: capacity.pending, pending_limit: capacity.limit,
|
|
298
|
+
available: capacity.available, backpressured: capacity.backpressured,
|
|
299
|
+
journal_bytes: journalBytes, journal_limit: Limits.RUN_CONTROL_JOURNAL_MAX_BYTES,
|
|
300
|
+
...(capacity.oldest_pending_at ? { oldest_pending_at: capacity.oldest_pending_at } : {}),
|
|
301
|
+
stale_pending: stalePending,
|
|
302
|
+
diagnostics,
|
|
228
303
|
actor_actions: Array.isArray(status.control) ? status.control : [],
|
|
229
304
|
runtime_actions: status.status === "running" ? ["kill"] : ["archive", "prune"],
|
|
230
305
|
endpoint: runInstanceId
|
|
231
306
|
? RunControlDelivery.readRunControlEndpoint(stateDir, runInstanceId)
|
|
232
307
|
: undefined,
|
|
233
|
-
recent_controls:
|
|
234
|
-
.
|
|
235
|
-
.reverse(),
|
|
308
|
+
recent_controls: journal.records.slice(-Math.max(1, Number(input.lines || 20))).reverse()
|
|
309
|
+
.map((control) => ControlProjection.projectRunControl(control as RunsControls.RunControlRecord)),
|
|
236
310
|
};
|
|
237
311
|
}
|
|
238
312
|
|
package/lib/tools-message.ts
CHANGED
|
@@ -46,8 +46,7 @@ function runNextActions(run: string, result: Record<string, unknown>): string[]
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export interface ControlToolDeps {
|
|
49
|
-
|
|
50
|
-
handleRuntimeMessage?: (
|
|
49
|
+
handleRuntimeControl?: (
|
|
51
50
|
action: string,
|
|
52
51
|
input: unknown,
|
|
53
52
|
) => Record<string, unknown>;
|
|
@@ -64,11 +63,11 @@ export function createControlToolDefinition<TContext = unknown>(
|
|
|
64
63
|
parameters: Schema.objectSchema(
|
|
65
64
|
{
|
|
66
65
|
target: Schema.stringSchema("Run target run:<id>, or literal runtime."),
|
|
67
|
-
action: Schema.stringSchema("Lowercase
|
|
66
|
+
action: Schema.stringSchema("Lowercase ASCII Control action, at most 64 characters."),
|
|
68
67
|
input: Schema.unionSchema([
|
|
69
|
-
Schema.stringSchema("Optional Control input string."),
|
|
70
|
-
Schema.looseObjectSchema("Optional structured Control input."),
|
|
71
|
-
Schema.arraySchema("Optional structured Control input array."),
|
|
68
|
+
Schema.stringSchema("Optional Control input string; serialized JSON must fit 380 bytes."),
|
|
69
|
+
Schema.looseObjectSchema("Optional structured Control input; serialized JSON must fit 380 bytes."),
|
|
70
|
+
Schema.arraySchema("Optional structured Control input array; serialized JSON must fit 380 bytes."),
|
|
72
71
|
]),
|
|
73
72
|
verbose: Schema.booleanSchema("Return full JSON instead of compact text."),
|
|
74
73
|
},
|
|
@@ -89,10 +88,10 @@ export function createControlToolDefinition<TContext = unknown>(
|
|
|
89
88
|
`unsupported runtime Control action: ${request.action}; use review.retry or review.reset`,
|
|
90
89
|
);
|
|
91
90
|
}
|
|
92
|
-
if (!deps.
|
|
91
|
+
if (!deps.handleRuntimeControl) {
|
|
93
92
|
throw new Error("runtime Control is unavailable");
|
|
94
93
|
}
|
|
95
|
-
result = deps.
|
|
94
|
+
result = deps.handleRuntimeControl(request.action, request.input);
|
|
96
95
|
} else {
|
|
97
96
|
const run = request.target.slice(4);
|
|
98
97
|
const status = ToolsAccess.assertRunAccessibleToContext(run, ctx);
|
package/lib/tools-response.ts
CHANGED
|
@@ -133,18 +133,6 @@ export function compactAsyncRunStatus(value: unknown): string {
|
|
|
133
133
|
return `\n${tokens.join(" ")}`;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
export function artifactNextActions(
|
|
137
|
-
run: unknown,
|
|
138
|
-
artifacts: Record<string, unknown>,
|
|
139
|
-
): string[] {
|
|
140
|
-
const id = String(run ?? "").trim();
|
|
141
|
-
if (!id || Object.keys(artifacts).length === 0) return [];
|
|
142
|
-
return [
|
|
143
|
-
`inspect target=run:${id} view=artifacts verbose=true`,
|
|
144
|
-
`inspect target=run:${id} view=messages`,
|
|
145
|
-
];
|
|
146
|
-
}
|
|
147
|
-
|
|
148
136
|
export function maybeJsonText(
|
|
149
137
|
value: unknown,
|
|
150
138
|
verbose: boolean | undefined,
|
package/lib/tools.ts
CHANGED
|
@@ -25,9 +25,9 @@ export interface CoreActorToolDefinitionDeps<
|
|
|
25
25
|
configPath: string;
|
|
26
26
|
getActiveTools: () => string[];
|
|
27
27
|
getRuntimeTool: (name: string) => unknown;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
handleRuntimeControl?: (
|
|
29
|
+
action: string,
|
|
30
|
+
input: unknown,
|
|
31
31
|
) => Record<string, unknown>;
|
|
32
32
|
registryRuntime: Pick<
|
|
33
33
|
RegisterToolRuntimeDeps<TContext>,
|
|
@@ -74,7 +74,7 @@ export function createCoreActorToolDefinitions<
|
|
|
74
74
|
}),
|
|
75
75
|
ToolsSpawn.createSpawnToolDefinition<TContext>(),
|
|
76
76
|
ToolsMessage.createControlToolDefinition<TContext>({
|
|
77
|
-
|
|
77
|
+
handleRuntimeControl: deps.handleRuntimeControl,
|
|
78
78
|
}),
|
|
79
79
|
ToolsInspect.createInspectToolDefinition<TContext>({
|
|
80
80
|
getTool: (name) => deps.getRuntimeTool(name),
|