pi-crew 0.1.37 → 0.1.39
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 +1 -1
- package/CHANGELOG.md +27 -0
- package/README.md +5 -0
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +11 -11
- package/agents/writer.md +11 -11
- package/docs/refactor-tasks-phase3.md +394 -394
- package/docs/refactor-tasks-phase4.md +564 -564
- package/docs/refactor-tasks-phase5.md +402 -402
- package/docs/refactor-tasks-phase6.md +662 -662
- package/docs/research-extension-examples.md +297 -297
- package/docs/research-extension-system.md +324 -324
- package/docs/research-optimization-plan.md +548 -548
- package/docs/research-pi-coding-agent.md +357 -357
- package/docs/research-source-pi-crew-reference.md +174 -174
- package/docs/resource-formats.md +10 -8
- package/docs/runtime-flow.md +148 -148
- package/docs/source-runtime-refactor-map.md +83 -83
- package/docs/usage.md +6 -0
- package/index.ts +6 -6
- package/package.json +3 -3
- package/schema.json +2 -2
- package/src/agents/agent-serializer.ts +34 -34
- package/src/config/config.ts +8 -4
- package/src/extension/cross-extension-rpc.ts +82 -82
- package/src/extension/import-index.ts +18 -2
- package/src/extension/register.ts +11 -1
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +30 -6
- package/src/extension/registration/subagent-tools.ts +8 -3
- package/src/extension/result-watcher.ts +98 -98
- package/src/extension/run-import.ts +12 -2
- package/src/extension/run-index.ts +12 -2
- package/src/extension/run-maintenance.ts +24 -24
- package/src/extension/team-tool/api.ts +54 -14
- package/src/extension/team-tool/cancel.ts +31 -31
- package/src/extension/team-tool/doctor.ts +179 -179
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +79 -79
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/status.ts +73 -73
- package/src/observability/correlation.ts +35 -35
- package/src/observability/event-to-metric.ts +54 -54
- package/src/observability/exporters/adapter.ts +24 -24
- package/src/observability/exporters/otlp-exporter.ts +65 -65
- package/src/observability/exporters/prometheus-exporter.ts +47 -47
- package/src/observability/metric-registry.ts +72 -72
- package/src/observability/metric-retention.ts +46 -46
- package/src/observability/metric-sink.ts +51 -51
- package/src/observability/metrics-primitives.ts +166 -166
- package/src/prompt/prompt-runtime.ts +68 -68
- package/src/runtime/agent-control.ts +64 -64
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -113
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/background-runner.ts +53 -53
- package/src/runtime/crash-recovery.ts +56 -56
- package/src/runtime/crew-agent-records.ts +54 -9
- package/src/runtime/crew-agent-runtime.ts +58 -58
- package/src/runtime/deadletter.ts +36 -36
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +88 -88
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +80 -80
- package/src/runtime/live-agent-control.ts +87 -78
- package/src/runtime/live-agent-manager.ts +85 -85
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +299 -299
- package/src/runtime/manifest-cache.ts +248 -212
- package/src/runtime/model-fallback.ts +261 -261
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +99 -99
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/policy-engine.ts +78 -78
- package/src/runtime/post-exit-stdio-guard.ts +86 -86
- package/src/runtime/process-status.ts +56 -56
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +59 -59
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +28 -28
- package/src/runtime/subagent-manager.ts +80 -12
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-output-context.ts +127 -106
- package/src/runtime/task-runner/live-executor.ts +98 -98
- package/src/runtime/task-runner/progress.ts +111 -111
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/team-runner.ts +1 -1
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/schema/config-schema.ts +21 -21
- package/src/schema/team-tool-schema.ts +100 -100
- package/src/state/artifact-store.ts +122 -108
- package/src/state/contracts.ts +105 -105
- package/src/state/jsonl-writer.ts +77 -77
- package/src/state/mailbox.ts +67 -22
- package/src/state/state-store.ts +36 -5
- package/src/state/task-claims.ts +42 -42
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/discover-teams.ts +27 -5
- package/src/teams/team-serializer.ts +38 -36
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/loaders.ts +158 -158
- package/src/ui/mascot.ts +441 -441
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/run-dashboard.ts +5 -2
- package/src/ui/run-snapshot-cache.ts +19 -8
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +54 -54
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/transcript-viewer.ts +15 -1
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/file-coalescer.ts +84 -84
- package/src/utils/frontmatter.ts +36 -36
- package/src/utils/fs-watch.ts +31 -31
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +12 -12
- package/src/utils/names.ts +26 -26
- package/src/utils/paths.ts +3 -2
- package/src/utils/safe-paths.ts +34 -0
- package/src/utils/sleep.ts +32 -32
- package/src/utils/timings.ts +31 -31
- package/src/utils/visual.ts +159 -159
- package/src/workflows/discover-workflows.ts +30 -3
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +29 -29
- package/workflows/fast-fix.workflow.md +22 -22
- package/workflows/implementation.workflow.md +38 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
|
@@ -1,106 +1,127 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
4
|
-
import { writeArtifact } from "../state/artifact-store.ts";
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return fs.
|
|
17
|
-
} catch {
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
4
|
+
import { writeArtifact } from "../state/artifact-store.ts";
|
|
5
|
+
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
6
|
+
import type { WorkflowStep } from "../workflows/workflow-config.ts";
|
|
7
|
+
|
|
8
|
+
export interface DependencyOutputContext {
|
|
9
|
+
dependencies: Array<{ taskId: string; title: string; status: string; result?: string; resultPath?: string }>;
|
|
10
|
+
sharedReads: Array<{ name: string; path: string; content: string }>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function containedExists(filePath: string, baseDir?: string): boolean {
|
|
14
|
+
try {
|
|
15
|
+
const safePath = baseDir ? resolveRealContainedPath(baseDir, filePath) : filePath;
|
|
16
|
+
return fs.existsSync(safePath);
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readIfSmall(filePath: string, maxBytes = 24_000, baseDir?: string): string | undefined {
|
|
23
|
+
try {
|
|
24
|
+
const safePath = baseDir ? resolveRealContainedPath(baseDir, filePath) : filePath;
|
|
25
|
+
const stat = fs.statSync(safePath);
|
|
26
|
+
if (stat.size > maxBytes) return `${fs.readFileSync(safePath, "utf-8").slice(0, maxBytes)}\n\n...(truncated ${stat.size - maxBytes} bytes)`;
|
|
27
|
+
return fs.readFileSync(safePath, "utf-8");
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function safeSharedName(name: string): string {
|
|
34
|
+
const normalized = name.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
35
|
+
if (!normalized || normalized.split("/").some((segment) => segment === "..") || path.isAbsolute(normalized)) throw new Error(`Invalid shared artifact name: ${name}`);
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function sharedPath(manifest: TeamRunManifest, name: string): string {
|
|
40
|
+
const sharedRoot = path.resolve(manifest.artifactsRoot, "shared");
|
|
41
|
+
const resolved = path.resolve(sharedRoot, safeSharedName(name));
|
|
42
|
+
const relative = path.relative(sharedRoot, resolved);
|
|
43
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Invalid shared artifact name: ${name}`);
|
|
44
|
+
return resolved;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function collectDependencyOutputContext(manifest: TeamRunManifest, tasks: TeamTaskState[], task: TeamTaskState, step: WorkflowStep): DependencyOutputContext {
|
|
48
|
+
const byStep = new Map(tasks.map((item) => [item.stepId, item]).filter((entry): entry is [string, TeamTaskState] => Boolean(entry[0])));
|
|
49
|
+
const byId = new Map(tasks.map((item) => [item.id, item]));
|
|
50
|
+
const dependencies = task.dependsOn.map((dep) => byStep.get(dep) ?? byId.get(dep)).filter((item): item is TeamTaskState => Boolean(item)).map((item) => ({
|
|
51
|
+
taskId: item.id,
|
|
52
|
+
title: item.title,
|
|
53
|
+
status: item.status,
|
|
54
|
+
resultPath: item.resultArtifact?.path,
|
|
55
|
+
result: item.resultArtifact ? readIfSmall(item.resultArtifact.path, 24_000, manifest.artifactsRoot) : undefined,
|
|
56
|
+
}));
|
|
57
|
+
const sharedReads = (step.reads === false ? [] : step.reads ?? []).map((name) => {
|
|
58
|
+
const filePath = sharedPath(manifest, name);
|
|
59
|
+
return { name, path: filePath, content: readIfSmall(filePath, 24_000, path.resolve(manifest.artifactsRoot, "shared")) ?? "" };
|
|
60
|
+
}).filter((item) => item.content.trim().length > 0);
|
|
61
|
+
return { dependencies, sharedReads };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function renderDependencyOutputContext(context: DependencyOutputContext): string {
|
|
65
|
+
const parts: string[] = [];
|
|
66
|
+
if (context.dependencies.length) {
|
|
67
|
+
parts.push("# Dependency Outputs", "");
|
|
68
|
+
for (const dep of context.dependencies) {
|
|
69
|
+
parts.push(`## ${dep.taskId} (${dep.title})`, `Status: ${dep.status}`, dep.resultPath ? `Result artifact: ${dep.resultPath}` : "", "", dep.result?.trim() || "(no result output)", "");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (context.sharedReads.length) {
|
|
73
|
+
parts.push("# Shared Run Context Reads", "");
|
|
74
|
+
for (const read of context.sharedReads) parts.push(`## shared/${read.name}`, `Path: ${read.path}`, "", read.content.trim(), "");
|
|
75
|
+
}
|
|
76
|
+
return parts.join("\n").trim();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function writeTaskSharedOutput(manifest: TeamRunManifest, step: WorkflowStep, task: TeamTaskState): ArtifactDescriptor | undefined {
|
|
80
|
+
if (step.output === false) return undefined;
|
|
81
|
+
const name = safeSharedName(step.output || `${task.id}.md`);
|
|
82
|
+
const source = task.resultArtifact ? readIfSmall(task.resultArtifact.path, 80_000, manifest.artifactsRoot) : undefined;
|
|
83
|
+
if (!source) return undefined;
|
|
84
|
+
return writeArtifact(manifest.artifactsRoot, {
|
|
85
|
+
kind: "metadata",
|
|
86
|
+
relativePath: `shared/${name}`,
|
|
87
|
+
producer: task.id,
|
|
88
|
+
content: source.endsWith("\n") ? source : `${source}\n`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function writeTaskInputsArtifact(manifest: TeamRunManifest, task: TeamTaskState, context: DependencyOutputContext): ArtifactDescriptor {
|
|
93
|
+
return writeArtifact(manifest.artifactsRoot, {
|
|
94
|
+
kind: "metadata",
|
|
95
|
+
relativePath: `metadata/${task.id}.inputs.json`,
|
|
96
|
+
producer: task.id,
|
|
97
|
+
content: `${JSON.stringify(context, null, 2)}\n`,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function aggregateTaskOutputs(tasks: TeamTaskState[], manifest?: TeamRunManifest): string {
|
|
102
|
+
return tasks.map((task, index) => {
|
|
103
|
+
const body = task.resultArtifact ? readIfSmall(task.resultArtifact.path, 40_000, manifest?.artifactsRoot) : undefined;
|
|
104
|
+
const hasBody = Boolean(body?.trim());
|
|
105
|
+
const expectedMissing = task.resultArtifact && !containedExists(task.resultArtifact.path, manifest?.artifactsRoot);
|
|
106
|
+
const status = task.status === "skipped"
|
|
107
|
+
? "SKIPPED"
|
|
108
|
+
: task.status === "failed"
|
|
109
|
+
? `FAILED${task.exitCode !== undefined ? ` (exit code ${task.exitCode ?? "null"})` : ""}${task.error ? `: ${task.error}` : ""}`
|
|
110
|
+
: expectedMissing
|
|
111
|
+
? `EMPTY OUTPUT (expected result artifact missing: ${task.resultArtifact?.path})`
|
|
112
|
+
: !hasBody
|
|
113
|
+
? "EMPTY OUTPUT (no textual response returned)"
|
|
114
|
+
: task.status.toUpperCase();
|
|
115
|
+
return [
|
|
116
|
+
`=== Task ${index + 1}: ${task.id} (${task.agent}) ===`,
|
|
117
|
+
`Status: ${status}`,
|
|
118
|
+
task.role ? `Role: ${task.role}` : "",
|
|
119
|
+
task.resultArtifact?.path ? `Result artifact: ${task.resultArtifact.path}` : "",
|
|
120
|
+
task.logArtifact?.path ? `Log artifact: ${task.logArtifact.path}` : "",
|
|
121
|
+
task.transcriptArtifact?.path ? `Transcript: ${task.transcriptArtifact.path}` : "",
|
|
122
|
+
task.usage ? `Usage: ${JSON.stringify(task.usage)}` : "",
|
|
123
|
+
"",
|
|
124
|
+
hasBody ? body!.trim() : status,
|
|
125
|
+
].filter(Boolean).join("\n");
|
|
126
|
+
}).join("\n\n");
|
|
127
|
+
}
|
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import type { AgentConfig } from "../../agents/agent-config.ts";
|
|
3
|
-
import type { CrewRuntimeConfig } from "../../config/config.ts";
|
|
4
|
-
import { writeArtifact } from "../../state/artifact-store.ts";
|
|
5
|
-
import { appendEvent } from "../../state/event-log.ts";
|
|
6
|
-
import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../../state/types.ts";
|
|
7
|
-
import type { WorkflowStep } from "../../workflows/workflow-config.ts";
|
|
8
|
-
import { appendCrewAgentEvent, appendCrewAgentOutput, emptyCrewAgentProgress, recordFromTask, upsertCrewAgent } from "../crew-agent-records.ts";
|
|
9
|
-
import { createStartupEvidence, type WorkerStartupEvidence } from "../worker-startup.ts";
|
|
10
|
-
import { runLiveSessionTask } from "../live-session-runtime.ts";
|
|
11
|
-
import { shouldAppendProgressEventUpdate, type ProgressEventSummary } from "../progress-event-coalescer.ts";
|
|
12
|
-
import { applyAgentProgressEvent, applyUsageToProgress, progressEventSummary, shouldFlushProgressEvent } from "./progress.ts";
|
|
13
|
-
import type { ParsedPiJsonOutput } from "../pi-json-output.ts";
|
|
14
|
-
|
|
15
|
-
export interface RunLiveTaskInput {
|
|
16
|
-
manifest: TeamRunManifest;
|
|
17
|
-
tasks: TeamTaskState[];
|
|
18
|
-
task: TeamTaskState;
|
|
19
|
-
step: WorkflowStep;
|
|
20
|
-
agent: AgentConfig;
|
|
21
|
-
prompt: string;
|
|
22
|
-
signal?: AbortSignal;
|
|
23
|
-
runtimeConfig?: CrewRuntimeConfig;
|
|
24
|
-
parentContext?: string;
|
|
25
|
-
parentModel?: unknown;
|
|
26
|
-
modelRegistry?: unknown;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface RunLiveTaskOutput {
|
|
30
|
-
task: TeamTaskState;
|
|
31
|
-
tasks: TeamTaskState[];
|
|
32
|
-
startupEvidence: WorkerStartupEvidence;
|
|
33
|
-
exitCode: number | null;
|
|
34
|
-
error?: string;
|
|
35
|
-
parsedOutput?: ParsedPiJsonOutput;
|
|
36
|
-
resultArtifact: ArtifactDescriptor;
|
|
37
|
-
logArtifact?: ArtifactDescriptor;
|
|
38
|
-
transcriptArtifact?: ArtifactDescriptor;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function updateTask(tasks: TeamTaskState[], updated: TeamTaskState): TeamTaskState[] {
|
|
42
|
-
return tasks.map((task) => task.id === updated.id ? updated : task);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function runLiveTask(input: RunLiveTaskInput): Promise<RunLiveTaskOutput> {
|
|
46
|
-
const { manifest, step, agent, prompt } = input;
|
|
47
|
-
let task = input.task;
|
|
48
|
-
let tasks = input.tasks;
|
|
49
|
-
const transcriptPath = `${manifest.artifactsRoot}/transcripts/${task.id}.jsonl`;
|
|
50
|
-
let lastAgentRecordPersistedAt = 0;
|
|
51
|
-
let lastRunProgressPersistedAt = 0;
|
|
52
|
-
let lastRunProgressSummary: ProgressEventSummary | undefined;
|
|
53
|
-
const persistLiveProgress = (event: unknown, force = false): void => {
|
|
54
|
-
const now = Date.now();
|
|
55
|
-
if (force || shouldFlushProgressEvent(event) || now - lastAgentRecordPersistedAt >= 500) {
|
|
56
|
-
upsertCrewAgent(manifest, recordFromTask(manifest, task, "live-session"));
|
|
57
|
-
lastAgentRecordPersistedAt = now;
|
|
58
|
-
}
|
|
59
|
-
const summary = progressEventSummary(task, event);
|
|
60
|
-
const decision = shouldAppendProgressEventUpdate({ previous: lastRunProgressSummary, next: summary, nowMs: now, lastAppendMs: lastRunProgressPersistedAt || undefined, minIntervalMs: 1000, force });
|
|
61
|
-
if (decision.shouldAppend) {
|
|
62
|
-
appendEvent(manifest.eventsPath, { type: "task.progress", runId: manifest.runId, taskId: task.id, data: { ...summary, coalesceReason: decision.reason } });
|
|
63
|
-
lastRunProgressSummary = summary;
|
|
64
|
-
lastRunProgressPersistedAt = now;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
const attemptStartedAt = new Date();
|
|
68
|
-
const liveResult = await runLiveSessionTask({
|
|
69
|
-
manifest,
|
|
70
|
-
task,
|
|
71
|
-
step,
|
|
72
|
-
agent,
|
|
73
|
-
prompt,
|
|
74
|
-
signal: input.signal,
|
|
75
|
-
transcriptPath,
|
|
76
|
-
runtimeConfig: input.runtimeConfig,
|
|
77
|
-
parentContext: input.parentContext,
|
|
78
|
-
parentModel: input.parentModel,
|
|
79
|
-
modelRegistry: input.modelRegistry,
|
|
80
|
-
onOutput: (text) => appendCrewAgentOutput(manifest, task.id, text),
|
|
81
|
-
onEvent: (event) => {
|
|
82
|
-
appendCrewAgentEvent(manifest, task.id, event);
|
|
83
|
-
task = { ...task, agentProgress: applyAgentProgressEvent(task.agentProgress ?? emptyCrewAgentProgress(), event, task.startedAt) };
|
|
84
|
-
tasks = updateTask(tasks, task);
|
|
85
|
-
persistLiveProgress(event);
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
const startupEvidence = createStartupEvidence({ command: "live-session", startedAt: attemptStartedAt, finishedAt: new Date(), promptSentAt: attemptStartedAt, promptAccepted: liveResult.exitCode === 0 && !liveResult.error, stderr: liveResult.stderr, error: liveResult.error, exitCode: liveResult.exitCode });
|
|
89
|
-
const exitCode = liveResult.exitCode;
|
|
90
|
-
const error = liveResult.error || (liveResult.exitCode && liveResult.exitCode !== 0 ? liveResult.stderr || `Live session exited with ${liveResult.exitCode}` : undefined);
|
|
91
|
-
const parsedOutput = { finalText: liveResult.stdout, textEvents: liveResult.stdout ? [liveResult.stdout] : [], jsonEvents: liveResult.jsonEvents, usage: liveResult.usage };
|
|
92
|
-
if (liveResult.usage) task = { ...task, usage: liveResult.usage, agentProgress: applyUsageToProgress(task.agentProgress, liveResult.usage) };
|
|
93
|
-
persistLiveProgress({ type: "attempt_finished" }, true);
|
|
94
|
-
const resultArtifact = writeArtifact(manifest.artifactsRoot, { kind: "result", relativePath: `results/${task.id}.txt`, content: liveResult.stdout || liveResult.stderr || "(no output)", producer: task.id });
|
|
95
|
-
const logArtifact = writeArtifact(manifest.artifactsRoot, { kind: "log", relativePath: `logs/${task.id}.log`, content: [`runtime=live-session`, `finalExitCode=${exitCode ?? "null"}`, `jsonEvents=${liveResult.jsonEvents}`, liveResult.usage ? `usage=${JSON.stringify(liveResult.usage)}` : "", "", "STDOUT:", liveResult.stdout, "", "STDERR:", liveResult.stderr].join("\n"), producer: task.id });
|
|
96
|
-
const transcriptArtifact = fs.existsSync(transcriptPath) ? writeArtifact(manifest.artifactsRoot, { kind: "log", relativePath: `transcripts/${task.id}.jsonl`, content: fs.readFileSync(transcriptPath, "utf-8"), producer: task.id }) : undefined;
|
|
97
|
-
return { task, tasks, startupEvidence, exitCode, error: error || undefined, parsedOutput, resultArtifact, logArtifact, transcriptArtifact };
|
|
98
|
-
}
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import type { AgentConfig } from "../../agents/agent-config.ts";
|
|
3
|
+
import type { CrewRuntimeConfig } from "../../config/config.ts";
|
|
4
|
+
import { writeArtifact } from "../../state/artifact-store.ts";
|
|
5
|
+
import { appendEvent } from "../../state/event-log.ts";
|
|
6
|
+
import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../../state/types.ts";
|
|
7
|
+
import type { WorkflowStep } from "../../workflows/workflow-config.ts";
|
|
8
|
+
import { appendCrewAgentEvent, appendCrewAgentOutput, emptyCrewAgentProgress, recordFromTask, upsertCrewAgent } from "../crew-agent-records.ts";
|
|
9
|
+
import { createStartupEvidence, type WorkerStartupEvidence } from "../worker-startup.ts";
|
|
10
|
+
import { runLiveSessionTask } from "../live-session-runtime.ts";
|
|
11
|
+
import { shouldAppendProgressEventUpdate, type ProgressEventSummary } from "../progress-event-coalescer.ts";
|
|
12
|
+
import { applyAgentProgressEvent, applyUsageToProgress, progressEventSummary, shouldFlushProgressEvent } from "./progress.ts";
|
|
13
|
+
import type { ParsedPiJsonOutput } from "../pi-json-output.ts";
|
|
14
|
+
|
|
15
|
+
export interface RunLiveTaskInput {
|
|
16
|
+
manifest: TeamRunManifest;
|
|
17
|
+
tasks: TeamTaskState[];
|
|
18
|
+
task: TeamTaskState;
|
|
19
|
+
step: WorkflowStep;
|
|
20
|
+
agent: AgentConfig;
|
|
21
|
+
prompt: string;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
runtimeConfig?: CrewRuntimeConfig;
|
|
24
|
+
parentContext?: string;
|
|
25
|
+
parentModel?: unknown;
|
|
26
|
+
modelRegistry?: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RunLiveTaskOutput {
|
|
30
|
+
task: TeamTaskState;
|
|
31
|
+
tasks: TeamTaskState[];
|
|
32
|
+
startupEvidence: WorkerStartupEvidence;
|
|
33
|
+
exitCode: number | null;
|
|
34
|
+
error?: string;
|
|
35
|
+
parsedOutput?: ParsedPiJsonOutput;
|
|
36
|
+
resultArtifact: ArtifactDescriptor;
|
|
37
|
+
logArtifact?: ArtifactDescriptor;
|
|
38
|
+
transcriptArtifact?: ArtifactDescriptor;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function updateTask(tasks: TeamTaskState[], updated: TeamTaskState): TeamTaskState[] {
|
|
42
|
+
return tasks.map((task) => task.id === updated.id ? updated : task);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runLiveTask(input: RunLiveTaskInput): Promise<RunLiveTaskOutput> {
|
|
46
|
+
const { manifest, step, agent, prompt } = input;
|
|
47
|
+
let task = input.task;
|
|
48
|
+
let tasks = input.tasks;
|
|
49
|
+
const transcriptPath = `${manifest.artifactsRoot}/transcripts/${task.id}.jsonl`;
|
|
50
|
+
let lastAgentRecordPersistedAt = 0;
|
|
51
|
+
let lastRunProgressPersistedAt = 0;
|
|
52
|
+
let lastRunProgressSummary: ProgressEventSummary | undefined;
|
|
53
|
+
const persistLiveProgress = (event: unknown, force = false): void => {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
if (force || shouldFlushProgressEvent(event) || now - lastAgentRecordPersistedAt >= 500) {
|
|
56
|
+
upsertCrewAgent(manifest, recordFromTask(manifest, task, "live-session"));
|
|
57
|
+
lastAgentRecordPersistedAt = now;
|
|
58
|
+
}
|
|
59
|
+
const summary = progressEventSummary(task, event);
|
|
60
|
+
const decision = shouldAppendProgressEventUpdate({ previous: lastRunProgressSummary, next: summary, nowMs: now, lastAppendMs: lastRunProgressPersistedAt || undefined, minIntervalMs: 1000, force });
|
|
61
|
+
if (decision.shouldAppend) {
|
|
62
|
+
appendEvent(manifest.eventsPath, { type: "task.progress", runId: manifest.runId, taskId: task.id, data: { ...summary, coalesceReason: decision.reason } });
|
|
63
|
+
lastRunProgressSummary = summary;
|
|
64
|
+
lastRunProgressPersistedAt = now;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const attemptStartedAt = new Date();
|
|
68
|
+
const liveResult = await runLiveSessionTask({
|
|
69
|
+
manifest,
|
|
70
|
+
task,
|
|
71
|
+
step,
|
|
72
|
+
agent,
|
|
73
|
+
prompt,
|
|
74
|
+
signal: input.signal,
|
|
75
|
+
transcriptPath,
|
|
76
|
+
runtimeConfig: input.runtimeConfig,
|
|
77
|
+
parentContext: input.parentContext,
|
|
78
|
+
parentModel: input.parentModel,
|
|
79
|
+
modelRegistry: input.modelRegistry,
|
|
80
|
+
onOutput: (text) => appendCrewAgentOutput(manifest, task.id, text),
|
|
81
|
+
onEvent: (event) => {
|
|
82
|
+
appendCrewAgentEvent(manifest, task.id, event);
|
|
83
|
+
task = { ...task, agentProgress: applyAgentProgressEvent(task.agentProgress ?? emptyCrewAgentProgress(), event, task.startedAt) };
|
|
84
|
+
tasks = updateTask(tasks, task);
|
|
85
|
+
persistLiveProgress(event);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const startupEvidence = createStartupEvidence({ command: "live-session", startedAt: attemptStartedAt, finishedAt: new Date(), promptSentAt: attemptStartedAt, promptAccepted: liveResult.exitCode === 0 && !liveResult.error, stderr: liveResult.stderr, error: liveResult.error, exitCode: liveResult.exitCode });
|
|
89
|
+
const exitCode = liveResult.exitCode;
|
|
90
|
+
const error = liveResult.error || (liveResult.exitCode && liveResult.exitCode !== 0 ? liveResult.stderr || `Live session exited with ${liveResult.exitCode}` : undefined);
|
|
91
|
+
const parsedOutput = { finalText: liveResult.stdout, textEvents: liveResult.stdout ? [liveResult.stdout] : [], jsonEvents: liveResult.jsonEvents, usage: liveResult.usage };
|
|
92
|
+
if (liveResult.usage) task = { ...task, usage: liveResult.usage, agentProgress: applyUsageToProgress(task.agentProgress, liveResult.usage) };
|
|
93
|
+
persistLiveProgress({ type: "attempt_finished" }, true);
|
|
94
|
+
const resultArtifact = writeArtifact(manifest.artifactsRoot, { kind: "result", relativePath: `results/${task.id}.txt`, content: liveResult.stdout || liveResult.stderr || "(no output)", producer: task.id });
|
|
95
|
+
const logArtifact = writeArtifact(manifest.artifactsRoot, { kind: "log", relativePath: `logs/${task.id}.log`, content: [`runtime=live-session`, `finalExitCode=${exitCode ?? "null"}`, `jsonEvents=${liveResult.jsonEvents}`, liveResult.usage ? `usage=${JSON.stringify(liveResult.usage)}` : "", "", "STDOUT:", liveResult.stdout, "", "STDERR:", liveResult.stderr].join("\n"), producer: task.id });
|
|
96
|
+
const transcriptArtifact = fs.existsSync(transcriptPath) ? writeArtifact(manifest.artifactsRoot, { kind: "log", relativePath: `transcripts/${task.id}.jsonl`, content: fs.readFileSync(transcriptPath, "utf-8"), producer: task.id }) : undefined;
|
|
97
|
+
return { task, tasks, startupEvidence, exitCode, error: error || undefined, parsedOutput, resultArtifact, logArtifact, transcriptArtifact };
|
|
98
|
+
}
|