agent-tempo 1.3.1 → 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 (197) 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 +224 -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 +53 -0
  65. package/dist/pi/mission-control/board.js +104 -0
  66. package/dist/pi/mission-control/extension.d.ts +44 -0
  67. package/dist/pi/mission-control/extension.js +251 -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 +95 -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 +208 -0
  79. package/dist/pi/pi-types.js +21 -0
  80. package/dist/pi/probe.d.ts +80 -0
  81. package/dist/pi/probe.js +154 -0
  82. package/dist/pi/render-tools.d.ts +17 -0
  83. package/dist/pi/render-tools.js +51 -0
  84. package/dist/pi/reset-pump.d.ts +47 -0
  85. package/dist/pi/reset-pump.js +85 -0
  86. package/dist/pi/tool-capability.d.ts +60 -0
  87. package/dist/pi/tool-capability.js +156 -0
  88. package/dist/pi/workflow-client.d.ts +158 -0
  89. package/dist/pi/workflow-client.js +289 -0
  90. package/dist/pi/zod-to-typebox.d.ts +74 -0
  91. package/dist/pi/zod-to-typebox.js +191 -0
  92. package/dist/server-tools.d.ts +2 -0
  93. package/dist/server-tools.js +50 -46
  94. package/dist/spawn.d.ts +55 -0
  95. package/dist/spawn.js +72 -0
  96. package/dist/tools/agent-types.d.ts +2 -2
  97. package/dist/tools/agent-types.js +22 -17
  98. package/dist/tools/attachment-info.d.ts +2 -2
  99. package/dist/tools/attachment-info.js +38 -33
  100. package/dist/tools/broadcast.d.ts +2 -2
  101. package/dist/tools/broadcast.js +69 -64
  102. package/dist/tools/cancel-stage.d.ts +2 -2
  103. package/dist/tools/cancel-stage.js +20 -15
  104. package/dist/tools/clear-state.d.ts +2 -2
  105. package/dist/tools/clear-state.js +25 -20
  106. package/dist/tools/coat-check-evict.d.ts +2 -2
  107. package/dist/tools/coat-check-evict.js +29 -24
  108. package/dist/tools/coat-check-get.d.ts +2 -2
  109. package/dist/tools/coat-check-get.js +38 -33
  110. package/dist/tools/coat-check-list.d.ts +2 -2
  111. package/dist/tools/coat-check-list.js +48 -43
  112. package/dist/tools/coat-check-put.d.ts +2 -2
  113. package/dist/tools/coat-check-put.js +38 -33
  114. package/dist/tools/cue.d.ts +2 -2
  115. package/dist/tools/cue.js +57 -52
  116. package/dist/tools/descriptor.d.ts +72 -0
  117. package/dist/tools/descriptor.js +39 -0
  118. package/dist/tools/destroy.d.ts +2 -2
  119. package/dist/tools/destroy.js +153 -148
  120. package/dist/tools/ensemble.d.ts +2 -2
  121. package/dist/tools/ensemble.js +71 -66
  122. package/dist/tools/evaluate-gate.d.ts +2 -2
  123. package/dist/tools/evaluate-gate.js +33 -27
  124. package/dist/tools/fetch-state.d.ts +2 -2
  125. package/dist/tools/fetch-state.js +42 -37
  126. package/dist/tools/gates.d.ts +2 -2
  127. package/dist/tools/gates.js +39 -34
  128. package/dist/tools/hosts.d.ts +2 -2
  129. package/dist/tools/hosts.js +25 -20
  130. package/dist/tools/listen.d.ts +2 -2
  131. package/dist/tools/listen.js +23 -18
  132. package/dist/tools/load-lineup.d.ts +2 -2
  133. package/dist/tools/load-lineup.js +324 -319
  134. package/dist/tools/migrate.d.ts +2 -2
  135. package/dist/tools/migrate.js +45 -40
  136. package/dist/tools/pause.d.ts +2 -2
  137. package/dist/tools/pause.js +34 -29
  138. package/dist/tools/play.d.ts +2 -2
  139. package/dist/tools/play.js +53 -48
  140. package/dist/tools/quality-gate.d.ts +2 -2
  141. package/dist/tools/quality-gate.js +26 -21
  142. package/dist/tools/recall.d.ts +2 -2
  143. package/dist/tools/recall.js +32 -27
  144. package/dist/tools/recruit.d.ts +2 -2
  145. package/dist/tools/recruit.js +325 -256
  146. package/dist/tools/release.d.ts +2 -2
  147. package/dist/tools/release.js +85 -80
  148. package/dist/tools/report.d.ts +2 -2
  149. package/dist/tools/report.js +28 -23
  150. package/dist/tools/reset.d.ts +3 -0
  151. package/dist/tools/reset.js +51 -0
  152. package/dist/tools/restart.d.ts +2 -2
  153. package/dist/tools/restart.js +51 -46
  154. package/dist/tools/restore.d.ts +2 -2
  155. package/dist/tools/restore.js +76 -71
  156. package/dist/tools/save-lineup.d.ts +2 -2
  157. package/dist/tools/save-lineup.js +32 -27
  158. package/dist/tools/save-state.d.ts +2 -2
  159. package/dist/tools/save-state.js +31 -26
  160. package/dist/tools/schedule.d.ts +2 -2
  161. package/dist/tools/schedule.js +133 -128
  162. package/dist/tools/schedules.d.ts +2 -2
  163. package/dist/tools/schedules.js +41 -36
  164. package/dist/tools/set-ensemble-description.d.ts +2 -2
  165. package/dist/tools/set-ensemble-description.js +26 -21
  166. package/dist/tools/set-name.d.ts +2 -2
  167. package/dist/tools/set-name.js +38 -33
  168. package/dist/tools/set-part.d.ts +2 -2
  169. package/dist/tools/set-part.js +20 -15
  170. package/dist/tools/shutdown.d.ts +2 -2
  171. package/dist/tools/shutdown.js +39 -34
  172. package/dist/tools/stage.d.ts +2 -2
  173. package/dist/tools/stage.js +28 -23
  174. package/dist/tools/stages.d.ts +2 -2
  175. package/dist/tools/stages.js +36 -31
  176. package/dist/tools/unschedule.d.ts +2 -2
  177. package/dist/tools/unschedule.js +30 -25
  178. package/dist/tools/who-am-i.d.ts +2 -2
  179. package/dist/tools/who-am-i.js +36 -31
  180. package/dist/tools/worktree.d.ts +2 -2
  181. package/dist/tools/worktree.js +134 -129
  182. package/dist/tui/index.js +6 -6
  183. package/dist/types.d.ts +47 -2
  184. package/dist/types.js +1 -1
  185. package/dist/utils/default-part.js +1 -0
  186. package/dist/utils/sdk-probe.d.ts +23 -0
  187. package/dist/utils/sdk-probe.js +46 -7
  188. package/dist/worker.d.ts +3 -1
  189. package/dist/worker.js +6 -2
  190. package/dist/workflows/session.js +70 -2
  191. package/dist/workflows/signals.d.ts +32 -2
  192. package/dist/workflows/signals.js +25 -2
  193. package/package.json +4 -1
  194. package/workflow-bundle.js +97 -6
  195. package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
  196. package/dist/tools/helpers.d.ts +0 -21
  197. package/dist/tools/helpers.js +0 -25
