pi-crew 0.1.45 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +97 -0
- package/README.md +5 -5
- 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/next-upgrade-roadmap.md +808 -0
- package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +261 -0
- package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +111 -0
- package/docs/research/AUDIT_OH_MY_PI.md +261 -0
- package/docs/research/AUDIT_PI_CREW.md +457 -0
- package/docs/research/CAVEMAN-DEEP-RESEARCH.md +281 -0
- package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +264 -0
- package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +343 -0
- package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +480 -0
- package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +354 -0
- package/docs/research/IMPLEMENTATION_PLAN.md +385 -0
- package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +502 -0
- package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +266 -0
- package/docs/research/REMAINING-GAPS-PLAN.md +363 -0
- package/docs/research/SESSION-SUMMARY-2026-05-08.md +146 -0
- package/docs/research/UI-RESPONSIVENESS-AUDIT.md +173 -0
- package/docs/research-awesome-agent-skills-distillation.md +100 -0
- package/docs/research-oh-my-pi-distillation.md +369 -0
- package/docs/source-runtime-refactor-map.md +24 -0
- package/docs/usage.md +3 -3
- package/install.mjs +52 -8
- package/package.json +99 -98
- package/schema.json +10 -1
- package/skills/async-worker-recovery/SKILL.md +42 -0
- package/skills/context-artifact-hygiene/SKILL.md +52 -0
- package/skills/delegation-patterns/SKILL.md +54 -0
- package/skills/mailbox-interactive/SKILL.md +40 -0
- package/skills/model-routing-context/SKILL.md +39 -0
- package/skills/multi-perspective-review/SKILL.md +58 -0
- package/skills/observability-reliability/SKILL.md +41 -0
- package/skills/orchestration/SKILL.md +157 -0
- package/skills/ownership-session-security/SKILL.md +41 -0
- package/skills/pi-extension-lifecycle/SKILL.md +39 -0
- package/skills/requirements-to-task-packet/SKILL.md +63 -0
- package/skills/resource-discovery-config/SKILL.md +41 -0
- package/skills/runtime-state-reader/SKILL.md +44 -0
- package/skills/secure-agent-orchestration-review/SKILL.md +45 -0
- package/skills/state-mutation-locking/SKILL.md +42 -0
- package/skills/systematic-debugging/SKILL.md +67 -0
- package/skills/ui-render-performance/SKILL.md +39 -0
- package/skills/verification-before-done/SKILL.md +57 -0
- package/skills/worktree-isolation/SKILL.md +39 -0
- package/src/agents/agent-config.ts +6 -0
- package/src/agents/agent-search.ts +98 -0
- package/src/agents/agent-serializer.ts +38 -34
- package/src/agents/discover-agents.ts +29 -15
- package/src/config/config.ts +72 -24
- package/src/config/defaults.ts +25 -0
- package/src/extension/autonomous-policy.ts +26 -33
- package/src/extension/help.ts +1 -0
- package/src/extension/management.ts +5 -0
- package/src/extension/project-init.ts +62 -2
- package/src/extension/register.ts +69 -22
- package/src/extension/registration/commands.ts +64 -25
- package/src/extension/registration/compaction-guard.ts +1 -1
- package/src/extension/registration/subagent-helpers.ts +8 -0
- package/src/extension/registration/subagent-tools.ts +149 -148
- package/src/extension/registration/team-tool.ts +14 -10
- package/src/extension/run-index.ts +35 -21
- package/src/extension/run-maintenance.ts +30 -5
- package/src/extension/team-tool/api.ts +47 -9
- package/src/extension/team-tool/cancel.ts +109 -5
- package/src/extension/team-tool/context.ts +8 -0
- package/src/extension/team-tool/intent-policy.ts +42 -0
- package/src/extension/team-tool/lifecycle-actions.ts +120 -79
- package/src/extension/team-tool/parallel-dispatch.ts +156 -0
- package/src/extension/team-tool/respond.ts +46 -18
- package/src/extension/team-tool/run.ts +55 -12
- package/src/extension/team-tool/status.ts +13 -2
- package/src/extension/team-tool-types.ts +3 -0
- package/src/extension/team-tool.ts +45 -14
- package/src/hooks/registry.ts +61 -0
- package/src/hooks/types.ts +41 -0
- package/src/observability/event-to-metric.ts +8 -1
- package/src/runtime/agent-control.ts +169 -63
- package/src/runtime/async-runner.ts +3 -1
- package/src/runtime/background-runner.ts +78 -53
- package/src/runtime/cancellation-token.ts +89 -0
- package/src/runtime/cancellation.ts +61 -0
- package/src/runtime/capability-inventory.ts +116 -0
- package/src/runtime/child-pi.ts +458 -444
- package/src/runtime/code-summary.ts +247 -0
- package/src/runtime/crash-recovery.ts +182 -0
- package/src/runtime/crew-agent-records.ts +70 -10
- package/src/runtime/crew-agent-runtime.ts +1 -0
- package/src/runtime/custom-tools/irc-tool.ts +201 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +90 -0
- package/src/runtime/deadletter.ts +1 -0
- package/src/runtime/delivery-coordinator.ts +48 -25
- package/src/runtime/effectiveness.ts +81 -0
- package/src/runtime/event-stream-bridge.ts +90 -0
- package/src/runtime/live-agent-control.ts +2 -1
- package/src/runtime/live-agent-manager.ts +179 -85
- package/src/runtime/live-control-realtime.ts +1 -1
- package/src/runtime/live-extension-bridge.ts +150 -0
- package/src/runtime/live-irc.ts +92 -0
- package/src/runtime/live-session-health.ts +100 -0
- package/src/runtime/live-session-runtime.ts +599 -305
- package/src/runtime/manifest-cache.ts +17 -2
- package/src/runtime/mcp-proxy.ts +113 -0
- package/src/runtime/model-fallback.ts +6 -4
- package/src/runtime/notebook-helpers.ts +90 -0
- package/src/runtime/orphan-sentinel.ts +7 -0
- package/src/runtime/output-validator.ts +187 -0
- package/src/runtime/parallel-utils.ts +57 -0
- package/src/runtime/parent-guard.ts +80 -0
- package/src/runtime/pi-args.ts +18 -3
- package/src/runtime/process-status.ts +5 -1
- package/src/runtime/prose-compressor.ts +164 -0
- package/src/runtime/result-extractor.ts +121 -0
- package/src/runtime/retry-executor.ts +81 -64
- package/src/runtime/runtime-resolver.ts +23 -10
- package/src/runtime/semaphore.ts +131 -0
- package/src/runtime/sensitive-paths.ts +92 -0
- package/src/runtime/skill-instructions.ts +222 -0
- package/src/runtime/stale-reconciler.ts +4 -14
- package/src/runtime/stream-preview.ts +177 -0
- package/src/runtime/subagent-manager.ts +6 -2
- package/src/runtime/subprocess-tool-registry.ts +67 -0
- package/src/runtime/task-output-context.ts +177 -127
- package/src/runtime/task-runner/capabilities.ts +78 -0
- package/src/runtime/task-runner/live-executor.ts +107 -101
- package/src/runtime/task-runner/prompt-builder.ts +72 -8
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -0
- package/src/runtime/task-runner/run-projection.ts +104 -0
- package/src/runtime/task-runner.ts +115 -5
- package/src/runtime/team-runner.ts +134 -19
- package/src/runtime/workspace-tree.ts +298 -0
- package/src/runtime/yield-handler.ts +189 -0
- package/src/schema/config-schema.ts +7 -0
- package/src/schema/team-tool-schema.ts +14 -4
- package/src/skills/discover-skills.ts +67 -0
- package/src/state/active-run-registry.ts +167 -0
- package/src/state/artifact-store.ts +4 -1
- package/src/state/atomic-write.ts +50 -1
- package/src/state/blob-store.ts +117 -0
- package/src/state/contracts.ts +2 -1
- package/src/state/event-log-rotation.ts +158 -0
- package/src/state/event-log.ts +52 -2
- package/src/state/mailbox.ts +129 -9
- package/src/state/state-store.ts +32 -5
- package/src/state/types.ts +64 -2
- package/src/teams/team-config.ts +1 -0
- package/src/ui/agent-management-overlay.ts +144 -0
- package/src/ui/crew-widget.ts +15 -5
- package/src/ui/dashboard-panes/cancellation-pane.ts +43 -0
- package/src/ui/dashboard-panes/capability-pane.ts +60 -0
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -11
- package/src/ui/dashboard-panes/progress-pane.ts +2 -0
- package/src/ui/live-run-sidebar.ts +4 -0
- package/src/ui/powerbar-publisher.ts +77 -15
- package/src/ui/render-coalescer.ts +51 -0
- package/src/ui/run-dashboard.ts +4 -0
- package/src/ui/run-event-bus.ts +209 -0
- package/src/ui/run-snapshot-cache.ts +78 -18
- package/src/ui/snapshot-types.ts +10 -0
- package/src/ui/transcript-entries.ts +258 -0
- package/src/utils/ids.ts +5 -0
- package/src/utils/incremental-reader.ts +104 -0
- package/src/utils/paths.ts +4 -2
- package/src/utils/scan-cache.ts +137 -0
- package/src/utils/sse-parser.ts +134 -0
- package/src/utils/task-name-generator.ts +337 -0
- package/src/utils/visual.ts +33 -2
- package/src/workflows/workflow-config.ts +1 -0
- package/src/worktree/cleanup.ts +2 -1
|
@@ -1,127 +1,177 @@
|
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return fs.
|
|
28
|
-
} catch {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function
|
|
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
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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 { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
6
|
+
import type { WorkflowStep } from "../workflows/workflow-config.ts";
|
|
7
|
+
|
|
8
|
+
export interface DependencyContextEntry {
|
|
9
|
+
taskId: string;
|
|
10
|
+
role: string;
|
|
11
|
+
status: string;
|
|
12
|
+
resultSummary: string;
|
|
13
|
+
resultPath?: string;
|
|
14
|
+
structuredResults?: Record<string, unknown>;
|
|
15
|
+
artifactsProduced?: string[];
|
|
16
|
+
usage?: { inputTokens: number; outputTokens: number; durationMs: number };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DependencyOutputContext {
|
|
20
|
+
dependencies: DependencyContextEntry[];
|
|
21
|
+
sharedReads: Array<{ name: string; path: string; content: string }>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function containedExists(filePath: string, baseDir?: string): boolean {
|
|
25
|
+
try {
|
|
26
|
+
const safePath = baseDir ? resolveRealContainedPath(baseDir, filePath) : filePath;
|
|
27
|
+
return fs.existsSync(safePath);
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readIfSmall(filePath: string, maxBytes = 24_000, baseDir?: string): string | undefined {
|
|
34
|
+
try {
|
|
35
|
+
const safePath = baseDir ? resolveRealContainedPath(baseDir, filePath) : filePath;
|
|
36
|
+
const stat = fs.statSync(safePath);
|
|
37
|
+
if (stat.size > maxBytes) return `${fs.readFileSync(safePath, "utf-8").slice(0, maxBytes)}\n\n...(truncated ${stat.size - maxBytes} bytes)`;
|
|
38
|
+
return fs.readFileSync(safePath, "utf-8");
|
|
39
|
+
} catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function safeSharedName(name: string): string {
|
|
45
|
+
const normalized = name.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
46
|
+
if (!normalized || normalized.split("/").some((segment) => segment === "..") || path.isAbsolute(normalized)) throw new Error(`Invalid shared artifact name: ${name}`);
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function sharedPath(manifest: TeamRunManifest, name: string): string {
|
|
51
|
+
const sharedRoot = path.resolve(manifest.artifactsRoot, "shared");
|
|
52
|
+
const resolved = path.resolve(sharedRoot, safeSharedName(name));
|
|
53
|
+
const relative = path.relative(sharedRoot, resolved);
|
|
54
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Invalid shared artifact name: ${name}`);
|
|
55
|
+
return resolved;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function tryParseJson(text: string): Record<string, unknown> | undefined {
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(text);
|
|
61
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed as Record<string, unknown>;
|
|
62
|
+
} catch {
|
|
63
|
+
// Not valid JSON object — return undefined.
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function listTaskArtifacts(manifest: TeamRunManifest, taskId: string): string[] | undefined {
|
|
69
|
+
const produced = manifest.artifacts.filter((a) => a.producer === taskId);
|
|
70
|
+
if (produced.length === 0) return undefined;
|
|
71
|
+
return produced.map((a) => {
|
|
72
|
+
const relative = path.relative(manifest.artifactsRoot, a.path);
|
|
73
|
+
return relative.startsWith("..") ? a.path : relative;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function aggregateUsage(task: TeamTaskState): DependencyContextEntry["usage"] {
|
|
78
|
+
if (!task.usage) return undefined;
|
|
79
|
+
const inputTokens = task.usage.input ?? 0;
|
|
80
|
+
const outputTokens = task.usage.output ?? 0;
|
|
81
|
+
const started = task.startedAt ? new Date(task.startedAt).getTime() : 0;
|
|
82
|
+
const finished = task.finishedAt ? new Date(task.finishedAt).getTime() : 0;
|
|
83
|
+
const durationMs = started && finished ? finished - started : 0;
|
|
84
|
+
if (inputTokens === 0 && outputTokens === 0 && durationMs === 0) return undefined;
|
|
85
|
+
return { inputTokens, outputTokens, durationMs };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function collectDependencyOutputContext(manifest: TeamRunManifest, tasks: TeamTaskState[], task: TeamTaskState, step: WorkflowStep): DependencyOutputContext {
|
|
89
|
+
const byStep = new Map(tasks.map((item) => [item.stepId, item]).filter((entry): entry is [string, TeamTaskState] => Boolean(entry[0])));
|
|
90
|
+
const byId = new Map(tasks.map((item) => [item.id, item]));
|
|
91
|
+
const dependencies = task.dependsOn.map((dep) => byStep.get(dep) ?? byId.get(dep)).filter((item): item is TeamTaskState => Boolean(item)).map((item) => {
|
|
92
|
+
const resultText = item.resultArtifact ? readIfSmall(item.resultArtifact.path, 24_000, manifest.artifactsRoot) : undefined;
|
|
93
|
+
return {
|
|
94
|
+
taskId: item.id,
|
|
95
|
+
role: item.role,
|
|
96
|
+
status: item.status,
|
|
97
|
+
resultSummary: resultText ?? "",
|
|
98
|
+
resultPath: item.resultArtifact?.path,
|
|
99
|
+
structuredResults: resultText ? tryParseJson(resultText) : undefined,
|
|
100
|
+
artifactsProduced: listTaskArtifacts(manifest, item.id),
|
|
101
|
+
usage: aggregateUsage(item),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
const sharedReads = (step.reads === false ? [] : step.reads ?? []).map((name) => {
|
|
105
|
+
const filePath = sharedPath(manifest, name);
|
|
106
|
+
return { name, path: filePath, content: readIfSmall(filePath, 24_000, path.resolve(manifest.artifactsRoot, "shared")) ?? "" };
|
|
107
|
+
}).filter((item) => item.content.trim().length > 0);
|
|
108
|
+
return { dependencies, sharedReads };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function renderDependencyOutputContext(context: DependencyOutputContext): string {
|
|
112
|
+
const parts: string[] = [];
|
|
113
|
+
if (context.dependencies.length) {
|
|
114
|
+
parts.push("# Dependency Outputs", "");
|
|
115
|
+
for (const dep of context.dependencies) {
|
|
116
|
+
parts.push(`## ${dep.taskId} (${dep.role})`, `Status: ${dep.status}`, dep.resultPath ? `Result artifact: ${dep.resultPath}` : "", "", dep.resultSummary?.trim() || "(no result output)", "");
|
|
117
|
+
if (dep.structuredResults) parts.push("Structured results:", JSON.stringify(dep.structuredResults, null, 2), "");
|
|
118
|
+
if (dep.artifactsProduced?.length) parts.push(`Artifacts produced: ${dep.artifactsProduced.join(", ")}`, "");
|
|
119
|
+
if (dep.usage) parts.push(`Usage: ${dep.usage.inputTokens} input tokens, ${dep.usage.outputTokens} output tokens, ${dep.usage.durationMs}ms`, "");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (context.sharedReads.length) {
|
|
123
|
+
parts.push("# Shared Run Context Reads", "");
|
|
124
|
+
for (const read of context.sharedReads) parts.push(`## shared/${read.name}`, `Path: ${read.path}`, "", read.content.trim(), "");
|
|
125
|
+
}
|
|
126
|
+
return parts.join("\n").trim();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function writeTaskSharedOutput(manifest: TeamRunManifest, step: WorkflowStep, task: TeamTaskState): ArtifactDescriptor | undefined {
|
|
130
|
+
if (step.output === false) return undefined;
|
|
131
|
+
const name = safeSharedName(step.output || `${task.id}.md`);
|
|
132
|
+
const source = task.resultArtifact ? readIfSmall(task.resultArtifact.path, 80_000, manifest.artifactsRoot) : undefined;
|
|
133
|
+
if (!source) return undefined;
|
|
134
|
+
return writeArtifact(manifest.artifactsRoot, {
|
|
135
|
+
kind: "metadata",
|
|
136
|
+
relativePath: `shared/${name}`,
|
|
137
|
+
producer: task.id,
|
|
138
|
+
content: source.endsWith("\n") ? source : `${source}\n`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function writeTaskInputsArtifact(manifest: TeamRunManifest, task: TeamTaskState, context: DependencyOutputContext): ArtifactDescriptor {
|
|
143
|
+
return writeArtifact(manifest.artifactsRoot, {
|
|
144
|
+
kind: "metadata",
|
|
145
|
+
relativePath: `metadata/${task.id}.inputs.json`,
|
|
146
|
+
producer: task.id,
|
|
147
|
+
content: `${JSON.stringify(context, null, 2)}\n`,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function aggregateTaskOutputs(tasks: TeamTaskState[], manifest?: TeamRunManifest): string {
|
|
152
|
+
return tasks.map((task, index) => {
|
|
153
|
+
const body = task.resultArtifact ? readIfSmall(task.resultArtifact.path, 40_000, manifest?.artifactsRoot) : undefined;
|
|
154
|
+
const hasBody = Boolean(body?.trim());
|
|
155
|
+
const expectedMissing = task.resultArtifact && !containedExists(task.resultArtifact.path, manifest?.artifactsRoot);
|
|
156
|
+
const status = task.status === "skipped"
|
|
157
|
+
? "SKIPPED"
|
|
158
|
+
: task.status === "failed"
|
|
159
|
+
? `FAILED${task.exitCode !== undefined ? ` (exit code ${task.exitCode ?? "null"})` : ""}${task.error ? `: ${task.error}` : ""}`
|
|
160
|
+
: expectedMissing
|
|
161
|
+
? `EMPTY OUTPUT (expected result artifact missing: ${task.resultArtifact?.path})`
|
|
162
|
+
: !hasBody
|
|
163
|
+
? "EMPTY OUTPUT (no textual response returned)"
|
|
164
|
+
: task.status.toUpperCase();
|
|
165
|
+
return [
|
|
166
|
+
`=== Task ${index + 1}: ${task.id} (${task.agent}) ===`,
|
|
167
|
+
`Status: ${status}`,
|
|
168
|
+
task.role ? `Role: ${task.role}` : "",
|
|
169
|
+
task.resultArtifact?.path ? `Result artifact: ${task.resultArtifact.path}` : "",
|
|
170
|
+
task.logArtifact?.path ? `Log artifact: ${task.logArtifact.path}` : "",
|
|
171
|
+
task.transcriptArtifact?.path ? `Transcript: ${task.transcriptArtifact.path}` : "",
|
|
172
|
+
task.usage ? `Usage: ${JSON.stringify(task.usage)}` : "",
|
|
173
|
+
"",
|
|
174
|
+
hasBody ? body!.trim() : status,
|
|
175
|
+
].filter(Boolean).join("\n");
|
|
176
|
+
}).join("\n\n");
|
|
177
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { AgentConfig } from "../../agents/agent-config.ts";
|
|
2
|
+
import type { CrewRuntimeKind } from "../crew-agent-runtime.ts";
|
|
3
|
+
|
|
4
|
+
export interface WorkerCapabilityInventory {
|
|
5
|
+
schemaVersion: 1;
|
|
6
|
+
taskId: string;
|
|
7
|
+
role: string;
|
|
8
|
+
agent: string;
|
|
9
|
+
runtime: CrewRuntimeKind;
|
|
10
|
+
permissionMode: string;
|
|
11
|
+
tools: string[];
|
|
12
|
+
extensions: string[];
|
|
13
|
+
skills: {
|
|
14
|
+
names: string[];
|
|
15
|
+
paths: string[];
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
};
|
|
18
|
+
model: {
|
|
19
|
+
requested?: string;
|
|
20
|
+
agentDefault?: string;
|
|
21
|
+
fallbacks: string[];
|
|
22
|
+
teamRole?: string;
|
|
23
|
+
step?: string;
|
|
24
|
+
};
|
|
25
|
+
inheritance: {
|
|
26
|
+
projectContext: boolean;
|
|
27
|
+
skills: boolean;
|
|
28
|
+
systemPromptMode: "replace" | "append";
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface BuildWorkerCapabilityInventoryInput {
|
|
33
|
+
taskId: string;
|
|
34
|
+
role: string;
|
|
35
|
+
agent: AgentConfig;
|
|
36
|
+
runtime: CrewRuntimeKind;
|
|
37
|
+
permissionMode: string;
|
|
38
|
+
skillNames?: string[];
|
|
39
|
+
skillPaths?: string[];
|
|
40
|
+
skillsDisabled: boolean;
|
|
41
|
+
modelOverride?: string;
|
|
42
|
+
teamRoleModel?: string;
|
|
43
|
+
stepModel?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function uniqueSorted(values: readonly string[] | undefined): string[] {
|
|
47
|
+
return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildWorkerCapabilityInventory(input: BuildWorkerCapabilityInventoryInput): WorkerCapabilityInventory {
|
|
51
|
+
return {
|
|
52
|
+
schemaVersion: 1,
|
|
53
|
+
taskId: input.taskId,
|
|
54
|
+
role: input.role,
|
|
55
|
+
agent: input.agent.name,
|
|
56
|
+
runtime: input.runtime,
|
|
57
|
+
permissionMode: input.permissionMode,
|
|
58
|
+
tools: uniqueSorted(input.agent.tools),
|
|
59
|
+
extensions: uniqueSorted(input.agent.extensions),
|
|
60
|
+
skills: {
|
|
61
|
+
names: uniqueSorted(input.skillNames),
|
|
62
|
+
paths: uniqueSorted(input.skillPaths),
|
|
63
|
+
disabled: input.skillsDisabled,
|
|
64
|
+
},
|
|
65
|
+
model: {
|
|
66
|
+
requested: input.modelOverride,
|
|
67
|
+
agentDefault: input.agent.model,
|
|
68
|
+
fallbacks: uniqueSorted(input.agent.fallbackModels),
|
|
69
|
+
teamRole: input.teamRoleModel,
|
|
70
|
+
step: input.stepModel,
|
|
71
|
+
},
|
|
72
|
+
inheritance: {
|
|
73
|
+
projectContext: input.agent.inheritProjectContext === true,
|
|
74
|
+
skills: input.agent.inheritSkills === true,
|
|
75
|
+
systemPromptMode: input.agent.systemPromptMode ?? "replace",
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -1,101 +1,107 @@
|
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
let
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
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
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
}
|
|
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
|
+
modelOverride?: string;
|
|
28
|
+
teamRoleModel?: string;
|
|
29
|
+
isCurrent?: () => boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RunLiveTaskOutput {
|
|
33
|
+
task: TeamTaskState;
|
|
34
|
+
tasks: TeamTaskState[];
|
|
35
|
+
startupEvidence: WorkerStartupEvidence;
|
|
36
|
+
exitCode: number | null;
|
|
37
|
+
error?: string;
|
|
38
|
+
parsedOutput?: ParsedPiJsonOutput;
|
|
39
|
+
resultArtifact: ArtifactDescriptor;
|
|
40
|
+
logArtifact?: ArtifactDescriptor;
|
|
41
|
+
transcriptArtifact?: ArtifactDescriptor;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function updateTask(tasks: TeamTaskState[], updated: TeamTaskState): TeamTaskState[] {
|
|
45
|
+
return tasks.map((task) => task.id === updated.id ? updated : task);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runLiveTask(input: RunLiveTaskInput): Promise<RunLiveTaskOutput> {
|
|
49
|
+
const { manifest, step, agent, prompt } = input;
|
|
50
|
+
let task = input.task;
|
|
51
|
+
let tasks = input.tasks;
|
|
52
|
+
const transcriptPath = `${manifest.artifactsRoot}/transcripts/${task.id}.jsonl`;
|
|
53
|
+
let lastAgentRecordPersistedAt = 0;
|
|
54
|
+
let lastRunProgressPersistedAt = 0;
|
|
55
|
+
let lastRunProgressSummary: ProgressEventSummary | undefined;
|
|
56
|
+
const persistLiveProgress = (event: unknown, force = false): void => {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if (force || shouldFlushProgressEvent(event) || now - lastAgentRecordPersistedAt >= 500) {
|
|
59
|
+
upsertCrewAgent(manifest, recordFromTask(manifest, task, "live-session"));
|
|
60
|
+
lastAgentRecordPersistedAt = now;
|
|
61
|
+
}
|
|
62
|
+
const summary = progressEventSummary(task, event);
|
|
63
|
+
const decision = shouldAppendProgressEventUpdate({ previous: lastRunProgressSummary, next: summary, nowMs: now, lastAppendMs: lastRunProgressPersistedAt || undefined, minIntervalMs: 1000, force });
|
|
64
|
+
if (decision.shouldAppend) {
|
|
65
|
+
appendEvent(manifest.eventsPath, { type: "task.progress", runId: manifest.runId, taskId: task.id, data: { ...summary, coalesceReason: decision.reason } });
|
|
66
|
+
lastRunProgressSummary = summary;
|
|
67
|
+
lastRunProgressPersistedAt = now;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const attemptStartedAt = new Date();
|
|
71
|
+
const isCurrent = input.isCurrent ?? (() => input.signal?.aborted !== true);
|
|
72
|
+
const liveResult = await runLiveSessionTask({
|
|
73
|
+
manifest,
|
|
74
|
+
task,
|
|
75
|
+
step,
|
|
76
|
+
agent,
|
|
77
|
+
prompt,
|
|
78
|
+
signal: input.signal,
|
|
79
|
+
transcriptPath,
|
|
80
|
+
runtimeConfig: input.runtimeConfig,
|
|
81
|
+
parentContext: input.parentContext,
|
|
82
|
+
parentModel: input.parentModel,
|
|
83
|
+
modelRegistry: input.modelRegistry,
|
|
84
|
+
modelOverride: input.modelOverride,
|
|
85
|
+
teamRoleModel: input.teamRoleModel,
|
|
86
|
+
isCurrent,
|
|
87
|
+
// Phase 2: Pass output schema for yield validation
|
|
88
|
+
outputSchema: undefined,
|
|
89
|
+
onOutput: (text) => appendCrewAgentOutput(manifest, task.id, text),
|
|
90
|
+
onEvent: (event) => {
|
|
91
|
+
appendCrewAgentEvent(manifest, task.id, event);
|
|
92
|
+
task = { ...task, agentProgress: applyAgentProgressEvent(task.agentProgress ?? emptyCrewAgentProgress(), event, task.startedAt) };
|
|
93
|
+
tasks = updateTask(tasks, task);
|
|
94
|
+
persistLiveProgress(event);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
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 });
|
|
98
|
+
const exitCode = liveResult.exitCode;
|
|
99
|
+
const error = liveResult.error || (liveResult.exitCode && liveResult.exitCode !== 0 ? liveResult.stderr || `Live session exited with ${liveResult.exitCode}` : undefined);
|
|
100
|
+
const parsedOutput = { finalText: liveResult.stdout, textEvents: liveResult.stdout ? [liveResult.stdout] : [], jsonEvents: liveResult.jsonEvents, usage: liveResult.usage };
|
|
101
|
+
if (liveResult.usage) task = { ...task, usage: liveResult.usage, agentProgress: applyUsageToProgress(task.agentProgress, liveResult.usage) };
|
|
102
|
+
persistLiveProgress({ type: "attempt_finished" }, true);
|
|
103
|
+
const resultArtifact = writeArtifact(manifest.artifactsRoot, { kind: "result", relativePath: `results/${task.id}.txt`, content: liveResult.stdout || liveResult.stderr || "(no output)", producer: task.id });
|
|
104
|
+
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 });
|
|
105
|
+
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;
|
|
106
|
+
return { task, tasks, startupEvidence, exitCode, error: error || undefined, parsedOutput, resultArtifact, logArtifact, transcriptArtifact };
|
|
107
|
+
}
|