agent-tempo 1.3.1 → 1.4.1

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 (199) hide show
  1. package/CLAUDE.md +39 -5
  2. package/README.md +6 -2
  3. package/dashboard/dist/assets/{index-D6Xyje_n.js → index-jmYe6rmS.js} +2 -2
  4. package/dashboard/dist/assets/index-jmYe6rmS.js.map +1 -0
  5. package/dashboard/dist/index.html +1 -1
  6. package/dashboard/package.json +1 -1
  7. package/dist/activities/outbox.d.ts +30 -1
  8. package/dist/activities/outbox.js +96 -3
  9. package/dist/adapters/base.js +5 -0
  10. package/dist/adapters/index.d.ts +1 -1
  11. package/dist/adapters/index.js +7 -0
  12. package/dist/adapters/pi/adapter.d.ts +2 -0
  13. package/dist/adapters/pi/adapter.js +43 -0
  14. package/dist/adapters/pi/index.d.ts +16 -0
  15. package/dist/adapters/pi/index.js +10 -0
  16. package/dist/client/core.js +9 -2
  17. package/dist/client/interface.d.ts +6 -0
  18. package/dist/config.d.ts +79 -0
  19. package/dist/config.js +74 -0
  20. package/dist/daemon.js +32 -1
  21. package/dist/http/aggregate.d.ts +22 -1
  22. package/dist/http/aggregate.js +41 -0
  23. package/dist/http/auth.d.ts +94 -8
  24. package/dist/http/auth.js +93 -9
  25. package/dist/http/body.d.ts +4 -1
  26. package/dist/http/body.js +6 -3
  27. package/dist/http/event-bus.js +1 -0
  28. package/dist/http/event-types.d.ts +34 -2
  29. package/dist/http/event-types.js +1 -0
  30. package/dist/http/gate-audit.d.ts +12 -0
  31. package/dist/http/gate-audit.js +95 -0
  32. package/dist/http/gate-registry.d.ts +167 -0
  33. package/dist/http/gate-registry.js +163 -0
  34. package/dist/http/gate-routes.d.ts +48 -0
  35. package/dist/http/gate-routes.js +102 -0
  36. package/dist/http/ingest-registry.d.ts +30 -0
  37. package/dist/http/ingest-registry.js +108 -0
  38. package/dist/http/inner-loop-routes.d.ts +66 -0
  39. package/dist/http/inner-loop-routes.js +182 -0
  40. package/dist/http/inner-loop.d.ts +92 -0
  41. package/dist/http/inner-loop.js +155 -0
  42. package/dist/http/server.d.ts +38 -3
  43. package/dist/http/server.js +211 -6
  44. package/dist/http/snapshot.d.ts +6 -0
  45. package/dist/http/snapshot.js +6 -0
  46. package/dist/pi/cue-pump.d.ts +61 -0
  47. package/dist/pi/cue-pump.js +95 -0
  48. package/dist/pi/extension.d.ts +45 -0
  49. package/dist/pi/extension.js +407 -0
  50. package/dist/pi/gate-client.d.ts +54 -0
  51. package/dist/pi/gate-client.js +136 -0
  52. package/dist/pi/headless.d.ts +85 -0
  53. package/dist/pi/headless.js +250 -0
  54. package/dist/pi/index.d.ts +28 -0
  55. package/dist/pi/index.js +43 -0
  56. package/dist/pi/inner-loop-client.d.ts +67 -0
  57. package/dist/pi/inner-loop-client.js +164 -0
  58. package/dist/pi/inner-loop-publisher.d.ts +187 -0
  59. package/dist/pi/inner-loop-publisher.js +236 -0
  60. package/dist/pi/lazy-proxy.d.ts +37 -0
  61. package/dist/pi/lazy-proxy.js +55 -0
  62. package/dist/pi/mission-control/actions.d.ts +48 -0
  63. package/dist/pi/mission-control/actions.js +98 -0
  64. package/dist/pi/mission-control/board.d.ts +88 -0
  65. package/dist/pi/mission-control/board.js +141 -0
  66. package/dist/pi/mission-control/extension.d.ts +51 -0
  67. package/dist/pi/mission-control/extension.js +330 -0
  68. package/dist/pi/mission-control/index.d.ts +15 -0
  69. package/dist/pi/mission-control/index.js +32 -0
  70. package/dist/pi/mission-control/inner-tail.d.ts +48 -0
  71. package/dist/pi/mission-control/inner-tail.js +76 -0
  72. package/dist/pi/mission-control/pi-ui.d.ts +43 -0
  73. package/dist/pi/mission-control/pi-ui.js +10 -0
  74. package/dist/pi/mission-control/render.d.ts +6 -0
  75. package/dist/pi/mission-control/render.js +98 -0
  76. package/dist/pi/phase-driver.d.ts +74 -0
  77. package/dist/pi/phase-driver.js +122 -0
  78. package/dist/pi/pi-types.d.ts +222 -0
  79. package/dist/pi/pi-types.js +21 -0
  80. package/dist/pi/probe.d.ts +99 -0
  81. package/dist/pi/probe.js +179 -0
  82. package/dist/pi/render-tools.d.ts +17 -0
  83. package/dist/pi/render-tools.js +56 -0
  84. package/dist/pi/reset-pump.d.ts +47 -0
  85. package/dist/pi/reset-pump.js +85 -0
  86. package/dist/pi/session-seed.d.ts +74 -0
  87. package/dist/pi/session-seed.js +103 -0
  88. package/dist/pi/tool-capability.d.ts +60 -0
  89. package/dist/pi/tool-capability.js +156 -0
  90. package/dist/pi/workflow-client.d.ts +158 -0
  91. package/dist/pi/workflow-client.js +289 -0
  92. package/dist/pi/zod-to-typebox.d.ts +74 -0
  93. package/dist/pi/zod-to-typebox.js +191 -0
  94. package/dist/server-tools.d.ts +2 -0
  95. package/dist/server-tools.js +50 -46
  96. package/dist/spawn.d.ts +55 -0
  97. package/dist/spawn.js +72 -0
  98. package/dist/tools/agent-types.d.ts +2 -2
  99. package/dist/tools/agent-types.js +22 -17
  100. package/dist/tools/attachment-info.d.ts +2 -2
  101. package/dist/tools/attachment-info.js +38 -33
  102. package/dist/tools/broadcast.d.ts +2 -2
  103. package/dist/tools/broadcast.js +69 -64
  104. package/dist/tools/cancel-stage.d.ts +2 -2
  105. package/dist/tools/cancel-stage.js +20 -15
  106. package/dist/tools/clear-state.d.ts +2 -2
  107. package/dist/tools/clear-state.js +25 -20
  108. package/dist/tools/coat-check-evict.d.ts +2 -2
  109. package/dist/tools/coat-check-evict.js +29 -24
  110. package/dist/tools/coat-check-get.d.ts +2 -2
  111. package/dist/tools/coat-check-get.js +38 -33
  112. package/dist/tools/coat-check-list.d.ts +2 -2
  113. package/dist/tools/coat-check-list.js +48 -43
  114. package/dist/tools/coat-check-put.d.ts +2 -2
  115. package/dist/tools/coat-check-put.js +38 -33
  116. package/dist/tools/cue.d.ts +2 -2
  117. package/dist/tools/cue.js +57 -52
  118. package/dist/tools/descriptor.d.ts +72 -0
  119. package/dist/tools/descriptor.js +39 -0
  120. package/dist/tools/destroy.d.ts +2 -2
  121. package/dist/tools/destroy.js +153 -148
  122. package/dist/tools/ensemble.d.ts +2 -2
  123. package/dist/tools/ensemble.js +71 -66
  124. package/dist/tools/evaluate-gate.d.ts +2 -2
  125. package/dist/tools/evaluate-gate.js +33 -27
  126. package/dist/tools/fetch-state.d.ts +2 -2
  127. package/dist/tools/fetch-state.js +42 -37
  128. package/dist/tools/gates.d.ts +2 -2
  129. package/dist/tools/gates.js +39 -34
  130. package/dist/tools/hosts.d.ts +2 -2
  131. package/dist/tools/hosts.js +25 -20
  132. package/dist/tools/listen.d.ts +2 -2
  133. package/dist/tools/listen.js +23 -18
  134. package/dist/tools/load-lineup.d.ts +2 -2
  135. package/dist/tools/load-lineup.js +324 -319
  136. package/dist/tools/migrate.d.ts +2 -2
  137. package/dist/tools/migrate.js +45 -40
  138. package/dist/tools/pause.d.ts +2 -2
  139. package/dist/tools/pause.js +34 -29
  140. package/dist/tools/play.d.ts +2 -2
  141. package/dist/tools/play.js +53 -48
  142. package/dist/tools/quality-gate.d.ts +2 -2
  143. package/dist/tools/quality-gate.js +26 -21
  144. package/dist/tools/recall.d.ts +2 -2
  145. package/dist/tools/recall.js +32 -27
  146. package/dist/tools/recruit.d.ts +2 -2
  147. package/dist/tools/recruit.js +340 -256
  148. package/dist/tools/release.d.ts +2 -2
  149. package/dist/tools/release.js +85 -80
  150. package/dist/tools/report.d.ts +2 -2
  151. package/dist/tools/report.js +28 -23
  152. package/dist/tools/reset.d.ts +3 -0
  153. package/dist/tools/reset.js +51 -0
  154. package/dist/tools/restart.d.ts +2 -2
  155. package/dist/tools/restart.js +51 -46
  156. package/dist/tools/restore.d.ts +2 -2
  157. package/dist/tools/restore.js +76 -71
  158. package/dist/tools/save-lineup.d.ts +2 -2
  159. package/dist/tools/save-lineup.js +32 -27
  160. package/dist/tools/save-state.d.ts +2 -2
  161. package/dist/tools/save-state.js +31 -26
  162. package/dist/tools/schedule.d.ts +2 -2
  163. package/dist/tools/schedule.js +133 -128
  164. package/dist/tools/schedules.d.ts +2 -2
  165. package/dist/tools/schedules.js +41 -36
  166. package/dist/tools/set-ensemble-description.d.ts +2 -2
  167. package/dist/tools/set-ensemble-description.js +26 -21
  168. package/dist/tools/set-name.d.ts +2 -2
  169. package/dist/tools/set-name.js +38 -33
  170. package/dist/tools/set-part.d.ts +2 -2
  171. package/dist/tools/set-part.js +20 -15
  172. package/dist/tools/shutdown.d.ts +2 -2
  173. package/dist/tools/shutdown.js +39 -34
  174. package/dist/tools/stage.d.ts +2 -2
  175. package/dist/tools/stage.js +28 -23
  176. package/dist/tools/stages.d.ts +2 -2
  177. package/dist/tools/stages.js +36 -31
  178. package/dist/tools/unschedule.d.ts +2 -2
  179. package/dist/tools/unschedule.js +30 -25
  180. package/dist/tools/who-am-i.d.ts +2 -2
  181. package/dist/tools/who-am-i.js +36 -31
  182. package/dist/tools/worktree.d.ts +2 -2
  183. package/dist/tools/worktree.js +134 -129
  184. package/dist/tui/index.js +6 -6
  185. package/dist/types.d.ts +47 -2
  186. package/dist/types.js +1 -1
  187. package/dist/utils/default-part.js +1 -0
  188. package/dist/utils/sdk-probe.d.ts +23 -0
  189. package/dist/utils/sdk-probe.js +46 -7
  190. package/dist/worker.d.ts +3 -1
  191. package/dist/worker.js +6 -2
  192. package/dist/workflows/session.js +70 -2
  193. package/dist/workflows/signals.d.ts +32 -2
  194. package/dist/workflows/signals.js +25 -2
  195. package/package.json +4 -1
  196. package/workflow-bundle.js +97 -6
  197. package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
  198. package/dist/tools/helpers.d.ts +0 -21
  199. package/dist/tools/helpers.js +0 -25
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAllTempoTools = buildAllTempoTools;
3
4
  exports.registerAllTempoTools = registerAllTempoTools;
