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,74 @@
1
+ /**
2
+ * Narrow zod → TypeBox converter (D1 / MD-B, Phase 1).
3
+ *
4
+ * agent-tempo tools declare their parameters as a `z.ZodRawShape`
5
+ * (`Record<string, z.ZodTypeAny>`) — the exact object handed to
6
+ * `McpServer.tool(...)` today (see `src/tools/helpers.ts` `defineTool`). The Pi
7
+ * front-end (`pi.registerTool({ parameters })`) wants a TypeBox schema instead.
8
+ * This module derives the TypeBox object schema from the zod shape so **zod
9
+ * stays the single source of truth** and the two surfaces cannot drift.
10
+ *
11
+ * DESIGN: this is deliberately NOT a general-purpose zod→TypeBox shim. It
12
+ * handles ONLY the feature subset the ~30 tools actually use (audited
13
+ * 2026-06-03) and **throws** — naming the field + feature — on anything else.
14
+ * Failing loud is the whole point (D1): a faithless silent conversion would
15
+ * ship a wrong tool schema to the model. The CI parity test (Track A / S5b)
16
+ * asserts the converter raised on nothing across the real tool set, so any new
17
+ * unaudited zod construct trips CI instead of producing a bad schema.
18
+ *
19
+ * Supported subset:
20
+ * - z.string() (+ .min/.max → minLength/maxLength, .regex → pattern)
21
+ * - z.number() (+ .int() → integer, .min/.max → minimum/maximum [inclusive])
22
+ * - z.boolean()
23
+ * - z.enum([...]) → Type.Union of Type.Literal (anyOf of const)
24
+ * - z.array(inner) (+ .min/.max → minItems/maxItems)
25
+ * - z.object({...}) (nested; appears as an array element today)
26
+ * - z.union([...]) SCALAR members only (string/number/boolean/enum/
27
+ * literal) — object/array/nested-union members throw
28
+ * - .optional() → Type.Optional (drops the field from `required`)
29
+ * - .describe(s) → TypeBox `{ description: s }` (Pi surfaces it)
30
+ *
31
+ * Everything else (.default, .nullable, .refine/.transform, .coerce, .email,
32
+ * .gt/.lt, z.record/any/unknown/null/date/tuple/intersection/nativeEnum, …)
33
+ * raises {@link UnsupportedZodFeatureError}.
34
+ *
35
+ * `.min`/`.max` are CONTEXT-SENSITIVE — the same modifier maps to
36
+ * minLength/maxLength (string), minimum/maximum (number), or minItems/maxItems
37
+ * (array) depending on the base zod type. The mapping is keyed on the base
38
+ * type, never on the modifier alone.
39
+ *
40
+ * TypeBox note (D12, confirmed): a TypeBox `Type.*` value IS a plain JSON-Schema
41
+ * object at runtime (`{ type, required, properties, … }`), so the output drops
42
+ * straight into Pi's `parameters` field with no further transform.
43
+ */
44
+ import { z } from 'zod';
45
+ import { type TObject } from 'typebox';
46
+ /** Where in the schema we are, for actionable error messages. */
47
+ interface ConvertContext {
48
+ /** Tool name, when the caller supplies it (richer errors). */
49
+ readonly tool?: string;
50
+ /** Dotted field path within the shape, e.g. `evaluations.status`. */
51
+ readonly path: string;
52
+ }
53
+ /**
54
+ * Thrown when a tool param schema uses a zod construct outside the audited
55
+ * subset. Carries the field path + offending feature so CI / callers can point
56
+ * at the exact source. A named class lets the parity test assert "raised on
57
+ * nothing" precisely.
58
+ */
59
+ export declare class UnsupportedZodFeatureError extends Error {
60
+ readonly context: ConvertContext;
61
+ readonly feature: string;
62
+ constructor(context: ConvertContext, feature: string);
63
+ }
64
+ /**
65
+ * Convert a tool's zod param shape into a TypeBox object schema suitable for
66
+ * `pi.registerTool({ parameters })`. This is the public entry point consumed by
67
+ * the Pi front-end (`src/pi/render-tools.ts`).
68
+ *
69
+ * @param shape The tool's `z.ZodRawShape` (the `paramsSchema` from `defineTool`).
70
+ * @param toolName Optional tool name, woven into error messages.
71
+ * @throws {UnsupportedZodFeatureError} on any zod construct outside the subset.
72
+ */
73
+ export declare function zodShapeToTypeBox(shape: z.ZodRawShape, toolName?: string): TObject;
74
+ export {};
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnsupportedZodFeatureError = void 0;
4
+ exports.zodShapeToTypeBox = zodShapeToTypeBox;
5
+ const typebox_1 = require("typebox");
6
+ /**
7
+ * Thrown when a tool param schema uses a zod construct outside the audited
8
+ * subset. Carries the field path + offending feature so CI / callers can point
9
+ * at the exact source. A named class lets the parity test assert "raised on
10
+ * nothing" precisely.
11
+ */
12
+ class UnsupportedZodFeatureError extends Error {
13
+ context;
14
+ feature;
15
+ constructor(context, feature) {
16
+ const where = `${context.tool ? `${context.tool}.` : ''}${context.path || '<root>'}`;
17
+ super(`zod→TypeBox: unsupported feature "${feature}" at ${where}. ` +
18
+ `Extend src/pi/zod-to-typebox.ts (and a unit test) if this construct is intentional.`);
19
+ this.context = context;
20
+ this.feature = feature;
21
+ this.name = 'UnsupportedZodFeatureError';
22
+ }
23
+ }
24
+ exports.UnsupportedZodFeatureError = UnsupportedZodFeatureError;
25
+ function defOf(zt) {
26
+ return zt._def;
27
+ }
28
+ const SCALAR_TYPE_NAMES = new Set([
29
+ 'ZodString',
30
+ 'ZodNumber',
31
+ 'ZodBoolean',
32
+ 'ZodEnum',
33
+ 'ZodLiteral',
34
+ ]);
35
+ /**
36
+ * Convert a tool's zod param shape into a TypeBox object schema suitable for
37
+ * `pi.registerTool({ parameters })`. This is the public entry point consumed by
38
+ * the Pi front-end (`src/pi/render-tools.ts`).
39
+ *
40
+ * @param shape The tool's `z.ZodRawShape` (the `paramsSchema` from `defineTool`).
41
+ * @param toolName Optional tool name, woven into error messages.
42
+ * @throws {UnsupportedZodFeatureError} on any zod construct outside the subset.
43
+ */
44
+ function zodShapeToTypeBox(shape, toolName) {
45
+ return buildObject(shape, { tool: toolName, path: '' });
46
+ }
47
+ function buildObject(shape, ctx, meta = {}) {
48
+ const props = {};
49
+ for (const [key, zt] of Object.entries(shape)) {
50
+ const fieldCtx = {
51
+ tool: ctx.tool,
52
+ path: ctx.path ? `${ctx.path}.${key}` : key,
53
+ };
54
+ const { inner, isOptional, outerDescription } = stripOptional(zt);
55
+ const schema = convert(inner, fieldCtx, outerDescription);
56
+ props[key] = isOptional ? typebox_1.Type.Optional(schema) : schema;
57
+ }
58
+ return typebox_1.Type.Object(props, meta);
59
+ }
60
+ /**
61
+ * Peel a single `.optional()` wrapper. Captures the description authored on the
62
+ * wrapper (e.g. `z.string().optional().describe('…')` puts it on the optional)
63
+ * so it survives onto the inner schema.
64
+ */
65
+ function stripOptional(zt) {
66
+ const def = defOf(zt);
67
+ if (def?.typeName === 'ZodOptional') {
68
+ return { inner: def.innerType, isOptional: true, outerDescription: def.description };
69
+ }
70
+ return { inner: zt, isOptional: false };
71
+ }
72
+ /** Convert a NON-optional zod type. `descOverride` wins over the node's own description. */
73
+ function convert(zt, ctx, descOverride) {
74
+ const def = defOf(zt);
75
+ const typeName = def?.typeName;
76
+ const description = descOverride ?? def?.description;
77
+ const meta = description !== undefined ? { description } : {};
78
+ switch (typeName) {
79
+ case 'ZodString':
80
+ return typebox_1.Type.String({ ...meta, ...stringConstraints(zt, ctx) });
81
+ case 'ZodNumber': {
82
+ const { isInt, opts } = numberConstraints(zt, ctx);
83
+ const numMeta = { ...meta, ...opts };
84
+ return isInt ? typebox_1.Type.Integer(numMeta) : typebox_1.Type.Number(numMeta);
85
+ }
86
+ case 'ZodBoolean':
87
+ return typebox_1.Type.Boolean(meta);
88
+ case 'ZodEnum': {
89
+ const values = def.values;
90
+ return typebox_1.Type.Union(values.map((v) => typebox_1.Type.Literal(v)), meta);
91
+ }
92
+ case 'ZodLiteral':
93
+ return typebox_1.Type.Literal(def.value, meta);
94
+ case 'ZodArray':
95
+ return typebox_1.Type.Array(arrayElement(def.type, ctx), {
96
+ ...meta,
97
+ ...arrayConstraints(zt, ctx),
98
+ });
99
+ case 'ZodObject':
100
+ return buildObject(def.shape(), ctx, meta);
101
+ case 'ZodUnion':
102
+ return convertUnion(zt, ctx, meta);
103
+ default:
104
+ throw new UnsupportedZodFeatureError(ctx, typeName ?? 'unknown (not a zod type)');
105
+ }
106
+ }
107
+ /** Array elements are converted as non-optional types; an optional element is rejected. */
108
+ function arrayElement(el, ctx) {
109
+ const { inner, isOptional } = stripOptional(el);
110
+ if (isOptional) {
111
+ throw new UnsupportedZodFeatureError({ ...ctx, path: `${ctx.path}[]` }, 'optional array element');
112
+ }
113
+ return convert(inner, { ...ctx, path: `${ctx.path}[]` });
114
+ }
115
+ function stringConstraints(zt, ctx) {
116
+ const out = {};
117
+ for (const check of (defOf(zt).checks ?? [])) {
118
+ switch (check.kind) {
119
+ case 'min':
120
+ out.minLength = check.value;
121
+ break;
122
+ case 'max':
123
+ out.maxLength = check.value;
124
+ break;
125
+ case 'regex':
126
+ out.pattern = check.regex.source;
127
+ break;
128
+ default:
129
+ throw new UnsupportedZodFeatureError(ctx, `z.string().${String(check.kind)}()`);
130
+ }
131
+ }
132
+ return out;
133
+ }
134
+ function numberConstraints(zt, ctx) {
135
+ let isInt = false;
136
+ const opts = {};
137
+ for (const check of (defOf(zt).checks ?? [])) {
138
+ switch (check.kind) {
139
+ case 'int':
140
+ isInt = true;
141
+ break;
142
+ case 'min':
143
+ if (check.inclusive === false) {
144
+ throw new UnsupportedZodFeatureError(ctx, 'z.number() exclusive minimum (.gt)');
145
+ }
146
+ opts.minimum = check.value;
147
+ break;
148
+ case 'max':
149
+ if (check.inclusive === false) {
150
+ throw new UnsupportedZodFeatureError(ctx, 'z.number() exclusive maximum (.lt)');
151
+ }
152
+ opts.maximum = check.value;
153
+ break;
154
+ default:
155
+ throw new UnsupportedZodFeatureError(ctx, `z.number().${String(check.kind)}()`);
156
+ }
157
+ }
158
+ return { isInt, opts };
159
+ }
160
+ function arrayConstraints(zt, ctx) {
161
+ // NOTE: zod stores array length bounds as named `_def` FIELDS
162
+ // (`minLength`/`maxLength`/`exactLength`), NOT as `_def.checks[]` entries like
163
+ // string and number bounds. Don't "unify" this with stringConstraints /
164
+ // numberConstraints — they read a genuinely different zod representation.
165
+ const def = defOf(zt);
166
+ const out = {};
167
+ if (def.exactLength) {
168
+ throw new UnsupportedZodFeatureError(ctx, 'z.array().length() (exact length)');
169
+ }
170
+ if (def.minLength)
171
+ out.minItems = def.minLength.value;
172
+ if (def.maxLength)
173
+ out.maxItems = def.maxLength.value;
174
+ return out;
175
+ }
176
+ function convertUnion(zt, ctx, meta) {
177
+ const options = defOf(zt).options;
178
+ const members = options.map((member, i) => {
179
+ const memberCtx = { ...ctx, path: `${ctx.path}|${i}` };
180
+ const { inner, isOptional } = stripOptional(member);
181
+ if (isOptional) {
182
+ throw new UnsupportedZodFeatureError(memberCtx, 'optional union member');
183
+ }
184
+ const memberTypeName = defOf(inner)?.typeName;
185
+ if (memberTypeName === undefined || !SCALAR_TYPE_NAMES.has(memberTypeName)) {
186
+ throw new UnsupportedZodFeatureError(memberCtx, `non-scalar union member (${memberTypeName ?? 'unknown'}); only string/number/boolean/enum/literal members are supported`);
187
+ }
188
+ return convert(inner, memberCtx);
189
+ });
190
+ return typebox_1.Type.Union(members, meta);
191
+ }
@@ -81,30 +81,30 @@ catch (err) {
81
81
  console.error(err && err.message);
82
82
  process.exit(1);
83
83
  }