@@ -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;
@@ -1,20 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerCancelStageTool = registerCancelStageTool;
3
+ exports.buildCancelStageTool = buildCancelStageTool;
4
4
  const zod_1 = require("zod");
5
- const helpers_1 = require("./helpers");
5
+ const descriptor_1 = require("./descriptor");
6
6
  const validation_1 = require("../utils/validation");
7
- function registerCancelStageTool(server, handle) {
8
- (0, helpers_1.defineTool)(server, 'cancel_stage', 'Cancel an active pipeline stage. Players are no longer tracked. Conductor only.', {
9
- name: zod_1.z.string().max(validation_1.STAGE_NAME_MAX).describe('Name of the stage to cancel'),
10
- }, async (args) => {
11
- const { name } = args;
12
- try {
13
- await handle.signal('cancelStage', name);
14
- return (0, helpers_1.ok)(`Stage **${name}** cancelled.`);
15
- }
16
- catch (err) {
17
- return (0, helpers_1.fail)(`Failed to cancel stage: ${(0, helpers_1.formatError)(err)}`);
18
- }
19
- });
7
+ function buildCancelStageTool(handle) {
8
+ return {
9
+ name: 'cancel_stage',
10
+ description: 'Cancel an active pipeline stage. Players are no longer tracked. Conductor only.',
11
+ params: {
12
+ name: zod_1.z.string().max(validation_1.STAGE_NAME_MAX).describe('Name of the stage to cancel'),
13
+ },
14
+ handler: async (args) => {
15
+ const { name } = args;
16
+ try {
17
+ await handle.signal('cancelStage', name);
18
+ return (0, descriptor_1.ok)(`Stage **${name}** cancelled.`);
19
+ }
20
+ catch (err) {
21
+ return (0, descriptor_1.fail)(`Failed to cancel stage: ${(0, descriptor_1.formatError)(err)}`);
22
+ }
23
+ },
24
+ };
20
25
  }
@@ -1,3 +1,3 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
1
  import { WorkflowHandle } from '@temporalio/client';
3
- export declare function registerClearStateTool(server: McpServer, handle: WorkflowHandle): void;
2
+ import { type TempoToolDescriptor } from './descriptor';
3
+ export declare function buildClearStateTool(handle: WorkflowHandle): TempoToolDescriptor;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerClearStateTool = registerClearStateTool;
3
+ exports.buildClearStateTool = buildClearStateTool;
4
4
  /**
5
5
  * `clear_state` — remove one of the calling player's saved-state slots
6
6
  * (#334 PR-1, ADR 0011).
@@ -14,24 +14,29 @@ exports.registerClearStateTool = registerClearStateTool;
14
14
  */
15
15
  const zod_1 = require("zod");
16
16
  const signals_1 = require("../workflows/signals");
17
- const helpers_1 = require("./helpers");
17
+ const descriptor_1 = require("./descriptor");
18
18
  const validation_1 = require("../utils/validation");
19
- function registerClearStateTool(server, handle) {
20
- (0, helpers_1.defineTool)(server, 'clear_state', `Clear one of your saved-state slots. Owner-only — you can only clear your own state. Idempotent (clearing an empty slot is a no-op). Returns whether the slot was non-empty before the clear.`, {
21
- key: zod_1.z.string().regex(validation_1.PLAYER_STATE_KEY_REGEX).max(validation_1.PLAYER_STATE_KEY_MAX).optional().describe(`Slot name (default "${validation_1.PLAYER_STATE_DEFAULT_KEY}").`),
22
- }, async (args) => {
23
- const { key } = args;
24
- const slotKey = key ?? validation_1.PLAYER_STATE_DEFAULT_KEY;
25
- try {
26
- const result = await handle.executeUpdate(signals_1.clearPlayerStateUpdate, {
27
- args: [{ key: slotKey }],
28
- });
29
- return (0, helpers_1.ok)(result.cleared
30
- ? `Cleared slot **"${slotKey}"**.`
31
- : `Slot **"${slotKey}"** was already empty.`);
32
- }
33
- catch (err) {
34
- return (0, helpers_1.fail)(`Failed to clear state: ${(0, helpers_1.formatError)(err)}`);
35
- }
36
- });
19
+ function buildClearStateTool(handle) {
20
+ return {
21
+ name: 'clear_state',
22
+ description: `Clear one of your saved-state slots. Owner-only — you can only clear your own state. Idempotent (clearing an empty slot is a no-op). Returns whether the slot was non-empty before the clear.`,
23
+ params: {
24
+ key: zod_1.z.string().regex(validation_1.PLAYER_STATE_KEY_REGEX).max(validation_1.PLAYER_STATE_KEY_MAX).optional().describe(`Slot name (default "${validation_1.PLAYER_STATE_DEFAULT_KEY}").`),
25
+ },
26
+ handler: async (args) => {
27
+ const { key } = args;
28
+ const slotKey = key ?? validation_1.PLAYER_STATE_DEFAULT_KEY;
29
+ try {
30
+ const result = await handle.executeUpdate(signals_1.clearPlayerStateUpdate, {
31
+ args: [{ key: slotKey }],
32
+ });
33
+ return (0, descriptor_1.ok)(result.cleared
34
+ ? `Cleared slot **"${slotKey}"**.`
35
+ : `Slot **"${slotKey}"** was already empty.`);
36
+ }
37
+ catch (err) {
38
+ return (0, descriptor_1.fail)(`Failed to clear state: ${(0, descriptor_1.formatError)(err)}`);
39
+ }
40
+ },
41
+ };
37
42
  }
@@ -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 registerCoatCheckEvictTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string): void;
3
+ import { type TempoToolDescriptor } from './descriptor';
4
+ export declare function buildCoatCheckEvictTool(client: Client, config: Config, getPlayerId: () => string): TempoToolDescriptor;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerCoatCheckEvictTool = registerCoatCheckEvictTool;
3
+ exports.buildCoatCheckEvictTool = buildCoatCheckEvictTool;
4
4
  /**
5
5
  * `coat_check_evict` — remove a coat-check entry (#318, ADR 0008) before
6
6
  * its TTL expires. Owner-or-conductor only: the workflow validator rejects
@@ -14,30 +14,35 @@ exports.registerCoatCheckEvictTool = registerCoatCheckEvictTool;
14
14
  const zod_1 = require("zod");
15
15
  const config_1 = require("../config");
16
16
  const maestro_signals_1 = require("../workflows/maestro-signals");
17
- const helpers_1 = require("./helpers");
17
+ const descriptor_1 = require("./descriptor");
18
18
  const validation_1 = require("../utils/validation");
19
- function registerCoatCheckEvictTool(server, client, config, getPlayerId) {
20
- (0, helpers_1.defineTool)(server, 'coat_check_evict', `Evict a coat-check entry (#318) before its TTL expires. Owner-or-conductor only — non-owners (and non-conductors) get a permission error.
19
+ function buildCoatCheckEvictTool(client, config, getPlayerId) {
20
+ return {
21
+ name: 'coat_check_evict',
22
+ description: `Evict a coat-check entry (#318) before its TTL expires. Owner-or-conductor only — non-owners (and non-conductors) get a permission error.
21
23
 
22
- Use to free a slot when this ensemble is at the 20-entry cap and you want to make room. \`evicted: false\` means the ticket was already gone (TTL-expired or evicted by someone else).`, {
23
- ticket: zod_1.z.string().regex(validation_1.COAT_CHECK_TICKET_REGEX).max(validation_1.COAT_CHECK_TICKET_MAX).describe(`The ticket id returned by an earlier \`coat_check_put\` (≤${validation_1.COAT_CHECK_TICKET_MAX} chars).`),
24
- }, async (args) => {
25
- const { ticket } = args;
26
- const evictedBy = getPlayerId();
27
- try {
28
- const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
29
- const result = await handle.executeUpdate(maestro_signals_1.coatCheckEvictUpdate, {
30
- args: [{ ticket, evictedBy }],
31
- });
32
- if (!result.evicted) {
33
- return (0, helpers_1.ok)(`Ticket **${ticket}** was already gone (no-op).`);
24
+ Use to free a slot when this ensemble is at the 20-entry cap and you want to make room. \`evicted: false\` means the ticket was already gone (TTL-expired or evicted by someone else).`,
25
+ params: {
26
+ ticket: zod_1.z.string().regex(validation_1.COAT_CHECK_TICKET_REGEX).max(validation_1.COAT_CHECK_TICKET_MAX).describe(`The ticket id returned by an earlier \`coat_check_put\` (≤${validation_1.COAT_CHECK_TICKET_MAX} chars).`),
27
+ },
28
+ handler: async (args) => {
29
+ const { ticket } = args;
30
+ const evictedBy = getPlayerId();
31
+ try {
32
+ const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
33
+ const result = await handle.executeUpdate(maestro_signals_1.coatCheckEvictUpdate, {
34
+ args: [{ ticket, evictedBy }],
35
+ });
36
+ if (!result.evicted) {
37
+ return (0, descriptor_1.ok)(`Ticket **${ticket}** was already gone (no-op).`);
38
+ }
39
+ return (0, descriptor_1.ok)(`Evicted ticket **${ticket}**.`);
34
40
  }
35
- return (0, helpers_1.ok)(`Evicted ticket **${ticket}**.`);
36
- }
37
- catch (err) {
38
- // Surfaces `CoatCheckEvictPermissionDenied` ApplicationFailure with
39
- // owner/conductor diagnostic from the workflow validator.
40
- return (0, helpers_1.fail)(`Failed to evict ticket: ${(0, helpers_1.formatError)(err)}`);
41
- }
42
- });
41
+ catch (err) {
42
+ // Surfaces `CoatCheckEvictPermissionDenied` ApplicationFailure with
43
+ // owner/conductor diagnostic from the workflow validator.
44
+ return (0, descriptor_1.fail)(`Failed to evict ticket: ${(0, descriptor_1.formatError)(err)}`);
45
+ }
46
+ },
47
+ };
43
48
  }
@@ -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 registerCoatCheckGetTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string): void;
3
+ import { type TempoToolDescriptor } from './descriptor';
4
+ export declare function buildCoatCheckGetTool(client: Client, config: Config, getPlayerId: () => string): TempoToolDescriptor;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerCoatCheckGetTool = registerCoatCheckGetTool;
3
+ exports.buildCoatCheckGetTool = buildCoatCheckGetTool;
4
4
  /**
5
5
  * `coat_check_get` — redeem a coat-check ticket and pull the stashed content
6
6
  * (#318, ADR 0008). Returns the entry's summary, content body, and audit
@@ -18,39 +18,44 @@ exports.registerCoatCheckGetTool = registerCoatCheckGetTool;
18
18
  const zod_1 = require("zod");
19
19
  const config_1 = require("../config");
20
20
  const maestro_signals_1 = require("../workflows/maestro-signals");
21
- const helpers_1 = require("./helpers");
21
+ const descriptor_1 = require("./descriptor");
22
22
  const validation_1 = require("../utils/validation");
23
- function registerCoatCheckGetTool(server, client, config, getPlayerId) {
24
- (0, helpers_1.defineTool)(server, 'coat_check_get', `Redeem a coat-check ticket (#318) and pull the stashed content. Returns the entry's summary, content body, and audit info — or "not found" when the ticket is missing / expired / evicted (no error, just empty).
23
+ function buildCoatCheckGetTool(client, config, getPlayerId) {
24
+ return {
25
+ name: 'coat_check_get',
26
+ description: `Redeem a coat-check ticket (#318) and pull the stashed content. Returns the entry's summary, content body, and audit info — or "not found" when the ticket is missing / expired / evicted (no error, just empty).
25
27
 
26
- Successful redemptions bump the entry's fetch-audit counters (\`lastFetchedAt\` / \`lastFetchedBy\` / \`fetchCount\`) so the putter can later see whether anyone has redeemed. \`coat_check_list\` won't bump these — only an actual redemption counts.`, {
27
- ticket: zod_1.z.string().regex(validation_1.COAT_CHECK_TICKET_REGEX).max(validation_1.COAT_CHECK_TICKET_MAX).describe(`The ticket id returned by an earlier \`coat_check_put\` (≤${validation_1.COAT_CHECK_TICKET_MAX} chars).`),
28
- }, async (args) => {
29
- const { ticket } = args;
30
- const fetchedBy = getPlayerId();
31
- try {
32
- const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
33
- const entry = await handle.executeUpdate(maestro_signals_1.coatCheckGetUpdate, {
34
- args: [{ ticket, fetchedBy }],
35
- });
36
- if (!entry) {
37
- return (0, helpers_1.ok)(`Ticket **${ticket}** is not found (missing, expired, or evicted).`);
28
+ Successful redemptions bump the entry's fetch-audit counters (\`lastFetchedAt\` / \`lastFetchedBy\` / \`fetchCount\`) so the putter can later see whether anyone has redeemed. \`coat_check_list\` won't bump these — only an actual redemption counts.`,
29
+ params: {
30
+ ticket: zod_1.z.string().regex(validation_1.COAT_CHECK_TICKET_REGEX).max(validation_1.COAT_CHECK_TICKET_MAX).describe(`The ticket id returned by an earlier \`coat_check_put\` (≤${validation_1.COAT_CHECK_TICKET_MAX} chars).`),
31
+ },
32
+ handler: async (args) => {
33
+ const { ticket } = args;
34
+ const fetchedBy = getPlayerId();
35
+ try {
36
+ const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
37
+ const entry = await handle.executeUpdate(maestro_signals_1.coatCheckGetUpdate, {
38
+ args: [{ ticket, fetchedBy }],
39
+ });
40
+ if (!entry) {
41
+ return (0, descriptor_1.ok)(`Ticket **${ticket}** is not found (missing, expired, or evicted).`);
42
+ }
43
+ const lines = [
44
+ `**Ticket ${ticket}** — stashed by ${entry.putBy} at ${entry.putAt}, expires ${entry.expiresAt}`,
45
+ `Summary: ${entry.summary}`,
46
+ ];
47
+ if (entry.contentType)
48
+ lines.push(`Content-Type: ${entry.contentType}`);
49
+ lines.push(`Size: ${entry.size} bytes`);
50
+ lines.push(`Fetches: ${entry.fetchCount}${entry.lastFetchedBy ? ` (last by ${entry.lastFetchedBy} at ${entry.lastFetchedAt})` : ''}`);
51
+ lines.push('');
52
+ lines.push('---');
53
+ lines.push(entry.content);
54
+ return (0, descriptor_1.ok)(lines.join('\n'));
38
55
  }
39
- const lines = [
40
- `**Ticket ${ticket}** stashed by ${entry.putBy} at ${entry.putAt}, expires ${entry.expiresAt}`,
41
- `Summary: ${entry.summary}`,
42
- ];
43
- if (entry.contentType)
44
- lines.push(`Content-Type: ${entry.contentType}`);
45
- lines.push(`Size: ${entry.size} bytes`);
46
- lines.push(`Fetches: ${entry.fetchCount}${entry.lastFetchedBy ? ` (last by ${entry.lastFetchedBy} at ${entry.lastFetchedAt})` : ''}`);
47
- lines.push('');
48
- lines.push('---');
49
- lines.push(entry.content);
50
- return (0, helpers_1.ok)(lines.join('\n'));
51
- }
52
- catch (err) {
53
- return (0, helpers_1.fail)(`Failed to redeem ticket: ${(0, helpers_1.formatError)(err)}`);
54
- }
55
- });
56
+ catch (err) {
57
+ return (0, descriptor_1.fail)(`Failed to redeem ticket: ${(0, descriptor_1.formatError)(err)}`);
58
+ }
59
+ },
60
+ };
56
61
  }
@@ -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 registerCoatCheckListTool(server: McpServer, client: Client, config: Config): void;
3
+ import { type TempoToolDescriptor } from './descriptor';
4
+ export declare function buildCoatCheckListTool(client: Client, config: Config): TempoToolDescriptor;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerCoatCheckListTool = registerCoatCheckListTool;
3
+ exports.buildCoatCheckListTool = buildCoatCheckListTool;
4
4
  /**
5
5
  * `coat_check_list` — list coat-check entry headers (#318, ADR 0008) for
6
6
  * the calling player's ensemble. Read-only — does NOT bump fetch-audit
@@ -12,49 +12,54 @@ exports.registerCoatCheckListTool = registerCoatCheckListTool;
12
12
  const zod_1 = require("zod");
13
13
  const config_1 = require("../config");
14
14
  const maestro_signals_1 = require("../workflows/maestro-signals");
15
- const helpers_1 = require("./helpers");
15
+ const descriptor_1 = require("./descriptor");
16
16
  const validation_1 = require("../utils/validation");
17
- function registerCoatCheckListTool(server, client, config) {
18
- (0, helpers_1.defineTool)(server, 'coat_check_list', `List coat-check entries (#318) for this ensemble. Read-only — does NOT bump fetch-audit counters; only \`coat_check_get\` does. Sorted newest-first. Pass \`unfetchedOnly: true\` to surface entries nobody has redeemed yet — useful for an owner cleaning up stale stashes.`, {
19
- putBy: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional().describe('Optional filter — only entries stashed by this player.'),
20
- prefix: zod_1.z.string().max(validation_1.COAT_CHECK_SUMMARY_MAX).optional().describe('Optional summary-prefix filter narrows the listing to entries whose summary starts with this string.'),
21
- unfetchedOnly: zod_1.z.boolean().optional().describe('When true, only return entries with fetchCount=0 (never redeemed). Default false.'),
22
- }, async (args) => {
23
- const { putBy, prefix, unfetchedOnly } = args;
24
- try {
25
- const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
26
- const filter = {
27
- ...(putBy !== undefined ? { putBy } : {}),
28
- ...(prefix !== undefined ? { prefix } : {}),
29
- ...(unfetchedOnly !== undefined ? { unfetchedOnly } : {}),
30
- };
31
- const headers = await handle.query(maestro_signals_1.coatCheckListQuery, filter);
32
- if (headers.length === 0) {
33
- const filters = [];
34
- if (putBy)
35
- filters.push(`putBy="${putBy}"`);
36
- if (prefix)
37
- filters.push(`prefix="${prefix}"`);
38
- if (unfetchedOnly)
39
- filters.push('unfetchedOnly=true');
40
- const suffix = filters.length > 0 ? ` (filter: ${filters.join(', ')})` : '';
41
- return (0, helpers_1.ok)(`No coat-check entries in this ensemble${suffix}.`);
17
+ function buildCoatCheckListTool(client, config) {
18
+ return {
19
+ name: 'coat_check_list',
20
+ description: `List coat-check entries (#318) for this ensemble. Read-onlydoes NOT bump fetch-audit counters; only \`coat_check_get\` does. Sorted newest-first. Pass \`unfetchedOnly: true\` to surface entries nobody has redeemed yet useful for an owner cleaning up stale stashes.`,
21
+ params: {
22
+ putBy: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional().describe('Optional filter — only entries stashed by this player.'),
23
+ prefix: zod_1.z.string().max(validation_1.COAT_CHECK_SUMMARY_MAX).optional().describe('Optional summary-prefix filter narrows the listing to entries whose summary starts with this string.'),
24
+ unfetchedOnly: zod_1.z.boolean().optional().describe('When true, only return entries with fetchCount=0 (never redeemed). Default false.'),
25
+ },
26
+ handler: async (args) => {
27
+ const { putBy, prefix, unfetchedOnly } = args;
28
+ try {
29
+ const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
30
+ const filter = {
31
+ ...(putBy !== undefined ? { putBy } : {}),
32
+ ...(prefix !== undefined ? { prefix } : {}),
33
+ ...(unfetchedOnly !== undefined ? { unfetchedOnly } : {}),
34
+ };
35
+ const headers = await handle.query(maestro_signals_1.coatCheckListQuery, filter);
36
+ if (headers.length === 0) {
37
+ const filters = [];
38
+ if (putBy)
39
+ filters.push(`putBy="${putBy}"`);
40
+ if (prefix)
41
+ filters.push(`prefix="${prefix}"`);
42
+ if (unfetchedOnly)
43
+ filters.push('unfetchedOnly=true');
44
+ const suffix = filters.length > 0 ? ` (filter: ${filters.join(', ')})` : '';
45
+ return (0, descriptor_1.ok)(`No coat-check entries in this ensemble${suffix}.`);
46
+ }
47
+ const lines = [];
48
+ lines.push(`${headers.length}/${validation_1.COAT_CHECK_SLOTS_MAX} coat-check ${headers.length === 1 ? 'entry' : 'entries'}:`);
49
+ lines.push('');
50
+ for (const h of headers) {
51
+ const fetchTag = h.fetchCount === 0
52
+ ? ' (unfetched)'
53
+ : ` (fetched ${h.fetchCount}× by ${h.lastFetchedBy ?? '(unknown)'} at ${h.lastFetchedAt ?? '(unknown)'})`;
54
+ const typeTag = h.contentType ? ` [${h.contentType}]` : '';
55
+ lines.push(`- **${h.ticket}** ${typeTag}— ${h.summary}`);
56
+ lines.push(` putBy=${h.putBy} · putAt=${h.putAt} · expires=${h.expiresAt} · ${h.size} bytes${fetchTag}`);
57
+ }
58
+ return (0, descriptor_1.ok)(lines.join('\n'));
42
59
  }
43
- const lines = [];
44
- lines.push(`${headers.length}/${validation_1.COAT_CHECK_SLOTS_MAX} coat-check ${headers.length === 1 ? 'entry' : 'entries'}:`);
45
- lines.push('');
46
- for (const h of headers) {
47
- const fetchTag = h.fetchCount === 0
48
- ? ' (unfetched)'
49
- : ` (fetched ${h.fetchCount}× by ${h.lastFetchedBy ?? '(unknown)'} at ${h.lastFetchedAt ?? '(unknown)'})`;
50
- const typeTag = h.contentType ? ` [${h.contentType}]` : '';
51
- lines.push(`- **${h.ticket}** ${typeTag}— ${h.summary}`);
52
- lines.push(` putBy=${h.putBy} · putAt=${h.putAt} · expires=${h.expiresAt} · ${h.size} bytes${fetchTag}`);
60
+ catch (err) {
61
+ return (0, descriptor_1.fail)(`Failed to list coat-check entries: ${(0, descriptor_1.formatError)(err)}`);
53
62
  }
54
- return (0, helpers_1.ok)(lines.join('\n'));
55
- }
56
- catch (err) {
57
- return (0, helpers_1.fail)(`Failed to list coat-check entries: ${(0, helpers_1.formatError)(err)}`);
58
- }
59
- });
63
+ },
64
+ };
60
65
  }
@@ -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 registerCoatCheckPutTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string): void;
3
+ import { type TempoToolDescriptor } from './descriptor';
4
+ export declare function buildCoatCheckPutTool(client: Client, config: Config, getPlayerId: () => string): TempoToolDescriptor;