4
5
  exports.buildServerInstructions = buildServerInstructions;
6
+ const descriptor_1 = require("./tools/descriptor");
5
7
  const ensemble_1 = require("./tools/ensemble");
6
8
  const cue_1 = require("./tools/cue");
7
9
  const set_part_1 = require("./tools/set-part");
@@ -32,6 +34,7 @@ const stages_1 = require("./tools/stages");
32
34
  const cancel_stage_1 = require("./tools/cancel-stage");
33
35
  const restart_1 = require("./tools/restart");
34
36
  const destroy_1 = require("./tools/destroy");
37
+ const reset_1 = require("./tools/reset");
35
38
  const migrate_1 = require("./tools/migrate");
36
39
  const attachment_info_1 = require("./tools/attachment-info");
37
40
  const hosts_1 = require("./tools/hosts");
@@ -55,55 +58,56 @@ const coat_check_evict_1 = require("./tools/coat-check-evict");
55
58
  * `opts.isConductor` — non-conductor players don't see them on either
56
59
  * surface.
57
60
  */
58
- function registerAllTempoTools(server, opts) {
61
+ function buildAllTempoTools(opts) {
59
62
  const { client, config, getPlayerId, setPlayerId, handle, workflowId, ownAgentType, isConductor } = opts;
60
- (0, ensemble_1.registerEnsembleTool)(server, client, config, getPlayerId, workflowId);
61
- (0, cue_1.registerCueTool)(server, client, config, getPlayerId, handle);
62
- (0, set_part_1.registerSetPartTool)(server, handle);
63
- (0, set_name_1.registerSetNameTool)(server, client, config, handle, getPlayerId, setPlayerId);
64
- (0, listen_1.registerListenTool)(server, handle);
65
- (0, recruit_1.registerRecruitTool)(server, client, config, getPlayerId, handle, ownAgentType);
66
- (0, report_1.registerReportTool)(server, handle);
67
- (0, schedule_1.registerScheduleTool)(server, client, config, getPlayerId);
68
- (0, unschedule_1.registerUnscheduleTool)(server, client, config);
69
- (0, schedules_1.registerSchedulesTool)(server, client, config);
70
- (0, save_lineup_1.registerSaveLineupTool)(server, client, config, getPlayerId, isConductor);
71
- (0, load_lineup_1.registerLoadLineupTool)(server, client, config, getPlayerId, ownAgentType, handle, setPlayerId, isConductor);
72
- (0, agent_types_1.registerAgentTypesTool)(server);
73
- (0, who_am_i_1.registerWhoAmITool)(server, handle, getPlayerId);
74
- (0, broadcast_1.registerBroadcastTool)(server, client, config, getPlayerId, handle);
75
- (0, recall_1.registerRecallTool)(server, handle, getPlayerId);
76
- (0, release_1.registerReleaseTool)(server, client, config, getPlayerId, handle);
77
- (0, pause_1.registerPauseTool)(server, client, config, getPlayerId);
78
- (0, play_1.registerPlayTool)(server, client, config, getPlayerId);
79
- (0, shutdown_1.registerShutdownTool)(server, client, config, getPlayerId);
80
- (0, restore_1.registerRestoreTool)(server, client, config, getPlayerId);
81
- (0, restart_1.registerRestartTool)(server, client, config, getPlayerId, handle);
82
- (0, destroy_1.registerDestroyTool)(server, client, config, getPlayerId, handle);
83
- (0, migrate_1.registerMigrateTool)(server, client, config, getPlayerId, handle);
84
- (0, attachment_info_1.registerAttachmentInfoTool)(server, client, config);
85
- (0, hosts_1.registerHostsTool)(server, client, config);
86
- (0, set_ensemble_description_1.registerSetEnsembleDescriptionTool)(server, client, config);
87
- // #334 PR-1 — owner-write / peer-read player saveable state.
88
- (0, save_state_1.registerSaveStateTool)(server, handle, getPlayerId);
89
- (0, fetch_state_1.registerFetchStateTool)(server, client, config, handle, getPlayerId);
90
- (0, clear_state_1.registerClearStateTool)(server, handle);
91
- // #318 ensemble-shared coat-check (put/get/list/evict). Any player can put;
92
- // any player can get/list; owner-or-conductor can evict. Audit identity is
93
- // set at the tool layer via getPlayerId() no playerId arg on any schema.
94
- (0, coat_check_put_1.registerCoatCheckPutTool)(server, client, config, getPlayerId);
95
- (0, coat_check_get_1.registerCoatCheckGetTool)(server, client, config, getPlayerId);
96
- (0, coat_check_list_1.registerCoatCheckListTool)(server, client, config);
97
- (0, coat_check_evict_1.registerCoatCheckEvictTool)(server, client, config, getPlayerId);
63
+ const tools = [
64
+ (0, ensemble_1.buildEnsembleTool)(client, config, getPlayerId, workflowId),
65
+ (0, cue_1.buildCueTool)(client, config, getPlayerId, handle),
66
+ (0, set_part_1.buildSetPartTool)(handle),
67
+ (0, set_name_1.buildSetNameTool)(client, config, handle, getPlayerId, setPlayerId),
68
+ (0, listen_1.buildListenTool)(handle),
69
+ (0, recruit_1.buildRecruitTool)(client, config, getPlayerId, handle, ownAgentType),
70
+ (0, report_1.buildReportTool)(handle),
71
+ (0, schedule_1.buildScheduleTool)(client, config, getPlayerId),
72
+ (0, unschedule_1.buildUnscheduleTool)(client, config),
73
+ (0, schedules_1.buildSchedulesTool)(client, config),
74
+ (0, save_lineup_1.buildSaveLineupTool)(client, config, getPlayerId, isConductor),
75
+ (0, load_lineup_1.buildLoadLineupTool)(client, config, getPlayerId, ownAgentType, handle, setPlayerId, isConductor),
76
+ (0, agent_types_1.buildAgentTypesTool)(),
77
+ (0, who_am_i_1.buildWhoAmITool)(handle, getPlayerId),
78
+ (0, broadcast_1.buildBroadcastTool)(client, config, getPlayerId, handle),
79
+ (0, recall_1.buildRecallTool)(handle, getPlayerId),
80
+ (0, release_1.buildReleaseTool)(client, config, getPlayerId, handle),
81
+ (0, pause_1.buildPauseTool)(client, config, getPlayerId),
82
+ (0, play_1.buildPlayTool)(client, config, getPlayerId),
83
+ (0, shutdown_1.buildShutdownTool)(client, config, getPlayerId),
84
+ (0, restore_1.buildRestoreTool)(client, config, getPlayerId),
85
+ (0, restart_1.buildRestartTool)(client, config, getPlayerId, handle),
86
+ (0, destroy_1.buildDestroyTool)(client, config, getPlayerId, handle),
87
+ (0, reset_1.buildResetTool)(handle, getPlayerId),
88
+ (0, migrate_1.buildMigrateTool)(client, config, getPlayerId, handle),
89
+ (0, attachment_info_1.buildAttachmentInfoTool)(client, config),
90
+ (0, hosts_1.buildHostsTool)(client, config),
91
+ (0, set_ensemble_description_1.buildSetEnsembleDescriptionTool)(client, config),
92
+ // #334 PR-1 owner-write / peer-read player saveable state.
93
+ (0, save_state_1.buildSaveStateTool)(handle, getPlayerId),
94
+ (0, fetch_state_1.buildFetchStateTool)(client, config, handle, getPlayerId),
95
+ (0, clear_state_1.buildClearStateTool)(handle),
96
+ // #318 ensemble-shared coat-check (put/get/list/evict). Any player can put;
97
+ // any player can get/list; owner-or-conductor can evict. Audit identity is
98
+ // set at the tool layer via getPlayerId() — no playerId arg on any schema.
99
+ (0, coat_check_put_1.buildCoatCheckPutTool)(client, config, getPlayerId),
100
+ (0, coat_check_get_1.buildCoatCheckGetTool)(client, config, getPlayerId),
101
+ (0, coat_check_list_1.buildCoatCheckListTool)(client, config),
102
+ (0, coat_check_evict_1.buildCoatCheckEvictTool)(client, config, getPlayerId),
103
+ ];
98
104
  if (isConductor) {
99
- (0, quality_gate_1.registerQualityGateTool)(server, handle, getPlayerId);
100
- (0, evaluate_gate_1.registerEvaluateGateTool)(server, handle, getPlayerId);
101
- (0, gates_1.registerGatesTool)(server, handle);
102
- (0, worktree_1.registerWorktreeTool)(server, client, config, handle, getPlayerId);
103
- (0, stage_1.registerStageTool)(server, handle, getPlayerId);
104
- (0, stages_1.registerStagesTool)(server, handle);
105
- (0, cancel_stage_1.registerCancelStageTool)(server, handle);
105
+ tools.push((0, quality_gate_1.buildQualityGateTool)(handle, getPlayerId), (0, evaluate_gate_1.buildEvaluateGateTool)(handle, getPlayerId), (0, gates_1.buildGatesTool)(handle), (0, worktree_1.buildWorktreeTool)(client, config, handle, getPlayerId), (0, stage_1.buildStageTool)(handle, getPlayerId), (0, stages_1.buildStagesTool)(handle), (0, cancel_stage_1.buildCancelStageTool)(handle));
106
106
  }
107
+ return tools;
108
+ }
109
+ function registerAllTempoTools(server, opts) {
110
+ (0, descriptor_1.renderToMcp)(server, buildAllTempoTools(opts));
107
111
  }
108
112
  /**
109
113
  * Build the `instructions` string that lands in `McpServer`'s `instructions`
package/dist/spawn.d.ts CHANGED
@@ -236,6 +236,61 @@ export interface OpenCodeAdapterResult {
236
236
  * specific provider key since the model is opaque pass-through).
237
237
  */
238
238
  export declare function spawnOpenCodeAdapter(opts: OpenCodeAdapterOpts): OpenCodeAdapterResult;
239
+ /**
240
+ * Options for {@link spawnPiHeadless}. Mirrors {@link OpenCodeAdapterOpts} for
241
+ * identity + Temporal connection + attachment handoff; adds Pi-specific knobs:
242
+ * `model` (provider/model via `AGENT_TEMPO_PI_MODEL`), `continueSessionId`
243
+ * (restart-resume via `AGENT_TEMPO_PI_CONTINUE_SESSION` → Pi `continueSession`),
244
+ * and `toolAccess` (MD-C tool-class policy via `AGENT_TEMPO_TOOL_ACCESS`).
245
+ *
246
+ * Unlike the other headless adapters, the Pi entry does NOT drive a
247
+ * `BaseAttachment` loop — it injects the `src/pi` extension into Pi's
248
+ * `createAgentSession`; the module-scope extension singleton owns the lifecycle.
249
+ */
250
+ export interface PiHeadlessAdapterOpts {
251
+ name: string;
252
+ ensemble: string;
253
+ temporalAddress: string;
254
+ temporalNamespace?: string;
255
+ temporalApiKey?: string;
256
+ temporalTlsCertPath?: string;
257
+ temporalTlsKeyPath?: string;
258
+ isConductor?: boolean;
259
+ workDir: string;
260
+ /** Directory for log + PID files. Defaults to `logs/` inside workDir. */
261
+ logDir?: string;
262
+ /** Pi provider/model selector (e.g. `anthropic/claude-opus-4-7`); absent → Pi default. */
263
+ model?: string;
264
+ /** Restart-resume: the Pi conversation id to continue (from `metadata.sessionId`). */
265
+ continueSessionId?: string;
266
+ /** MD-C tool-class policy: `restricted` (default) | `standard` | `full`. */
267
+ toolAccess?: string;
268
+ /**
269
+ * 3c Tier-2 ingest token (minted by the daemon outbox, scoped to this player's
270
+ * workflowId). Threaded into the subprocess env as `AGENT_TEMPO_INGEST_TOKEN`
271
+ * so the inner-loop publisher can authenticate `POST /inner/ingest`. NOTE:
272
+ * spawn.ts only THREADS the token — minting lives in the daemon-only outbox
273
+ * (this module runs outside the daemon and must not import the registry).
274
+ */
275
+ ingestToken?: string;
276
+ /** PR-D attachment-lease handoff (renew rather than fresh-claim on boot). */
277
+ attachmentId?: string;
278
+ attachmentRunId?: string;
279
+ adapterId?: string;
280
+ }
281
+ export interface PiHeadlessAdapterResult {
282
+ pid: number | undefined;
283
+ logPath: string;
284
+ pidPath: string;
285
+ }
286
+ /**
287
+ * Spawn the headless Pi runtime as a detached subprocess. Pattern matches
288
+ * {@link spawnOpenCodeAdapter} — no TTY, log + PID files, env carries identity +
289
+ * Temporal settings + attachment handoff + the Pi model / continue-session /
290
+ * tool-access knobs. The entry constructs `createAgentSession` with the `src/pi`
291
+ * extension injected inline; the singleton claims + heartbeats + registers tools.
292
+ */
293
+ export declare function spawnPiHeadless(opts: PiHeadlessAdapterOpts): PiHeadlessAdapterResult;
239
294
  /**
240
295
  * Options for {@link spawnClaudeCodeHeadlessAdapter}. Mirrors
241
296
  * {@link ClaudeApiAdapterOpts} for identity + Temporal connection +
package/dist/spawn.js CHANGED
@@ -12,6 +12,7 @@ exports.spawnCopilotBridge = spawnCopilotBridge;
12
12
  exports.spawnMockAdapter = spawnMockAdapter;
13
13
  exports.spawnClaudeApiAdapter = spawnClaudeApiAdapter;
14
14
  exports.spawnOpenCodeAdapter = spawnOpenCodeAdapter;
15
+ exports.spawnPiHeadless = spawnPiHeadless;
15
16
  exports.spawnClaudeCodeHeadlessAdapter = spawnClaudeCodeHeadlessAdapter;
16
17
  const child_process_1 = require("child_process");
17
18
  const fs_1 = require("fs");
@@ -671,6 +672,77 @@ function spawnOpenCodeAdapter(opts) {
671
672
  log(`Spawned opencode adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}"${opts.model ? ` (model=${opts.model})` : ''}${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
672
673
  return { pid: child.pid, logPath, pidPath };
673
674
  }
675
+ /**
676
+ * Resolve the path to the Pi headless adapter entry point. Mirrors
677
+ * {@link resolveOpenCodePath} — dev (ts-node) + prod (compiled .js) both launch
678
+ * the same code through the same `require.main === module` gate.
679
+ */
680
+ function resolvePiPath() {
681
+ const isDev = __filename.endsWith('.ts');
682
+ if (isDev) {
683
+ return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'pi', 'adapter.ts')] };
684
+ }
685
+ return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'pi', 'adapter.js')] };
686
+ }
687
+ /**
688
+ * Spawn the headless Pi runtime as a detached subprocess. Pattern matches
689
+ * {@link spawnOpenCodeAdapter} — no TTY, log + PID files, env carries identity +
690
+ * Temporal settings + attachment handoff + the Pi model / continue-session /
691
+ * tool-access knobs. The entry constructs `createAgentSession` with the `src/pi`
692
+ * extension injected inline; the singleton claims + heartbeats + registers tools.
693
+ */
694
+ function spawnPiHeadless(opts) {
695
+ const { cmd, args } = resolvePiPath();
696
+ const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
697
+ const logName = opts.name || `pi-${Date.now()}`;
698
+ const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
699
+ const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
700
+ (0, fs_1.mkdirSync)(logDirPath, { recursive: true });
701
+ const logFd = (0, fs_1.openSync)(logPath, 'a');
702
+ const toolAccess = opts.toolAccess || 'restricted';
703
+ let child;
704
+ try {
705
+ child = (0, child_process_1.spawn)(cmd, args, {
706
+ cwd: opts.workDir,
707
+ detached: true,
708
+ stdio: ['ignore', logFd, logFd],
709
+ env: {
710
+ ...process.env,
711
+ [config_1.ENV.ENSEMBLE]: opts.ensemble,
712
+ [config_1.ENV.PLAYER_NAME]: opts.name,
713
+ [config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
714
+ [config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
715
+ ...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
716
+ ...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
717
+ ...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
718
+ ...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
719
+ // Model selection: recruit-arg → AGENT_TEMPO_PI_MODEL → Pi default.
720
+ ...(opts.model ? { [config_1.ENV.PI_MODEL]: opts.model } : {}),
721
+ // Restart-resume: continue the prior Pi conversation.
722
+ ...(opts.continueSessionId ? { [config_1.ENV.PI_CONTINUE_SESSION]: opts.continueSessionId } : {}),
723
+ // MD-C: tool-class policy. ALWAYS set (default 'restricted' — the safe
724
+ // unsupervised default + an explicit value for the gate + audit trail).
725
+ [config_1.ENV.TOOL_ACCESS]: toolAccess,
726
+ // 3c Tier-2: per-player ingest token (minted by the daemon outbox).
727
+ // Absent → the inner-loop publisher's HTTP client no-ops (no fine tail).
728
+ ...(opts.ingestToken ? { [config_1.ENV.INGEST_TOKEN]: opts.ingestToken } : {}),
729
+ // Attachment handoff — extension renews via claimAttachment(expectedAttachmentId).
730
+ ...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
731
+ ...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
732
+ ...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
733
+ },
734
+ });
735
+ child.unref();
736
+ }
737
+ finally {
738
+ (0, fs_1.closeSync)(logFd);
739
+ }
740
+ if (child.pid != null) {
741
+ (0, fs_1.writeFileSync)(pidPath, String(child.pid));
742
+ }
743
+ log(`Spawned pi headless adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}" (toolAccess=${toolAccess})${opts.model ? ` (model=${opts.model})` : ''}${opts.continueSessionId ? ` (continue=${opts.continueSessionId})` : ''}${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
744
+ return { pid: child.pid, logPath, pidPath };
745
+ }
674
746
  /**
675
747
  * Resolve the path to the claude-code-headless adapter entry point.
676
748
  * Mirrors {@link resolveClaudeApiPath} so dev (ts-node) and prod
@@ -1,2 +1,2 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerAgentTypesTool(server: McpServer): void;
1
+ import { type TempoToolDescriptor } from './descriptor';
2
+ export declare function buildAgentTypesTool(): TempoToolDescriptor;
@@ -1,21 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerAgentTypesTool = registerAgentTypesTool;
4
- const helpers_1 = require("./helpers");
3
+ exports.buildAgentTypesTool = buildAgentTypesTool;
4
+ const descriptor_1 = require("./descriptor");
5
5
  const agent_types_1 = require("../ensemble/agent-types");
6
- function registerAgentTypesTool(server) {
7
- (0, helpers_1.defineTool)(server, 'agent_types', 'List available player types (agent definitions) that can be used when recruiting', {}, async () => {
8
- const types = (0, agent_types_1.listAgentTypes)();
9
- if (types.length === 0) {
10
- return (0, helpers_1.ok)('No agent types found.');
11
- }
12
- const lines = types.map(t => {
13
- const src = t.source === 'shipped' ? '(shipped)' : t.source === 'user' ? '(user)' : '(project)';
14
- const tools = t.allowedTools && t.allowedTools.length > 0
15
- ? `\n Allowed tools: ${t.allowedTools.join(', ')}`
16
- : '';
17
- return `**${t.name}** ${src}\n ${t.description || 'No description'}${tools}`;
18
- });
19
- return (0, helpers_1.ok)(lines.join('\n\n'));
20
- });
6
+ function buildAgentTypesTool() {
7
+ return {
8
+ name: 'agent_types',
9
+ description: 'List available player types (agent definitions) that can be used when recruiting',
10
+ params: {},
11
+ handler: async () => {
12
+ const types = (0, agent_types_1.listAgentTypes)();
13
+ if (types.length === 0) {
14
+ return (0, descriptor_1.ok)('No agent types found.');
15
+ }
16
+ const lines = types.map(t => {
17
+ const src = t.source === 'shipped' ? '(shipped)' : t.source === 'user' ? '(user)' : '(project)';
18
+ const tools = t.allowedTools && t.allowedTools.length > 0
19
+ ? `\n Allowed tools: ${t.allowedTools.join(', ')}`
20
+ : '';
21
+ return `**${t.name}** ${src}\n ${t.description || 'No description'}${tools}`;
22
+ });
23
+ return (0, descriptor_1.ok)(lines.join('\n\n'));
24
+ },
25
+ };
21
26
  }
@@ -1,4 +1,4 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
1
  import { Client } from '@temporalio/client';
3
2
  import { Config } from '../config';
4
- export declare function registerAttachmentInfoTool(server: McpServer, client: Client, config: Config): void;
3
+ import { type TempoToolDescriptor } from './descriptor';
4
+ export declare function buildAttachmentInfoTool(client: Client, config: Config): TempoToolDescriptor;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerAttachmentInfoTool = registerAttachmentInfoTool;
3
+ exports.buildAttachmentInfoTool = buildAttachmentInfoTool;
4
4
  /**
5
5
  * `attachment_info` — diagnostic query for the V2 attachment lifecycle (design §§2.2, 2.4).
6
6
  *
@@ -11,38 +11,43 @@ exports.registerAttachmentInfoTool = registerAttachmentInfoTool;
11
11
  const zod_1 = require("zod");
12
12
  const resolve_1 = require("./resolve");
13
13
  const signals_1 = require("../workflows/signals");
14
- const helpers_1 = require("./helpers");
14
+ const descriptor_1 = require("./descriptor");
15
15
  const validation_1 = require("../utils/validation");
16
- function registerAttachmentInfoTool(server, client, config) {
17
- (0, helpers_1.defineTool)(server, 'attachment_info', 'Query the V2 attachment lifecycle state of a session — phase (booting/attached/processing/awaiting/draining/detached/gone), current attachment holder (host + adapter + lease expiry), preferred host, and in-flight message count. Read-only diagnostic.', {
18
- playerId: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('The player name to inspect'),
19
- }, async (args) => {
20
- const { playerId } = args;
21
- const nameError = (0, validation_1.validatePlayerName)(playerId);
22
- if (nameError)
23
- return (0, helpers_1.fail)(nameError);
24
- try {
25
- const resolved = await (0, resolve_1.resolveSession)(client, config.ensemble, playerId);
26
- if (!resolved)
27
- return (0, helpers_1.fail)(`No session found with name "${playerId}".`);
28
- const info = await resolved.query(signals_1.attachmentInfoQuery);
29
- const lines = [
30
- `**${playerId}** — phase: \`${info.phase}\``,
31
- `in-flight messages: ${info.inFlightCount}`,
32
- ];
33
- if (info.currentAttachment) {
34
- const a = info.currentAttachment;
35
- lines.push(`attached on: **${a.hostname}** (adapter: ${a.adapterId}/${a.adapterClass}, attachmentId: \`${a.attachmentId.slice(0, 8)}…\`)`);
36
- lines.push(`lease expires: ${a.expiresAt}`);
16
+ function buildAttachmentInfoTool(client, config) {
17
+ return {
18
+ name: 'attachment_info',
19
+ description: 'Query the V2 attachment lifecycle state of a session — phase (booting/attached/processing/awaiting/draining/detached/gone), current attachment holder (host + adapter + lease expiry), preferred host, and in-flight message count. Read-only diagnostic.',
20
+ params: {
21
+ playerId: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('The player name to inspect'),
22
+ },
23
+ handler: async (args) => {
24
+ const { playerId } = args;
25
+ const nameError = (0, validation_1.validatePlayerName)(playerId);
26
+ if (nameError)
27
+ return (0, descriptor_1.fail)(nameError);
28
+ try {
29
+ const resolved = await (0, resolve_1.resolveSession)(client, config.ensemble, playerId);
30
+ if (!resolved)
31
+ return (0, descriptor_1.fail)(`No session found with name "${playerId}".`);
32
+ const info = await resolved.query(signals_1.attachmentInfoQuery);
33
+ const lines = [
34
+ `**${playerId}** phase: \`${info.phase}\``,
35
+ `in-flight messages: ${info.inFlightCount}`,
36
+ ];
37
+ if (info.currentAttachment) {
38
+ const a = info.currentAttachment;
39
+ lines.push(`attached on: **${a.hostname}** (adapter: ${a.adapterId}/${a.adapterClass}, attachmentId: \`${a.attachmentId.slice(0, 8)}…\`)`);
40
+ lines.push(`lease expires: ${a.expiresAt}`);
41
+ }
42
+ if (info.preferredHost)
43
+ lines.push(`preferred host: ${info.preferredHost}`);
44
+ if (info.processingSince)
45
+ lines.push(`processing since: ${info.processingSince}`);
46
+ return (0, descriptor_1.ok)(lines.join('\n'));
37
47
  }
38
- if (info.preferredHost)
39
- lines.push(`preferred host: ${info.preferredHost}`);
40
- if (info.processingSince)
41
- lines.push(`processing since: ${info.processingSince}`);
42
- return (0, helpers_1.ok)(lines.join('\n'));
43
- }
44
- catch (err) {
45
- return (0, helpers_1.fail)(`Failed to query attachment info: ${(0, helpers_1.formatError)(err)}`);
46
- }
47
- });
48
+ catch (err) {
49
+ return (0, descriptor_1.fail)(`Failed to query attachment info: ${(0, descriptor_1.formatError)(err)}`);
50
+ }
51
+ },
52
+ };
48
53
  }
@@ -1,4 +1,4 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
1
  import { Client, WorkflowHandle } from '@temporalio/client';
3
2
  import { Config } from '../config';
4
- export declare function registerBroadcastTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle): void;
3
+ import { type TempoToolDescriptor } from './descriptor';
4
+ export declare function buildBroadcastTool(client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle): TempoToolDescriptor;
@@ -1,76 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerBroadcastTool = registerBroadcastTool;
3
+ exports.buildBroadcastTool = buildBroadcastTool;
4
4
  const zod_1 = require("zod");
