pi-crew 0.2.2 → 0.2.4
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 +57 -32
- package/CHANGELOG.md +466 -413
- package/LICENSE +21 -21
- package/NOTICE.md +16 -16
- package/README.md +323 -323
- package/docs/FEATURE_INTAKE.md +126 -0
- package/docs/HARNESS.md +86 -0
- package/docs/HARNESS_BACKLOG.md +41 -0
- package/docs/TEST_MATRIX.md +49 -0
- package/docs/actions-reference.md +595 -595
- package/docs/architecture.md +180 -180
- package/docs/code-review-2026-05-11.md +592 -0
- package/docs/commands-reference.md +347 -347
- package/docs/comparison-pi-subagents-vs-pi-crew.md +303 -0
- package/docs/decisions/0001-durable-state.md +41 -0
- package/docs/decisions/0002-child-process-for-async.md +42 -0
- package/docs/decisions/0003-depth-guard.md +36 -0
- package/docs/decisions/0004-execfile-over-exec.md +34 -0
- package/docs/decisions/0005-no-parameter-properties.md +49 -0
- package/docs/decisions/0006-publish-bundled-esm.md +63 -0
- package/docs/decisions/0007-active-run-binary-index.md +54 -0
- package/docs/decisions/0008-child-pi-warm-pool.md +61 -0
- package/docs/decisions/README.md +23 -0
- package/docs/followup-plan-2026-05-12.md +463 -0
- package/docs/followup-review-2026-05-12.md +297 -0
- package/docs/followup-review-round3-2026-05-12.md +342 -0
- package/docs/followup-review-round4-2026-05-13.md +107 -0
- package/docs/implementation-plan-top3.md +333 -0
- package/docs/live-mailbox-runtime.md +36 -36
- package/docs/next-upgrade-roadmap.md +808 -808
- package/docs/oh-my-pi-research.md +509 -0
- package/docs/perf/baseline-2026-05.md +113 -0
- package/docs/perf/final-report-2026-05.md +206 -0
- package/docs/perf/sprint-1-report.md +71 -0
- package/docs/perf/sprint-2-report.md +81 -0
- package/docs/perf/sprint-2.5-report.md +53 -0
- package/docs/perf/sprint-3-report.md +36 -0
- package/docs/perf/sprint-4-report.md +47 -0
- package/docs/perf/sprint-5-report.md +51 -0
- package/docs/perf/sprint-6-report.md +94 -0
- package/docs/perf/sprint-7-report.md +74 -0
- package/docs/perf/upgrade-plan-2026-05.md +147 -0
- package/docs/pi-subagents3-deep-analysis.md +508 -0
- package/docs/product/README.md +31 -0
- package/docs/product/platform.md +27 -0
- package/docs/product/runtime-safety.md +37 -0
- package/docs/product/team-run.md +39 -0
- package/docs/product/team-tool.md +37 -0
- package/docs/publishing.md +65 -65
- package/docs/resource-formats.md +134 -134
- package/docs/runtime-analysis-child-vs-live.md +171 -0
- package/docs/runtime-flow.md +148 -148
- package/docs/runtime-migration-in-process-analysis.md +250 -0
- package/docs/stories/README.md +30 -0
- package/docs/stories/backlog.md +36 -0
- package/docs/templates/decision.md +27 -0
- package/docs/templates/story.md +44 -0
- package/docs/templates/validation-report.md +32 -0
- package/docs/usage.md +238 -238
- package/index.ts +7 -6
- package/install.mjs +65 -65
- package/package.json +107 -99
- package/schema.json +222 -222
- package/skills/child-pi-spawning/SKILL.md +213 -0
- package/skills/context-artifact-hygiene/SKILL.md +32 -0
- package/skills/event-log-tracing/SKILL.md +299 -0
- package/skills/git-master/SKILL.md +225 -24
- package/skills/live-agent-lifecycle/SKILL.md +192 -0
- package/skills/mailbox-interactive/SKILL.md +300 -19
- package/skills/model-routing-context/SKILL.md +94 -0
- package/skills/multi-perspective-review/SKILL.md +88 -0
- package/skills/read-only-explorer/SKILL.md +250 -26
- package/skills/safe-bash/SKILL.md +307 -21
- package/skills/verification-before-done/SKILL.md +11 -2
- package/skills/widget-rendering/SKILL.md +258 -0
- package/skills/workspace-isolation/SKILL.md +202 -0
- package/skills/worktree-isolation/SKILL.md +202 -18
- package/src/adapters/claude-adapter.ts +25 -25
- package/src/adapters/codex-adapter.ts +21 -21
- package/src/adapters/cursor-adapter.ts +17 -17
- package/src/adapters/export-util.ts +137 -137
- package/src/adapters/index.ts +15 -15
- package/src/adapters/registry.ts +18 -18
- package/src/adapters/types.ts +23 -23
- package/src/agents/agent-config.ts +38 -38
- package/src/agents/agent-serializer.ts +38 -38
- package/src/agents/discover-agents.ts +121 -118
- package/src/config/config.ts +740 -858
- package/src/config/defaults.ts +96 -96
- package/src/config/drift-detector.ts +211 -211
- package/src/config/markers.ts +327 -327
- package/src/config/resilient-parser.ts +109 -108
- package/src/config/suggestions.ts +74 -74
- package/src/config/types.ts +199 -0
- package/src/extension/async-notifier.ts +123 -89
- package/src/extension/autonomous-policy.ts +169 -169
- package/src/extension/cross-extension-rpc.ts +104 -103
- package/src/extension/help.ts +47 -47
- package/src/extension/import-index.ts +69 -69
- package/src/extension/management.ts +395 -382
- package/src/extension/notification-router.ts +116 -116
- package/src/extension/notification-sink.ts +51 -51
- package/src/extension/project-init.ts +168 -168
- package/src/extension/register.ts +859 -668
- package/src/extension/registration/artifact-cleanup.ts +15 -15
- package/src/extension/registration/command-utils.ts +54 -54
- package/src/extension/registration/commands.ts +559 -452
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +102 -102
- package/src/extension/registration/subagent-tools.ts +220 -158
- package/src/extension/registration/team-tool.ts +159 -98
- package/src/extension/registration/viewers.ts +29 -0
- package/src/extension/result-watcher.ts +128 -128
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-export.ts +73 -73
- package/src/extension/run-import.ts +84 -84
- package/src/extension/run-index.ts +94 -94
- package/src/extension/run-maintenance.ts +142 -142
- package/src/extension/session-summary.ts +8 -8
- package/src/extension/team-manager-command.ts +96 -95
- package/src/extension/team-recommendation.ts +188 -188
- package/src/extension/team-tool/api.ts +5 -2
- package/src/extension/team-tool/cancel.ts +224 -209
- package/src/extension/team-tool/config-patch.ts +36 -36
- package/src/extension/team-tool/context.ts +60 -60
- package/src/extension/team-tool/doctor.ts +242 -242
- package/src/extension/team-tool/handle-settings.ts +421 -195
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +139 -139
- package/src/extension/team-tool/parallel-dispatch.ts +156 -156
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +112 -111
- package/src/extension/team-tool/run.ts +246 -228
- package/src/extension/team-tool/status.ts +110 -110
- package/src/extension/team-tool-types.ts +13 -13
- package/src/extension/team-tool.ts +16 -4
- package/src/extension/tool-result.ts +16 -16
- package/src/extension/validate-resources.ts +77 -77
- package/src/hooks/registry.ts +61 -61
- package/src/hooks/types.ts +40 -40
- package/src/i18n.ts +184 -184
- package/src/observability/correlation.ts +35 -35
- package/src/observability/event-to-metric.ts +68 -68
- package/src/observability/exporters/adapter.ts +30 -30
- package/src/observability/exporters/otlp-exporter.ts +106 -92
- package/src/observability/exporters/prometheus-exporter.ts +54 -54
- package/src/observability/metric-registry.ts +87 -87
- package/src/observability/metric-retention.ts +54 -54
- package/src/observability/metric-sink.ts +81 -56
- package/src/observability/metrics-primitives.ts +167 -167
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/adaptive-plan.ts +338 -0
- package/src/runtime/agent-control.ts +169 -169
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/async-runner.ts +153 -79
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -100
- package/src/runtime/background-runner.ts +122 -88
- package/src/runtime/cancellation.ts +61 -61
- package/src/runtime/capability-inventory.ts +116 -116
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi.ts +541 -463
- package/src/runtime/code-summary.ts +247 -247
- package/src/runtime/compaction-summary.ts +271 -271
- package/src/runtime/concurrency.ts +58 -58
- package/src/runtime/crash-recovery.ts +317 -301
- package/src/runtime/crew-agent-records.ts +379 -281
- package/src/runtime/crew-agent-runtime.ts +60 -60
- package/src/runtime/cross-extension-rpc.ts +72 -0
- package/src/runtime/custom-tools/irc-tool.ts +201 -201
- package/src/runtime/custom-tools/submit-result-tool.ts +90 -90
- package/src/runtime/deadletter.ts +47 -47
- package/src/runtime/delivery-coordinator.ts +176 -176
- package/src/runtime/delta-conflict.ts +360 -360
- package/src/runtime/diagnostic-export.ts +102 -102
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/effectiveness.ts +82 -81
- package/src/runtime/errors/crew-errors.ts +166 -0
- package/src/runtime/event-stream-bridge.ts +92 -92
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +234 -106
- package/src/runtime/heartbeat-watcher.ts +145 -124
- package/src/runtime/iteration-hooks.ts +267 -264
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-agent-manager.ts +377 -179
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +676 -599
- package/src/runtime/loop-gates.ts +129 -129
- package/src/runtime/manifest-cache.ts +263 -263
- package/src/runtime/mcp-proxy.ts +113 -113
- package/src/runtime/metric-parser.ts +40 -40
- package/src/runtime/model-fallback.ts +282 -274
- package/src/runtime/model-resolver.ts +118 -0
- package/src/runtime/output-validator.ts +187 -187
- package/src/runtime/overflow-recovery.ts +175 -175
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +156 -156
- package/src/runtime/parent-guard.ts +80 -80
- package/src/runtime/phase-progress.ts +217 -217
- package/src/runtime/pi-args.ts +165 -165
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +167 -167
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +125 -122
- package/src/runtime/post-exit-stdio-guard.ts +86 -86
- package/src/runtime/process-status.ts +97 -73
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +81 -81
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/run-tracker.ts +99 -0
- package/src/runtime/runtime-policy.ts +21 -0
- package/src/runtime/runtime-resolver.ts +94 -90
- package/src/runtime/scheduler.ts +294 -0
- package/src/runtime/semaphore.ts +131 -131
- package/src/runtime/sensitive-paths.ts +92 -92
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/settings-store.ts +103 -0
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/skill-instructions.ts +222 -222
- package/src/runtime/stale-reconciler.ts +198 -189
- package/src/runtime/streaming-output.ts +47 -0
- package/src/runtime/subagent-manager.ts +404 -395
- package/src/runtime/subprocess-tool-registry.ts +67 -67
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-graph-scheduler.ts +122 -122
- package/src/runtime/task-graph.ts +207 -207
- package/src/runtime/task-output-context.ts +177 -177
- package/src/runtime/task-packet.ts +93 -93
- package/src/runtime/task-quality.ts +207 -207
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +131 -113
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +139 -139
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/run-projection.ts +103 -103
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +469 -458
- package/src/runtime/team-runner.ts +693 -945
- package/src/runtime/usage-tracker.ts +71 -0
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workflow-state.ts +187 -187
- package/src/runtime/yield-handler.ts +190 -189
- package/src/schema/config-schema.ts +172 -168
- package/src/schema/team-tool-schema.ts +126 -125
- package/src/schema/validation-types.ts +151 -148
- package/src/skills/discover-skills.ts +67 -67
- package/src/skills/skill-templates.ts +374 -374
- package/src/state/active-run-registry.ts +227 -191
- package/src/state/artifact-store.ts +130 -129
- package/src/state/atomic-write.ts +262 -178
- package/src/state/blob-store.ts +116 -116
- package/src/state/contracts.ts +111 -111
- package/src/state/event-log-rotation.ts +161 -158
- package/src/state/event-log.ts +383 -240
- package/src/state/event-reconstructor.ts +217 -217
- package/src/state/jsonl-writer.ts +82 -82
- package/src/state/locks.ts +146 -148
- package/src/state/mailbox.ts +446 -405
- package/src/state/state-store.ts +364 -351
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +285 -285
- 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 +116 -116
- package/src/teams/team-config.ts +27 -27
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/agent-management-overlay.ts +144 -144
- package/src/ui/crew-widget.ts +487 -370
- package/src/ui/dashboard-panes/agents-pane.ts +109 -28
- package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
- package/src/ui/dashboard-panes/capability-pane.ts +59 -59
- package/src/ui/dashboard-panes/health-pane.ts +30 -30
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
- package/src/ui/dashboard-panes/progress-pane.ts +30 -30
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -10
- package/src/ui/heartbeat-aggregator.ts +63 -63
- package/src/ui/keybinding-map.ts +97 -94
- package/src/ui/live-conversation-overlay.ts +152 -0
- package/src/ui/live-run-sidebar.ts +180 -180
- package/src/ui/mascot.ts +442 -442
- package/src/ui/overlays/agent-picker-overlay.ts +57 -57
- package/src/ui/overlays/confirm-overlay.ts +58 -58
- package/src/ui/overlays/mailbox-compose-overlay.ts +144 -144
- package/src/ui/overlays/mailbox-compose-preview.ts +63 -63
- package/src/ui/overlays/mailbox-detail-overlay.ts +122 -122
- package/src/ui/pi-ui-compat.ts +57 -57
- package/src/ui/powerbar-publisher.ts +221 -197
- package/src/ui/render-scheduler.ts +216 -143
- package/src/ui/run-action-dispatcher.ts +118 -117
- package/src/ui/run-dashboard.ts +526 -464
- package/src/ui/run-event-bus.ts +208 -208
- package/src/ui/run-snapshot-cache.ts +826 -777
- package/src/ui/settings-overlay.ts +721 -0
- package/src/ui/snapshot-types.ts +86 -70
- package/src/ui/theme-adapter.ts +190 -190
- package/src/ui/tool-progress-formatter.ts +89 -0
- package/src/ui/transcript-cache.ts +94 -94
- package/src/ui/transcript-viewer.ts +335 -335
- package/src/utils/conflict-detect.ts +662 -0
- package/src/utils/env-filter.ts +30 -0
- package/src/utils/file-coalescer.ts +86 -86
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/fs-watch.ts +88 -31
- package/src/utils/gh-protocol.ts +479 -0
- package/src/utils/ids.ts +17 -17
- package/src/utils/incremental-reader.ts +104 -104
- package/src/utils/internal-error.ts +6 -6
- package/src/utils/names.ts +27 -27
- package/src/utils/paths.ts +102 -63
- package/src/utils/redaction.ts +44 -44
- package/src/utils/resolve-shell.ts +34 -0
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +136 -136
- package/src/utils/sleep.ts +2 -1
- package/src/utils/sse-parser.ts +134 -134
- package/src/utils/task-name-generator.ts +337 -337
- package/src/utils/timings.ts +33 -33
- package/src/utils/visual.ts +243 -198
- package/src/workflows/discover-workflows.ts +139 -139
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/workflows/workflow-config.ts +26 -26
- package/src/workflows/workflow-serializer.ts +32 -32
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/cleanup.ts +75 -72
- package/src/worktree/worktree-manager.ts +188 -146
- 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/tsconfig.json +19 -19
- package/workflows/default.workflow.md +30 -30
- package/workflows/fast-fix.workflow.md +23 -23
- package/workflows/implementation.workflow.md +43 -43
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
- package/skills/task-packet/SKILL.md +0 -28
- package/skills/verify-evidence/SKILL.md +0 -27
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
import { loadConfig } from "../../config/config.ts";
|
|
2
|
-
import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
|
|
3
|
-
import { appendEvent, readEvents } from "../../state/event-log.ts";
|
|
4
|
-
import { readDeliveryState, readMailbox } from "../../state/mailbox.ts";
|
|
5
|
-
import { loadRunManifestById, updateRunStatus, saveRunTasks } from "../../state/state-store.ts";
|
|
6
|
-
import { aggregateUsage, formatUsage } from "../../state/usage.ts";
|
|
7
|
-
import { applyAttentionState, formatActivityAge, resolveCrewControlConfig } from "../../runtime/agent-control.ts";
|
|
8
|
-
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
9
|
-
import { checkProcessLiveness, isActiveRunStatus } from "../../runtime/process-status.ts";
|
|
10
|
-
import { formatTaskGraphLines, waitingReason } from "../../runtime/task-display.ts";
|
|
11
|
-
import { verifyTaskCompletion, formatOutputPreview } from "../../runtime/completion-guard.ts";
|
|
12
|
-
import { evaluateRunEffectiveness } from "../../runtime/effectiveness.ts";
|
|
13
|
-
import type { PiTeamsToolResult } from "../tool-result.ts";
|
|
14
|
-
import { result, type TeamContext } from "./context.ts";
|
|
15
|
-
|
|
16
|
-
export function handleStatus(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
17
|
-
if (!params.runId) return result("Status requires runId.", { action: "status", status: "error" }, true);
|
|
18
|
-
const loaded = loadRunManifestById(ctx.cwd, params.runId);
|
|
19
|
-
if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "status", status: "error" }, true);
|
|
20
|
-
let { manifest, tasks } = loaded;
|
|
21
|
-
let asyncLivenessLine: string | undefined;
|
|
22
|
-
if (manifest.async) {
|
|
23
|
-
const asyncState = manifest.async;
|
|
24
|
-
const liveness = checkProcessLiveness(asyncState.pid);
|
|
25
|
-
asyncLivenessLine = `Async: pid=${asyncState.pid ?? "unknown"} alive=${liveness.alive ? "true" : "false"} detail=${liveness.detail} log=${asyncState.logPath} spawnedAt=${asyncState.spawnedAt}`;
|
|
26
|
-
if (!liveness.alive && isActiveRunStatus(manifest.status)) {
|
|
27
|
-
manifest = updateRunStatus(manifest, "failed", `Async process stale: ${liveness.detail}`);
|
|
28
|
-
tasks = tasks.map((task) => task.status === "running" ? { ...task, status: "cancelled" as const, finishedAt: new Date().toISOString(), error: "Async process died; task was not completed." } : task);
|
|
29
|
-
saveRunTasks(manifest, tasks);
|
|
30
|
-
appendEvent(manifest.eventsPath, { type: "async.stale", runId: manifest.runId, message: liveness.detail, data: { pid: asyncState.pid } });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const counts = new Map<string, number>();
|
|
34
|
-
for (const task of tasks) counts.set(task.status, (counts.get(task.status) ?? 0) + 1);
|
|
35
|
-
const allEvents = readEvents(manifest.eventsPath);
|
|
36
|
-
const events = allEvents.slice(-8);
|
|
37
|
-
const attentionByTask = new Map(allEvents.filter((event) => event.type === "task.attention" && event.taskId).map((event) => [event.taskId!, event]));
|
|
38
|
-
const controlConfig = resolveCrewControlConfig(loadConfig(ctx.cwd).config);
|
|
39
|
-
const crewAgents = readCrewAgents(manifest).map((agent) => applyAttentionState(manifest, agent, controlConfig));
|
|
40
|
-
const artifactLines = manifest.artifacts.slice(-10).map((artifact) => `- ${artifact.kind}: ${artifact.path}${artifact.sizeBytes !== undefined ? ` (${artifact.sizeBytes} bytes)` : ""}`);
|
|
41
|
-
const deliveryState = readDeliveryState(manifest);
|
|
42
|
-
const ackTimeoutMs = loadConfig(ctx.cwd).config.runtime?.groupJoinAckTimeoutMs;
|
|
43
|
-
const groupJoinLines: string[] = [];
|
|
44
|
-
for (const message of readMailbox(manifest, "outbox").filter((m) => m.data?.kind === "group_join").slice(-5)) {
|
|
45
|
-
const ack = deliveryState.messages[message.id] === "acknowledged" ? "acknowledged" : "pending";
|
|
46
|
-
const ageMs = Date.now() - new Date(message.createdAt).getTime();
|
|
47
|
-
const requestId = String(message.data?.requestId ?? "unknown");
|
|
48
|
-
const timedOut = ack === "pending" && ackTimeoutMs !== undefined && Number.isFinite(ageMs) && ageMs > ackTimeoutMs;
|
|
49
|
-
if (timedOut && !allEvents.some((event) => event.type === "agent.group_join.ack_timeout" && event.data?.requestId === requestId)) {
|
|
50
|
-
appendEvent(manifest.eventsPath, { type: "agent.group_join.ack_timeout", runId: manifest.runId, message: "Group join delivery ack timed out; mailbox delivery remains the fallback.", data: { requestId, messageId: message.id, batchId: message.data?.batchId, partial: message.data?.partial, ageMs, ackTimeoutMs } });
|
|
51
|
-
}
|
|
52
|
-
groupJoinLines.push(`- ${String(message.data?.partial) === "true" ? "partial" : "completed"} request=${requestId} message=${message.id} ack=${timedOut ? "timeout" : ack}`);
|
|
53
|
-
}
|
|
54
|
-
const totalUsage = aggregateUsage(tasks);
|
|
55
|
-
const completedTasks = tasks.filter((task) => task.status === "completed");
|
|
56
|
-
const effectiveness = evaluateRunEffectiveness({ manifest, tasks, executeWorkers: manifest.runtimeResolution?.kind !== "scaffold", runtimeConfig: loadConfig(ctx.cwd).config.runtime });
|
|
57
|
-
const noObservedWorkTasks = effectiveness.noObservedWorkTaskIds.map((id) => tasks.find((task) => task.id === id)).filter((task): task is typeof tasks[number] => task !== undefined);
|
|
58
|
-
const attentionTasks = effectiveness.needsAttentionTaskIds.map((id) => tasks.find((task) => task.id === id)).filter((task): task is typeof tasks[number] => task !== undefined);
|
|
59
|
-
const activeAgents = crewAgents.filter((agent) => agent.status === "running");
|
|
60
|
-
const completedAgents = crewAgents.filter((agent) => agent.status !== "running");
|
|
61
|
-
const waitingTasks = tasks.filter((task) => task.status === "queued" || task.status === "waiting");
|
|
62
|
-
const agentLine = (agent: typeof crewAgents[number]): string => `- ${agent.id} [${agent.status}] ${agent.role} -> ${agent.agent} runtime=${agent.runtime}${agent.model ? ` model=${agent.model}` : ""}${agent.usage ? ` usage=${formatUsage(agent.usage)}` : ""}${agent.progress?.activityState ? ` activityState=${agent.progress.activityState}` : ""}${formatActivityAge(agent) ? ` activity=${formatActivityAge(agent)}` : ""}${agent.progress?.currentTool ? ` tool=${agent.progress.currentTool}` : ""}${agent.toolUses ? ` tools=${agent.toolUses}` : ""}${!agent.usage && agent.progress?.tokens ? ` tokens=${agent.progress.tokens}` : ""}${agent.progress?.turns ? ` turns=${agent.progress.turns}` : ""}${agent.jsonEvents !== undefined ? ` jsonEvents=${agent.jsonEvents}` : ""}${agent.outputPath ? ` output=${agent.outputPath}` : ""}${agent.transcriptPath ? ` transcript=${agent.transcriptPath}` : ""}${agent.statusPath ? ` status=${agent.statusPath}` : ""}${agent.error ? ` error=${agent.error}` : ""}`;
|
|
63
|
-
const lines = [
|
|
64
|
-
`Run: ${manifest.runId}`,
|
|
65
|
-
`Team: ${manifest.team}`,
|
|
66
|
-
`Workflow: ${manifest.workflow ?? "(none)"}`,
|
|
67
|
-
`Status: ${manifest.status}`,
|
|
68
|
-
`Workspace mode: ${manifest.workspaceMode}`,
|
|
69
|
-
...(manifest.runtimeResolution ? [`Runtime: ${manifest.runtimeResolution.kind}`, `Runtime safety: ${manifest.runtimeResolution.safety}`, `Runtime requested: ${manifest.runtimeResolution.requestedMode}${manifest.runtimeResolution.reason ? ` (${manifest.runtimeResolution.reason})` : ""}`] : []),
|
|
70
|
-
`Goal: ${manifest.goal}`,
|
|
71
|
-
`Created: ${manifest.createdAt}`,
|
|
72
|
-
`Updated: ${manifest.updatedAt}`,
|
|
73
|
-
`State: ${manifest.stateRoot}`,
|
|
74
|
-
`Artifacts: ${manifest.artifactsRoot}`,
|
|
75
|
-
...(asyncLivenessLine ? [asyncLivenessLine] : []),
|
|
76
|
-
"Task graph:",
|
|
77
|
-
...formatTaskGraphLines(tasks),
|
|
78
|
-
"Tasks:",
|
|
79
|
-
...(tasks.length ? tasks.map((task) => `- ${task.id} [${task.status}] ${task.role} -> ${task.agent}${task.taskPacket ? ` scope=${task.taskPacket.scope}` : ""}${task.verification ? ` green=${task.verification.observedGreenLevel}/${task.verification.requiredGreenLevel}` : ""}${task.modelAttempts?.length ? ` attempts=${task.modelAttempts.length}` : ""}${task.modelRouting ? ` modelRouting=${task.modelRouting.requested ? `${task.modelRouting.requested}->` : ""}${task.modelRouting.resolved}${task.modelRouting.usedAttempt ? ` attempt=${task.modelRouting.usedAttempt + 1}` : ""}` : ""}${task.agentProgress?.activityState ? ` activityState=${task.agentProgress.activityState}` : ""}${attentionByTask.get(task.id)?.data?.reason ? ` attention=${String(attentionByTask.get(task.id)?.data?.reason)}` : ""}${task.jsonEvents !== undefined ? ` jsonEvents=${task.jsonEvents}` : ""}${task.usage ? ` usage=${JSON.stringify(task.usage)}` : ""}${task.resultArtifact ? ` result=${task.resultArtifact.path}` : ""}${task.transcriptArtifact ? ` transcript=${task.transcriptArtifact.path}` : ""}${task.worktree ? ` worktree=${task.worktree.path}` : ""}${task.error ? ` error=${task.error}` : ""}`) : ["- (none)"]),
|
|
80
|
-
`Task counts: ${[...counts.entries()].map(([status, count]) => `${status}=${count}`).join(", ") || "none"}`,
|
|
81
|
-
"Effectiveness:",
|
|
82
|
-
`- observable=${effectiveness.observable}/${Math.max(1, effectiveness.completed)} completed tasks`,
|
|
83
|
-
`- workerExecution=${effectiveness.workerExecution} guard=${effectiveness.guardMode} severity=${effectiveness.severity}`,
|
|
84
|
-
`- noObservedWork=${effectiveness.noObservedWorkTaskIds.length ? effectiveness.noObservedWorkTaskIds.join(",") : "none"}`,
|
|
85
|
-
`- needsAttention=${effectiveness.needsAttentionTaskIds.length ? effectiveness.needsAttentionTaskIds.join(",") : "none"}`,
|
|
86
|
-
"Completion verification",
|
|
87
|
-
...(tasks.filter((t) => t.status === "completed").length ? tasks.filter((t) => t.status === "completed").map((t) => {
|
|
88
|
-
const guard = verifyTaskCompletion(t, manifest);
|
|
89
|
-
return `- ${t.id} green=${guard.greenLevel}/3${guard.warnings.length ? ` warnings=[${guard.warnings.join(", ")}]` : ""}`;
|
|
90
|
-
}) : ["- (no completed tasks)"]),
|
|
91
|
-
"Active agents:",
|
|
92
|
-
...(activeAgents.length ? activeAgents.map(agentLine) : ["- (none)"]),
|
|
93
|
-
"Waiting tasks:",
|
|
94
|
-
...(waitingTasks.length ? waitingTasks.map((task) => `- ${task.id} [queued] ${task.role} -> ${task.agent} ${waitingReason(task, tasks) ?? "waiting"}`) : ["- (none)"]),
|
|
95
|
-
"Completed agents:",
|
|
96
|
-
...(completedAgents.length ? completedAgents.map(agentLine) : ["- (none)"]),
|
|
97
|
-
"Policy decisions:",
|
|
98
|
-
...(manifest.policyDecisions?.length ? manifest.policyDecisions.map((item) => `- ${item.action} (${item.reason})${item.taskId ? ` ${item.taskId}` : ""}: ${item.message}`) : ["- (none)"]),
|
|
99
|
-
`Total usage: ${formatUsage(totalUsage)}`,
|
|
100
|
-
"Group joins:",
|
|
101
|
-
...(groupJoinLines.length ? groupJoinLines : ["- (none)"]),
|
|
102
|
-
"",
|
|
103
|
-
"Recent artifacts:",
|
|
104
|
-
...(artifactLines.length ? artifactLines : ["- (none)"]),
|
|
105
|
-
"",
|
|
106
|
-
"Recent events:",
|
|
107
|
-
...(events.length ? events.map((event) => `- ${event.time} ${event.type}${event.taskId ? ` ${event.taskId}` : ""}${event.message ? `: ${event.message}` : ""}`) : ["- (none)"]),
|
|
108
|
-
];
|
|
109
|
-
return result(lines.join("\n"), { action: "status", status: "ok", runId: manifest.runId, artifactsRoot: manifest.artifactsRoot, intent: `status ${manifest.runId}: ${manifest.status}` });
|
|
110
|
-
}
|
|
1
|
+
import { loadConfig } from "../../config/config.ts";
|
|
2
|
+
import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
|
|
3
|
+
import { appendEvent, readEvents } from "../../state/event-log.ts";
|
|
4
|
+
import { readDeliveryState, readMailbox } from "../../state/mailbox.ts";
|
|
5
|
+
import { loadRunManifestById, updateRunStatus, saveRunTasks } from "../../state/state-store.ts";
|
|
6
|
+
import { aggregateUsage, formatUsage } from "../../state/usage.ts";
|
|
7
|
+
import { applyAttentionState, formatActivityAge, resolveCrewControlConfig } from "../../runtime/agent-control.ts";
|
|
8
|
+
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
9
|
+
import { checkProcessLiveness, isActiveRunStatus } from "../../runtime/process-status.ts";
|
|
10
|
+
import { formatTaskGraphLines, waitingReason } from "../../runtime/task-display.ts";
|
|
11
|
+
import { verifyTaskCompletion, formatOutputPreview } from "../../runtime/completion-guard.ts";
|
|
12
|
+
import { evaluateRunEffectiveness } from "../../runtime/effectiveness.ts";
|
|
13
|
+
import type { PiTeamsToolResult } from "../tool-result.ts";
|
|
14
|
+
import { result, type TeamContext } from "./context.ts";
|
|
15
|
+
|
|
16
|
+
export function handleStatus(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
17
|
+
if (!params.runId) return result("Status requires runId.", { action: "status", status: "error" }, true);
|
|
18
|
+
const loaded = loadRunManifestById(ctx.cwd, params.runId);
|
|
19
|
+
if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "status", status: "error" }, true);
|
|
20
|
+
let { manifest, tasks } = loaded;
|
|
21
|
+
let asyncLivenessLine: string | undefined;
|
|
22
|
+
if (manifest.async) {
|
|
23
|
+
const asyncState = manifest.async;
|
|
24
|
+
const liveness = checkProcessLiveness(asyncState.pid);
|
|
25
|
+
asyncLivenessLine = `Async: pid=${asyncState.pid ?? "unknown"} alive=${liveness.alive ? "true" : "false"} detail=${liveness.detail} log=${asyncState.logPath} spawnedAt=${asyncState.spawnedAt}`;
|
|
26
|
+
if (!liveness.alive && isActiveRunStatus(manifest.status)) {
|
|
27
|
+
manifest = updateRunStatus(manifest, "failed", `Async process stale: ${liveness.detail}`);
|
|
28
|
+
tasks = tasks.map((task) => task.status === "running" ? { ...task, status: "cancelled" as const, finishedAt: new Date().toISOString(), error: "Async process died; task was not completed." } : task);
|
|
29
|
+
saveRunTasks(manifest, tasks);
|
|
30
|
+
appendEvent(manifest.eventsPath, { type: "async.stale", runId: manifest.runId, message: liveness.detail, data: { pid: asyncState.pid } });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const counts = new Map<string, number>();
|
|
34
|
+
for (const task of tasks) counts.set(task.status, (counts.get(task.status) ?? 0) + 1);
|
|
35
|
+
const allEvents = readEvents(manifest.eventsPath);
|
|
36
|
+
const events = allEvents.slice(-8);
|
|
37
|
+
const attentionByTask = new Map(allEvents.filter((event) => event.type === "task.attention" && event.taskId).map((event) => [event.taskId!, event]));
|
|
38
|
+
const controlConfig = resolveCrewControlConfig(loadConfig(ctx.cwd).config);
|
|
39
|
+
const crewAgents = readCrewAgents(manifest).map((agent) => applyAttentionState(manifest, agent, controlConfig));
|
|
40
|
+
const artifactLines = manifest.artifacts.slice(-10).map((artifact) => `- ${artifact.kind}: ${artifact.path}${artifact.sizeBytes !== undefined ? ` (${artifact.sizeBytes} bytes)` : ""}`);
|
|
41
|
+
const deliveryState = readDeliveryState(manifest);
|
|
42
|
+
const ackTimeoutMs = loadConfig(ctx.cwd).config.runtime?.groupJoinAckTimeoutMs;
|
|
43
|
+
const groupJoinLines: string[] = [];
|
|
44
|
+
for (const message of readMailbox(manifest, "outbox").filter((m) => m.data?.kind === "group_join").slice(-5)) {
|
|
45
|
+
const ack = deliveryState.messages[message.id] === "acknowledged" ? "acknowledged" : "pending";
|
|
46
|
+
const ageMs = Date.now() - new Date(message.createdAt).getTime();
|
|
47
|
+
const requestId = String(message.data?.requestId ?? "unknown");
|
|
48
|
+
const timedOut = ack === "pending" && ackTimeoutMs !== undefined && Number.isFinite(ageMs) && ageMs > ackTimeoutMs;
|
|
49
|
+
if (timedOut && !allEvents.some((event) => event.type === "agent.group_join.ack_timeout" && event.data?.requestId === requestId)) {
|
|
50
|
+
appendEvent(manifest.eventsPath, { type: "agent.group_join.ack_timeout", runId: manifest.runId, message: "Group join delivery ack timed out; mailbox delivery remains the fallback.", data: { requestId, messageId: message.id, batchId: message.data?.batchId, partial: message.data?.partial, ageMs, ackTimeoutMs } });
|
|
51
|
+
}
|
|
52
|
+
groupJoinLines.push(`- ${String(message.data?.partial) === "true" ? "partial" : "completed"} request=${requestId} message=${message.id} ack=${timedOut ? "timeout" : ack}`);
|
|
53
|
+
}
|
|
54
|
+
const totalUsage = aggregateUsage(tasks);
|
|
55
|
+
const completedTasks = tasks.filter((task) => task.status === "completed");
|
|
56
|
+
const effectiveness = evaluateRunEffectiveness({ manifest, tasks, executeWorkers: manifest.runtimeResolution?.kind !== "scaffold", runtimeConfig: loadConfig(ctx.cwd).config.runtime });
|
|
57
|
+
const noObservedWorkTasks = effectiveness.noObservedWorkTaskIds.map((id) => tasks.find((task) => task.id === id)).filter((task): task is typeof tasks[number] => task !== undefined);
|
|
58
|
+
const attentionTasks = effectiveness.needsAttentionTaskIds.map((id) => tasks.find((task) => task.id === id)).filter((task): task is typeof tasks[number] => task !== undefined);
|
|
59
|
+
const activeAgents = crewAgents.filter((agent) => agent.status === "running");
|
|
60
|
+
const completedAgents = crewAgents.filter((agent) => agent.status !== "running");
|
|
61
|
+
const waitingTasks = tasks.filter((task) => task.status === "queued" || task.status === "waiting");
|
|
62
|
+
const agentLine = (agent: typeof crewAgents[number]): string => `- ${agent.id} [${agent.status}] ${agent.role} -> ${agent.agent} runtime=${agent.runtime}${agent.model ? ` model=${agent.model}` : ""}${agent.usage ? ` usage=${formatUsage(agent.usage)}` : ""}${agent.progress?.activityState ? ` activityState=${agent.progress.activityState}` : ""}${formatActivityAge(agent) ? ` activity=${formatActivityAge(agent)}` : ""}${agent.progress?.currentTool ? ` tool=${agent.progress.currentTool}` : ""}${agent.toolUses ? ` tools=${agent.toolUses}` : ""}${!agent.usage && agent.progress?.tokens ? ` tokens=${agent.progress.tokens}` : ""}${agent.progress?.turns ? ` turns=${agent.progress.turns}` : ""}${agent.jsonEvents !== undefined ? ` jsonEvents=${agent.jsonEvents}` : ""}${agent.outputPath ? ` output=${agent.outputPath}` : ""}${agent.transcriptPath ? ` transcript=${agent.transcriptPath}` : ""}${agent.statusPath ? ` status=${agent.statusPath}` : ""}${agent.error ? ` error=${agent.error}` : ""}`;
|
|
63
|
+
const lines = [
|
|
64
|
+
`Run: ${manifest.runId}`,
|
|
65
|
+
`Team: ${manifest.team}`,
|
|
66
|
+
`Workflow: ${manifest.workflow ?? "(none)"}`,
|
|
67
|
+
`Status: ${manifest.status}`,
|
|
68
|
+
`Workspace mode: ${manifest.workspaceMode}`,
|
|
69
|
+
...(manifest.runtimeResolution ? [`Runtime: ${manifest.runtimeResolution.kind}`, `Runtime safety: ${manifest.runtimeResolution.safety}`, `Runtime requested: ${manifest.runtimeResolution.requestedMode}${manifest.runtimeResolution.reason ? ` (${manifest.runtimeResolution.reason})` : ""}`] : []),
|
|
70
|
+
`Goal: ${manifest.goal}`,
|
|
71
|
+
`Created: ${manifest.createdAt}`,
|
|
72
|
+
`Updated: ${manifest.updatedAt}`,
|
|
73
|
+
`State: ${manifest.stateRoot}`,
|
|
74
|
+
`Artifacts: ${manifest.artifactsRoot}`,
|
|
75
|
+
...(asyncLivenessLine ? [asyncLivenessLine] : []),
|
|
76
|
+
"Task graph:",
|
|
77
|
+
...formatTaskGraphLines(tasks),
|
|
78
|
+
"Tasks:",
|
|
79
|
+
...(tasks.length ? tasks.map((task) => `- ${task.id} [${task.status}] ${task.role} -> ${task.agent}${task.taskPacket ? ` scope=${task.taskPacket.scope}` : ""}${task.verification ? ` green=${task.verification.observedGreenLevel}/${task.verification.requiredGreenLevel}` : ""}${task.modelAttempts?.length ? ` attempts=${task.modelAttempts.length}` : ""}${task.modelRouting ? ` modelRouting=${task.modelRouting.requested ? `${task.modelRouting.requested}->` : ""}${task.modelRouting.resolved}${task.modelRouting.usedAttempt ? ` attempt=${task.modelRouting.usedAttempt + 1}` : ""}` : ""}${task.agentProgress?.activityState ? ` activityState=${task.agentProgress.activityState}` : ""}${attentionByTask.get(task.id)?.data?.reason ? ` attention=${String(attentionByTask.get(task.id)?.data?.reason)}` : ""}${task.jsonEvents !== undefined ? ` jsonEvents=${task.jsonEvents}` : ""}${task.usage ? ` usage=${JSON.stringify(task.usage)}` : ""}${task.resultArtifact ? ` result=${task.resultArtifact.path}` : ""}${task.transcriptArtifact ? ` transcript=${task.transcriptArtifact.path}` : ""}${task.worktree ? ` worktree=${task.worktree.path}` : ""}${task.error ? ` error=${task.error}` : ""}`) : ["- (none)"]),
|
|
80
|
+
`Task counts: ${[...counts.entries()].map(([status, count]) => `${status}=${count}`).join(", ") || "none"}`,
|
|
81
|
+
"Effectiveness:",
|
|
82
|
+
`- observable=${effectiveness.observable}/${Math.max(1, effectiveness.completed)} completed tasks`,
|
|
83
|
+
`- workerExecution=${effectiveness.workerExecution} guard=${effectiveness.guardMode} severity=${effectiveness.severity}`,
|
|
84
|
+
`- noObservedWork=${effectiveness.noObservedWorkTaskIds.length ? effectiveness.noObservedWorkTaskIds.join(",") : "none"}`,
|
|
85
|
+
`- needsAttention=${effectiveness.needsAttentionTaskIds.length ? effectiveness.needsAttentionTaskIds.join(",") : "none"}`,
|
|
86
|
+
"Completion verification",
|
|
87
|
+
...(tasks.filter((t) => t.status === "completed").length ? tasks.filter((t) => t.status === "completed").map((t) => {
|
|
88
|
+
const guard = verifyTaskCompletion(t, manifest);
|
|
89
|
+
return `- ${t.id} green=${guard.greenLevel}/3${guard.warnings.length ? ` warnings=[${guard.warnings.join(", ")}]` : ""}`;
|
|
90
|
+
}) : ["- (no completed tasks)"]),
|
|
91
|
+
"Active agents:",
|
|
92
|
+
...(activeAgents.length ? activeAgents.map(agentLine) : ["- (none)"]),
|
|
93
|
+
"Waiting tasks:",
|
|
94
|
+
...(waitingTasks.length ? waitingTasks.map((task) => `- ${task.id} [queued] ${task.role} -> ${task.agent} ${waitingReason(task, tasks) ?? "waiting"}`) : ["- (none)"]),
|
|
95
|
+
"Completed agents:",
|
|
96
|
+
...(completedAgents.length ? completedAgents.map(agentLine) : ["- (none)"]),
|
|
97
|
+
"Policy decisions:",
|
|
98
|
+
...(manifest.policyDecisions?.length ? manifest.policyDecisions.map((item) => `- ${item.action} (${item.reason})${item.taskId ? ` ${item.taskId}` : ""}: ${item.message}`) : ["- (none)"]),
|
|
99
|
+
`Total usage: ${formatUsage(totalUsage)}`,
|
|
100
|
+
"Group joins:",
|
|
101
|
+
...(groupJoinLines.length ? groupJoinLines : ["- (none)"]),
|
|
102
|
+
"",
|
|
103
|
+
"Recent artifacts:",
|
|
104
|
+
...(artifactLines.length ? artifactLines : ["- (none)"]),
|
|
105
|
+
"",
|
|
106
|
+
"Recent events:",
|
|
107
|
+
...(events.length ? events.map((event) => `- ${event.time} ${event.type}${event.taskId ? ` ${event.taskId}` : ""}${event.message ? `: ${event.message}` : ""}`) : ["- (none)"]),
|
|
108
|
+
];
|
|
109
|
+
return result(lines.join("\n"), { action: "status", status: "ok", runId: manifest.runId, artifactsRoot: manifest.artifactsRoot, intent: `status ${manifest.runId}: ${manifest.status}` });
|
|
110
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export interface TeamToolDetails {
|
|
2
|
-
action: string;
|
|
3
|
-
status: "ok" | "error" | "planned";
|
|
4
|
-
runId?: string;
|
|
5
|
-
artifactsRoot?: string;
|
|
6
|
-
abortedIds?: string[];
|
|
7
|
-
missingIds?: string[];
|
|
8
|
-
foreignIds?: string[];
|
|
9
|
-
intent?: string;
|
|
10
|
-
resumedIds?: string[];
|
|
11
|
-
retriedTaskIds?: string[];
|
|
12
|
-
mailboxIds?: string[];
|
|
13
|
-
}
|
|
1
|
+
export interface TeamToolDetails {
|
|
2
|
+
action: string;
|
|
3
|
+
status: "ok" | "error" | "planned";
|
|
4
|
+
runId?: string;
|
|
5
|
+
artifactsRoot?: string;
|
|
6
|
+
abortedIds?: string[];
|
|
7
|
+
missingIds?: string[];
|
|
8
|
+
foreignIds?: string[];
|
|
9
|
+
intent?: string;
|
|
10
|
+
resumedIds?: string[];
|
|
11
|
+
retriedTaskIds?: string[];
|
|
12
|
+
mailboxIds?: string[];
|
|
13
|
+
}
|
|
@@ -32,6 +32,7 @@ import type { executeTeamRun as ExecuteTeamRunFn } from "../runtime/team-runner.
|
|
|
32
32
|
let _cachedExecuteTeamRun: typeof ExecuteTeamRunFn | undefined;
|
|
33
33
|
async function executeTeamRun(...args: Parameters<typeof ExecuteTeamRunFn>): Promise<Awaited<ReturnType<typeof ExecuteTeamRunFn>>> {
|
|
34
34
|
if (!_cachedExecuteTeamRun) {
|
|
35
|
+
// LAZY: heavy runtime — defer 1.4s import cost until team run actually executes.
|
|
35
36
|
const mod = await import("../runtime/team-runner.ts");
|
|
36
37
|
_cachedExecuteTeamRun = mod.executeTeamRun;
|
|
37
38
|
}
|
|
@@ -48,7 +49,18 @@ import { parsePiJsonOutput } from "../runtime/pi-json-output.ts";
|
|
|
48
49
|
import { buildParentContext, configRecord, formatScoped, result, type TeamContext } from "./team-tool/context.ts";
|
|
49
50
|
import { autonomousPatchFromConfig, configPatchFromConfig, effectiveRunConfig, formatAutonomyStatus } from "./team-tool/config-patch.ts";
|
|
50
51
|
import { handleApi } from "./team-tool/api.ts";
|
|
51
|
-
|
|
52
|
+
// Lazy-loaded: run.ts pulls in spawnBackgroundTeamRun, resolveCrewRuntime, etc.
|
|
53
|
+
// Static import fails silently in some jiti contexts (child-process), leaving handleRun undefined.
|
|
54
|
+
import type { handleRun as HandleRunFn } from "./team-tool/run.ts";
|
|
55
|
+
let _cachedHandleRun: typeof HandleRunFn | undefined;
|
|
56
|
+
async function handleRun(...args: Parameters<typeof HandleRunFn>): Promise<Awaited<ReturnType<typeof HandleRunFn>>> {
|
|
57
|
+
if (!_cachedHandleRun) {
|
|
58
|
+
// LAZY: run.ts pulls in spawnBackgroundTeamRun + resolveCrewRuntime; also avoids jiti import race in child-process contexts.
|
|
59
|
+
const mod = await import("./team-tool/run.ts");
|
|
60
|
+
_cachedHandleRun = mod.handleRun;
|
|
61
|
+
}
|
|
62
|
+
return _cachedHandleRun(...args);
|
|
63
|
+
}
|
|
52
64
|
import { handleDoctor } from "./team-tool/doctor.ts";
|
|
53
65
|
import { handleStatus } from "./team-tool/status.ts";
|
|
54
66
|
import { handleArtifacts, handleEvents, handleSummary } from "./team-tool/inspect.ts";
|
|
@@ -62,7 +74,7 @@ import { normalizeSkillOverride } from "../runtime/skill-instructions.ts";
|
|
|
62
74
|
|
|
63
75
|
export type { TeamToolDetails } from "./team-tool-types.ts";
|
|
64
76
|
export type { TeamContext } from "./team-tool/context.ts";
|
|
65
|
-
export { handleRun }
|
|
77
|
+
export { handleRun };
|
|
66
78
|
export { handleDoctor } from "./team-tool/doctor.ts";
|
|
67
79
|
export { handleStatus } from "./team-tool/status.ts";
|
|
68
80
|
export { handleArtifacts, handleEvents, handleSummary } from "./team-tool/inspect.ts";
|
|
@@ -191,7 +203,7 @@ export async function handleResume(params: TeamToolParamsValue, ctx: TeamContext
|
|
|
191
203
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
192
204
|
const recovered = recoverCheckpointedTasks(loaded.manifest, loaded.tasks);
|
|
193
205
|
const resumeManifest = recovered.manifest;
|
|
194
|
-
const executedConfig = effectiveRunConfig(loadedConfig.config, params.config);
|
|
206
|
+
const executedConfig = { ...effectiveRunConfig(loadedConfig.config, params.config) };
|
|
195
207
|
// Preserve original manifest scaffold mode when resume has no explicit mode override
|
|
196
208
|
// AND workers are not explicitly disabled. If workers are disabled, let
|
|
197
209
|
// resolveCrewRuntime detect it and return blocked safety.
|
|
@@ -225,7 +237,7 @@ export async function handleResume(params: TeamToolParamsValue, ctx: TeamContext
|
|
|
225
237
|
if (replay.messages.length) appendEvent(runtimeManifest.eventsPath, { type: "mailbox.replayed", runId: runtimeManifest.runId, message: `Replayed ${replay.messages.length} pending inbox message(s).`, data: { messageIds: replay.messages.map((message) => message.id), taskIds: replay.messages.map((message) => message.taskId).filter(Boolean) } });
|
|
226
238
|
const executeWorkers = runtime.kind !== "scaffold";
|
|
227
239
|
const resumeSkillOverride = normalizeSkillOverride(params.skill) ?? runtimeManifest.skillOverride;
|
|
228
|
-
const executed = await executeTeamRun({ manifest: runtimeManifest, tasks: resetTasks, team, workflow, agents, executeWorkers, limits: executedConfig.limits, runtime, runtimeConfig: executedConfig.runtime, parentContext: buildParentContext(ctx), parentModel: ctx.model, modelRegistry: ctx.modelRegistry, modelOverride: params.model, skillOverride: resumeSkillOverride, signal: ctx.signal, reliability: executedConfig.reliability, metricRegistry: ctx.metricRegistry });
|
|
240
|
+
const executed = await executeTeamRun({ manifest: runtimeManifest, tasks: resetTasks, team, workflow, agents, executeWorkers, limits: executedConfig.limits, runtime, runtimeConfig: executedConfig.runtime, parentContext: buildParentContext(ctx), parentModel: ctx.model, modelRegistry: ctx.modelRegistry, modelOverride: params.model, skillOverride: resumeSkillOverride, signal: ctx.signal, reliability: executedConfig.reliability, metricRegistry: ctx.metricRegistry, workspaceId: ctx.cwd });
|
|
229
241
|
return result([`Resumed run ${executed.manifest.runId}.`, `Status: ${executed.manifest.status}`, `Tasks: ${executed.tasks.length}`, `Artifacts: ${executed.manifest.artifactsRoot}`].join("\n"), { action: "resume", status: executed.manifest.status === "failed" ? "error" : "ok", runId: executed.manifest.runId, artifactsRoot: executed.manifest.artifactsRoot }, executed.manifest.status === "failed");
|
|
230
242
|
});
|
|
231
243
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
2
|
-
import type { TeamToolDetails } from "./team-tool-types.ts";
|
|
3
|
-
|
|
4
|
-
export type PiTeamsToolResult<TDetails = TeamToolDetails> = AgentToolResult<TDetails> & { isError?: boolean };
|
|
5
|
-
|
|
6
|
-
export function toolResult<TDetails>(text: string, details: TDetails, isError = false): PiTeamsToolResult<TDetails> {
|
|
7
|
-
return { content: [{ type: "text", text }], details, isError };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function isToolError(result: { isError?: boolean }): boolean {
|
|
11
|
-
return result.isError === true;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function textFromToolResult(result: { content?: Array<{ type: string; text?: string }> }): string {
|
|
15
|
-
return result.content?.map((item) => item.text ?? "").join("\n") ?? "";
|
|
16
|
-
}
|
|
1
|
+
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import type { TeamToolDetails } from "./team-tool-types.ts";
|
|
3
|
+
|
|
4
|
+
export type PiTeamsToolResult<TDetails = TeamToolDetails> = AgentToolResult<TDetails> & { isError?: boolean };
|
|
5
|
+
|
|
6
|
+
export function toolResult<TDetails>(text: string, details: TDetails, isError = false): PiTeamsToolResult<TDetails> {
|
|
7
|
+
return { content: [{ type: "text", text }], details, isError };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function isToolError(result: { isError?: boolean }): boolean {
|
|
11
|
+
return result.isError === true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function textFromToolResult(result: { content?: Array<{ type: string; text?: string }> }): string {
|
|
15
|
+
return result.content?.map((item) => item.text ?? "").join("\n") ?? "";
|
|
16
|
+
}
|
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
2
|
-
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
3
|
-
import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
|
|
4
|
-
import { validateWorkflowForTeam } from "../workflows/validate-workflow.ts";
|
|
5
|
-
|
|
6
|
-
export interface ValidationIssue {
|
|
7
|
-
level: "error" | "warning";
|
|
8
|
-
resource: string;
|
|
9
|
-
message: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ValidationReport {
|
|
13
|
-
issues: ValidationIssue[];
|
|
14
|
-
agents: number;
|
|
15
|
-
teams: number;
|
|
16
|
-
workflows: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function validateResources(cwd: string): ValidationReport {
|
|
20
|
-
const agents = allAgents(discoverAgents(cwd));
|
|
21
|
-
const teams = allTeams(discoverTeams(cwd));
|
|
22
|
-
const workflows = allWorkflows(discoverWorkflows(cwd));
|
|
23
|
-
const agentNames = new Set(agents.map((agent) => agent.name));
|
|
24
|
-
const workflowNames = new Set(workflows.map((workflow) => workflow.name));
|
|
25
|
-
const issues: ValidationIssue[] = [];
|
|
26
|
-
|
|
27
|
-
for (const agent of agents) {
|
|
28
|
-
const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter((value): value is string => typeof value === "string" && value.length > 0);
|
|
29
|
-
for (const model of modelValues) {
|
|
30
|
-
if (/\s/.test(model)) {
|
|
31
|
-
issues.push({ level: "warning", resource: `agent:${agent.name}`, message: `Model reference '${model}' contains whitespace.` });
|
|
32
|
-
}
|
|
33
|
-
if (model.includes("/") && model.split("/").some((part) => part.trim() === "")) {
|
|
34
|
-
issues.push({ level: "warning", resource: `agent:${agent.name}`, message: `Model reference '${model}' has an empty provider/model segment.` });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
for (const team of teams) {
|
|
40
|
-
for (const role of team.roles) {
|
|
41
|
-
if (!agentNames.has(role.agent)) {
|
|
42
|
-
issues.push({ level: "error", resource: `team:${team.name}`, message: `Role '${role.name}' references unknown agent '${role.agent}'.` });
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (team.defaultWorkflow && !workflowNames.has(team.defaultWorkflow)) {
|
|
46
|
-
issues.push({ level: "error", resource: `team:${team.name}`, message: `defaultWorkflow references unknown workflow '${team.defaultWorkflow}'.` });
|
|
47
|
-
}
|
|
48
|
-
const workflow = workflows.find((candidate) => candidate.name === team.defaultWorkflow);
|
|
49
|
-
if (workflow) {
|
|
50
|
-
for (const error of validateWorkflowForTeam(workflow, team)) {
|
|
51
|
-
issues.push({ level: "error", resource: `workflow:${workflow.name}`, message: `Team '${team.name}': ${error}` });
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
for (const workflow of workflows) {
|
|
57
|
-
if (workflow.steps.length === 0) {
|
|
58
|
-
issues.push({ level: "warning", resource: `workflow:${workflow.name}`, message: "Workflow has no steps." });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { issues, agents: agents.length, teams: teams.length, workflows: workflows.length };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function formatValidationReport(report: ValidationReport): string {
|
|
66
|
-
const lines = [
|
|
67
|
-
"pi-crew resource validation:",
|
|
68
|
-
`Agents: ${report.agents}`,
|
|
69
|
-
`Teams: ${report.teams}`,
|
|
70
|
-
`Workflows: ${report.workflows}`,
|
|
71
|
-
`Issues: ${report.issues.length}`,
|
|
72
|
-
];
|
|
73
|
-
if (report.issues.length > 0) {
|
|
74
|
-
lines.push("", ...report.issues.map((issue) => `- ${issue.level.toUpperCase()} ${issue.resource}: ${issue.message}`));
|
|
75
|
-
}
|
|
76
|
-
return lines.join("\n");
|
|
77
|
-
}
|
|
1
|
+
import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
2
|
+
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
3
|
+
import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
|
|
4
|
+
import { validateWorkflowForTeam } from "../workflows/validate-workflow.ts";
|
|
5
|
+
|
|
6
|
+
export interface ValidationIssue {
|
|
7
|
+
level: "error" | "warning";
|
|
8
|
+
resource: string;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ValidationReport {
|
|
13
|
+
issues: ValidationIssue[];
|
|
14
|
+
agents: number;
|
|
15
|
+
teams: number;
|
|
16
|
+
workflows: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function validateResources(cwd: string): ValidationReport {
|
|
20
|
+
const agents = allAgents(discoverAgents(cwd));
|
|
21
|
+
const teams = allTeams(discoverTeams(cwd));
|
|
22
|
+
const workflows = allWorkflows(discoverWorkflows(cwd));
|
|
23
|
+
const agentNames = new Set(agents.map((agent) => agent.name));
|
|
24
|
+
const workflowNames = new Set(workflows.map((workflow) => workflow.name));
|
|
25
|
+
const issues: ValidationIssue[] = [];
|
|
26
|
+
|
|
27
|
+
for (const agent of agents) {
|
|
28
|
+
const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter((value): value is string => typeof value === "string" && value.length > 0);
|
|
29
|
+
for (const model of modelValues) {
|
|
30
|
+
if (/\s/.test(model)) {
|
|
31
|
+
issues.push({ level: "warning", resource: `agent:${agent.name}`, message: `Model reference '${model}' contains whitespace.` });
|
|
32
|
+
}
|
|
33
|
+
if (model.includes("/") && model.split("/").some((part) => part.trim() === "")) {
|
|
34
|
+
issues.push({ level: "warning", resource: `agent:${agent.name}`, message: `Model reference '${model}' has an empty provider/model segment.` });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const team of teams) {
|
|
40
|
+
for (const role of team.roles) {
|
|
41
|
+
if (!agentNames.has(role.agent)) {
|
|
42
|
+
issues.push({ level: "error", resource: `team:${team.name}`, message: `Role '${role.name}' references unknown agent '${role.agent}'.` });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (team.defaultWorkflow && !workflowNames.has(team.defaultWorkflow)) {
|
|
46
|
+
issues.push({ level: "error", resource: `team:${team.name}`, message: `defaultWorkflow references unknown workflow '${team.defaultWorkflow}'.` });
|
|
47
|
+
}
|
|
48
|
+
const workflow = workflows.find((candidate) => candidate.name === team.defaultWorkflow);
|
|
49
|
+
if (workflow) {
|
|
50
|
+
for (const error of validateWorkflowForTeam(workflow, team)) {
|
|
51
|
+
issues.push({ level: "error", resource: `workflow:${workflow.name}`, message: `Team '${team.name}': ${error}` });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const workflow of workflows) {
|
|
57
|
+
if (workflow.steps.length === 0) {
|
|
58
|
+
issues.push({ level: "warning", resource: `workflow:${workflow.name}`, message: "Workflow has no steps." });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { issues, agents: agents.length, teams: teams.length, workflows: workflows.length };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function formatValidationReport(report: ValidationReport): string {
|
|
66
|
+
const lines = [
|
|
67
|
+
"pi-crew resource validation:",
|
|
68
|
+
`Agents: ${report.agents}`,
|
|
69
|
+
`Teams: ${report.teams}`,
|
|
70
|
+
`Workflows: ${report.workflows}`,
|
|
71
|
+
`Issues: ${report.issues.length}`,
|
|
72
|
+
];
|
|
73
|
+
if (report.issues.length > 0) {
|
|
74
|
+
lines.push("", ...report.issues.map((issue) => `- ${issue.level.toUpperCase()} ${issue.resource}: ${issue.message}`));
|
|
75
|
+
}
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
package/src/hooks/registry.ts
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import type { HookDefinition, HookName, HookContext, HookResult, HookExecutionReport } from "./types.ts";
|
|
2
|
-
import { appendEvent } from "../state/event-log.ts";
|
|
3
|
-
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
-
import { runEventBus } from "../ui/run-event-bus.ts";
|
|
5
|
-
|
|
6
|
-
const registry = new Map<HookName, HookDefinition[]>();
|
|
7
|
-
|
|
8
|
-
export function registerHook(definition: HookDefinition): void {
|
|
9
|
-
const hooks = registry.get(definition.name) ?? [];
|
|
10
|
-
hooks.push(definition);
|
|
11
|
-
registry.set(definition.name, hooks);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function clearHooks(): void {
|
|
15
|
-
registry.clear();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getHooks(name: HookName): HookDefinition[] {
|
|
19
|
-
return registry.get(name) ?? [];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export async function executeHook(name: HookName, ctx: HookContext): Promise<HookExecutionReport> {
|
|
23
|
-
const hooks = getHooks(name);
|
|
24
|
-
if (hooks.length === 0) return { hookName: name, outcome: "allow", durationMs: 0 };
|
|
25
|
-
const start = Date.now();
|
|
26
|
-
const diagnostics: string[] = [];
|
|
27
|
-
let capturedModifications: Record<string, unknown> | undefined;
|
|
28
|
-
for (const hook of hooks) {
|
|
29
|
-
try {
|
|
30
|
-
const result: HookResult = await hook.handler(ctx);
|
|
31
|
-
if (hook.mode === "blocking" && result.outcome === "block") {
|
|
32
|
-
return { hookName: name, outcome: "block", durationMs: Date.now() - start, reason: result.reason };
|
|
33
|
-
}
|
|
34
|
-
if (result.outcome === "modify" && result.data) {
|
|
35
|
-
Object.assign(ctx, result.data);
|
|
36
|
-
capturedModifications = { ...result.data };
|
|
37
|
-
}
|
|
38
|
-
} catch (error) {
|
|
39
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
40
|
-
if (hook.mode === "blocking") {
|
|
41
|
-
return { hookName: name, outcome: "block", durationMs: Date.now() - start, reason: `Hook error: ${message}` };
|
|
42
|
-
}
|
|
43
|
-
// Non-blocking hook errors are accumulated as diagnostics; continue to next hook
|
|
44
|
-
diagnostics.push(message);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (diagnostics.length > 0) {
|
|
48
|
-
return { hookName: name, outcome: "diagnostic", durationMs: Date.now() - start, reason: diagnostics.join("; "), modifiedData: capturedModifications };
|
|
49
|
-
}
|
|
50
|
-
return { hookName: name, outcome: "allow", durationMs: Date.now() - start, modifiedData: capturedModifications };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function appendHookEvent(manifest: TeamRunManifest, report: HookExecutionReport): void {
|
|
54
|
-
appendEvent(manifest.eventsPath, {
|
|
55
|
-
type: "hook.executed",
|
|
56
|
-
runId: manifest.runId,
|
|
57
|
-
message: `Hook ${report.hookName} completed with outcome=${report.outcome}${report.reason ? `: ${report.reason}` : ""}`,
|
|
58
|
-
data: { hookName: report.hookName, outcome: report.outcome, durationMs: report.durationMs, reason: report.reason },
|
|
59
|
-
});
|
|
60
|
-
runEventBus.emit({ type: "effectiveness_changed", runId: manifest.runId, data: { hookName: report.hookName, outcome: report.outcome } });
|
|
61
|
-
}
|
|
1
|
+
import type { HookDefinition, HookName, HookContext, HookResult, HookExecutionReport } from "./types.ts";
|
|
2
|
+
import { appendEvent } from "../state/event-log.ts";
|
|
3
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
+
import { runEventBus } from "../ui/run-event-bus.ts";
|
|
5
|
+
|
|
6
|
+
const registry = new Map<HookName, HookDefinition[]>();
|
|
7
|
+
|
|
8
|
+
export function registerHook(definition: HookDefinition): void {
|
|
9
|
+
const hooks = registry.get(definition.name) ?? [];
|
|
10
|
+
hooks.push(definition);
|
|
11
|
+
registry.set(definition.name, hooks);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function clearHooks(): void {
|
|
15
|
+
registry.clear();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getHooks(name: HookName): HookDefinition[] {
|
|
19
|
+
return registry.get(name) ?? [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function executeHook(name: HookName, ctx: HookContext): Promise<HookExecutionReport> {
|
|
23
|
+
const hooks = getHooks(name);
|
|
24
|
+
if (hooks.length === 0) return { hookName: name, outcome: "allow", durationMs: 0 };
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
const diagnostics: string[] = [];
|
|
27
|
+
let capturedModifications: Record<string, unknown> | undefined;
|
|
28
|
+
for (const hook of hooks) {
|
|
29
|
+
try {
|
|
30
|
+
const result: HookResult = await hook.handler(ctx);
|
|
31
|
+
if (hook.mode === "blocking" && result.outcome === "block") {
|
|
32
|
+
return { hookName: name, outcome: "block", durationMs: Date.now() - start, reason: result.reason };
|
|
33
|
+
}
|
|
34
|
+
if (result.outcome === "modify" && result.data) {
|
|
35
|
+
Object.assign(ctx, result.data);
|
|
36
|
+
capturedModifications = { ...result.data };
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
40
|
+
if (hook.mode === "blocking") {
|
|
41
|
+
return { hookName: name, outcome: "block", durationMs: Date.now() - start, reason: `Hook error: ${message}` };
|
|
42
|
+
}
|
|
43
|
+
// Non-blocking hook errors are accumulated as diagnostics; continue to next hook
|
|
44
|
+
diagnostics.push(message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (diagnostics.length > 0) {
|
|
48
|
+
return { hookName: name, outcome: "diagnostic", durationMs: Date.now() - start, reason: diagnostics.join("; "), modifiedData: capturedModifications };
|
|
49
|
+
}
|
|
50
|
+
return { hookName: name, outcome: "allow", durationMs: Date.now() - start, modifiedData: capturedModifications };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function appendHookEvent(manifest: TeamRunManifest, report: HookExecutionReport): void {
|
|
54
|
+
appendEvent(manifest.eventsPath, {
|
|
55
|
+
type: "hook.executed",
|
|
56
|
+
runId: manifest.runId,
|
|
57
|
+
message: `Hook ${report.hookName} completed with outcome=${report.outcome}${report.reason ? `: ${report.reason}` : ""}`,
|
|
58
|
+
data: { hookName: report.hookName, outcome: report.outcome, durationMs: report.durationMs, reason: report.reason },
|
|
59
|
+
});
|
|
60
|
+
runEventBus.emit({ type: "effectiveness_changed", runId: manifest.runId, data: { hookName: report.hookName, outcome: report.outcome } });
|
|
61
|
+
}
|