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
|
@@ -1,179 +1,377 @@
|
|
|
1
|
-
import type { CrewAgentRecord } from "./crew-agent-runtime.ts";
|
|
2
|
-
import type { IrcMessage } from "./live-irc.ts";
|
|
3
|
-
|
|
4
|
-
type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
1
|
+
import type { CrewAgentRecord } from "./crew-agent-runtime.ts";
|
|
2
|
+
import type { IrcMessage } from "./live-irc.ts";
|
|
3
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
4
|
+
import type { appendEvent } from "../state/event-log.ts";
|
|
5
|
+
|
|
6
|
+
type LiveSessionHandle = {
|
|
7
|
+
steer?: (text: string) => Promise<void>;
|
|
8
|
+
prompt?: (text: string, options?: Record<string, unknown>) => Promise<void>;
|
|
9
|
+
abort?: () => Promise<void> | void;
|
|
10
|
+
dispose?: () => void;
|
|
11
|
+
/** Upstream session stats (input/output/cacheWrite tokens, context %). */
|
|
12
|
+
getSessionStats?: () => {
|
|
13
|
+
tokens?: { input?: number; output?: number; cacheWrite?: number; cacheRead?: number };
|
|
14
|
+
contextUsage?: { percent?: number | null; window?: number };
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Real-time activity state for a live-session agent. */
|
|
19
|
+
export interface LiveAgentActivity {
|
|
20
|
+
/** Currently active tools (toolName → description). */
|
|
21
|
+
activeTools: Map<string, string>;
|
|
22
|
+
/** Total tool invocations. */
|
|
23
|
+
toolUses: number;
|
|
24
|
+
/** Current turn count. */
|
|
25
|
+
turnCount: number;
|
|
26
|
+
/** Effective max turns (undefined = unlimited). */
|
|
27
|
+
maxTurns?: number;
|
|
28
|
+
/** Latest assistant text snippet. */
|
|
29
|
+
responseText: string;
|
|
30
|
+
/** Number of context compactions survived. */
|
|
31
|
+
compactionCount: number;
|
|
32
|
+
/** Started-at timestamp (ms epoch). */
|
|
33
|
+
startedAtMs: number;
|
|
34
|
+
/** Completed-at timestamp (ms epoch, 0 = still running). */
|
|
35
|
+
completedAtMs: number;
|
|
36
|
+
/** Model name used for this agent (e.g. "sonnet", "haiku"). */
|
|
37
|
+
modelName?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface LiveAgentHandle {
|
|
41
|
+
agentId: string;
|
|
42
|
+
taskId: string;
|
|
43
|
+
runId: string;
|
|
44
|
+
/** Workspace where this agent was spawned — used for session-scoped visibility. */
|
|
45
|
+
workspaceId: string;
|
|
46
|
+
role?: string;
|
|
47
|
+
agent?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
/** Model name used for this agent (e.g. "sonnet", "haiku"). */
|
|
50
|
+
modelName?: string;
|
|
51
|
+
session: LiveSessionHandle;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
updatedAt: string;
|
|
54
|
+
status: CrewAgentRecord["status"];
|
|
55
|
+
pendingSteers: string[];
|
|
56
|
+
pendingFollowUps: string[];
|
|
57
|
+
/** Phase 7: Pending IRC messages for this agent. */
|
|
58
|
+
pendingMessages: IrcMessage[];
|
|
59
|
+
/** G1-G6: Real-time activity tracking (in-memory only). */
|
|
60
|
+
activity: LiveAgentActivity;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const liveAgents = new Map<string, LiveAgentHandle>();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* List all live agents for a specific workspace.
|
|
67
|
+
* Only agents belonging to the given workspaceId are returned.
|
|
68
|
+
*/
|
|
69
|
+
export function listLiveAgentsByWorkspace(workspaceId: string): LiveAgentHandle[] {
|
|
70
|
+
return listLiveAgents().filter((a) => a.workspaceId === workspaceId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* List only active agents (running/queued/waiting) for a specific workspace.
|
|
75
|
+
*/
|
|
76
|
+
export function listActiveLiveAgentsByWorkspace(workspaceId: string): LiveAgentHandle[] {
|
|
77
|
+
return listActiveLiveAgents().filter((a) => a.workspaceId === workspaceId);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function registerLiveAgent(input: Omit<LiveAgentHandle, "createdAt" | "updatedAt" | "pendingSteers" | "pendingFollowUps" | "pendingMessages" | "activity"> & { workspaceId: string }, eventLogFn?: typeof appendEvent, eventsPath?: string): LiveAgentHandle {
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
const existing = liveAgents.get(input.agentId);
|
|
83
|
+
const handle: LiveAgentHandle = {
|
|
84
|
+
...input,
|
|
85
|
+
createdAt: existing?.createdAt ?? now,
|
|
86
|
+
updatedAt: now,
|
|
87
|
+
pendingSteers: existing?.pendingSteers ?? [],
|
|
88
|
+
pendingFollowUps: existing?.pendingFollowUps ?? [],
|
|
89
|
+
pendingMessages: existing?.pendingMessages ?? [],
|
|
90
|
+
activity: existing?.activity ?? {
|
|
91
|
+
activeTools: new Map(),
|
|
92
|
+
toolUses: 0,
|
|
93
|
+
turnCount: 0,
|
|
94
|
+
responseText: "",
|
|
95
|
+
compactionCount: 0,
|
|
96
|
+
startedAtMs: Date.now(),
|
|
97
|
+
completedAtMs: 0,
|
|
98
|
+
modelName: undefined,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
liveAgents.set(input.agentId, handle);
|
|
102
|
+
try { if (eventLogFn && eventsPath) eventLogFn(eventsPath, { type: "live_agent.registered", runId: input.runId, taskId: input.taskId, message: `Live agent registered: ${input.agent} (${input.role})`, data: { agentId: input.agentId, role: input.role, agent: input.agent, workspaceId: input.workspaceId } }); } catch { /* non-critical */ }
|
|
103
|
+
if (handle.pendingSteers.length && typeof handle.session.steer === "function") {
|
|
104
|
+
const pending = [...handle.pendingSteers];
|
|
105
|
+
handle.pendingSteers.length = 0;
|
|
106
|
+
for (const message of pending) void handle.session.steer(message).catch(() => {});
|
|
107
|
+
}
|
|
108
|
+
if (handle.pendingFollowUps.length && typeof handle.session.prompt === "function") {
|
|
109
|
+
const pending = [...handle.pendingFollowUps];
|
|
110
|
+
handle.pendingFollowUps.length = 0;
|
|
111
|
+
for (const message of pending) void handle.session.prompt(message, { source: "api", expandPromptTemplates: false }).catch(() => {});
|
|
112
|
+
}
|
|
113
|
+
return handle;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function updateLiveAgentStatus(agentId: string, status: CrewAgentRecord["status"]): void {
|
|
117
|
+
const handle = liveAgents.get(agentId);
|
|
118
|
+
if (!handle) return;
|
|
119
|
+
handle.status = status;
|
|
120
|
+
handle.updatedAt = new Date().toISOString();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function safeDisposeLiveSession(handle: LiveAgentHandle): void {
|
|
124
|
+
try { handle.session.dispose?.(); } catch (error) {
|
|
125
|
+
logInternalError("live-agent-manager.dispose", error, `agentId=${handle.agentId}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function removeLiveAgentHandle(agentId: string): LiveAgentHandle | undefined {
|
|
130
|
+
const handle = liveAgents.get(agentId);
|
|
131
|
+
if (!handle) return undefined;
|
|
132
|
+
liveAgents.delete(agentId);
|
|
133
|
+
safeDisposeLiveSession(handle);
|
|
134
|
+
return handle;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function disposeLiveAgentSession(agentIdOrTaskId: string): void {
|
|
138
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
139
|
+
if (!handle) return;
|
|
140
|
+
safeDisposeLiveSession(handle);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function terminateLiveAgent(agentIdOrTaskId: string, status: CrewAgentRecord["status"] = "stopped", eventLogFn?: typeof appendEvent, eventsPath?: string): Promise<LiveAgentHandle | undefined> {
|
|
144
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
145
|
+
if (!handle) return undefined;
|
|
146
|
+
handle.status = status;
|
|
147
|
+
handle.updatedAt = new Date().toISOString();
|
|
148
|
+
liveAgents.delete(handle.agentId);
|
|
149
|
+
try { if (eventLogFn && eventsPath) eventLogFn(eventsPath, { type: "live_agent.terminated", runId: handle.runId, taskId: handle.taskId, message: `Live agent terminated: ${handle.agent} status=${status}`, data: { agentId: handle.agentId, status, role: handle.role, workspaceId: handle.workspaceId } }); } catch { /* non-critical */ }
|
|
150
|
+
try {
|
|
151
|
+
await handle.session.abort?.();
|
|
152
|
+
} finally {
|
|
153
|
+
safeDisposeLiveSession(handle);
|
|
154
|
+
}
|
|
155
|
+
return handle;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function terminateLiveAgentsForRun(runId: string, status: CrewAgentRecord["status"] = "failed", eventLogFn?: typeof appendEvent, eventsPath?: string): Promise<number> {
|
|
159
|
+
const agents = [...liveAgents.values()].filter((agent) => agent.runId === runId);
|
|
160
|
+
await Promise.all(agents.map((agent) => terminateLiveAgent(agent.agentId, status, eventLogFn, eventsPath)));
|
|
161
|
+
return agents.length;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function getLiveAgent(agentIdOrTaskId: string): LiveAgentHandle | undefined {
|
|
165
|
+
return liveAgents.get(agentIdOrTaskId) ?? [...liveAgents.values()].find((entry) => entry.taskId === agentIdOrTaskId);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Maximum time a terminal live agent handle stays in memory (10 minutes). */
|
|
169
|
+
const STALE_HANDLE_MS = 10 * 60 * 1000;
|
|
170
|
+
/** Maximum time a running/queued live agent handle stays without any update (30 minutes).
|
|
171
|
+
* After this, the agent is presumed dead — the real process would have updated the handle. */
|
|
172
|
+
const STALE_RUNNING_HANDLE_MS = 30 * 60 * 1000;
|
|
173
|
+
|
|
174
|
+
/** Remove dead live agent handles.
|
|
175
|
+
* Evicts: (1) terminal-status handles older than STALE_HANDLE_MS, and
|
|
176
|
+
* (2) running/queued handles with no update for STALE_RUNNING_HANDLE_MS.
|
|
177
|
+
* Called periodically by the widget refresh cycle.
|
|
178
|
+
* Returns the number of handles evicted.
|
|
179
|
+
*/
|
|
180
|
+
export function evictStaleLiveAgentHandles(now = Date.now()): number {
|
|
181
|
+
let evicted = 0;
|
|
182
|
+
for (const [agentId, handle] of liveAgents) {
|
|
183
|
+
const age = now - new Date(handle.updatedAt).getTime();
|
|
184
|
+
const isActive = handle.status === "running" || handle.status === "queued" || handle.status === "waiting";
|
|
185
|
+
if (!isActive) {
|
|
186
|
+
// Terminal handle — evict after grace period
|
|
187
|
+
if (age > STALE_HANDLE_MS) {
|
|
188
|
+
liveAgents.delete(agentId);
|
|
189
|
+
safeDisposeLiveSession(handle);
|
|
190
|
+
evicted++;
|
|
191
|
+
}
|
|
192
|
+
} else if (age > STALE_RUNNING_HANDLE_MS) {
|
|
193
|
+
// Active-status handle with no update for 30min — presumed dead
|
|
194
|
+
liveAgents.delete(agentId);
|
|
195
|
+
safeDisposeLiveSession(handle);
|
|
196
|
+
evicted++;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return evicted;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function listLiveAgents(): LiveAgentHandle[] {
|
|
203
|
+
return [...liveAgents.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function listActiveLiveAgents(): LiveAgentHandle[] {
|
|
207
|
+
return listLiveAgents().filter((agent) => agent.status === "running" || agent.status === "queued" || agent.status === "waiting");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function getLiveAgentContextPercent(agentIdOrTaskId: string): number | null {
|
|
211
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
212
|
+
if (!handle || handle.status !== "running") return null;
|
|
213
|
+
try {
|
|
214
|
+
return handle.session.getSessionStats?.().contextUsage?.percent ?? null;
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function steerLiveAgent(agentIdOrTaskId: string, message: string): Promise<LiveAgentHandle> {
|
|
221
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
222
|
+
if (!handle) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
223
|
+
if (typeof handle.session.steer !== "function") {
|
|
224
|
+
handle.pendingSteers.push(message);
|
|
225
|
+
return handle;
|
|
226
|
+
}
|
|
227
|
+
await handle.session.steer(message);
|
|
228
|
+
handle.updatedAt = new Date().toISOString();
|
|
229
|
+
return handle;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function followUpLiveAgent(agentIdOrTaskId: string, prompt: string): Promise<LiveAgentHandle> {
|
|
233
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
234
|
+
if (!handle) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
235
|
+
if (typeof handle.session.prompt !== "function") {
|
|
236
|
+
handle.pendingFollowUps.push(prompt);
|
|
237
|
+
return handle;
|
|
238
|
+
}
|
|
239
|
+
await handle.session.prompt(prompt, { source: "api", expandPromptTemplates: false });
|
|
240
|
+
handle.updatedAt = new Date().toISOString();
|
|
241
|
+
return handle;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export async function stopLiveAgent(agentIdOrTaskId: string): Promise<LiveAgentHandle> {
|
|
245
|
+
const stopped = await terminateLiveAgent(agentIdOrTaskId, "stopped");
|
|
246
|
+
if (!stopped) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
247
|
+
return stopped;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export async function resumeLiveAgent(agentIdOrTaskId: string, prompt: string): Promise<LiveAgentHandle> {
|
|
251
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
252
|
+
if (!handle) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
253
|
+
if (typeof handle.session.prompt !== "function") throw new Error(`Live agent '${agentIdOrTaskId}' does not expose prompt().`);
|
|
254
|
+
handle.status = "running";
|
|
255
|
+
await handle.session.prompt(prompt, { source: "api", expandPromptTemplates: false });
|
|
256
|
+
handle.status = "completed";
|
|
257
|
+
handle.updatedAt = new Date().toISOString();
|
|
258
|
+
return handle;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** G2: Track tool start for a live agent. */
|
|
262
|
+
export function trackLiveAgentToolStart(agentIdOrTaskId: string, toolName: string): void {
|
|
263
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
264
|
+
if (!handle) return;
|
|
265
|
+
handle.activity.activeTools.set(toolName, toolName);
|
|
266
|
+
handle.activity.toolUses++;
|
|
267
|
+
handle.updatedAt = new Date().toISOString();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** G2: Track tool end for a live agent. */
|
|
271
|
+
export function trackLiveAgentToolEnd(agentIdOrTaskId: string, toolName: string): void {
|
|
272
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
273
|
+
if (!handle) return;
|
|
274
|
+
handle.activity.activeTools.delete(toolName);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** G3/G6: Track turn end and compaction. */
|
|
278
|
+
export function trackLiveAgentTurnEnd(agentIdOrTaskId: string, compaction = false): void {
|
|
279
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
280
|
+
if (!handle) return;
|
|
281
|
+
handle.activity.turnCount++;
|
|
282
|
+
if (compaction) handle.activity.compactionCount++;
|
|
283
|
+
handle.activity.activeTools.clear();
|
|
284
|
+
handle.updatedAt = new Date().toISOString();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** G2: Track assistant response text. */
|
|
288
|
+
export function trackLiveAgentResponseText(agentIdOrTaskId: string, text: string): void {
|
|
289
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
290
|
+
if (!handle) return;
|
|
291
|
+
handle.activity.responseText = text.slice(-200);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Mark live agent completed with timestamp. */
|
|
295
|
+
export function markLiveAgentCompleted(agentIdOrTaskId: string): void {
|
|
296
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
297
|
+
if (!handle) return;
|
|
298
|
+
handle.activity.completedAtMs = Date.now();
|
|
299
|
+
handle.activity.activeTools.clear();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function clearLiveAgentsForTest(): void {
|
|
303
|
+
liveAgents.clear();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Phase 7/G4: Send an IRC message to a specific live agent (DM).
|
|
307
|
+
* Uses non-blocking delivery via sendCustomMessage when available.
|
|
308
|
+
* Falls back to session.prompt (blocking) when not.
|
|
309
|
+
*/
|
|
310
|
+
export function sendIrcMessage(targetAgentId: string, message: IrcMessage): void {
|
|
311
|
+
const handle = getLiveAgent(targetAgentId);
|
|
312
|
+
if (!handle) return;
|
|
313
|
+
handle.pendingMessages.push(message);
|
|
314
|
+
handle.updatedAt = new Date().toISOString();
|
|
315
|
+
// G4: Try non-blocking delivery via sendCustomMessage
|
|
316
|
+
const session = handle.session as Record<string, unknown>;
|
|
317
|
+
if (typeof session.sendCustomMessage === "function") {
|
|
318
|
+
try {
|
|
319
|
+
(session.sendCustomMessage as (msg: unknown, opts?: unknown) => void)(
|
|
320
|
+
{ customType: "irc", content: `[DM from ${message.from}] ${message.content}`, display: "collapsed" },
|
|
321
|
+
{ deliverAs: "followUp", triggerTurn: false },
|
|
322
|
+
);
|
|
323
|
+
return;
|
|
324
|
+
} catch {
|
|
325
|
+
// Fall through to prompt-based delivery
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Fallback: inject as prompt (blocking)
|
|
329
|
+
if (typeof handle.session.prompt === "function") {
|
|
330
|
+
const ircPrompt = `[Message from ${message.from}] ${message.content}`;
|
|
331
|
+
void handle.session.prompt(ircPrompt, { source: "api", expandPromptTemplates: false }).catch(() => {});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Phase 7/G4: Broadcast an IRC message to all live agents except the sender.
|
|
336
|
+
* Uses non-blocking delivery via sendCustomMessage when available.
|
|
337
|
+
* Returns recipient IDs.
|
|
338
|
+
*/
|
|
339
|
+
export function broadcastIrcMessage(fromAgentId: string, message: IrcMessage): string[] {
|
|
340
|
+
const recipients: string[] = [];
|
|
341
|
+
for (const handle of liveAgents.values()) {
|
|
342
|
+
if (handle.agentId === fromAgentId) continue;
|
|
343
|
+
if (handle.status !== "running" && handle.status !== "queued") continue;
|
|
344
|
+
handle.pendingMessages.push(message);
|
|
345
|
+
handle.updatedAt = new Date().toISOString();
|
|
346
|
+
// G4: Try non-blocking delivery
|
|
347
|
+
const session = handle.session as Record<string, unknown>;
|
|
348
|
+
if (typeof session.sendCustomMessage === "function") {
|
|
349
|
+
try {
|
|
350
|
+
(session.sendCustomMessage as (msg: unknown, opts?: unknown) => void)(
|
|
351
|
+
{ customType: "irc", content: `[Broadcast from ${message.from}] ${message.content}`, display: "collapsed" },
|
|
352
|
+
{ deliverAs: "followUp", triggerTurn: false },
|
|
353
|
+
);
|
|
354
|
+
recipients.push(handle.agentId);
|
|
355
|
+
continue;
|
|
356
|
+
} catch {
|
|
357
|
+
// Fall through to prompt-based delivery
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Fallback: inject as prompt
|
|
361
|
+
if (typeof handle.session.prompt === "function") {
|
|
362
|
+
const ircPrompt = `[Broadcast from ${message.from}] ${message.content}`;
|
|
363
|
+
void handle.session.prompt(ircPrompt, { source: "api", expandPromptTemplates: false }).catch(() => {});
|
|
364
|
+
}
|
|
365
|
+
recipients.push(handle.agentId);
|
|
366
|
+
}
|
|
367
|
+
return recipients;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Phase 7: Get pending IRC messages for an agent (and clear them). */
|
|
371
|
+
export function drainIrcMessages(agentIdOrTaskId: string): IrcMessage[] {
|
|
372
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
373
|
+
if (!handle) return [];
|
|
374
|
+
const messages = [...handle.pendingMessages];
|
|
375
|
+
handle.pendingMessages.length = 0;
|
|
376
|
+
return messages;
|
|
377
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import type { LiveAgentControlRequest } from "./live-agent-control.ts";
|
|
2
|
-
|
|
3
|
-
export interface LiveControlRealtimeMessage {
|
|
4
|
-
type: "live-control";
|
|
5
|
-
version: 1;
|
|
6
|
-
request: LiveAgentControlRequest;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
type Listener = (request: LiveAgentControlRequest) => void | Promise<void>;
|
|
10
|
-
|
|
11
|
-
const listeners = new Set<Listener>();
|
|
12
|
-
|
|
13
|
-
export function publishLiveControlRealtime(request: LiveAgentControlRequest): void {
|
|
14
|
-
for (const listener of [...listeners]) void listener(request);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function subscribeLiveControlRealtime(listener: Listener): () => void {
|
|
18
|
-
listeners.add(listener);
|
|
19
|
-
return () => listeners.delete(listener);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function liveControlRealtimeMessage(request: LiveAgentControlRequest): LiveControlRealtimeMessage {
|
|
23
|
-
return { type: "live-control", version: 1, request };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function parseLiveControlRealtimeMessage(raw: unknown): LiveAgentControlRequest | undefined {
|
|
27
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
|
|
28
|
-
const message = raw as { type?: unknown; version?: unknown; request?: unknown };
|
|
29
|
-
if (message.type !== "live-control" || message.version !== 1 || !message.request || typeof message.request !== "object" || Array.isArray(message.request)) return undefined;
|
|
30
|
-
const request = message.request as Partial<LiveAgentControlRequest>;
|
|
31
|
-
return typeof request.id === "string" && typeof request.runId === "string" && typeof request.taskId === "string" && (request.operation === "steer" || request.operation === "follow-up" || request.operation === "stop" || request.operation === "resume") && typeof request.createdAt === "string" ? request as LiveAgentControlRequest : undefined;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function clearLiveControlRealtimeForTest(): void {
|
|
35
|
-
listeners.clear();
|
|
36
|
-
}
|
|
1
|
+
import type { LiveAgentControlRequest } from "./live-agent-control.ts";
|
|
2
|
+
|
|
3
|
+
export interface LiveControlRealtimeMessage {
|
|
4
|
+
type: "live-control";
|
|
5
|
+
version: 1;
|
|
6
|
+
request: LiveAgentControlRequest;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type Listener = (request: LiveAgentControlRequest) => void | Promise<void>;
|
|
10
|
+
|
|
11
|
+
const listeners = new Set<Listener>();
|
|
12
|
+
|
|
13
|
+
export function publishLiveControlRealtime(request: LiveAgentControlRequest): void {
|
|
14
|
+
for (const listener of [...listeners]) void listener(request);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function subscribeLiveControlRealtime(listener: Listener): () => void {
|
|
18
|
+
listeners.add(listener);
|
|
19
|
+
return () => listeners.delete(listener);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function liveControlRealtimeMessage(request: LiveAgentControlRequest): LiveControlRealtimeMessage {
|
|
23
|
+
return { type: "live-control", version: 1, request };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseLiveControlRealtimeMessage(raw: unknown): LiveAgentControlRequest | undefined {
|
|
27
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
|
|
28
|
+
const message = raw as { type?: unknown; version?: unknown; request?: unknown };
|
|
29
|
+
if (message.type !== "live-control" || message.version !== 1 || !message.request || typeof message.request !== "object" || Array.isArray(message.request)) return undefined;
|
|
30
|
+
const request = message.request as Partial<LiveAgentControlRequest>;
|
|
31
|
+
return typeof request.id === "string" && typeof request.runId === "string" && typeof request.taskId === "string" && (request.operation === "steer" || request.operation === "follow-up" || request.operation === "stop" || request.operation === "resume") && typeof request.createdAt === "string" ? request as LiveAgentControlRequest : undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function clearLiveControlRealtimeForTest(): void {
|
|
35
|
+
listeners.clear();
|
|
36
|
+
}
|