5
5
  const crypto_1 = require("crypto");
6
6
  const signals_1 = require("../workflows/signals");
7
- const helpers_1 = require("./helpers");
7
+ const descriptor_1 = require("./descriptor");
8
8
  const validation_1 = require("../utils/validation");
9
9
  const search_attributes_1 = require("../utils/search-attributes");
10
- function registerBroadcastTool(server, client, config, getPlayerId, handle) {
11
- (0, helpers_1.defineTool)(server, 'broadcast', 'Send a message to all active players in the ensemble. Optionally filter by player type.', {
12
- message: zod_1.z.string().max(validation_1.MESSAGE_MAX).describe('The message to broadcast'),
13
- type: zod_1.z.string().optional().describe('Only send to players of this type (e.g., "tempo-soloist")'),
14
- includeStale: zod_1.z.boolean().optional().describe('Include disconnected sessions (draining/detached phases; default: false). Argument name kept for backward compatibility.'),
15
- }, async (args) => {
16
- const { message, type: playerType, includeStale: rawIncludeStale } = args;
17
- const includeDisconnected = rawIncludeStale === true;
18
- try {
19
- const query = `WorkflowType = "agentSessionWorkflow" AND ExecutionStatus = "Running"`;
20
- const targets = [];
21
- for await (const workflow of client.workflow.list({ query })) {
22
- try {
23
- const wfHandle = client.workflow.getHandle(workflow.workflowId);
24
- const metadata = await wfHandle.query('getMetadata');
25
- // Filter by ensemble
26
- if (metadata.ensemble !== config.ensemble)
27
- continue;
28
- // Exclude sender
29
- if (metadata.playerId === getPlayerId())
30
- continue;
31
- // Filter by attachment phase (post-#176). Phase lives on the
32
- // `AgentTempoAttachmentState` search attribute.
33
- const phase = (0, search_attributes_1.getAttachmentPhase)(workflow);
34
- if (!(0, validation_1.shouldIncludeInBroadcast)(phase, includeDisconnected))
35
- continue;
36
- // Filter by player type if specified
37
- if (playerType && metadata.playerType !== playerType)
38
- continue;
39
- targets.push({
40
- playerId: metadata.playerId,
41
- playerType: metadata.playerType,
42
- });
10
+ function buildBroadcastTool(client, config, getPlayerId, handle) {
11
+ return {
12
+ name: 'broadcast',
13
+ description: 'Send a message to all active players in the ensemble. Optionally filter by player type.',
14
+ params: {
15
+ message: zod_1.z.string().max(validation_1.MESSAGE_MAX).describe('The message to broadcast'),
16
+ type: zod_1.z.string().optional().describe('Only send to players of this type (e.g., "tempo-soloist")'),
17
+ includeStale: zod_1.z.boolean().optional().describe('Include disconnected sessions (draining/detached phases; default: false). Argument name kept for backward compatibility.'),
18
+ },
19
+ handler: async (args) => {
20
+ const { message, type: playerType, includeStale: rawIncludeStale } = args;
21
+ const includeDisconnected = rawIncludeStale === true;
22
+ try {
23
+ const query = `WorkflowType = "agentSessionWorkflow" AND ExecutionStatus = "Running"`;
24
+ const targets = [];
25
+ for await (const workflow of client.workflow.list({ query })) {
26
+ try {
27
+ const wfHandle = client.workflow.getHandle(workflow.workflowId);
28
+ const metadata = await wfHandle.query('getMetadata');
29
+ // Filter by ensemble
30
+ if (metadata.ensemble !== config.ensemble)
31
+ continue;
32
+ // Exclude sender
33
+ if (metadata.playerId === getPlayerId())
34
+ continue;
35
+ // Filter by attachment phase (post-#176). Phase lives on the
36
+ // `AgentTempoAttachmentState` search attribute.
37
+ const phase = (0, search_attributes_1.getAttachmentPhase)(workflow);
38
+ if (!(0, validation_1.shouldIncludeInBroadcast)(phase, includeDisconnected))
39
+ continue;
40
+ // Filter by player type if specified
41
+ if (playerType && metadata.playerType !== playerType)
42
+ continue;
43
+ targets.push({
44
+ playerId: metadata.playerId,
45
+ playerType: metadata.playerType,
46
+ });
47
+ }
48
+ catch {
49
+ // Workflow may have just completed — skip it
50
+ }
43
51
  }
44
- catch {
45
- // Workflow may have just completed skip it
52
+ if (targets.length === 0) {
53
+ return (0, descriptor_1.ok)('No active players matched the broadcast filter.');
46
54
  }
55
+ // #357: stamp every fan-out cue with the same `broadcastId` so the
56
+ // TUI can fold the N deliveries into one chat row. Generated ONCE
57
+ // here in the MCP-tool process — not inside the workflow — so
58
+ // workflow determinism is preserved (the workflow only sees the id
59
+ // as an opaque string on `OutboxEntryInput`).
60
+ const broadcastId = (0, crypto_1.randomUUID)();
61
+ // Fan out cue outbox entries for each target.
62
+ const entryIds = [];
63
+ for (const target of targets) {
64
+ const entry = {
65
+ type: 'cue',
66
+ targetPlayerId: target.playerId,
67
+ message,
68
+ broadcastId,
69
+ };
70
+ const entryId = await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
71
+ entryIds.push(entryId);
72
+ }
73
+ const names = targets.map((t) => t.playerId);
74
+ return (0, descriptor_1.ok)(`Broadcast sent to ${targets.length} player${targets.length === 1 ? '' : 's'}: ${names.join(', ')}`);
47
75
  }
48
- if (targets.length === 0) {
49
- return (0, helpers_1.ok)('No active players matched the broadcast filter.');
50
- }
51
- // #357: stamp every fan-out cue with the same `broadcastId` so the
52
- // TUI can fold the N deliveries into one chat row. Generated ONCE
53
- // here in the MCP-tool process — not inside the workflow — so
54
- // workflow determinism is preserved (the workflow only sees the id
55
- // as an opaque string on `OutboxEntryInput`).
56
- const broadcastId = (0, crypto_1.randomUUID)();
57
- // Fan out cue outbox entries for each target.
58
- const entryIds = [];
59
- for (const target of targets) {
60
- const entry = {
61
- type: 'cue',
62
- targetPlayerId: target.playerId,
63
- message,
64
- broadcastId,
65
- };
66
- const entryId = await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
67
- entryIds.push(entryId);
76
+ catch (err) {
77
+ return (0, descriptor_1.fail)(`Failed to broadcast: ${(0, descriptor_1.formatError)(err)}`);
68
78
  }
69
- const names = targets.map((t) => t.playerId);
70
- return (0, helpers_1.ok)(`Broadcast sent to ${targets.length} player${targets.length === 1 ? '' : 's'}: ${names.join(', ')}`);
71
- }
72
- catch (err) {
73
- return (0, helpers_1.fail)(`Failed to broadcast: ${(0, helpers_1.formatError)(err)}`);
74
- }
75
- });
79
+ },
80
+ };
76
81
  }
@@ -1,3 +1,3 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
1
  import { WorkflowHandle } from '@temporalio/client';
3
- export declare function registerCancelStageTool(server: McpServer, handle: WorkflowHandle): void;
2
+ import { type TempoToolDescriptor } from './descriptor';
3
+ export declare function buildCancelStageTool(handle: WorkflowHandle): TempoToolDescriptor;