agent-tempo 1.2.0 → 1.4.0

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.
Files changed (281) hide show
  1. package/CLAUDE.md +253 -219
  2. package/LICENSE +21 -21
  3. package/README.md +293 -289
  4. package/assets/icon-dark.svg +9 -9
  5. package/assets/icon.svg +9 -9
  6. package/assets/logo-dark.svg +11 -11
  7. package/assets/logo-light.svg +11 -11
  8. package/dashboard/README.md +91 -91
  9. package/dashboard/dist/assets/{index-D6Xyje_n.js → index-jmYe6rmS.js} +2 -2
  10. package/dashboard/dist/assets/index-jmYe6rmS.js.map +1 -0
  11. package/dashboard/dist/index.html +20 -20
  12. package/dashboard/package.json +47 -47
  13. package/dist/activities/outbox.d.ts +30 -1
  14. package/dist/activities/outbox.js +96 -3
  15. package/dist/adapters/base.js +5 -0
  16. package/dist/adapters/copilot/adapter.js +12 -1
  17. package/dist/adapters/index.d.ts +1 -1
  18. package/dist/adapters/index.js +7 -0
  19. package/dist/adapters/pi/adapter.d.ts +2 -0
  20. package/dist/adapters/pi/adapter.js +43 -0
  21. package/dist/adapters/pi/index.d.ts +16 -0
  22. package/dist/adapters/pi/index.js +10 -0
  23. package/dist/cli/global-wrapper.d.ts +19 -0
  24. package/dist/cli/global-wrapper.js +169 -0
  25. package/dist/cli/help-text.js +97 -97
  26. package/dist/cli/startup.js +11 -0
  27. package/dist/cli/upgrade-command.js +81 -81
  28. package/dist/cli.js +12 -0
  29. package/dist/client/core.js +9 -2
  30. package/dist/client/interface.d.ts +6 -0
  31. package/dist/config.d.ts +79 -0
  32. package/dist/config.js +74 -0
  33. package/dist/daemon.js +37 -1
  34. package/dist/http/aggregate.d.ts +22 -1
  35. package/dist/http/aggregate.js +41 -0
  36. package/dist/http/auth.d.ts +94 -8
  37. package/dist/http/auth.js +93 -9
  38. package/dist/http/body.d.ts +4 -1
  39. package/dist/http/body.js +6 -3
  40. package/dist/http/event-bus.js +1 -0
  41. package/dist/http/event-types.d.ts +34 -2
  42. package/dist/http/event-types.js +1 -0
  43. package/dist/http/gate-audit.d.ts +12 -0
  44. package/dist/http/gate-audit.js +95 -0
  45. package/dist/http/gate-registry.d.ts +167 -0
  46. package/dist/http/gate-registry.js +163 -0
  47. package/dist/http/gate-routes.d.ts +48 -0
  48. package/dist/http/gate-routes.js +102 -0
  49. package/dist/http/ingest-registry.d.ts +30 -0
  50. package/dist/http/ingest-registry.js +108 -0
  51. package/dist/http/inner-loop-routes.d.ts +66 -0
  52. package/dist/http/inner-loop-routes.js +182 -0
  53. package/dist/http/inner-loop.d.ts +92 -0
  54. package/dist/http/inner-loop.js +155 -0
  55. package/dist/http/server.d.ts +38 -3
  56. package/dist/http/server.js +211 -6
  57. package/dist/http/snapshot.d.ts +6 -0
  58. package/dist/http/snapshot.js +6 -0
  59. package/dist/pi/cue-pump.d.ts +61 -0
  60. package/dist/pi/cue-pump.js +95 -0
  61. package/dist/pi/extension.d.ts +45 -0
  62. package/dist/pi/extension.js +407 -0
  63. package/dist/pi/gate-client.d.ts +54 -0
  64. package/dist/pi/gate-client.js +136 -0
  65. package/dist/pi/headless.d.ts +85 -0
  66. package/dist/pi/headless.js +224 -0
  67. package/dist/pi/index.d.ts +28 -0
  68. package/dist/pi/index.js +43 -0
  69. package/dist/pi/inner-loop-client.d.ts +67 -0
  70. package/dist/pi/inner-loop-client.js +164 -0
  71. package/dist/pi/inner-loop-publisher.d.ts +187 -0
  72. package/dist/pi/inner-loop-publisher.js +236 -0
  73. package/dist/pi/lazy-proxy.d.ts +37 -0
  74. package/dist/pi/lazy-proxy.js +55 -0
  75. package/dist/pi/mission-control/actions.d.ts +48 -0
  76. package/dist/pi/mission-control/actions.js +98 -0
  77. package/dist/pi/mission-control/board.d.ts +53 -0
  78. package/dist/pi/mission-control/board.js +104 -0
  79. package/dist/pi/mission-control/extension.d.ts +44 -0
  80. package/dist/pi/mission-control/extension.js +251 -0
  81. package/dist/pi/mission-control/index.d.ts +15 -0
  82. package/dist/pi/mission-control/index.js +32 -0
  83. package/dist/pi/mission-control/inner-tail.d.ts +48 -0
  84. package/dist/pi/mission-control/inner-tail.js +76 -0
  85. package/dist/pi/mission-control/pi-ui.d.ts +43 -0
  86. package/dist/pi/mission-control/pi-ui.js +10 -0
  87. package/dist/pi/mission-control/render.d.ts +6 -0
  88. package/dist/pi/mission-control/render.js +95 -0
  89. package/dist/pi/phase-driver.d.ts +74 -0
  90. package/dist/pi/phase-driver.js +122 -0
  91. package/dist/pi/pi-types.d.ts +208 -0
  92. package/dist/pi/pi-types.js +21 -0
  93. package/dist/pi/probe.d.ts +80 -0
  94. package/dist/pi/probe.js +154 -0
  95. package/dist/pi/render-tools.d.ts +17 -0
  96. package/dist/pi/render-tools.js +51 -0
  97. package/dist/pi/reset-pump.d.ts +47 -0
  98. package/dist/pi/reset-pump.js +85 -0
  99. package/dist/pi/tool-capability.d.ts +60 -0
  100. package/dist/pi/tool-capability.js +156 -0
  101. package/dist/pi/workflow-client.d.ts +158 -0
  102. package/dist/pi/workflow-client.js +289 -0
  103. package/dist/pi/zod-to-typebox.d.ts +74 -0
  104. package/dist/pi/zod-to-typebox.js +191 -0
  105. package/dist/scripts/verify-daemon-isolation-guard.js +24 -24
  106. package/dist/server-tools.d.ts +2 -0
  107. package/dist/server-tools.js +50 -46
  108. package/dist/server.js +4 -0
  109. package/dist/spawn.d.ts +55 -0
  110. package/dist/spawn.js +84 -12
  111. package/dist/tools/agent-types.d.ts +2 -2
  112. package/dist/tools/agent-types.js +22 -17
  113. package/dist/tools/attachment-info.d.ts +2 -2
  114. package/dist/tools/attachment-info.js +38 -33
  115. package/dist/tools/broadcast.d.ts +2 -2
  116. package/dist/tools/broadcast.js +69 -64
  117. package/dist/tools/cancel-stage.d.ts +2 -2
  118. package/dist/tools/cancel-stage.js +20 -15
  119. package/dist/tools/clear-state.d.ts +2 -2
  120. package/dist/tools/clear-state.js +25 -20
  121. package/dist/tools/coat-check-evict.d.ts +2 -2
  122. package/dist/tools/coat-check-evict.js +30 -25
  123. package/dist/tools/coat-check-get.d.ts +2 -2
  124. package/dist/tools/coat-check-get.js +39 -34
  125. package/dist/tools/coat-check-list.d.ts +2 -2
  126. package/dist/tools/coat-check-list.js +48 -43
  127. package/dist/tools/coat-check-put.d.ts +2 -2
  128. package/dist/tools/coat-check-put.js +41 -36
  129. package/dist/tools/cue.d.ts +2 -2
  130. package/dist/tools/cue.js +57 -52
  131. package/dist/tools/descriptor.d.ts +72 -0
  132. package/dist/tools/descriptor.js +39 -0
  133. package/dist/tools/destroy.d.ts +2 -2
  134. package/dist/tools/destroy.js +153 -148
  135. package/dist/tools/ensemble.d.ts +2 -2
  136. package/dist/tools/ensemble.js +71 -66
  137. package/dist/tools/evaluate-gate.d.ts +2 -2
  138. package/dist/tools/evaluate-gate.js +33 -27
  139. package/dist/tools/fetch-state.d.ts +2 -2
  140. package/dist/tools/fetch-state.js +43 -38
  141. package/dist/tools/gates.d.ts +2 -2
  142. package/dist/tools/gates.js +39 -34
  143. package/dist/tools/hosts.d.ts +2 -2
  144. package/dist/tools/hosts.js +25 -20
  145. package/dist/tools/listen.d.ts +2 -2
  146. package/dist/tools/listen.js +23 -18
  147. package/dist/tools/load-lineup.d.ts +2 -2
  148. package/dist/tools/load-lineup.js +324 -319
  149. package/dist/tools/migrate.d.ts +2 -2
  150. package/dist/tools/migrate.js +45 -40
  151. package/dist/tools/pause.d.ts +2 -2
  152. package/dist/tools/pause.js +34 -29
  153. package/dist/tools/play.d.ts +2 -2
  154. package/dist/tools/play.js +53 -48
  155. package/dist/tools/quality-gate.d.ts +2 -2
  156. package/dist/tools/quality-gate.js +26 -21
  157. package/dist/tools/recall.d.ts +2 -2
  158. package/dist/tools/recall.js +32 -27
  159. package/dist/tools/recruit.d.ts +2 -2
  160. package/dist/tools/recruit.js +325 -256
  161. package/dist/tools/release.d.ts +2 -2
  162. package/dist/tools/release.js +85 -80
  163. package/dist/tools/report.d.ts +2 -2
  164. package/dist/tools/report.js +28 -23
  165. package/dist/tools/reset.d.ts +3 -0
  166. package/dist/tools/reset.js +51 -0
  167. package/dist/tools/restart.d.ts +2 -2
  168. package/dist/tools/restart.js +51 -46
  169. package/dist/tools/restore.d.ts +2 -2
  170. package/dist/tools/restore.js +76 -71
  171. package/dist/tools/save-lineup.d.ts +2 -2
  172. package/dist/tools/save-lineup.js +32 -27
  173. package/dist/tools/save-state.d.ts +2 -2
  174. package/dist/tools/save-state.js +43 -38
  175. package/dist/tools/schedule.d.ts +2 -2
  176. package/dist/tools/schedule.js +133 -128
  177. package/dist/tools/schedules.d.ts +2 -2
  178. package/dist/tools/schedules.js +41 -36
  179. package/dist/tools/set-ensemble-description.d.ts +2 -2
  180. package/dist/tools/set-ensemble-description.js +26 -21
  181. package/dist/tools/set-name.d.ts +2 -2
  182. package/dist/tools/set-name.js +38 -33
  183. package/dist/tools/set-part.d.ts +2 -2
  184. package/dist/tools/set-part.js +20 -15
  185. package/dist/tools/shutdown.d.ts +2 -2
  186. package/dist/tools/shutdown.js +39 -34
  187. package/dist/tools/stage.d.ts +2 -2
  188. package/dist/tools/stage.js +28 -23
  189. package/dist/tools/stages.d.ts +2 -2
  190. package/dist/tools/stages.js +36 -31
  191. package/dist/tools/unschedule.d.ts +2 -2
  192. package/dist/tools/unschedule.js +30 -25
  193. package/dist/tools/who-am-i.d.ts +2 -2
  194. package/dist/tools/who-am-i.js +36 -31
  195. package/dist/tools/worktree.d.ts +2 -2
  196. package/dist/tools/worktree.js +134 -129
  197. package/dist/tui/index.js +6 -6
  198. package/dist/types.d.ts +47 -2
  199. package/dist/types.js +1 -1
  200. package/dist/utils/default-part.js +1 -0
  201. package/dist/utils/grpc-shutdown-guard.d.ts +52 -0
  202. package/dist/utils/grpc-shutdown-guard.js +88 -0
  203. package/dist/utils/sdk-probe.d.ts +23 -0
  204. package/dist/utils/sdk-probe.js +46 -7
  205. package/dist/worker.d.ts +3 -1
  206. package/dist/worker.js +6 -2
  207. package/dist/workflows/session.js +70 -2
  208. package/dist/workflows/signals.d.ts +32 -2
  209. package/dist/workflows/signals.js +25 -2
  210. package/examples/agents/tempo-composer.md +56 -56
  211. package/examples/agents/tempo-conductor.md +117 -117
  212. package/examples/agents/tempo-critic.md +73 -73
  213. package/examples/agents/tempo-improv.md +74 -74
  214. package/examples/agents/tempo-liner.md +75 -75
  215. package/examples/agents/tempo-roadie.md +61 -61
  216. package/examples/agents/tempo-soloist.md +71 -71
  217. package/examples/agents/tempo-tuner.md +94 -94
  218. package/examples/ensembles/tempo-big-band.yaml +146 -146
  219. package/examples/ensembles/tempo-dev-team.yaml +58 -58
  220. package/examples/ensembles/tempo-headless-jam.yaml +77 -77
  221. package/examples/ensembles/tempo-jam-session.yaml +41 -41
  222. package/examples/ensembles/tempo-mock-jam.yaml +79 -79
  223. package/examples/ensembles/tempo-review-squad.yaml +32 -32
  224. package/package.json +176 -173
  225. package/packaging/launchd/com.agent.tempo.plist +46 -46
  226. package/packaging/systemd/agent-tempo.service +32 -32
  227. package/packaging/windows/install-task.ps1 +71 -71
  228. package/scenarios/conductor-recruit-mock.yaml +33 -33
  229. package/scenarios/echo-roundtrip.yaml +15 -15
  230. package/scenarios/multi-player-handoff.yaml +38 -38
  231. package/scenarios/recruit-cascade.yaml +38 -38
  232. package/scenarios/two-player-conversation.yaml +33 -33
  233. package/workflow-bundle.js +97 -6
  234. package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
  235. package/dist/activities/claude-stop.d.ts +0 -21
  236. package/dist/activities/claude-stop.js +0 -94
  237. package/dist/channel.d.ts +0 -3
  238. package/dist/channel.js +0 -48
  239. package/dist/copilot-bridge.d.ts +0 -22
  240. package/dist/copilot-bridge.js +0 -565
  241. package/dist/scripts/258-spotcheck.js +0 -303
  242. package/dist/tools/detach.d.ts +0 -4
  243. package/dist/tools/detach.js +0 -45
  244. package/dist/tools/encore.d.ts +0 -4
  245. package/dist/tools/encore.js +0 -31
  246. package/dist/tools/helpers.d.ts +0 -21
  247. package/dist/tools/helpers.js +0 -25
  248. package/dist/tools/pause-ensemble.d.ts +0 -4
  249. package/dist/tools/pause-ensemble.js +0 -58
  250. package/dist/tools/resume-ensemble.d.ts +0 -4
  251. package/dist/tools/resume-ensemble.js +0 -79
  252. package/dist/tools/stop.d.ts +0 -4
  253. package/dist/tools/stop.js +0 -29
  254. package/dist/tui/client.d.ts +0 -6
  255. package/dist/tui/client.js +0 -9
  256. package/dist/tui/components/ActivityLog.d.ts +0 -16
  257. package/dist/tui/components/ActivityLog.js +0 -36
  258. package/dist/tui/components/CommandOverlay.d.ts +0 -15
  259. package/dist/tui/components/CommandOverlay.js +0 -34
  260. package/dist/tui/components/ConductorChat.d.ts +0 -16
  261. package/dist/tui/components/ConductorChat.js +0 -32
  262. package/dist/tui/components/EnsembleListView.d.ts +0 -14
  263. package/dist/tui/components/EnsembleListView.js +0 -32
  264. package/dist/tui/components/EnsemblePanel.d.ts +0 -12
  265. package/dist/tui/components/EnsemblePanel.js +0 -40
  266. package/dist/tui/components/InputBar.d.ts +0 -13
  267. package/dist/tui/components/InputBar.js +0 -58
  268. package/dist/tui/components/ScheduleOverlay.d.ts +0 -13
  269. package/dist/tui/components/ScheduleOverlay.js +0 -113
  270. package/dist/tui/components/TopBar.d.ts +0 -12
  271. package/dist/tui/components/TopBar.js +0 -15
  272. package/dist/tui/core-api.d.ts +0 -26
  273. package/dist/tui/core-api.js +0 -67
  274. package/dist/tui/hooks/useEnsembleDiscovery.d.ts +0 -3
  275. package/dist/tui/hooks/useEnsembleDiscovery.js +0 -30
  276. package/dist/tui/hooks/useMaestroPoller.d.ts +0 -3
  277. package/dist/tui/hooks/useMaestroPoller.js +0 -36
  278. package/dist/tui/hooks/useSendCommand.d.ts +0 -7
  279. package/dist/tui/hooks/useSendCommand.js +0 -29
  280. package/dist/utils/bg-preflight.d.ts +0 -25
  281. package/dist/utils/bg-preflight.js +0 -154
