pi-crew 0.5.2 → 0.5.6
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 +183 -0
- package/README.md +17 -1
- package/docs/architecture.md +2 -0
- package/docs/bugs/cross-session-notification-leakage.md +82 -0
- package/docs/coding-agent-optimization.md +268 -0
- package/docs/deep-review-report.md +384 -0
- package/docs/distillation/cybersecurity-patterns.md +294 -0
- package/docs/migration-v0.4-v0.5.md +208 -0
- package/docs/optimization-plan.md +642 -0
- package/docs/pi-crew-v0.5.5-audit-fix-plan.md +133 -0
- package/docs/pi-mono-opportunities.md +969 -0
- package/docs/pi-mono-review.md +291 -0
- package/docs/skills/REFERENCE.md +144 -0
- package/package.json +12 -9
- package/skills/artifact-analysis-loop/SKILL.md +302 -0
- package/skills/async-worker-recovery/SKILL.md +19 -1
- package/skills/child-pi-spawning/SKILL.md +19 -6
- package/skills/context-artifact-hygiene/SKILL.md +19 -2
- package/skills/delegation-patterns/SKILL.md +68 -3
- package/skills/detection-pipeline-design/SKILL.md +285 -0
- package/skills/event-log-tracing/SKILL.md +20 -6
- package/skills/git-master/SKILL.md +20 -6
- package/skills/hunting-investigation-loop/SKILL.md +401 -0
- package/skills/incident-playbook-construction/SKILL.md +383 -0
- package/skills/live-agent-lifecycle/SKILL.md +20 -6
- package/skills/mailbox-interactive/SKILL.md +19 -6
- package/skills/model-routing-context/SKILL.md +19 -1
- package/skills/multi-perspective-review/SKILL.md +19 -4
- package/skills/observability-reliability/SKILL.md +19 -2
- package/skills/orchestration/SKILL.md +20 -2
- package/skills/ownership-session-security/SKILL.md +20 -2
- package/skills/pi-extension-lifecycle/SKILL.md +20 -2
- package/skills/post-mortem/SKILL.md +7 -2
- package/skills/read-only-explorer/SKILL.md +20 -6
- package/skills/requirements-to-task-packet/SKILL.md +23 -3
- package/skills/resource-discovery-config/SKILL.md +20 -2
- package/skills/runtime-state-reader/SKILL.md +20 -2
- package/skills/safe-bash/SKILL.md +21 -6
- package/skills/scrutinize/SKILL.md +20 -2
- package/skills/secure-agent-orchestration-review/SKILL.md +29 -2
- package/skills/security-review/SKILL.md +560 -0
- package/skills/state-mutation-locking/SKILL.md +22 -2
- package/skills/systematic-debugging/SKILL.md +8 -6
- package/skills/threat-hypothesis-framework/SKILL.md +175 -0
- package/skills/ui-render-performance/SKILL.md +20 -2
- package/skills/verification-before-done/SKILL.md +17 -2
- package/skills/widget-rendering/SKILL.md +21 -6
- package/skills/workspace-isolation/SKILL.md +20 -6
- package/skills/worktree-isolation/SKILL.md +20 -6
- package/src/agents/agent-config.ts +40 -1
- package/src/benchmark/benchmark-runner.ts +45 -0
- package/src/benchmark/feedback-loop.ts +5 -0
- package/src/config/config.ts +32 -5
- package/src/config/role-tools.ts +82 -0
- package/src/config/suggestions.ts +8 -0
- package/src/config/types.ts +4 -0
- package/src/extension/async-notifier.ts +10 -1
- package/src/extension/crew-cleanup.ts +114 -0
- package/src/extension/cross-extension-rpc.ts +1 -1
- package/src/extension/notification-router.ts +18 -0
- package/src/extension/register.ts +27 -19
- package/src/extension/registration/subagent-tools.ts +1 -1
- package/src/extension/team-tool/anchor.ts +201 -0
- package/src/extension/team-tool/api.ts +2 -1
- package/src/extension/team-tool/auto-summarize.ts +154 -0
- package/src/extension/team-tool/run.ts +42 -7
- package/src/extension/team-tool.ts +44 -2
- package/src/hooks/registry.ts +1 -3
- package/src/observability/event-bus.ts +69 -0
- package/src/observability/event-to-metric.ts +0 -2
- package/src/runtime/anchor-manager.ts +473 -0
- package/src/runtime/async-runner.ts +8 -4
- package/src/runtime/auto-summarize.ts +350 -0
- package/src/runtime/background-runner.ts +10 -3
- package/src/runtime/budget-tracker.ts +354 -0
- package/src/runtime/chain-runner.ts +507 -0
- package/src/runtime/child-pi.ts +123 -35
- package/src/runtime/crash-recovery.ts +5 -4
- package/src/runtime/crew-agent-runtime.ts +1 -0
- package/src/runtime/custom-tools/irc-tool.ts +13 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +3 -2
- package/src/runtime/delivery-coordinator.ts +10 -3
- package/src/runtime/dynamic-script-runner.ts +482 -0
- package/src/runtime/foreground-control.ts +87 -17
- package/src/runtime/handoff-manager.ts +589 -0
- package/src/runtime/hidden-handoff.ts +424 -0
- package/src/runtime/live-agent-manager.ts +20 -4
- package/src/runtime/live-session-runtime.ts +39 -4
- package/src/runtime/manifest-cache.ts +2 -1
- package/src/runtime/model-resolver.ts +16 -4
- package/src/runtime/phase-tracker.ts +373 -0
- package/src/runtime/pi-args.ts +11 -1
- package/src/runtime/pi-json-output.ts +31 -0
- package/src/runtime/pipeline-runner.ts +514 -0
- package/src/runtime/progress-tracker.ts +124 -0
- package/src/runtime/retry-runner.ts +354 -0
- package/src/runtime/sandbox.ts +252 -0
- package/src/runtime/scheduler.ts +7 -2
- package/src/runtime/skill-effectiveness.ts +473 -0
- package/src/runtime/skill-instructions.ts +37 -3
- package/src/runtime/subagent-manager.ts +1 -1
- package/src/runtime/task-graph.ts +11 -1
- package/src/runtime/task-runner.ts +92 -18
- package/src/runtime/team-runner.ts +13 -12
- package/src/runtime/tool-progress.ts +10 -3
- package/src/runtime/verification-gates.ts +367 -0
- package/src/schema/team-tool-schema.ts +37 -0
- package/src/skills/discover-skills.ts +5 -0
- package/src/state/active-run-registry.ts +9 -2
- package/src/state/contracts.ts +9 -0
- package/src/state/crew-init.ts +3 -3
- package/src/state/decision-ledger.ts +98 -55
- package/src/state/event-log-rotation.ts +2 -2
- package/src/state/event-log.ts +144 -10
- package/src/state/hook-instinct-bridge.ts +5 -5
- package/src/state/mailbox.ts +10 -0
- package/src/state/run-cache.ts +18 -8
- package/src/state/state-store.ts +3 -1
- package/src/state/types.ts +4 -0
- package/src/tools/safe-bash-extension.ts +1 -0
- package/src/tools/safe-bash.ts +152 -20
- package/src/types/new-api-types.ts +34 -0
- package/src/ui/agent-management-overlay.ts +5 -1
- package/src/ui/crew-widget.ts +29 -15
- package/src/ui/overlays/mailbox-detail-overlay.ts +13 -2
- package/src/ui/powerbar-publisher.ts +101 -7
- package/src/ui/tool-render.ts +15 -15
- package/src/ui/transcript-cache.ts +13 -0
- package/src/utils/bm25-search.ts +16 -8
- package/src/utils/env-filter.ts +8 -5
- package/src/utils/redaction.ts +169 -15
- package/src/utils/session-utils.ts +52 -0
- package/src/utils/sse-parser.ts +10 -1
- package/src/worktree/cleanup.ts +6 -1
- package/src/worktree/worktree-manager.ts +32 -13
- package/workflows/chain.workflow.md +252 -0
- package/workflows/pipeline.workflow.md +27 -0
|
@@ -7,10 +7,11 @@ import type {
|
|
|
7
7
|
TeamRunManifest,
|
|
8
8
|
TeamTaskState,
|
|
9
9
|
UsageState,
|
|
10
|
+
VerificationEvidence,
|
|
10
11
|
} from "../state/types.ts";
|
|
11
12
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
12
13
|
import { writeArtifact } from "../state/artifact-store.ts";
|
|
13
|
-
import { appendEvent, appendEventFireAndForget } from "../state/event-log.ts";
|
|
14
|
+
import { appendEvent, appendEventAsync, appendEventFireAndForget } from "../state/event-log.ts";
|
|
14
15
|
import { saveRunManifest } from "../state/state-store.ts";
|
|
15
16
|
import { createTaskClaim } from "../state/task-claims.ts";
|
|
16
17
|
import {
|
|
@@ -38,6 +39,7 @@ import { runChildPi, type ChildPiLifecycleEvent } from "./child-pi.ts";
|
|
|
38
39
|
import { buildTaskPacket } from "./task-packet.ts";
|
|
39
40
|
import { executeHook, appendHookEvent } from "../hooks/registry.ts";
|
|
40
41
|
import { createVerificationEvidence } from "./green-contract.ts";
|
|
42
|
+
import { executeVerificationCommands, computeGreenLevelFromResults } from "./verification-gates.ts";
|
|
41
43
|
import { createStartupEvidence } from "./worker-startup.ts";
|
|
42
44
|
import { permissionForRole } from "./role-permission.ts";
|
|
43
45
|
import { crewHooks } from "./crew-hooks.ts";
|
|
@@ -212,7 +214,7 @@ export async function runTeamTask(
|
|
|
212
214
|
"started",
|
|
213
215
|
));
|
|
214
216
|
upsertCrewAgent(manifest, recordFromTask(manifest, task, runtimeKind));
|
|
215
|
-
|
|
217
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
216
218
|
type: "task.started",
|
|
217
219
|
runId: manifest.runId,
|
|
218
220
|
taskId: task.id,
|
|
@@ -244,6 +246,7 @@ export async function runTeamTask(
|
|
|
244
246
|
teamRole: { skills: input.teamRoleSkills },
|
|
245
247
|
step: input.step,
|
|
246
248
|
override: input.skillOverride,
|
|
249
|
+
runId: manifest.runId, // ECC INSTINCT: Enable skill confidence tracking
|
|
247
250
|
})
|
|
248
251
|
: undefined;
|
|
249
252
|
const skillBlock = input.skillBlock ?? renderedSkills?.block;
|
|
@@ -420,6 +423,11 @@ export async function runTeamTask(
|
|
|
420
423
|
graceTurns: input.runtimeConfig?.graceTurns,
|
|
421
424
|
inheritContext: input.runtimeConfig?.inheritContext,
|
|
422
425
|
parentContext: input.parentContext,
|
|
426
|
+
excludeContextBash: input.runtimeConfig?.excludeContextBash,
|
|
427
|
+
sessionId: manifest.sessionId,
|
|
428
|
+
role: task.role,
|
|
429
|
+
runId: manifest.runId,
|
|
430
|
+
agentId: task.id,
|
|
423
431
|
onSpawn: (pid) => {
|
|
424
432
|
try {
|
|
425
433
|
({ task, tasks } = checkpointTask(
|
|
@@ -444,13 +452,13 @@ export async function runTeamTask(
|
|
|
444
452
|
}
|
|
445
453
|
},
|
|
446
454
|
onLifecycleEvent: (event: ChildPiLifecycleEvent) => {
|
|
447
|
-
|
|
455
|
+
void appendEventAsync(manifest.eventsPath, {
|
|
448
456
|
type: `worker.${event.type}` as const,
|
|
449
457
|
runId: manifest.runId,
|
|
450
458
|
taskId: task.id,
|
|
451
459
|
message: `Worker lifecycle: ${event.type}${event.error ? ` error=${event.error}` : ""}${event.exitCode != null ? ` exit=${event.exitCode}` : ""}`,
|
|
452
460
|
data: { ...event },
|
|
453
|
-
});
|
|
461
|
+
}).catch((error) => logInternalError("task-runner.lifecycle-event", error, `taskId=${task.id}, type=${event.type}`));
|
|
454
462
|
},
|
|
455
463
|
onStdoutLine: (line) => {
|
|
456
464
|
appendCrewAgentOutput(manifest, task.id, line);
|
|
@@ -590,7 +598,7 @@ export async function runTeamTask(
|
|
|
590
598
|
attemptStartedAt.toISOString(),
|
|
591
599
|
),
|
|
592
600
|
);
|
|
593
|
-
|
|
601
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
594
602
|
type: "worker.cancelled",
|
|
595
603
|
runId: manifest.runId,
|
|
596
604
|
taskId: task.id,
|
|
@@ -863,7 +871,7 @@ export async function runTeamTask(
|
|
|
863
871
|
}
|
|
864
872
|
} else if (!error) {
|
|
865
873
|
noYield = true;
|
|
866
|
-
|
|
874
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
867
875
|
type: "task.needs_attention",
|
|
868
876
|
runId: manifest.runId,
|
|
869
877
|
taskId: task.id,
|
|
@@ -896,6 +904,16 @@ export async function runTeamTask(
|
|
|
896
904
|
})
|
|
897
905
|
: undefined;
|
|
898
906
|
|
|
907
|
+
// Capture unified patches from edit tool results
|
|
908
|
+
const patchArtifact = parsedOutput?.patches?.length
|
|
909
|
+
? writeArtifact(manifest.artifactsRoot, {
|
|
910
|
+
kind: "patch",
|
|
911
|
+
relativePath: `patches/${task.id}.patch`,
|
|
912
|
+
content: parsedOutput.patches.join("\n---\n"),
|
|
913
|
+
producer: task.id,
|
|
914
|
+
})
|
|
915
|
+
: undefined;
|
|
916
|
+
|
|
899
917
|
const mutationGuardMode =
|
|
900
918
|
input.runtimeConfig?.completionMutationGuard ?? "warn";
|
|
901
919
|
const mutationGuard =
|
|
@@ -959,7 +977,7 @@ export async function runTeamTask(
|
|
|
959
977
|
if (outputText) {
|
|
960
978
|
outputValidation = validateWorkerOutput(task.role, outputText);
|
|
961
979
|
if (!outputValidation.valid) {
|
|
962
|
-
|
|
980
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
963
981
|
type: "task.output_validation",
|
|
964
982
|
runId: manifest.runId,
|
|
965
983
|
taskId: task.id,
|
|
@@ -983,6 +1001,70 @@ export async function runTeamTask(
|
|
|
983
1001
|
}
|
|
984
1002
|
}
|
|
985
1003
|
|
|
1004
|
+
// --- ECC VERIFICATION_LOOP: Compute verification evidence before building task object ---
|
|
1005
|
+
// Compute verification evidence (may be async if verification commands need to run)
|
|
1006
|
+
const baseEvidence = createVerificationEvidence(
|
|
1007
|
+
taskPacket.verification,
|
|
1008
|
+
!error,
|
|
1009
|
+
error
|
|
1010
|
+
? `Task failed: ${error}`
|
|
1011
|
+
: runtimeKind === "scaffold"
|
|
1012
|
+
? "Safe scaffold mode; verification commands were not executed."
|
|
1013
|
+
: `${runtimeKind} worker finished without reporting a verification failure.`,
|
|
1014
|
+
);
|
|
1015
|
+
|
|
1016
|
+
// Only execute verification commands when:
|
|
1017
|
+
// 1. Task completed successfully (no error)
|
|
1018
|
+
// 2. Verification contract has commands
|
|
1019
|
+
// 3. Not in scaffold mode (scaffold mode intentionally skips execution)
|
|
1020
|
+
let verificationEvidence: VerificationEvidence = baseEvidence;
|
|
1021
|
+
if (!error && runtimeKind !== "scaffold" && taskPacket.verification?.commands?.length) {
|
|
1022
|
+
try {
|
|
1023
|
+
const commandResults = await executeVerificationCommands(
|
|
1024
|
+
taskPacket.verification,
|
|
1025
|
+
task.cwd,
|
|
1026
|
+
manifest.runId,
|
|
1027
|
+
task.id,
|
|
1028
|
+
manifest.artifactsRoot,
|
|
1029
|
+
input.signal,
|
|
1030
|
+
);
|
|
1031
|
+
|
|
1032
|
+
// Compute observed green level from results
|
|
1033
|
+
const observedGreenLevel = computeGreenLevelFromResults(
|
|
1034
|
+
commandResults,
|
|
1035
|
+
taskPacket.verification.requiredGreenLevel,
|
|
1036
|
+
);
|
|
1037
|
+
|
|
1038
|
+
// Determine satisfaction based on green level
|
|
1039
|
+
const requiredLevel = taskPacket.verification.requiredGreenLevel;
|
|
1040
|
+
const satisfied =
|
|
1041
|
+
observedGreenLevel === "none" ? false :
|
|
1042
|
+
observedGreenLevel === "targeted" ? requiredLevel === "targeted" :
|
|
1043
|
+
observedGreenLevel === "package" ? ["targeted", "package"].includes(requiredLevel) :
|
|
1044
|
+
observedGreenLevel === "workspace" ? ["targeted", "package", "workspace"].includes(requiredLevel) :
|
|
1045
|
+
observedGreenLevel === "merge_ready";
|
|
1046
|
+
|
|
1047
|
+
const allPassed = commandResults.every(r => r.status === "passed");
|
|
1048
|
+
const failedCount = commandResults.filter(r => r.status === "failed").length;
|
|
1049
|
+
|
|
1050
|
+
verificationEvidence = {
|
|
1051
|
+
requiredGreenLevel: taskPacket.verification.requiredGreenLevel,
|
|
1052
|
+
observedGreenLevel,
|
|
1053
|
+
satisfied: satisfied && allPassed,
|
|
1054
|
+
commands: commandResults,
|
|
1055
|
+
notes: allPassed
|
|
1056
|
+
? `${commandResults.length} verification commands passed`
|
|
1057
|
+
: `${failedCount}/${commandResults.length} verification commands failed`,
|
|
1058
|
+
};
|
|
1059
|
+
} catch (execError) {
|
|
1060
|
+
// On execution error, return base evidence with error note
|
|
1061
|
+
verificationEvidence = {
|
|
1062
|
+
...baseEvidence,
|
|
1063
|
+
notes: `Verification execution failed: ${execError instanceof Error ? execError.message : String(execError)}`,
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
986
1068
|
task = {
|
|
987
1069
|
...task,
|
|
988
1070
|
status: error ? "failed" : noYield ? "needs_attention" : "completed",
|
|
@@ -999,16 +1081,7 @@ export async function runTeamTask(
|
|
|
999
1081
|
}
|
|
1000
1082
|
: task.agentProgress,
|
|
1001
1083
|
error,
|
|
1002
|
-
verification:
|
|
1003
|
-
taskPacket.verification,
|
|
1004
|
-
!error,
|
|
1005
|
-
error
|
|
1006
|
-
? `Task failed: ${error}`
|
|
1007
|
-
: runtimeKind === "scaffold"
|
|
1008
|
-
? "Safe scaffold mode; verification commands were not executed."
|
|
1009
|
-
: `${runtimeKind} worker finished without reporting a verification failure.`,
|
|
1010
|
-
),
|
|
1011
|
-
promptArtifact,
|
|
1084
|
+
verification: verificationEvidence,
|
|
1012
1085
|
resultArtifact,
|
|
1013
1086
|
claim: undefined,
|
|
1014
1087
|
heartbeat: touchWorkerHeartbeat(
|
|
@@ -1105,6 +1178,7 @@ export async function runTeamTask(
|
|
|
1105
1178
|
...(transcriptArtifact ? [transcriptArtifact] : []),
|
|
1106
1179
|
...(diffArtifact ? [diffArtifact] : []),
|
|
1107
1180
|
...(diffStatArtifact ? [diffStatArtifact] : []),
|
|
1181
|
+
...(patchArtifact ? [patchArtifact] : []),
|
|
1108
1182
|
],
|
|
1109
1183
|
};
|
|
1110
1184
|
saveRunManifest(manifest);
|
|
@@ -1117,7 +1191,7 @@ export async function runTeamTask(
|
|
|
1117
1191
|
cwd: manifest.cwd,
|
|
1118
1192
|
});
|
|
1119
1193
|
appendHookEvent(manifest, hookReport);
|
|
1120
|
-
|
|
1194
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
1121
1195
|
type: error ? "task.failed" : noYield ? "task.needs_attention" : "task.completed",
|
|
1122
1196
|
runId: manifest.runId,
|
|
1123
1197
|
taskId: task.id,
|
|
@@ -6,7 +6,7 @@ import type { CrewRuntimeKind } from "./crew-agent-runtime.ts";
|
|
|
6
6
|
import { resolveTaskRuntimeKind } from "./runtime-policy.ts";
|
|
7
7
|
import { writeArtifact } from "../state/artifact-store.ts";
|
|
8
8
|
import { executeHook, appendHookEvent } from "../hooks/registry.ts";
|
|
9
|
-
import { appendEvent, appendEventFireAndForget } from "../state/event-log.ts";
|
|
9
|
+
import { appendEvent, appendEventAsync, appendEventFireAndForget } from "../state/event-log.ts";
|
|
10
10
|
import type { TeamConfig } from "../teams/team-config.ts";
|
|
11
11
|
import type { ArtifactDescriptor, PolicyDecision, TeamRunManifest, TaskAttemptState, TeamTaskState } from "../state/types.ts";
|
|
12
12
|
import { loadRunManifestById, saveRunManifest, saveRunManifestAsync, saveRunTasksAsync, updateRunStatus } from "../state/state-store.ts";
|
|
@@ -36,6 +36,7 @@ import { registerRunPromise, resolveRunPromise, rejectRunPromise } from "./run-t
|
|
|
36
36
|
import { clearTrackedTaskUsage } from "./usage-tracker.ts";
|
|
37
37
|
import { CrewCancellationError, buildSyntheticTerminalEvidence, cancellationReasonFromSignal } from "./cancellation.ts";
|
|
38
38
|
import { effectivenessPolicyDecision, evaluateRunEffectiveness, formatRunEffectivenessLines } from "./effectiveness.ts";
|
|
39
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
39
40
|
|
|
40
41
|
export interface ExecuteTeamRunInput {
|
|
41
42
|
manifest: TeamRunManifest;
|
|
@@ -279,7 +280,7 @@ export async function executeTeamRun(input: ExecuteTeamRunInput): Promise<{ mani
|
|
|
279
280
|
resolveRunPromise(manifest.runId, result);
|
|
280
281
|
cleanupUsage();
|
|
281
282
|
// Terminate live agents for this run — agents are done when the run ends.
|
|
282
|
-
void terminateLiveAgentsForRun(manifest.runId, "completed", appendEvent, manifest.eventsPath).catch(() => {});
|
|
283
|
+
void terminateLiveAgentsForRun(manifest.runId, "completed", appendEvent, manifest.eventsPath).catch((error) => logInternalError("team-runner.completed.terminate", error, `runId=${manifest.runId}`));
|
|
283
284
|
|
|
284
285
|
// Emit run completion hook (100% reliable, fire-and-forget)
|
|
285
286
|
crewHooks.emit({ type: "run_completed", timestamp: new Date().toISOString(), runId: manifest.runId, data: { status: result.manifest.status, taskCount: result.tasks.length } });
|
|
@@ -395,7 +396,7 @@ async function executeTeamRunCore(
|
|
|
395
396
|
return base;
|
|
396
397
|
});
|
|
397
398
|
await saveRunTasksAsync(manifest, tasks);
|
|
398
|
-
for (const taskId of cancelledTaskIds)
|
|
399
|
+
for (const taskId of cancelledTaskIds) await appendEventAsync(manifest.eventsPath, { type: "task.cancelled", runId: manifest.runId, taskId, message, data: { reason: cancelReason.code } });
|
|
399
400
|
manifest = updateRunStatus(manifest, "cancelled", message, { data: { reason: cancelReason.code, cancelledTaskIds } });
|
|
400
401
|
return { manifest, tasks };
|
|
401
402
|
}
|
|
@@ -429,7 +430,7 @@ async function executeTeamRunCore(
|
|
|
429
430
|
};
|
|
430
431
|
const preconditions = validatePhasePreconditions(wfMachine, wfContext);
|
|
431
432
|
if (!preconditions.ready) {
|
|
432
|
-
|
|
433
|
+
await appendEventAsync(manifest.eventsPath, { type: "workflow.preconditions", runId: manifest.runId, message: `Workflow phase '${wfMachine.phases[wfMachine.currentPhaseIndex]?.name}' is missing inputs: ${preconditions.blocking.join(", ")}`, data: { phaseIndex: wfMachine.currentPhaseIndex, phaseName: wfMachine.phases[wfMachine.currentPhaseIndex]?.name, blocking: preconditions.blocking } });
|
|
433
434
|
} else {
|
|
434
435
|
// Advance the machine past completed phases.
|
|
435
436
|
while (wfMachine.currentPhaseIndex < wfMachine.phases.length && wfMachine.phases[wfMachine.currentPhaseIndex]?.status === "completed") {
|
|
@@ -441,7 +442,7 @@ async function executeTeamRunCore(
|
|
|
441
442
|
const readyRoles = effectiveReady.map((taskId) => tasks.find((task) => task.id === taskId)?.role).filter((role): role is string => Boolean(role));
|
|
442
443
|
const concurrency = resolveBatchConcurrency({ workflowName: workflow.name, workflowMaxConcurrency: workflow.maxConcurrency, teamMaxConcurrency: input.team.maxConcurrency, limitMaxConcurrentWorkers: input.limits?.maxConcurrentWorkers, allowUnboundedConcurrency: input.limits?.allowUnboundedConcurrency, readyCount: effectiveReady.length, workspaceMode: manifest.workspaceMode, readyRoles });
|
|
443
444
|
if (concurrency.reason.includes(";unbounded:")) {
|
|
444
|
-
|
|
445
|
+
await appendEventAsync(manifest.eventsPath, { type: "limits.unbounded", runId: manifest.runId, message: "Unbounded worker concurrency was explicitly enabled for this run.", data: { concurrencyReason: concurrency.reason, maxConcurrent: concurrency.maxConcurrent } });
|
|
445
446
|
}
|
|
446
447
|
const approvalPending = isPlanApprovalPending(manifest);
|
|
447
448
|
const readyIds = approvalPending ? effectiveReady : effectiveReady.slice(0, concurrency.selectedCount);
|
|
@@ -474,7 +475,7 @@ async function executeTeamRunCore(
|
|
|
474
475
|
}
|
|
475
476
|
const batchTasks = readyBatch.filter((task) => tasks.find((t) => t.id === task.id && t.status !== "skipped"));
|
|
476
477
|
if (batchTasks.length > 1) {
|
|
477
|
-
|
|
478
|
+
await appendEventAsync(manifest.eventsPath, { type: "task.parallel_start", runId: manifest.runId, message: `Launching ${batchTasks.length} tasks in PARALLEL (concurrency=${concurrency.selectedCount}): ${batchTasks.map((t) => `${t.role}(${t.id})`).join(", ")}`, data: { taskIds: batchTasks.map((t) => t.id), roles: batchTasks.map((t) => t.role), concurrency: concurrency.selectedCount } });
|
|
478
479
|
}
|
|
479
480
|
const results = await mapConcurrent(
|
|
480
481
|
batchTasks,
|
|
@@ -519,7 +520,7 @@ async function executeTeamRunCore(
|
|
|
519
520
|
attemptId: (attempt) => `${manifest.runId}:${task.id}:attempt-${attempt}`,
|
|
520
521
|
onAttemptFailed: (attempt, error, delayMs, info) => {
|
|
521
522
|
lastAttemptId = info.attemptId;
|
|
522
|
-
|
|
523
|
+
appendEventAsync(manifest.eventsPath, { type: "crew.task.retry_attempt", runId: manifest.runId, taskId: task.id, message: error.message, data: { attempt, attemptId: info.attemptId, delayMs }, metadata: { attemptId: info.attemptId } }).catch((error) => logInternalError("team-runner.retry-attempt", error, `taskId=${task.id}`));
|
|
523
524
|
input.metricRegistry?.histogram("crew.task.retry_delay_ms", "Retry backoff delay, milliseconds").observe({ runId: manifest.runId, taskId: task.id }, delayMs);
|
|
524
525
|
},
|
|
525
526
|
onRetryGivenUp: (attempts, error, info) => {
|
|
@@ -536,7 +537,7 @@ async function executeTeamRunCore(
|
|
|
536
537
|
const freshManifest = fresh?.manifest ?? manifest;
|
|
537
538
|
const freshTasks = fresh?.tasks ?? tasks;
|
|
538
539
|
const cancelledTasks = freshTasks.map((item) => item.id === task.id && (item.status === "queued" || item.status === "running") ? { ...item, status: "cancelled" as const, finishedAt: new Date().toISOString(), error: `${reason.message} (${reason.code})` } : item);
|
|
539
|
-
|
|
540
|
+
appendEventAsync(freshManifest.eventsPath, { type: "task.cancelled", runId: freshManifest.runId, taskId: task.id, message: reason.message, data: { reason, phase: "retry" }, metadata: lastAttemptId ? { attemptId: lastAttemptId } : undefined }).catch((error) => logInternalError("team-runner.cancelled", error, `taskId=${task.id}`));
|
|
540
541
|
return { manifest: updateRunStatus(freshManifest, "cancelled", reason.message), tasks: cancelledTasks };
|
|
541
542
|
}
|
|
542
543
|
if (lastFailed) return lastFailed;
|
|
@@ -586,10 +587,10 @@ async function executeTeamRunCore(
|
|
|
586
587
|
const transition = transitionPhase(wfMachine, pi, phaseStatus, wfContext);
|
|
587
588
|
wfMachine = transition.machine;
|
|
588
589
|
if (transition.guardResult && !transition.guardResult.allowed) {
|
|
589
|
-
|
|
590
|
+
await appendEventAsync(manifest.eventsPath, { type: "workflow.phase_guard_blocked", runId: manifest.runId, message: `Workflow phase '${phase.name}' guard blocked: ${transition.guardResult.reason ?? "unknown"}`, data: { phaseIndex: pi, phaseName: phase.name, reason: transition.guardResult.reason } });
|
|
590
591
|
break;
|
|
591
592
|
}
|
|
592
|
-
|
|
593
|
+
await appendEventAsync(manifest.eventsPath, { type: phaseStatus === "failed" ? "workflow.phase_failed" : "workflow.phase_completed", runId: manifest.runId, message: `Workflow phase '${phase.name}' ${phaseStatus}.`, data: { phaseIndex: pi, phaseStatus } });
|
|
593
594
|
}
|
|
594
595
|
wfMachine = { ...wfMachine, currentPhaseIndex: pi + 1 };
|
|
595
596
|
}
|
|
@@ -603,7 +604,7 @@ async function executeTeamRunCore(
|
|
|
603
604
|
await saveRunTasksAsync(manifest, tasks);
|
|
604
605
|
saveCrewAgents(manifest, recordsForMaterializedTasks(manifest, tasks, runtimeKind));
|
|
605
606
|
await saveRunManifestAsync(manifest);
|
|
606
|
-
|
|
607
|
+
await appendEventAsync(manifest.eventsPath, { type: "run.cancelled", runId: manifest.runId, message, data: { reason, phase: "task-batch", cancelledResultRunId: cancelledResult?.manifest.runId } });
|
|
607
608
|
return { manifest, tasks };
|
|
608
609
|
}
|
|
609
610
|
queueIndex = buildTaskGraphIndex(tasks);
|
|
@@ -651,7 +652,7 @@ async function executeTeamRunCore(
|
|
|
651
652
|
const effectivenessDecision = effectivenessPolicyDecision(effectiveness);
|
|
652
653
|
if (effectivenessDecision) {
|
|
653
654
|
manifest = { ...manifest, policyDecisions: [...(manifest.policyDecisions ?? []), effectivenessDecision], updatedAt: new Date().toISOString() };
|
|
654
|
-
|
|
655
|
+
await appendEventAsync(manifest.eventsPath, { type: "run.effectiveness", runId: manifest.runId, message: effectivenessDecision.message, data: { effectiveness, policyDecision: effectivenessDecision } });
|
|
655
656
|
}
|
|
656
657
|
const blockingDecision = manifest.policyDecisions?.find((item) => item.action === "block" || item.action === "escalate");
|
|
657
658
|
if (failed) {
|
|
@@ -150,12 +150,18 @@ export interface ToolProgressDisplay {
|
|
|
150
150
|
* Format tool progress for display
|
|
151
151
|
*/
|
|
152
152
|
export function formatToolProgress(progress: CrewAgentProgress, maxContextTokens = 128000): ToolProgressDisplay {
|
|
153
|
-
const recentTools
|
|
153
|
+
const recentTools: Array<{
|
|
154
|
+
tool: string;
|
|
155
|
+
args?: string;
|
|
156
|
+
startedAt?: string;
|
|
157
|
+
endedAt?: string;
|
|
158
|
+
status: "running" | "done" | "error";
|
|
159
|
+
}> = progress.recentTools.map((t) => ({
|
|
154
160
|
tool: t.tool,
|
|
155
161
|
args: t.args,
|
|
156
162
|
startedAt: t.startedAt,
|
|
157
163
|
endedAt: t.endedAt,
|
|
158
|
-
status: t.endedAt ? "done" : "running" as const,
|
|
164
|
+
status: t.endedAt ? ("done" as const) : ("running" as const),
|
|
159
165
|
}));
|
|
160
166
|
|
|
161
167
|
// If there's a currentTool but no endedAt, it's still running
|
|
@@ -167,7 +173,8 @@ export function formatToolProgress(progress: CrewAgentProgress, maxContextTokens
|
|
|
167
173
|
tool: progress.currentTool,
|
|
168
174
|
args: progress.currentToolArgs,
|
|
169
175
|
startedAt: progress.currentToolStartedAt,
|
|
170
|
-
|
|
176
|
+
endedAt: undefined as string | undefined,
|
|
177
|
+
status: "running" as const,
|
|
171
178
|
});
|
|
172
179
|
}
|
|
173
180
|
|