84
- const detector = `
85
- // Step 1: load daemon-command (should leave require.cache clean of Temporal).
86
- require(${JSON.stringify(DAEMON_COMMAND_DIST)});
87
- // Step 2: simulate a regression — load @temporalio/client directly.
88
- // This is what would happen if a future edit to daemon-command (or one of
89
- // its deps) accidentally imported something Temporal-adjacent.
90
- require(${JSON.stringify(temporalClientPath)});
91
- // Step 3: run the same detector as test/daemon-command-isolation.test.ts.
92
- const forbidden = [
93
- /[\\\\/]@temporalio[\\\\/]/,
94
- /[\\\\/]rxjs[\\\\/]/,
95
- /[\\\\/]@grpc[\\\\/]/,
96
- /[\\\\/]nice-grpc(?:-[^\\\\/]+)?[\\\\/]/,
97
- /[\\\\/]long[\\\\/]umd[\\\\/]/,
98
- ];
99
- const hits = Object.keys(require.cache).filter((k) => forbidden.some((re) => re.test(k)));
100
- if (hits.length > 0) {
101
- console.log('Detector found ' + hits.length + ' forbidden module(s) in require.cache:');
102
- for (const h of hits.slice(0, 3)) console.log(' ' + h);
103
- if (hits.length > 3) console.log(' ... (' + (hits.length - 3) + ' more)');
104
- process.exit(0);
105
- }
106
- console.error('BUG: detector did not flag any of the injected forbidden modules.');
107
- process.exit(1);
84
+ const detector = `
85
+ // Step 1: load daemon-command (should leave require.cache clean of Temporal).
86
+ require(${JSON.stringify(DAEMON_COMMAND_DIST)});
87
+ // Step 2: simulate a regression — load @temporalio/client directly.
88
+ // This is what would happen if a future edit to daemon-command (or one of
89
+ // its deps) accidentally imported something Temporal-adjacent.
90
+ require(${JSON.stringify(temporalClientPath)});
91
+ // Step 3: run the same detector as test/daemon-command-isolation.test.ts.
92
+ const forbidden = [
93
+ /[\\\\/]@temporalio[\\\\/]/,
94
+ /[\\\\/]rxjs[\\\\/]/,
95
+ /[\\\\/]@grpc[\\\\/]/,
96
+ /[\\\\/]nice-grpc(?:-[^\\\\/]+)?[\\\\/]/,
97
+ /[\\\\/]long[\\\\/]umd[\\\\/]/,
98
+ ];
99
+ const hits = Object.keys(require.cache).filter((k) => forbidden.some((re) => re.test(k)));
100
+ if (hits.length > 0) {
101
+ console.log('Detector found ' + hits.length + ' forbidden module(s) in require.cache:');
102
+ for (const h of hits.slice(0, 3)) console.log(' ' + h);
103
+ if (hits.length > 3) console.log(' ... (' + (hits.length - 3) + ' more)');
104
+ process.exit(0);
105
+ }
106
+ console.error('BUG: detector did not flag any of the injected forbidden modules.');
107
+ process.exit(1);
108
108
  `;
109
109
  const result = (0, child_process_1.spawnSync)(process.execPath, ['-e', detector], {
110
110
  stdio: ['ignore', 'inherit', 'inherit'],
@@ -20,6 +20,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
20
20
  import { Client, WorkflowHandle } from '@temporalio/client';
21
21
  import { Config } from './config';
22
22
  import { AgentType } from './types';
23
+ import { type TempoToolDescriptor } from './tools/descriptor';
23
24
  /**
24
25
  * Identity + state context every tool registration consumes. The two
25
26
  * surfaces (stdio MCP server in `src/server.ts`, in-process MCP server in
@@ -57,6 +58,7 @@ export interface RegisterAllTempoToolsOpts {
57
58
  * `opts.isConductor` — non-conductor players don't see them on either
58
59
  * surface.
59
60
  */
61
+ export declare function buildAllTempoTools(opts: RegisterAllTempoToolsOpts): TempoToolDescriptor[];
60
62
  export declare function registerAllTempoTools(server: McpServer, opts: RegisterAllTempoToolsOpts): void;
61
63
  /**
62
64
  * Identity + ensemble context for {@link buildServerInstructions}.
@@ -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/server.js CHANGED
@@ -56,9 +56,13 @@ const server_tools_1 = require("./server-tools");
56
56
  const adapters_1 = require("./adapters");
57
57
  const agent_types_1 = require("./ensemble/agent-types");
58
58
  const parent_death_watchdog_1 = require("./utils/parent-death-watchdog");
59
+ const grpc_shutdown_guard_1 = require("./utils/grpc-shutdown-guard");
59
60
  const log = (...args) => console.error('[agent-tempo]', ...args);
60
61
  async function main() {
61
62
  (0, parent_death_watchdog_1.installParentDeathWatchdog)();
63
+ // Neutralize the Temporal/grpc-js "Channel has been shut down" retry-after-
64
+ // close race. See src/utils/grpc-shutdown-guard.ts.
65
+ (0, grpc_shutdown_guard_1.installGrpcShutdownGuard)();
62
66
  // Only activate when explicitly opted in via AGENT_TEMPO_ENSEMBLE
63
67
  if (!process.env[config_1.ENV.ENSEMBLE]) {
64
68
  log(`${config_1.ENV.ENSEMBLE} not set — MCP server idle (no workflow started)`);
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 +