@@ -0,0 +1,48 @@
1
+ /** Env var holding the daemon admin (T3) token (writes + gate + inner tail). */
2
+ export declare const ADMIN_TOKEN_ENV = "AGENT_TEMPO_HTTP_ADMIN_TOKEN";
3
+ export type ActionFetch = (url: string, init: {
4
+ method: string;
5
+ headers: Record<string, string>;
6
+ body?: string;
7
+ }) => Promise<{
8
+ status: number;
9
+ text(): Promise<string>;
10
+ }>;
11
+ export type ActionResult = {
12
+ ok: true;
13
+ status: number;
14
+ } | {
15
+ ok: false;
16
+ error: string;
17
+ };
18
+ export interface MissionControlActionsOptions {
19
+ ensemble: string;
20
+ /** Admin (T3) token. Defaults to `process.env[ADMIN_TOKEN_ENV]`. */
21
+ adminToken?: string;
22
+ /** Daemon base URL. Defaults to `http://127.0.0.1:${readPortFile() ?? 8473}`. */
23
+ baseUrl?: string;
24
+ /** HTTP transport. Defaults to global `fetch`. */
25
+ fetchFn?: ActionFetch;
26
+ }
27
+ /** HTTP client for the daemon operator-action surface. All calls bearer-authed. */
28
+ export declare class MissionControlActions {
29
+ private readonly ensemble;
30
+ private readonly adminToken;
31
+ private readonly baseUrlOverride;
32
+ private readonly fetchFn;
33
+ constructor(opts: MissionControlActionsOptions);
34
+ /** Whether the client is usable (token + transport present). */
35
+ get ready(): boolean;
36
+ private baseUrl;
37
+ private post;
38
+ private ens;
39
+ private player;
40
+ cue(to: string, message: string): Promise<ActionResult>;
41
+ pause(): Promise<ActionResult>;
42
+ play(release?: boolean): Promise<ActionResult>;
43
+ restart(playerId: string, reason?: string): Promise<ActionResult>;
44
+ destroy(playerId: string, reason?: string): Promise<ActionResult>;
45
+ gateArm(playerId: string): Promise<ActionResult>;
46
+ gateDisarm(playerId: string): Promise<ActionResult>;
47
+ gateDecide(playerId: string, requestId: string, decision: 'allow' | 'deny'): Promise<ActionResult>;
48
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MissionControlActions = exports.ADMIN_TOKEN_ENV = void 0;
4
+ /**
5
+ * Mission-control action client (3f) — DRIVE = HTTP (decision 1). Operator
6
+ * controls POST to the daemon's write surface (T2) + gate endpoints (T3), the
7
+ * SAME surface the dashboard uses — NOT in-Pi MCP tools. The widget holds the
8
+ * ADMIN token (T3) and presents it as a bearer.
9
+ *
10
+ * Injected fetch / readPort / token so it's unit-testable without a daemon.
11
+ */
12
+ const port_file_1 = require("../../http/port-file");
13
+ /** Env var holding the daemon admin (T3) token (writes + gate + inner tail). */
14
+ exports.ADMIN_TOKEN_ENV = 'AGENT_TEMPO_HTTP_ADMIN_TOKEN';
15
+ const DEFAULT_PORT = 8473;
16
+ function resolveFetch() {
17
+ const g = globalThis.fetch;
18
+ return typeof g === 'function' ? g : null;
19
+ }
20
+ /** HTTP client for the daemon operator-action surface. All calls bearer-authed. */
21
+ class MissionControlActions {
22
+ ensemble;
23
+ adminToken;
24
+ baseUrlOverride;
25
+ fetchFn;
26
+ constructor(opts) {
27
+ this.ensemble = opts.ensemble;
28
+ this.adminToken = opts.adminToken ?? process.env[exports.ADMIN_TOKEN_ENV];
29
+ this.baseUrlOverride = opts.baseUrl;
30
+ this.fetchFn = opts.fetchFn ?? resolveFetch();
31
+ }
32
+ /** Whether the client is usable (token + transport present). */
33
+ get ready() {
34
+ return Boolean(this.adminToken) && this.fetchFn !== null;
35
+ }
36
+ baseUrl() {
37
+ if (this.baseUrlOverride)
38
+ return this.baseUrlOverride.replace(/\/$/, '');
39
+ const port = (0, port_file_1.readPortFile)() ?? DEFAULT_PORT;
40
+ return `http://127.0.0.1:${port}`;
41
+ }
42
+ async post(pathSuffix, body) {
43
+ if (!this.adminToken)
44
+ return { ok: false, error: `no admin token (set ${exports.ADMIN_TOKEN_ENV})` };
45
+ if (!this.fetchFn)
46
+ return { ok: false, error: 'no fetch transport available' };
47
+ const base = this.baseUrl();
48
+ if (base === null)
49
+ return { ok: false, error: 'daemon HTTP not reachable (no port)' };
50
+ try {
51
+ const res = await this.fetchFn(`${base}${pathSuffix}`, {
52
+ method: 'POST',
53
+ headers: { Authorization: `Bearer ${this.adminToken}`, 'Content-Type': 'application/json' },
54
+ body: JSON.stringify(body ?? {}),
55
+ });
56
+ if (res.status >= 200 && res.status < 300)
57
+ return { ok: true, status: res.status };
58
+ const detail = (await res.text().catch(() => '')).slice(0, 200);
59
+ return { ok: false, error: `HTTP ${res.status}${detail ? `: ${detail}` : ''}` };
60
+ }
61
+ catch (err) {
62
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
63
+ }
64
+ }
65
+ ens() {
66
+ return encodeURIComponent(this.ensemble);
67
+ }
68
+ player(p) {
69
+ return `${this.ens()}/${encodeURIComponent(p)}`;
70
+ }
71
+ // ── Ensemble write surface (T2) ──
72
+ cue(to, message) {
73
+ return this.post(`/v1/ensembles/${this.ens()}/cue`, { to, message });
74
+ }
75
+ pause() {
76
+ return this.post(`/v1/ensembles/${this.ens()}/pause`, {});
77
+ }
78
+ play(release) {
79
+ return this.post(`/v1/ensembles/${this.ens()}/play`, release ? { release } : {});
80
+ }
81
+ restart(playerId, reason) {
82
+ return this.post(`/v1/ensembles/${this.ens()}/restart`, { playerId, ...(reason ? { reason } : {}) });
83
+ }
84
+ destroy(playerId, reason) {
85
+ return this.post(`/v1/ensembles/${this.ens()}/destroy`, { playerId, ...(reason ? { reason } : {}) });
86
+ }
87
+ // ── Operator gate plane (T3) ──
88
+ gateArm(playerId) {
89
+ return this.post(`/v1/players/${this.player(playerId)}/gate-arm`, {});
90
+ }
91
+ gateDisarm(playerId) {
92
+ return this.post(`/v1/players/${this.player(playerId)}/gate-disarm`, {});
93
+ }
94
+ gateDecide(playerId, requestId, decision) {
95
+ return this.post(`/v1/players/${this.player(playerId)}/gate/${encodeURIComponent(requestId)}`, { decision });
96
+ }
97
+ }
98
+ exports.MissionControlActions = MissionControlActions;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Mission-control board model + reducers (3f) — PURE, no Pi/daemon/IO.
3
+ *
4
+ * Builds an in-memory view of the ensemble from the daemon's coarse SSE stream
5
+ * (`TempoEvent` over `/v1/events/:ensemble`) and a fine inner-loop tail
6
+ * (`InnerFrame` over `/v1/players/:e/:p/inner`) for the SELECTED player. The
7
+ * extension applies events here, then renders the model on a throttled tick —
8
+ * decoupling event-rate from render-rate (decision 3). Pure so it unit-tests
9
+ * without Pi or the daemon.
10
+ */
11
+ import type { TempoEvent, AttachmentPhase } from '../../http/event-types';
12
+ import type { InnerFrame } from '../inner-loop-publisher';
13
+ /** One row on the board — the coarse, always-on view of a player. */
14
+ export interface PlayerRow {
15
+ playerId: string;
16
+ isConductor: boolean;
17
+ phase?: AttachmentPhase;
18
+ part: string;
19
+ /** Tool currently executing (3c coarse), `null`/undefined = idle. */
20
+ currentTool?: string | null;
21
+ /** Context-window usage fraction/percent (3c coarse). */
22
+ contextPercent?: number;
23
+ /** ISO timestamp of the last coarse activity. */
24
+ lastActivityAt?: string;
25
+ }
26
+ /** Default cap on the retained fine-tail frames for the selected player. */
27
+ export declare const DEFAULT_TAIL_LIMIT = 200;
28
+ export interface BoardModel {
29
+ ensemble: string;
30
+ /** playerId → row, insertion-ordered by the Map. */
31
+ players: Map<string, PlayerRow>;
32
+ /** The player whose fine inner-loop tail is shown, or null. */
33
+ selected: string | null;
34
+ /** Bounded ring of the selected player's recent inner frames (oldest→newest). */
35
+ innerTail: InnerFrame[];
36
+ /** Max retained tail frames. */
37
+ tailLimit: number;
38
+ /** Monotonic counter — bumped on every mutation so the render tick can skip no-op ticks. */
39
+ revision: number;
40
+ }
41
+ export declare function initBoard(ensemble: string, tailLimit?: number): BoardModel;
42
+ /**
43
+ * Fold one coarse `TempoEvent` into the board (mutates + bumps revision). Unknown
44
+ * event kinds are ignored — the board only tracks the player set + phase + the
45
+ * 3c coarse activity fields.
46
+ */
47
+ export declare function applyTempoEvent(model: BoardModel, ev: TempoEvent): void;
48
+ /** Append a fine inner-loop frame for the selected player (bounded ring). */
49
+ export declare function applyInnerFrame(model: BoardModel, frame: InnerFrame): void;
50
+ /** Select a player for the fine tail (clears the prior tail). No-op if absent. */
51
+ export declare function selectPlayer(model: BoardModel, playerId: string | null): boolean;
52
+ /** Sorted player ids — conductor first, then alphabetical (stable board ordering). */
53
+ export declare function sortedPlayerIds(model: BoardModel): string[];
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TAIL_LIMIT = void 0;
4
+ exports.initBoard = initBoard;
5
+ exports.applyTempoEvent = applyTempoEvent;
6
+ exports.applyInnerFrame = applyInnerFrame;
7
+ exports.selectPlayer = selectPlayer;
8
+ exports.sortedPlayerIds = sortedPlayerIds;
9
+ /** Default cap on the retained fine-tail frames for the selected player. */
10
+ exports.DEFAULT_TAIL_LIMIT = 200;
11
+ function initBoard(ensemble, tailLimit = exports.DEFAULT_TAIL_LIMIT) {
12
+ return { ensemble, players: new Map(), selected: null, innerTail: [], tailLimit, revision: 0 };
13
+ }
14
+ /** Project a PlayerSummaryV1 (snapshot / player.added) into a row. */
15
+ function rowFromSummary(p) {
16
+ return {
17
+ playerId: p.playerId,
18
+ isConductor: p.isConductor,
19
+ ...(p.phase !== undefined ? { phase: p.phase } : {}),
20
+ part: p.part ?? '',
21
+ ...(p.currentTool !== undefined ? { currentTool: p.currentTool } : {}),
22
+ ...(p.contextPercent !== undefined ? { contextPercent: p.contextPercent } : {}),
23
+ ...(p.lastActivityAt !== undefined ? { lastActivityAt: p.lastActivityAt } : {}),
24
+ };
25
+ }
26
+ /**
27
+ * Fold one coarse `TempoEvent` into the board (mutates + bumps revision). Unknown
28
+ * event kinds are ignored — the board only tracks the player set + phase + the
29
+ * 3c coarse activity fields.
30
+ */
31
+ function applyTempoEvent(model, ev) {
32
+ switch (ev.type) {
33
+ case 'snapshot': {
34
+ // Authoritative rebuild from the snapshot's player list.
35
+ model.players = new Map(ev.payload.players.map((p) => [p.playerId, rowFromSummary(p)]));
36
+ // Drop a selection that no longer exists.
37
+ if (model.selected && !model.players.has(model.selected)) {
38
+ model.selected = null;
39
+ model.innerTail = [];
40
+ }
41
+ break;
42
+ }
43
+ case 'player.added': {
44
+ model.players.set(ev.payload.playerId, rowFromSummary(ev.payload));
45
+ break;
46
+ }
47
+ case 'player.removed': {
48
+ model.players.delete(ev.payload.playerId);
49
+ if (model.selected === ev.payload.playerId) {
50
+ model.selected = null;
51
+ model.innerTail = [];
52
+ }
53
+ break;
54
+ }
55
+ case 'player.phase_changed': {
56
+ const row = model.players.get(ev.payload.playerId);
57
+ if (row) {
58
+ row.phase = ev.payload.phase;
59
+ row.lastActivityAt = ev.payload.at;
60
+ }
61
+ break;
62
+ }
63
+ case 'player.activity': {
64
+ const row = model.players.get(ev.payload.playerId);
65
+ if (row) {
66
+ row.currentTool = ev.payload.currentTool;
67
+ if (ev.payload.contextPercent !== undefined)
68
+ row.contextPercent = ev.payload.contextPercent;
69
+ row.lastActivityAt = ev.payload.at;
70
+ }
71
+ break;
72
+ }
73
+ default:
74
+ return; // not board-relevant — no revision bump
75
+ }
76
+ model.revision++;
77
+ }
78
+ /** Append a fine inner-loop frame for the selected player (bounded ring). */
79
+ function applyInnerFrame(model, frame) {
80
+ model.innerTail.push(frame);
81
+ if (model.innerTail.length > model.tailLimit) {
82
+ model.innerTail.splice(0, model.innerTail.length - model.tailLimit);
83
+ }
84
+ model.revision++;
85
+ }
86
+ /** Select a player for the fine tail (clears the prior tail). No-op if absent. */
87
+ function selectPlayer(model, playerId) {
88
+ if (playerId !== null && !model.players.has(playerId))
89
+ return false;
90
+ model.selected = playerId;
91
+ model.innerTail = [];
92
+ model.revision++;
93
+ return true;
94
+ }
95
+ /** Sorted player ids — conductor first, then alphabetical (stable board ordering). */
96
+ function sortedPlayerIds(model) {
97
+ return [...model.players.values()]
98
+ .sort((a, b) => {
99
+ if (a.isConductor !== b.isConductor)
100
+ return a.isConductor ? -1 : 1;
101
+ return a.playerId.localeCompare(b.playerId);
102
+ })
103
+ .map((r) => r.playerId);
104
+ }
@@ -0,0 +1,44 @@
1
+ import { type BoardModel } from './board';
2
+ import { MissionControlActions } from './actions';
3
+ import type { McExtensionAPI, McExtensionContext } from './pi-ui';
4
+ /** Injectable seams (production defaults; tests override). */
5
+ export interface MissionControlDeps {
6
+ ensemble?: string;
7
+ adminToken?: string;
8
+ baseUrl?: string;
9
+ renderThrottleMs?: number;
10
+ }
11
+ /**
12
+ * The operator-command + board controller. Holds the model + the action client;
13
+ * command methods are independently unit-testable with a fake actions + ctx.
14
+ * The lifecycle (SSE/render/teardown) lives in {@link createMissionControlExtension}.
15
+ */
16
+ export declare class Controller {
17
+ readonly model: BoardModel;
18
+ readonly actions: MissionControlActions;
19
+ /** Set by the extension so /tail can (re)open the fine SSE; null in unit tests. */
20
+ onTailRequest: ((playerId: string | null) => void) | null;
21
+ constructor(ensemble: string, actions: MissionControlActions);
22
+ private notify;
23
+ private report;
24
+ /** First whitespace-delimited token + the remainder. */
25
+ private static splitFirst;
26
+ cmdPlayers(ctx: McExtensionContext): Promise<void>;
27
+ cmdTail(args: string, ctx: McExtensionContext): Promise<void>;
28
+ cmdCue(args: string, ctx: McExtensionContext): Promise<void>;
29
+ cmdPause(_args: string, ctx: McExtensionContext): Promise<void>;
30
+ cmdPlay(args: string, ctx: McExtensionContext): Promise<void>;
31
+ cmdRestart(args: string, ctx: McExtensionContext): Promise<void>;
32
+ cmdDestroy(args: string, ctx: McExtensionContext): Promise<void>;
33
+ cmdReset(args: string, ctx: McExtensionContext): Promise<void>;
34
+ cmdArm(args: string, ctx: McExtensionContext): Promise<void>;
35
+ cmdGate(args: string, ctx: McExtensionContext): Promise<void>;
36
+ }
37
+ /**
38
+ * Build the mission-control extension (default-export shape). The operator's Pi
39
+ * loads it in OBSERVER mode. `deps` overrides config/token/baseUrl for tests.
40
+ */
41
+ export declare function createMissionControlExtension(deps?: MissionControlDeps): (pi: McExtensionAPI) => void;
42
+ /** Default export — the loadable Pi extension. */
43
+ declare const missionControlExtension: (pi: McExtensionAPI) => void;
44
+ export default missionControlExtension;
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Controller = void 0;
4
+ exports.createMissionControlExtension = createMissionControlExtension;
5
+ /**
6
+ * Mission-control Pi extension (3f) — turns ONE interactive Pi TUI into an
7
+ * ensemble mission-control board + operator controller.
8
+ *
9
+ * Three ruled decisions:
10
+ * 1. DRIVE = HTTP — controls POST to the daemon write/gate surface ({@link MissionControlActions}).
11
+ * 2. OBSERVER-ONLY — this extension NEVER claimAttachment / registers as a player.
12
+ * 3. RENDER THROTTLE ~200ms — events fold into the in-memory {@link BoardModel};
13
+ * a tick re-renders only when the model changed (revision bump), so the
14
+ * /inner tail can't thrash the TUI.
15
+ *
16
+ * Lifecycle: `session_start` opens the coarse SSE (`/v1/events/:ensemble` via the
17
+ * Node `subscribe` path), starts the render tick, and registers operator
18
+ * commands; `session_shutdown` tears all of it down + clears the widget.
19
+ */
20
+ const config_1 = require("../../config");
21
+ const port_file_1 = require("../../http/port-file");
22
+ const subscribe_1 = require("../../client/subscribe");
23
+ const board_1 = require("./board");
24
+ const render_1 = require("./render");
25
+ const actions_1 = require("./actions");
26
+ const inner_tail_1 = require("./inner-tail");
27
+ const WIDGET_KEY = 'mission-control';
28
+ const DEFAULT_RENDER_THROTTLE_MS = 200;
29
+ const DEFAULT_PORT = 8473;
30
+ /**
31
+ * The operator-command + board controller. Holds the model + the action client;
32
+ * command methods are independently unit-testable with a fake actions + ctx.
33
+ * The lifecycle (SSE/render/teardown) lives in {@link createMissionControlExtension}.
34
+ */
35
+ class Controller {
36
+ model;
37
+ actions;
38
+ /** Set by the extension so /tail can (re)open the fine SSE; null in unit tests. */
39
+ onTailRequest = null;
40
+ constructor(ensemble, actions) {
41
+ this.model = (0, board_1.initBoard)(ensemble);
42
+ this.actions = actions;
43
+ }
44
+ notify(ctx, msg) {
45
+ if (ctx.hasUI)
46
+ ctx.ui.notify(msg);
47
+ }
48
+ report(ctx, label, r) {
49
+ this.notify(ctx, r.ok ? `${label} ✓` : `${label} failed: ${r.error}`);
50
+ }
51
+ /** First whitespace-delimited token + the remainder. */
52
+ static splitFirst(args) {
53
+ const t = args.trim();
54
+ const i = t.indexOf(' ');
55
+ return i < 0 ? [t, ''] : [t.slice(0, i), t.slice(i + 1).trim()];
56
+ }
57
+ async cmdPlayers(ctx) {
58
+ const ids = (0, board_1.sortedPlayerIds)(this.model);
59
+ this.notify(ctx, ids.length ? `Players (${ids.length}): ${ids.join(', ')}` : 'No players in the ensemble.');
60
+ }
61
+ async cmdTail(args, ctx) {
62
+ const target = args.trim();
63
+ if (!target || target === 'off') {
64
+ (0, board_1.selectPlayer)(this.model, null);
65
+ this.onTailRequest?.(null);
66
+ this.notify(ctx, 'Inner-loop tail off.');
67
+ return;
68
+ }
69
+ if (!(0, board_1.selectPlayer)(this.model, target)) {
70
+ this.notify(ctx, `No such player: ${target}`);
71
+ return;
72
+ }
73
+ this.onTailRequest?.(target);
74
+ this.notify(ctx, `Tailing ${target}.`);
75
+ }
76
+ async cmdCue(args, ctx) {
77
+ const [to, message] = Controller.splitFirst(args);
78
+ if (!to || !message) {
79
+ this.notify(ctx, 'Usage: /cue <player> <message>');
80
+ return;
81
+ }
82
+ this.report(ctx, `cue → ${to}`, await this.actions.cue(to, message));
83
+ }
84
+ async cmdPause(_args, ctx) {
85
+ this.report(ctx, 'pause', await this.actions.pause());
86
+ }
87
+ async cmdPlay(args, ctx) {
88
+ const release = args.trim() === 'release';
89
+ this.report(ctx, 'play', await this.actions.play(release));
90
+ }
91
+ async cmdRestart(args, ctx) {
92
+ const [p, reason] = Controller.splitFirst(args);
93
+ if (!p) {
94
+ this.notify(ctx, 'Usage: /restart <player> [reason]');
95
+ return;
96
+ }
97
+ this.report(ctx, `restart ${p}`, await this.actions.restart(p, reason || undefined));
98
+ }
99
+ async cmdDestroy(args, ctx) {
100
+ const [p, reason] = Controller.splitFirst(args);
101
+ if (!p) {
102
+ this.notify(ctx, 'Usage: /destroy <player> [reason]');
103
+ return;
104
+ }
105
+ this.report(ctx, `destroy ${p}`, await this.actions.destroy(p, reason || undefined));
106
+ }
107
+ async cmdReset(args, ctx) {
108
+ const p = args.trim();
109
+ if (!p) {
110
+ this.notify(ctx, 'Usage: /reset <player>');
111
+ return;
112
+ }
113
+ // D14 reset has NO daemon HTTP route yet (MCP/outbox only). Surface clearly
114
+ // rather than silently fail. Wiring a POST /v1/ensembles/:e/reset is a daemon
115
+ // follow-up (flagged to the conductor).
116
+ this.notify(ctx, `reset ${p}: not available over the daemon HTTP surface yet (MCP/outbox only). Flagged for a daemon route.`);
117
+ }
118
+ async cmdArm(args, ctx) {
119
+ const [p, mode] = Controller.splitFirst(args);
120
+ if (!p) {
121
+ this.notify(ctx, 'Usage: /arm <player> [off]');
122
+ return;
123
+ }
124
+ const off = mode.trim() === 'off';
125
+ this.report(ctx, `${off ? 'disarm' : 'arm'} ${p}`, off ? await this.actions.gateDisarm(p) : await this.actions.gateArm(p));
126
+ }
127
+ async cmdGate(args, ctx) {
128
+ const [reqId, decisionRaw] = Controller.splitFirst(args);
129
+ const decision = decisionRaw.trim();
130
+ if (!reqId || (decision !== 'allow' && decision !== 'deny')) {
131
+ this.notify(ctx, 'Usage: /gate <requestId> allow|deny (decides for the tailed player)');
132
+ return;
133
+ }
134
+ if (!this.model.selected) {
135
+ this.notify(ctx, 'Select a player first with /tail <player> — gate decisions are per-player.');
136
+ return;
137
+ }
138
+ this.report(ctx, `gate ${reqId} ${decision}`, await this.actions.gateDecide(this.model.selected, reqId, decision));
139
+ }
140
+ }
141
+ exports.Controller = Controller;
142
+ function resolveBaseUrl(override) {
143
+ if (override)
144
+ return override.replace(/\/$/, '');
145
+ return `http://127.0.0.1:${(0, port_file_1.readPortFile)() ?? DEFAULT_PORT}`;
146
+ }
147
+ const log = (...args) => {
148
+ // eslint-disable-next-line no-console
149
+ console.error('[agent-tempo:pi:mission-control]', ...args);
150
+ };
151
+ /**
152
+ * Build the mission-control extension (default-export shape). The operator's Pi
153
+ * loads it in OBSERVER mode. `deps` overrides config/token/baseUrl for tests.
154
+ */
155
+ function createMissionControlExtension(deps = {}) {
156
+ return (pi) => {
157
+ const ensemble = deps.ensemble ?? (0, config_1.getConfig)().ensemble;
158
+ const adminToken = deps.adminToken ?? process.env[actions_1.ADMIN_TOKEN_ENV];
159
+ const baseUrl = resolveBaseUrl(deps.baseUrl);
160
+ const throttleMs = deps.renderThrottleMs ?? DEFAULT_RENDER_THROTTLE_MS;
161
+ const actions = new actions_1.MissionControlActions({ ensemble, ...(adminToken ? { adminToken } : {}), baseUrl });
162
+ const ctrl = new Controller(ensemble, actions);
163
+ // Per-session lifecycle state (re-created on each session_start).
164
+ let coarseAbort = null;
165
+ let tailAbort = null;
166
+ let renderTimer = null;
167
+ let lastRenderedRevision = -1;
168
+ let activeCtx = null;
169
+ const renderNow = () => {
170
+ if (!activeCtx?.hasUI)
171
+ return;
172
+ if (ctrl.model.revision === lastRenderedRevision)
173
+ return; // throttle: skip no-op ticks
174
+ lastRenderedRevision = ctrl.model.revision;
175
+ activeCtx.ui.setWidget(WIDGET_KEY, (0, render_1.renderBoard)(ctrl.model), { placement: 'aboveEditor' });
176
+ };
177
+ const startCoarse = () => {
178
+ if (!adminToken) {
179
+ log(`no admin token (${actions_1.ADMIN_TOKEN_ENV}) — board limited / disabled`);
180
+ }
181
+ coarseAbort = new AbortController();
182
+ const subscribe = (0, subscribe_1.createSubscribe)({ baseUrl, ...(adminToken ? { token: adminToken } : {}) });
183
+ void (async () => {
184
+ try {
185
+ for await (const ev of subscribe(ensemble, { signal: coarseAbort.signal })) {
186
+ (0, board_1.applyTempoEvent)(ctrl.model, ev);
187
+ }
188
+ }
189
+ catch (err) {
190
+ if (!coarseAbort?.signal.aborted)
191
+ log('coarse SSE ended:', err instanceof Error ? err.message : err);
192
+ }
193
+ })();
194
+ };
195
+ const openTail = (playerId) => {
196
+ tailAbort?.abort();
197
+ tailAbort = null;
198
+ if (playerId === null || !adminToken)
199
+ return;
200
+ tailAbort = new AbortController();
201
+ const fetchFn = globalThis.fetch;
202
+ if (!fetchFn)
203
+ return;
204
+ void (0, inner_tail_1.openInnerTail)({
205
+ baseUrl, adminToken, ensemble, playerId,
206
+ signal: tailAbort.signal,
207
+ fetchFn: fetchFn,
208
+ onFrame: (f) => (0, board_1.applyInnerFrame)(ctrl.model, f),
209
+ onError: (m) => log(`inner tail (${playerId}):`, m),
210
+ });
211
+ };
212
+ ctrl.onTailRequest = openTail;
213
+ const teardown = () => {
214
+ coarseAbort?.abort();
215
+ coarseAbort = null;
216
+ tailAbort?.abort();
217
+ tailAbort = null;
218
+ if (renderTimer) {
219
+ clearInterval(renderTimer);
220
+ renderTimer = null;
221
+ }
222
+ if (activeCtx?.hasUI)
223
+ activeCtx.ui.setWidget(WIDGET_KEY, undefined);
224
+ activeCtx = null;
225
+ };
226
+ pi.on('session_start', (_event, ctx) => {
227
+ activeCtx = ctx;
228
+ lastRenderedRevision = -1;
229
+ startCoarse();
230
+ renderTimer = setInterval(renderNow, throttleMs);
231
+ if (typeof renderTimer.unref === 'function')
232
+ renderTimer.unref();
233
+ renderNow();
234
+ });
235
+ pi.on('session_shutdown', () => teardown());
236
+ // Operator commands (display-only widget → slash-commands drive everything).
237
+ pi.registerCommand('players', { description: 'List ensemble players', handler: (_a, ctx) => ctrl.cmdPlayers(ctx) });
238
+ pi.registerCommand('tail', { description: 'Tail a player\'s inner loop (/tail <player> | off)', handler: (a, ctx) => ctrl.cmdTail(a, ctx) });
239
+ pi.registerCommand('cue', { description: 'Send a message to a player (/cue <player> <msg>)', handler: (a, ctx) => ctrl.cmdCue(a, ctx) });
240
+ pi.registerCommand('pause', { description: 'Pause the ensemble', handler: (a, ctx) => ctrl.cmdPause(a, ctx) });
241
+ pi.registerCommand('play', { description: 'Resume the ensemble (/play [release])', handler: (a, ctx) => ctrl.cmdPlay(a, ctx) });
242
+ pi.registerCommand('restart', { description: 'Restart a player (/restart <player> [reason])', handler: (a, ctx) => ctrl.cmdRestart(a, ctx) });
243
+ pi.registerCommand('destroy', { description: 'Destroy a player (/destroy <player> [reason])', handler: (a, ctx) => ctrl.cmdDestroy(a, ctx) });
244
+ pi.registerCommand('reset', { description: 'Clean-wipe a player (/reset <player>)', handler: (a, ctx) => ctrl.cmdReset(a, ctx) });
245
+ pi.registerCommand('arm', { description: 'Arm/disarm the operator gate for a player (/arm <player> [off])', handler: (a, ctx) => ctrl.cmdArm(a, ctx) });
246
+ pi.registerCommand('gate', { description: 'Decide a gate request for the tailed player (/gate <reqId> allow|deny)', handler: (a, ctx) => ctrl.cmdGate(a, ctx) });
247
+ };
248
+ }
249
+ /** Default export — the loadable Pi extension. */
250
+ const missionControlExtension = createMissionControlExtension();
251
+ exports.default = missionControlExtension;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Mission-control (3f) — a Pi extension that turns one interactive Pi TUI into
3
+ * an ensemble mission-control board + operator controller. Observer-only,
4
+ * HTTP-driven, throttled render. See ./extension.ts.
5
+ *
6
+ * The default export is the loadable Pi extension.
7
+ */
8
+ export { default, createMissionControlExtension, Controller } from './extension';
9
+ export type { MissionControlDeps } from './extension';
10
+ export { initBoard, applyTempoEvent, applyInnerFrame, selectPlayer, sortedPlayerIds, DEFAULT_TAIL_LIMIT, } from './board';
11
+ export type { BoardModel, PlayerRow } from './board';
12
+ export { renderBoard } from './render';
13
+ export { MissionControlActions, ADMIN_TOKEN_ENV } from './actions';
14
+ export type { ActionResult } from './actions';
15
+ export { parseInnerSse, openInnerTail } from './inner-tail';
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.openInnerTail = exports.parseInnerSse = exports.ADMIN_TOKEN_ENV = exports.MissionControlActions = exports.renderBoard = exports.DEFAULT_TAIL_LIMIT = exports.sortedPlayerIds = exports.selectPlayer = exports.applyInnerFrame = exports.applyTempoEvent = exports.initBoard = exports.Controller = exports.createMissionControlExtension = exports.default = void 0;
7
+ /**
8
+ * Mission-control (3f) — a Pi extension that turns one interactive Pi TUI into
9
+ * an ensemble mission-control board + operator controller. Observer-only,
10
+ * HTTP-driven, throttled render. See ./extension.ts.
11
+ *
12
+ * The default export is the loadable Pi extension.
13
+ */
14
+ var extension_1 = require("./extension");
15
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(extension_1).default; } });
16
+ Object.defineProperty(exports, "createMissionControlExtension", { enumerable: true, get: function () { return extension_1.createMissionControlExtension; } });
17
+ Object.defineProperty(exports, "Controller", { enumerable: true, get: function () { return extension_1.Controller; } });
18
+ var board_1 = require("./board");
19
+ Object.defineProperty(exports, "initBoard", { enumerable: true, get: function () { return board_1.initBoard; } });
20
+ Object.defineProperty(exports, "applyTempoEvent", { enumerable: true, get: function () { return board_1.applyTempoEvent; } });
21
+ Object.defineProperty(exports, "applyInnerFrame", { enumerable: true, get: function () { return board_1.applyInnerFrame; } });
22
+ Object.defineProperty(exports, "selectPlayer", { enumerable: true, get: function () { return board_1.selectPlayer; } });
23
+ Object.defineProperty(exports, "sortedPlayerIds", { enumerable: true, get: function () { return board_1.sortedPlayerIds; } });
24
+ Object.defineProperty(exports, "DEFAULT_TAIL_LIMIT", { enumerable: true, get: function () { return board_1.DEFAULT_TAIL_LIMIT; } });
25
+ var render_1 = require("./render");
26
+ Object.defineProperty(exports, "renderBoard", { enumerable: true, get: function () { return render_1.renderBoard; } });
27
+ var actions_1 = require("./actions");
28
+ Object.defineProperty(exports, "MissionControlActions", { enumerable: true, get: function () { return actions_1.MissionControlActions; } });
29
+ Object.defineProperty(exports, "ADMIN_TOKEN_ENV", { enumerable: true, get: function () { return actions_1.ADMIN_TOKEN_ENV; } });
30
+ var inner_tail_1 = require("./inner-tail");
31
+ Object.defineProperty(exports, "parseInnerSse", { enumerable: true, get: function () { return inner_tail_1.parseInnerSse; } });
32
+ Object.defineProperty(exports, "openInnerTail", { enumerable: true, get: function () { return inner_tail_1.openInnerTail; } });