pi-crew 0.2.3 → 0.2.5
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 -448
- 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 -592
- 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-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 -100
- 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 -104
- 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 -159
- package/src/extension/registration/team-tool.ts +159 -99
- 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 -96
- 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 -229
- package/src/extension/team-tool/status.ts +110 -110
- package/src/extension/team-tool-types.ts +13 -13
- package/src/extension/team-tool.ts +344 -344
- 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 -153
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -100
- package/src/runtime/background-runner.ts +122 -89
- 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 -461
- 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 -267
- 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 -600
- 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 -125
- 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 -91
- 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 -400
- 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 -459
- 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 -190
- package/src/schema/config-schema.ts +172 -168
- package/src/schema/team-tool-schema.ts +126 -126
- 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 -195
- 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 -303
- package/src/state/event-reconstructor.ts +217 -217
- package/src/state/jsonl-writer.ts +82 -82
- package/src/state/locks.ts +146 -146
- 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 -118
- 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/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/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +136 -136
- 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 -75
- package/src/worktree/worktree-manager.ts +188 -188
- 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
package/src/ui/run-event-bus.ts
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
import type { TeamEvent } from "../state/event-log.ts";
|
|
2
|
-
|
|
3
|
-
export type RunEventType =
|
|
4
|
-
| "task_started"
|
|
5
|
-
| "task_completed"
|
|
6
|
-
| "task_failed"
|
|
7
|
-
| "task_cancelled"
|
|
8
|
-
| "worker_status"
|
|
9
|
-
| "mailbox_updated"
|
|
10
|
-
| "effectiveness_changed"
|
|
11
|
-
| "run_started"
|
|
12
|
-
| "run_completed"
|
|
13
|
-
| "run_blocked"
|
|
14
|
-
| "run_cancelled";
|
|
15
|
-
|
|
16
|
-
/** Typed channel names for category-based event subscription. */
|
|
17
|
-
export type EventChannel =
|
|
18
|
-
| "worker:progress"
|
|
19
|
-
| "worker:lifecycle"
|
|
20
|
-
| "worker:stream"
|
|
21
|
-
| "run:state"
|
|
22
|
-
| "ui:invalidate";
|
|
23
|
-
|
|
24
|
-
/** Sets used by classifyEventChannel for O(1) lookup. */
|
|
25
|
-
const WORKER_PROGRESS_TYPES = new Set([
|
|
26
|
-
"tool_execution_start", "tool_result", "agent_progress", "worker_status",
|
|
27
|
-
]);
|
|
28
|
-
const WORKER_LIFECYCLE_TYPES = new Set([
|
|
29
|
-
"task.started", "task.completed", "task.failed",
|
|
30
|
-
"task_started", "task_completed", "task_failed", "task_cancelled",
|
|
31
|
-
"run.started", "run.completed", "run.cancelled", "run.failed",
|
|
32
|
-
"run_started", "run_completed", "run_cancelled", "run_blocked",
|
|
33
|
-
]);
|
|
34
|
-
const WORKER_STREAM_TYPES = new Set([
|
|
35
|
-
"stdout_chunk", "stderr_chunk", "stream",
|
|
36
|
-
]);
|
|
37
|
-
const RUN_STATE_TYPES = new Set([
|
|
38
|
-
"manifest.saved", "task.claimed", "task.unclaimed", "mailbox_updated",
|
|
39
|
-
]);
|
|
40
|
-
const UI_INVALIDATE_TYPES = new Set([
|
|
41
|
-
"effectiveness_changed", "snapshot_stale",
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
/** Classify an event type string into a typed channel. */
|
|
45
|
-
export function classifyEventChannel(type: string): EventChannel {
|
|
46
|
-
if (WORKER_PROGRESS_TYPES.has(type)) return "worker:progress";
|
|
47
|
-
if (WORKER_LIFECYCLE_TYPES.has(type)) return "worker:lifecycle";
|
|
48
|
-
if (WORKER_STREAM_TYPES.has(type)) return "worker:stream";
|
|
49
|
-
if (RUN_STATE_TYPES.has(type)) return "run:state";
|
|
50
|
-
if (UI_INVALIDATE_TYPES.has(type)) return "ui:invalidate";
|
|
51
|
-
return "worker:progress"; // default fallback
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface RunEventPayload {
|
|
55
|
-
type: RunEventType;
|
|
56
|
-
runId: string;
|
|
57
|
-
taskId?: string;
|
|
58
|
-
timestamp?: string;
|
|
59
|
-
data?: unknown;
|
|
60
|
-
channel?: EventChannel;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export type RunEventCallback = (event: RunEventPayload) => void;
|
|
64
|
-
|
|
65
|
-
class RunEventBus {
|
|
66
|
-
#listeners = new Map<string, Set<RunEventCallback>>();
|
|
67
|
-
#globalListeners = new Set<RunEventCallback>();
|
|
68
|
-
#channelListeners = new Map<EventChannel, Set<RunEventCallback>>();
|
|
69
|
-
#channelRunListeners = new Map<string, Map<EventChannel, Set<RunEventCallback>>>();
|
|
70
|
-
|
|
71
|
-
on(runId: string, callback: RunEventCallback): () => void {
|
|
72
|
-
const listeners = this.#listeners.get(runId) ?? new Set();
|
|
73
|
-
listeners.add(callback);
|
|
74
|
-
this.#listeners.set(runId, listeners);
|
|
75
|
-
return () => { listeners.delete(callback); if (listeners.size === 0) this.#listeners.delete(runId); };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
onAny(callback: RunEventCallback): () => void {
|
|
79
|
-
this.#globalListeners.add(callback);
|
|
80
|
-
return () => { this.#globalListeners.delete(callback); };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
off(runId: string, callback: RunEventCallback): void {
|
|
84
|
-
const listeners = this.#listeners.get(runId);
|
|
85
|
-
if (listeners) {
|
|
86
|
-
listeners.delete(callback);
|
|
87
|
-
if (listeners.size === 0) this.#listeners.delete(runId);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Subscribe to all events on a specific channel. */
|
|
92
|
-
onChannel(channel: EventChannel, callback: RunEventCallback): () => void {
|
|
93
|
-
const listeners = this.#channelListeners.get(channel) ?? new Set();
|
|
94
|
-
listeners.add(callback);
|
|
95
|
-
this.#channelListeners.set(channel, listeners);
|
|
96
|
-
return () => {
|
|
97
|
-
listeners.delete(callback);
|
|
98
|
-
if (listeners.size === 0) this.#channelListeners.delete(channel);
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Subscribe to events on a specific channel for a given runId. */
|
|
103
|
-
onChannelForRun(channel: EventChannel, runId: string, callback: RunEventCallback): () => void {
|
|
104
|
-
const runKey = `${channel}::${runId}`;
|
|
105
|
-
const runMap = this.#channelRunListeners.get(runKey) ?? new Map();
|
|
106
|
-
const listeners = runMap.get(channel) ?? new Set();
|
|
107
|
-
listeners.add(callback);
|
|
108
|
-
runMap.set(channel, listeners);
|
|
109
|
-
this.#channelRunListeners.set(runKey, runMap);
|
|
110
|
-
return () => {
|
|
111
|
-
listeners.delete(callback);
|
|
112
|
-
if (listeners.size === 0) runMap.delete(channel);
|
|
113
|
-
if (runMap.size === 0) this.#channelRunListeners.delete(runKey);
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
emit(event: RunEventPayload): void {
|
|
118
|
-
// Auto-classify channel if not already set.
|
|
119
|
-
// M2: Use local variable for routing, but also set on event
|
|
120
|
-
// for subscriber API contract (listeners read event.channel).
|
|
121
|
-
if (!event.channel) {
|
|
122
|
-
(event as { channel?: EventChannel }).channel = classifyEventChannel(event.type);
|
|
123
|
-
}
|
|
124
|
-
const channel = event.channel!;
|
|
125
|
-
|
|
126
|
-
// Existing: runId-specific listeners
|
|
127
|
-
const listeners = this.#listeners.get(event.runId);
|
|
128
|
-
if (listeners) {
|
|
129
|
-
for (const cb of listeners) {
|
|
130
|
-
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Existing: global listeners
|
|
135
|
-
for (const cb of this.#globalListeners) {
|
|
136
|
-
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// New: channel listeners
|
|
140
|
-
const channelListeners = this.#channelListeners.get(channel);
|
|
141
|
-
if (channelListeners) {
|
|
142
|
-
for (const cb of channelListeners) {
|
|
143
|
-
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// New: channel+runId listeners
|
|
148
|
-
const runKey = `${channel}::${event.runId}`;
|
|
149
|
-
const runMap = this.#channelRunListeners.get(runKey);
|
|
150
|
-
if (runMap) {
|
|
151
|
-
const runChannelListeners = runMap.get(channel);
|
|
152
|
-
if (runChannelListeners) {
|
|
153
|
-
for (const cb of runChannelListeners) {
|
|
154
|
-
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Dispose all subscriptions including channel-based ones. */
|
|
161
|
-
dispose(): void {
|
|
162
|
-
this.#listeners.clear();
|
|
163
|
-
this.#globalListeners.clear();
|
|
164
|
-
this.#channelListeners.clear();
|
|
165
|
-
this.#channelRunListeners.clear();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
listenerCount(runId?: string): number {
|
|
169
|
-
if (runId) return this.#listeners.get(runId)?.size ?? 0;
|
|
170
|
-
let total = this.#globalListeners.size;
|
|
171
|
-
for (const set of this.#listeners.values()) total += set.size;
|
|
172
|
-
for (const set of this.#channelListeners.values()) total += set.size;
|
|
173
|
-
for (const runMap of this.#channelRunListeners.values()) {
|
|
174
|
-
for (const set of runMap.values()) total += set.size;
|
|
175
|
-
}
|
|
176
|
-
return total;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Global singleton run event bus for UI-first event delivery. */
|
|
181
|
-
export const runEventBus = new RunEventBus();
|
|
182
|
-
|
|
183
|
-
/** Derive a RunEventType from a TeamEvent. */
|
|
184
|
-
export function teamEventToRunEventType(event: TeamEvent): RunEventType | undefined {
|
|
185
|
-
const type = event.type;
|
|
186
|
-
if (type === "task.started") return "task_started";
|
|
187
|
-
if (type === "task.completed") return "task_completed";
|
|
188
|
-
if (type === "task.failed") return "task_failed";
|
|
189
|
-
if (type === "run.completed") return "run_completed";
|
|
190
|
-
if (type === "run.blocked") return "run_blocked";
|
|
191
|
-
if (type === "run.running") return "run_started";
|
|
192
|
-
if (type === "run.cancelled") return "run_cancelled";
|
|
193
|
-
if (type === "task.progress" || type === "mailbox.message_queued" || type === "mailbox.message_delivered") return "mailbox_updated";
|
|
194
|
-
if (type === "run.effectiveness" || type === "task.attention") return "effectiveness_changed";
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Emit a run event from a TeamEvent. */
|
|
199
|
-
export function emitFromTeamEvent(event: TeamEvent): void {
|
|
200
|
-
const type = teamEventToRunEventType(event);
|
|
201
|
-
if (!type) return;
|
|
202
|
-
runEventBus.emit({
|
|
203
|
-
type,
|
|
204
|
-
runId: event.runId,
|
|
205
|
-
taskId: event.taskId,
|
|
206
|
-
timestamp: event.time,
|
|
207
|
-
data: event.data,
|
|
208
|
-
});
|
|
1
|
+
import type { TeamEvent } from "../state/event-log.ts";
|
|
2
|
+
|
|
3
|
+
export type RunEventType =
|
|
4
|
+
| "task_started"
|
|
5
|
+
| "task_completed"
|
|
6
|
+
| "task_failed"
|
|
7
|
+
| "task_cancelled"
|
|
8
|
+
| "worker_status"
|
|
9
|
+
| "mailbox_updated"
|
|
10
|
+
| "effectiveness_changed"
|
|
11
|
+
| "run_started"
|
|
12
|
+
| "run_completed"
|
|
13
|
+
| "run_blocked"
|
|
14
|
+
| "run_cancelled";
|
|
15
|
+
|
|
16
|
+
/** Typed channel names for category-based event subscription. */
|
|
17
|
+
export type EventChannel =
|
|
18
|
+
| "worker:progress"
|
|
19
|
+
| "worker:lifecycle"
|
|
20
|
+
| "worker:stream"
|
|
21
|
+
| "run:state"
|
|
22
|
+
| "ui:invalidate";
|
|
23
|
+
|
|
24
|
+
/** Sets used by classifyEventChannel for O(1) lookup. */
|
|
25
|
+
const WORKER_PROGRESS_TYPES = new Set([
|
|
26
|
+
"tool_execution_start", "tool_result", "agent_progress", "worker_status",
|
|
27
|
+
]);
|
|
28
|
+
const WORKER_LIFECYCLE_TYPES = new Set([
|
|
29
|
+
"task.started", "task.completed", "task.failed",
|
|
30
|
+
"task_started", "task_completed", "task_failed", "task_cancelled",
|
|
31
|
+
"run.started", "run.completed", "run.cancelled", "run.failed",
|
|
32
|
+
"run_started", "run_completed", "run_cancelled", "run_blocked",
|
|
33
|
+
]);
|
|
34
|
+
const WORKER_STREAM_TYPES = new Set([
|
|
35
|
+
"stdout_chunk", "stderr_chunk", "stream",
|
|
36
|
+
]);
|
|
37
|
+
const RUN_STATE_TYPES = new Set([
|
|
38
|
+
"manifest.saved", "task.claimed", "task.unclaimed", "mailbox_updated",
|
|
39
|
+
]);
|
|
40
|
+
const UI_INVALIDATE_TYPES = new Set([
|
|
41
|
+
"effectiveness_changed", "snapshot_stale",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
/** Classify an event type string into a typed channel. */
|
|
45
|
+
export function classifyEventChannel(type: string): EventChannel {
|
|
46
|
+
if (WORKER_PROGRESS_TYPES.has(type)) return "worker:progress";
|
|
47
|
+
if (WORKER_LIFECYCLE_TYPES.has(type)) return "worker:lifecycle";
|
|
48
|
+
if (WORKER_STREAM_TYPES.has(type)) return "worker:stream";
|
|
49
|
+
if (RUN_STATE_TYPES.has(type)) return "run:state";
|
|
50
|
+
if (UI_INVALIDATE_TYPES.has(type)) return "ui:invalidate";
|
|
51
|
+
return "worker:progress"; // default fallback
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface RunEventPayload {
|
|
55
|
+
type: RunEventType;
|
|
56
|
+
runId: string;
|
|
57
|
+
taskId?: string;
|
|
58
|
+
timestamp?: string;
|
|
59
|
+
data?: unknown;
|
|
60
|
+
channel?: EventChannel;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type RunEventCallback = (event: RunEventPayload) => void;
|
|
64
|
+
|
|
65
|
+
class RunEventBus {
|
|
66
|
+
#listeners = new Map<string, Set<RunEventCallback>>();
|
|
67
|
+
#globalListeners = new Set<RunEventCallback>();
|
|
68
|
+
#channelListeners = new Map<EventChannel, Set<RunEventCallback>>();
|
|
69
|
+
#channelRunListeners = new Map<string, Map<EventChannel, Set<RunEventCallback>>>();
|
|
70
|
+
|
|
71
|
+
on(runId: string, callback: RunEventCallback): () => void {
|
|
72
|
+
const listeners = this.#listeners.get(runId) ?? new Set();
|
|
73
|
+
listeners.add(callback);
|
|
74
|
+
this.#listeners.set(runId, listeners);
|
|
75
|
+
return () => { listeners.delete(callback); if (listeners.size === 0) this.#listeners.delete(runId); };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onAny(callback: RunEventCallback): () => void {
|
|
79
|
+
this.#globalListeners.add(callback);
|
|
80
|
+
return () => { this.#globalListeners.delete(callback); };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
off(runId: string, callback: RunEventCallback): void {
|
|
84
|
+
const listeners = this.#listeners.get(runId);
|
|
85
|
+
if (listeners) {
|
|
86
|
+
listeners.delete(callback);
|
|
87
|
+
if (listeners.size === 0) this.#listeners.delete(runId);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Subscribe to all events on a specific channel. */
|
|
92
|
+
onChannel(channel: EventChannel, callback: RunEventCallback): () => void {
|
|
93
|
+
const listeners = this.#channelListeners.get(channel) ?? new Set();
|
|
94
|
+
listeners.add(callback);
|
|
95
|
+
this.#channelListeners.set(channel, listeners);
|
|
96
|
+
return () => {
|
|
97
|
+
listeners.delete(callback);
|
|
98
|
+
if (listeners.size === 0) this.#channelListeners.delete(channel);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Subscribe to events on a specific channel for a given runId. */
|
|
103
|
+
onChannelForRun(channel: EventChannel, runId: string, callback: RunEventCallback): () => void {
|
|
104
|
+
const runKey = `${channel}::${runId}`;
|
|
105
|
+
const runMap = this.#channelRunListeners.get(runKey) ?? new Map();
|
|
106
|
+
const listeners = runMap.get(channel) ?? new Set();
|
|
107
|
+
listeners.add(callback);
|
|
108
|
+
runMap.set(channel, listeners);
|
|
109
|
+
this.#channelRunListeners.set(runKey, runMap);
|
|
110
|
+
return () => {
|
|
111
|
+
listeners.delete(callback);
|
|
112
|
+
if (listeners.size === 0) runMap.delete(channel);
|
|
113
|
+
if (runMap.size === 0) this.#channelRunListeners.delete(runKey);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
emit(event: RunEventPayload): void {
|
|
118
|
+
// Auto-classify channel if not already set.
|
|
119
|
+
// M2: Use local variable for routing, but also set on event
|
|
120
|
+
// for subscriber API contract (listeners read event.channel).
|
|
121
|
+
if (!event.channel) {
|
|
122
|
+
(event as { channel?: EventChannel }).channel = classifyEventChannel(event.type);
|
|
123
|
+
}
|
|
124
|
+
const channel = event.channel!;
|
|
125
|
+
|
|
126
|
+
// Existing: runId-specific listeners
|
|
127
|
+
const listeners = this.#listeners.get(event.runId);
|
|
128
|
+
if (listeners) {
|
|
129
|
+
for (const cb of listeners) {
|
|
130
|
+
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Existing: global listeners
|
|
135
|
+
for (const cb of this.#globalListeners) {
|
|
136
|
+
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// New: channel listeners
|
|
140
|
+
const channelListeners = this.#channelListeners.get(channel);
|
|
141
|
+
if (channelListeners) {
|
|
142
|
+
for (const cb of channelListeners) {
|
|
143
|
+
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// New: channel+runId listeners
|
|
148
|
+
const runKey = `${channel}::${event.runId}`;
|
|
149
|
+
const runMap = this.#channelRunListeners.get(runKey);
|
|
150
|
+
if (runMap) {
|
|
151
|
+
const runChannelListeners = runMap.get(channel);
|
|
152
|
+
if (runChannelListeners) {
|
|
153
|
+
for (const cb of runChannelListeners) {
|
|
154
|
+
try { cb(event); } catch { /* subscriber errors are non-fatal */ }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Dispose all subscriptions including channel-based ones. */
|
|
161
|
+
dispose(): void {
|
|
162
|
+
this.#listeners.clear();
|
|
163
|
+
this.#globalListeners.clear();
|
|
164
|
+
this.#channelListeners.clear();
|
|
165
|
+
this.#channelRunListeners.clear();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
listenerCount(runId?: string): number {
|
|
169
|
+
if (runId) return this.#listeners.get(runId)?.size ?? 0;
|
|
170
|
+
let total = this.#globalListeners.size;
|
|
171
|
+
for (const set of this.#listeners.values()) total += set.size;
|
|
172
|
+
for (const set of this.#channelListeners.values()) total += set.size;
|
|
173
|
+
for (const runMap of this.#channelRunListeners.values()) {
|
|
174
|
+
for (const set of runMap.values()) total += set.size;
|
|
175
|
+
}
|
|
176
|
+
return total;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Global singleton run event bus for UI-first event delivery. */
|
|
181
|
+
export const runEventBus = new RunEventBus();
|
|
182
|
+
|
|
183
|
+
/** Derive a RunEventType from a TeamEvent. */
|
|
184
|
+
export function teamEventToRunEventType(event: TeamEvent): RunEventType | undefined {
|
|
185
|
+
const type = event.type;
|
|
186
|
+
if (type === "task.started") return "task_started";
|
|
187
|
+
if (type === "task.completed") return "task_completed";
|
|
188
|
+
if (type === "task.failed") return "task_failed";
|
|
189
|
+
if (type === "run.completed") return "run_completed";
|
|
190
|
+
if (type === "run.blocked") return "run_blocked";
|
|
191
|
+
if (type === "run.running") return "run_started";
|
|
192
|
+
if (type === "run.cancelled") return "run_cancelled";
|
|
193
|
+
if (type === "task.progress" || type === "mailbox.message_queued" || type === "mailbox.message_delivered") return "mailbox_updated";
|
|
194
|
+
if (type === "run.effectiveness" || type === "task.attention") return "effectiveness_changed";
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Emit a run event from a TeamEvent. */
|
|
199
|
+
export function emitFromTeamEvent(event: TeamEvent): void {
|
|
200
|
+
const type = teamEventToRunEventType(event);
|
|
201
|
+
if (!type) return;
|
|
202
|
+
runEventBus.emit({
|
|
203
|
+
type,
|
|
204
|
+
runId: event.runId,
|
|
205
|
+
taskId: event.taskId,
|
|
206
|
+
timestamp: event.time,
|
|
207
|
+
data: event.data,
|
|
208
|
+
});
|
|
209
209
|
}
|