claude-tempo 0.8.0 → 0.10.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 (66) hide show
  1. package/CLAUDE.md +27 -4
  2. package/README.md +81 -18
  3. package/assets/icon-32.png +0 -0
  4. package/assets/icon-64.png +0 -0
  5. package/assets/icon-dark-32.png +0 -0
  6. package/assets/icon-dark-64.png +0 -0
  7. package/assets/icon-dark.svg +9 -0
  8. package/assets/icon.svg +9 -0
  9. package/assets/logo-dark.svg +11 -0
  10. package/assets/logo-light.svg +11 -0
  11. package/dist/activities/outbox.d.ts +65 -0
  12. package/dist/activities/outbox.js +219 -0
  13. package/dist/cli/commands.d.ts +6 -1
  14. package/dist/cli/commands.js +202 -40
  15. package/dist/cli.js +9 -3
  16. package/dist/config.d.ts +3 -0
  17. package/dist/config.js +6 -0
  18. package/dist/ensemble/agent-types.d.ts +23 -0
  19. package/dist/ensemble/agent-types.js +125 -0
  20. package/dist/ensemble/loader.d.ts +3 -3
  21. package/dist/ensemble/loader.js +20 -14
  22. package/dist/ensemble/saver.d.ts +6 -6
  23. package/dist/ensemble/saver.js +14 -12
  24. package/dist/ensemble/schema.d.ts +7 -1
  25. package/dist/ensemble/schema.js +1 -1
  26. package/dist/server.js +60 -19
  27. package/dist/spawn.d.ts +10 -0
  28. package/dist/spawn.js +120 -2
  29. package/dist/tools/agent-types.d.ts +2 -0
  30. package/dist/tools/agent-types.js +18 -0
  31. package/dist/tools/cue.d.ts +2 -2
  32. package/dist/tools/cue.js +11 -18
  33. package/dist/tools/ensemble.js +5 -1
  34. package/dist/tools/load-ensemble.js +22 -4
  35. package/dist/tools/load-lineup.d.ts +5 -0
  36. package/dist/tools/load-lineup.js +303 -0
  37. package/dist/tools/recruit.d.ts +2 -2
  38. package/dist/tools/recruit.js +46 -138
  39. package/dist/tools/report.d.ts +2 -3
  40. package/dist/tools/report.js +11 -19
  41. package/dist/tools/save-lineup.d.ts +4 -0
  42. package/dist/tools/save-lineup.js +43 -0
  43. package/dist/tools/stop.d.ts +2 -2
  44. package/dist/tools/stop.js +10 -17
  45. package/dist/tools/who-am-i.d.ts +3 -0
  46. package/dist/tools/who-am-i.js +24 -0
  47. package/dist/types.d.ts +70 -0
  48. package/dist/worker.d.ts +13 -0
  49. package/dist/worker.js +36 -3
  50. package/dist/workflows/session.js +123 -1
  51. package/dist/workflows/signals.d.ts +6 -2
  52. package/dist/workflows/signals.js +4 -1
  53. package/examples/agents/tempo-composer.md +44 -0
  54. package/examples/agents/tempo-conductor.md +52 -0
  55. package/examples/agents/tempo-critic.md +45 -0
  56. package/examples/agents/tempo-improv.md +47 -0
  57. package/examples/agents/tempo-liner.md +48 -0
  58. package/examples/agents/tempo-roadie.md +48 -0
  59. package/examples/agents/tempo-soloist.md +45 -0
  60. package/examples/agents/tempo-tuner.md +49 -0
  61. package/examples/ensembles/tempo-big-band.yaml +146 -0
  62. package/examples/ensembles/tempo-dev-team.yaml +58 -0
  63. package/examples/ensembles/tempo-jam-session.yaml +41 -0
  64. package/examples/ensembles/tempo-review-squad.yaml +32 -0
  65. package/package.json +3 -1
  66. package/workflow-bundle.js +128 -3
package/CLAUDE.md CHANGED
@@ -21,16 +21,31 @@ src/
21
21
  ├── workflows/
22
22
  │ ├── session.ts # claude-session workflow
23
23
  │ └── signals.ts # Signal/query type definitions
24
+ ├── activities/
25
+ │ ├── outbox.ts # Outbox delivery activities (cue, report, stop, recruit)
26
+ │ └── schedule-fire.ts # Schedule fire activity
27
+ ├── ensemble/
28
+ │ ├── schema.ts # Lineup type definitions
29
+ │ ├── loader.ts # Load and validate YAML lineups
30
+ │ ├── saver.ts # Save live ensemble state to YAML
31
+ │ └── agent-types.ts # Agent type discovery, resolution, and lineup resolution
24
32
  ├── tools/
25
33
  │ ├── ensemble.ts # Discover active sessions
26
- │ ├── cue.ts # Send message to peer
34
+ │ ├── cue.ts # Send message to peer (via outbox)
27
35
  │ ├── set-name.ts # Set session name
28
36
  │ ├── set-part.ts # Update own summary
37
+ │ ├── who-am-i.ts # Query own identity, role, and session details
38
+ │ ├── agent-types.ts # Discover available player types (agent definitions)
29
39
  │ ├── resolve.ts # Search-attribute session lookup
30
40
  │ ├── listen.ts # Manual message check
31
- │ ├── recruit.ts # Spawn new session
32
- │ ├── report.ts # Report to conductor
33
- │ ├── stop.ts # Stop a session
41
+ │ ├── recruit.ts # Spawn new session (via outbox), supports `type` param
42
+ │ ├── report.ts # Report to conductor (via outbox)
43
+ │ ├── stop.ts # Stop a session (via outbox)
44
+ │ ├── load-lineup.ts # Load an ensemble lineup, recruit players
45
+ │ ├── save-lineup.ts # Save current ensemble state as a lineup
46
+ │ ├── schedule.ts # Create one-shot or recurring schedules
47
+ │ ├── unschedule.ts # Cancel a named schedule
48
+ │ ├── schedules.ts # List active schedules
34
49
  │ └── helpers.ts # Zod/MCP tool registration wrapper
35
50
  ├── types.ts # Shared type definitions
36
51
  ├── channel.ts # Claude channel notification helper
@@ -60,6 +75,10 @@ npm test
60
75
  > **Important**: Always run `npm run build` after changing workflow code (`src/workflows/`).
61
76
  > The build pre-bundles workflows into `workflow-bundle.js` so all workers use identical code.
62
77
 
78
+ > **Dual workers**: Each session runs two Temporal workers — a shared `claude-tempo` queue
79
+ > (workflows + delivery activities) and a per-host `claude-tempo-{hostname}` queue (spawn activities only).
80
+ > Both are created via `createWorkers()` in `src/worker.ts`.
81
+
63
82
  ## Key Concepts
64
83
 
65
84
  - **Player**: A Claude Code session registered as a Temporal workflow
@@ -70,6 +89,10 @@ npm test
70
89
  - **Recruit**: Spawning a new Claude Code session as a player. The workflow is pre-created with the initial message before the process spawns, ensuring reliable delivery.
71
90
  - **set_name**: Players start with a random hex ID; `set_name` updates the `ClaudeTempoPlayerId` search attribute to a human-readable name
72
91
  - **Session status**: Each session has a status (`pending` → `active` → `stale`) tracked via `ClaudeTempoStatus` search attribute. Pre-created workflows start as `pending`, transition to `active` when the process connects, and become `stale` if messages go undelivered for 3+ minutes.
92
+ - **Outbox**: Outbound requests (cue, report, stop, recruit) go through the session's own workflow outbox instead of directly signaling other workflows. The workflow's dispatch loop processes entries via activities, decoupling tools from cross-workflow signaling.
93
+ - **Per-host task queues**: Each host runs a `claude-tempo-{hostname}` activity worker for local-only operations (e.g., `spawnProcess`). This enables cross-machine recruiting — the `recruit` tool accepts an optional `host` parameter to route the spawn to a remote machine's task queue.
94
+ - **Player types**: Reusable agent definitions in Claude Code's standard subagent format (`.md` files with YAML frontmatter). Ensemble lineups can reference types by name via a `type` field on players. Three-tier lookup: project `.claude/agents/` → user `~/.claude/agents/` → shipped `examples/agents/`. Players know their type via workflow metadata and the `who_am_i` tool.
95
+ - **Agent type discovery**: The `agent_types` MCP tool and `claude-tempo agent-types` CLI command let conductors discover available player types. Shipped examples (tempo-conductor, tempo-composer, tempo-soloist, tempo-tuner, tempo-critic, tempo-roadie, tempo-improv, tempo-liner) work out of the box. Ensemble lineups: tempo-big-band (full lifecycle), tempo-dev-team (feature work), tempo-review-squad (parallel review), tempo-jam-session (exploration).
73
96
 
74
97
  ## Dashboard
75
98
 
package/README.md CHANGED
@@ -98,7 +98,7 @@ claude-tempo <command> [options]
98
98
 
99
99
  | Command | Description |
100
100
  |---------|-------------|
101
- | `up [ensemble]` | First-time setup: start Temporal, configure MCP, launch conductor. Use `--from` to load a blueprint. |
101
+ | `up [ensemble]` | First-time setup: start Temporal, configure MCP, launch conductor. Use `--lineup` to load a lineup. |
102
102
  | `down` | Stop Temporal, terminate sessions, remove MCP config |
103
103
  | `server` | Start the Temporal dev server and register search attributes |
104
104
  | `conduct [ensemble]` | Start a conductor session (one per ensemble). Use `--resume` or `--replace` if one exists. |
@@ -108,7 +108,8 @@ claude-tempo <command> [options]
108
108
  | `stop [ensemble]` | Stop sessions (`-n <name>` for one, `--all` for everything) |
109
109
  | `init` | Register claude-tempo MCP server globally (`--project` for per-directory) |
110
110
  | `preflight` | Run environment checks |
111
- | `ensemble <sub>` | Manage saved blueprints (`save`, `list`, `show`) |
111
+ | `ensemble <sub>` | Manage saved lineups (`save`, `list`, `show`) |
112
+ | `agent-types <sub>` | Manage player types (`list`, `show <name>`, `init`) |
112
113
  | `help` | Show usage info |
113
114
 
114
115
  ### Global options
@@ -123,7 +124,7 @@ claude-tempo <command> [options]
123
124
  --skip-preflight Skip preflight checks (start/conduct)
124
125
  -d, --dir <path> Target directory (default: cwd)
125
126
  --background Run Temporal in background (server only)
126
- --from <file> Load an ensemble blueprint on startup (up only)
127
+ --lineup <name|file> Load an ensemble lineup by name or file path (up only)
127
128
  --resume Resume an existing conductor session (conduct only)
128
129
  --replace Stop existing conductor and start fresh (conduct only)
129
130
  ```
@@ -221,8 +222,10 @@ These tools are available inside Claude Code sessions connected to claude-tempo:
221
222
  | `schedule` | Create a one-shot or recurring schedule to cue a player. |
222
223
  | `unschedule` | Cancel a named schedule. |
223
224
  | `schedules` | List all active schedules. |
224
- | `save_ensemble` | Save the current ensemble as a YAML blueprint (conductor only). |
225
- | `load_ensemble` | Load a blueprint to recruit players and create schedules. |
225
+ | `who_am_i` | Get your identity, role, player type, and session details. |
226
+ | `agent_types` | List available player types with name, description, and source. |
227
+ | `save_lineup` | Save the current ensemble as a YAML lineup (conductor only). |
228
+ | `load_lineup` | Load a lineup to recruit players and create schedules. |
226
229
 
227
230
  ## Scheduling
228
231
 
@@ -255,11 +258,11 @@ Schedules support one-shot delays, fixed times, and recurring intervals with opt
255
258
  - `claude-tempo status` shows active schedules alongside sessions
256
259
  - A single durable scheduler workflow per ensemble manages all schedules using Temporal timers
257
260
 
258
- ## Ensemble Blueprints
261
+ ## Ensemble Lineups
259
262
 
260
- Define reusable ensemble configurations as YAML files. A blueprint specifies which players to recruit, what instructions to give them, what schedules to create, and optionally which custom agent files to use.
263
+ Define reusable ensemble configurations as YAML files. A lineup specifies which players to recruit, what instructions to give them, what schedules to create, and optionally which custom agent files to use.
261
264
 
262
- ### Example blueprint
265
+ ### Example lineup
263
266
 
264
267
  ```yaml
265
268
  name: my-project
@@ -287,29 +290,29 @@ schedules:
287
290
  delay: 10m
288
291
  ```
289
292
 
290
- ### Three ways to use blueprints
293
+ ### Three ways to use lineups
291
294
 
292
- 1. **From the CLI** — load a blueprint when starting an ensemble:
295
+ 1. **From the CLI** — load a lineup when starting an ensemble:
293
296
 
294
297
  ```bash
295
- claude-tempo up --from my-blueprint.yaml
298
+ claude-tempo up --lineup my-lineup.yaml
296
299
  ```
297
300
 
298
- 2. **From inside a session** — use the `load_ensemble` tool:
301
+ 2. **From inside a session** — use the `load_lineup` tool:
299
302
 
300
- *"Load the blueprint from ~/.claude-tempo/ensembles/my-project.yaml"*
303
+ *"Load the lineup from ~/.claude-tempo/ensembles/my-project.yaml"*
301
304
 
302
- 3. **Save the current state** — snapshot a running ensemble as a blueprint (conductor only):
305
+ 3. **Save the current state** — snapshot a running ensemble as a lineup (conductor only):
303
306
 
304
- *"Save this ensemble as a blueprint called my-project"*
307
+ *"Save this ensemble as a lineup called my-project"*
305
308
 
306
309
  ### Natural language examples
307
310
 
308
311
  Tell your session things like:
309
312
 
310
- - *"Load the my-project blueprint"*
311
- - *"Save this ensemble as a blueprint"*
312
- - *"Load the blueprint from /repos/configs/team.yaml"*
313
+ - *"Load the my-project lineup"*
314
+ - *"Save this ensemble as a lineup"*
315
+ - *"Load the lineup from /repos/configs/team.yaml"*
313
316
 
314
317
  ### Fan-out schedules
315
318
 
@@ -329,6 +332,66 @@ players:
329
332
  instructions: "Review the latest PR for security issues"
330
333
  ```
331
334
 
335
+ ## Player Types
336
+
337
+ Player types are reusable agent definitions in Claude Code's standard subagent format — `.md` files with YAML frontmatter specifying name, description, and optional model. They let you define specialized roles once and reuse them across lineups.
338
+
339
+ ### How player types work
340
+
341
+ Reference a type by name in a lineup's `type` field:
342
+
343
+ ```yaml
344
+ players:
345
+ - name: arch
346
+ type: tempo-composer
347
+ - name: eng
348
+ type: tempo-soloist
349
+ ```
350
+
351
+ When a player is recruited with a type, the agent definition is resolved and passed to the session. Players know their type via the `who_am_i` tool.
352
+
353
+ ### Three-tier lookup
354
+
355
+ Player types are resolved in order (first match wins):
356
+
357
+ 1. **Project** — `.claude/agents/` in the project directory
358
+ 2. **User** — `~/.claude/agents/` in the user's home directory
359
+ 3. **Shipped** — `examples/agents/` bundled with claude-tempo
360
+
361
+ Project and user types are resolved natively by Claude Code via `--agent <name>`. Shipped types fall back to `--system-prompt <path>`.
362
+
363
+ ### Shipped player types
364
+
365
+ | Type | Description |
366
+ |------|-------------|
367
+ | `tempo-conductor` | Orchestrates the ensemble — breaks down tasks, delegates to players, tracks progress |
368
+ | `tempo-composer` | Software architect — designs system structure, defines interfaces, makes technology decisions |
369
+ | `tempo-soloist` | Senior engineer — implements features, fixes bugs, writes tests, delivers working code |
370
+ | `tempo-tuner` | QA engineer — designs test strategies, finds bugs, validates edge cases |
371
+ | `tempo-critic` | Code reviewer — evaluates changes for correctness, security, performance, maintainability |
372
+ | `tempo-roadie` | DevOps engineer — manages CI/CD, deployments, infrastructure, environment configuration |
373
+ | `tempo-improv` | Researcher and explorer — investigates unknowns, runs spikes, evaluates options |
374
+ | `tempo-liner` | Documentation specialist — owns README, CHANGELOG, CLAUDE.md, and PR descriptions |
375
+
376
+ ### Shipped lineups
377
+
378
+ | Lineup | Description |
379
+ |--------|-------------|
380
+ | `tempo-big-band` | Full-lifecycle ensemble with all 8 player types — design, implement, test, review, and ship |
381
+ | `tempo-dev-team` | Feature development — conductor, composer, two soloists, and a tuner |
382
+ | `tempo-review-squad` | Three critics with different focus areas for thorough parallel code review |
383
+ | `tempo-jam-session` | Exploratory ensemble for spikes, research, and problems where the path is unclear |
384
+
385
+ ### Discovery
386
+
387
+ Use the `agent_types` MCP tool inside a session or the CLI:
388
+
389
+ ```bash
390
+ claude-tempo agent-types list # show available types
391
+ claude-tempo agent-types show <name> # print full definition
392
+ claude-tempo agent-types init # copy shipped examples to ~/.claude/agents/
393
+ ```
394
+
332
395
  ## Conductors
333
396
 
334
397
  A **conductor** is an optional special player that acts as an orchestration hub. Use one when you want:
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
2
+ <!-- Ultra-minimal metronome icon: triangle + pendulum (dark mode) -->
3
+ <!-- Metronome body — single-stroke triangle -->
4
+ <path d="M32 8 L14 54 L50 54 Z" stroke="#FAF3EE" stroke-width="3" fill="none" stroke-linejoin="round"/>
5
+ <!-- Pendulum arm (angled right) -->
6
+ <line x1="32" y1="46" x2="44" y2="14" stroke="#E07A5F" stroke-width="3" stroke-linecap="round"/>
7
+ <!-- Pivot dot -->
8
+ <circle cx="32" cy="46" r="3" fill="#E07A5F"/>
9
+ </svg>
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
2
+ <!-- Ultra-minimal metronome icon: triangle + pendulum -->
3
+ <!-- Metronome body — single-stroke triangle -->
4
+ <path d="M32 8 L14 54 L50 54 Z" stroke="#1B2838" stroke-width="3" fill="none" stroke-linejoin="round"/>
5
+ <!-- Pendulum arm (angled right) -->
6
+ <line x1="32" y1="46" x2="44" y2="14" stroke="#E07A5F" stroke-width="3" stroke-linecap="round"/>
7
+ <!-- Pivot dot -->
8
+ <circle cx="32" cy="46" r="3" fill="#E07A5F"/>
9
+ </svg>
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 140" fill="none">
2
+ <!-- Ultra-minimal metronome: triangle outline + pendulum line (dark mode) -->
3
+ <!-- Metronome body — single-stroke triangle -->
4
+ <path d="M160 18 L122 100 L198 100 Z" stroke="#FAF3EE" stroke-width="3" fill="none" stroke-linejoin="round"/>
5
+ <!-- Pendulum arm (angled right ~18deg) -->
6
+ <line x1="160" y1="88" x2="182" y2="24" stroke="#E07A5F" stroke-width="3" stroke-linecap="round"/>
7
+ <!-- Pivot dot -->
8
+ <circle cx="160" cy="88" r="3.5" fill="#E07A5F"/>
9
+ <!-- Text -->
10
+ <text x="160" y="132" text-anchor="middle" font-family="'JetBrains Mono','SF Mono','Consolas',monospace" font-size="18" font-weight="600" fill="#FAF3EE" letter-spacing="-0.5">claude-tempo</text>
11
+ </svg>
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 140" fill="none">
2
+ <!-- Ultra-minimal metronome: triangle outline + pendulum line -->
3
+ <!-- Metronome body — single-stroke triangle -->
4
+ <path d="M160 18 L122 100 L198 100 Z" stroke="#1B2838" stroke-width="3" fill="none" stroke-linejoin="round"/>
5
+ <!-- Pendulum arm (angled right ~18deg) -->
6
+ <line x1="160" y1="88" x2="182" y2="24" stroke="#E07A5F" stroke-width="3" stroke-linecap="round"/>
7
+ <!-- Pivot dot -->
8
+ <circle cx="160" cy="88" r="3.5" fill="#E07A5F"/>
9
+ <!-- Text -->
10
+ <text x="160" y="132" text-anchor="middle" font-family="'JetBrains Mono','SF Mono','Consolas',monospace" font-size="18" font-weight="600" fill="#1B2838" letter-spacing="-0.5">claude-tempo</text>
11
+ </svg>
@@ -0,0 +1,65 @@
1
+ import { Client } from '@temporalio/client';
2
+ import { Config } from '../config';
3
+ import { AgentType } from '../types';
4
+ export interface DeliverCueInput {
5
+ ensemble: string;
6
+ fromPlayerId: string;
7
+ targetPlayerId: string;
8
+ message: string;
9
+ }
10
+ export interface DeliverReportInput {
11
+ ensemble: string;
12
+ fromPlayerId: string;
13
+ text: string;
14
+ reportType: 'result' | 'blocker' | 'question';
15
+ }
16
+ export interface TerminateSessionInput {
17
+ ensemble: string;
18
+ targetPlayerId: string;
19
+ terminatedBy: string;
20
+ }
21
+ export interface StartRecruitedSessionInput {
22
+ ensemble: string;
23
+ targetName: string;
24
+ workDir: string;
25
+ isConductor: boolean;
26
+ initialMessage?: string;
27
+ fromPlayerId: string;
28
+ agent: AgentType;
29
+ systemPrompt?: string;
30
+ taskQueue: string;
31
+ agentDefinition?: string;
32
+ agentDefinitionDescription?: string;
33
+ }
34
+ export interface SpawnProcessInput {
35
+ targetName: string;
36
+ workDir: string;
37
+ isConductor: boolean;
38
+ agent: AgentType;
39
+ systemPrompt?: string;
40
+ ensemble: string;
41
+ temporalAddress: string;
42
+ temporalNamespace: string;
43
+ temporalApiKey?: string;
44
+ temporalTlsCertPath?: string;
45
+ temporalTlsKeyPath?: string;
46
+ agentDefinition?: string;
47
+ agentDefinitionPath?: string;
48
+ nativeResolvable?: boolean;
49
+ }
50
+ export interface OutboxActivityResult {
51
+ success: boolean;
52
+ error?: string;
53
+ }
54
+ export interface OutboxActivities {
55
+ deliverCue(input: DeliverCueInput): Promise<OutboxActivityResult>;
56
+ deliverReport(input: DeliverReportInput): Promise<OutboxActivityResult>;
57
+ terminateSession(input: TerminateSessionInput): Promise<OutboxActivityResult>;
58
+ startRecruitedSession(input: StartRecruitedSessionInput): Promise<OutboxActivityResult>;
59
+ spawnProcess(input: SpawnProcessInput): Promise<OutboxActivityResult>;
60
+ }
61
+ /**
62
+ * Create outbox delivery activities bound to a Temporal client and config.
63
+ * The returned object is registered with the worker as activities.
64
+ */
65
+ export declare function createOutboxActivities(client: Client, config: Config): OutboxActivities;
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createOutboxActivities = createOutboxActivities;
37
+ const client_1 = require("@temporalio/client");
38
+ const activity_1 = require("@temporalio/activity");
39
+ const os = __importStar(require("os"));
40
+ const config_1 = require("../config");
41
+ const git_info_1 = require("../git-info");
42
+ const spawn_1 = require("../spawn");
43
+ const config_2 = require("../config");
44
+ const log = (...args) => console.error('[claude-tempo:outbox]', ...args);
45
+ // ── Helper: resolve session by player name ──
46
+ async function resolveSession(client, ensemble, playerName) {
47
+ const query = `WorkflowType = "claudeSessionWorkflow" AND ExecutionStatus = "Running"`;
48
+ for await (const wf of client.workflow.list({ query })) {
49
+ try {
50
+ const handle = client.workflow.getHandle(wf.workflowId);
51
+ const metadata = await handle.query('getMetadata');
52
+ if (metadata.ensemble === ensemble && metadata.playerId === playerName) {
53
+ return handle;
54
+ }
55
+ }
56
+ catch {
57
+ // Workflow may have just completed — skip
58
+ }
59
+ }
60
+ return null;
61
+ }
62
+ /**
63
+ * Create outbox delivery activities bound to a Temporal client and config.
64
+ * The returned object is registered with the worker as activities.
65
+ */
66
+ function createOutboxActivities(client, config) {
67
+ return {
68
+ async deliverCue(input) {
69
+ const { ensemble, fromPlayerId, targetPlayerId, message } = input;
70
+ const handle = await resolveSession(client, ensemble, targetPlayerId);
71
+ if (!handle) {
72
+ throw activity_1.ApplicationFailure.nonRetryable(`No active session found for "${targetPlayerId}"`);
73
+ }
74
+ await handle.signal('receiveMessage', { from: fromPlayerId, text: message });
75
+ return { success: true };
76
+ },
77
+ async deliverReport(input) {
78
+ const { ensemble, fromPlayerId, text, reportType } = input;
79
+ const conductorId = (0, config_1.conductorWorkflowId)(ensemble);
80
+ const handle = client.workflow.getHandle(conductorId);
81
+ await handle.signal('playerReport', { playerId: fromPlayerId, text, type: reportType });
82
+ return { success: true };
83
+ },
84
+ async terminateSession(input) {
85
+ const { ensemble, targetPlayerId, terminatedBy } = input;
86
+ const handle = await resolveSession(client, ensemble, targetPlayerId);
87
+ if (!handle) {
88
+ throw activity_1.ApplicationFailure.nonRetryable(`No active session found for "${targetPlayerId}"`);
89
+ }
90
+ // Signal target to mark as terminated
91
+ await handle.signal('updateMetadata', { status: 'terminated', terminatedBy });
92
+ // Notify conductor about the termination (best effort)
93
+ try {
94
+ const conductorId = (0, config_1.conductorWorkflowId)(ensemble);
95
+ const conductorHandle = client.workflow.getHandle(conductorId);
96
+ await conductorHandle.signal('receiveMessage', {
97
+ from: 'system',
98
+ text: `Session "${targetPlayerId}" was terminated by ${terminatedBy}.`,
99
+ });
100
+ }
101
+ catch {
102
+ // Conductor may not exist — that's fine
103
+ }
104
+ return { success: true };
105
+ },
106
+ async startRecruitedSession(input) {
107
+ const { ensemble, targetName, workDir, isConductor, initialMessage, fromPlayerId, agent, systemPrompt, taskQueue, agentDefinition, agentDefinitionDescription } = input;
108
+ try {
109
+ const workflowId = isConductor
110
+ ? (0, config_1.conductorWorkflowId)(ensemble)
111
+ : (0, config_1.sessionWorkflowId)(ensemble, targetName);
112
+ const { gitRoot, gitBranch } = (0, git_info_1.getGitInfo)(workDir);
113
+ const sessionInput = {
114
+ metadata: {
115
+ playerId: targetName,
116
+ ensemble,
117
+ hostname: os.hostname(),
118
+ workDir,
119
+ gitRoot,
120
+ gitBranch,
121
+ isConductor,
122
+ agentType: agent,
123
+ status: 'pending',
124
+ ...(agentDefinition ? { playerType: agentDefinition } : {}),
125
+ ...(agentDefinitionDescription ? { playerTypeDescription: agentDefinitionDescription } : {}),
126
+ recruitedBy: fromPlayerId,
127
+ },
128
+ autoSummary: `Session in ${require('path').basename(workDir)}`,
129
+ disableStaleDetection: true,
130
+ ...(initialMessage ? {
131
+ messages: [{
132
+ id: require('crypto').randomUUID(),
133
+ from: fromPlayerId,
134
+ text: initialMessage,
135
+ timestamp: new Date().toISOString(),
136
+ delivered: false,
137
+ }],
138
+ } : {}),
139
+ };
140
+ await client.workflow.start('claudeSessionWorkflow', {
141
+ workflowId,
142
+ taskQueue,
143
+ args: [sessionInput],
144
+ workflowIdConflictPolicy: client_1.WorkflowIdConflictPolicy.USE_EXISTING,
145
+ searchAttributes: {
146
+ ...(gitRoot ? { ClaudeTempoGitRoot: [gitRoot] } : {}),
147
+ ClaudeTempoHostname: [os.hostname()],
148
+ ClaudeTempoEnsemble: [ensemble],
149
+ ClaudeTempoPlayerId: [targetName],
150
+ },
151
+ });
152
+ log(`Pre-created workflow ${workflowId} for recruit "${targetName}"`);
153
+ return { success: true };
154
+ }
155
+ catch (err) {
156
+ throw activity_1.ApplicationFailure.nonRetryable(`Failed to start recruited session "${targetName}": ${err instanceof Error ? err.message : String(err)}`);
157
+ }
158
+ },
159
+ async spawnProcess(input) {
160
+ const { targetName, workDir, isConductor, agent, systemPrompt, ensemble, temporalAddress, temporalNamespace, temporalApiKey, temporalTlsCertPath, temporalTlsKeyPath, agentDefinition, agentDefinitionPath, nativeResolvable } = input;
161
+ try {
162
+ if (agent === 'copilot') {
163
+ const { pid } = (0, spawn_1.spawnCopilotBridge)({
164
+ name: targetName,
165
+ ensemble,
166
+ temporalAddress,
167
+ temporalNamespace,
168
+ temporalApiKey,
169
+ temporalTlsCertPath,
170
+ temporalTlsKeyPath,
171
+ isConductor,
172
+ workDir,
173
+ });
174
+ log(`Spawned copilot-bridge (pid ${pid}) in ${workDir} as "${targetName}"`);
175
+ }
176
+ else {
177
+ // Resolve agent flags: --agent (native) > --system-prompt (shipped/legacy)
178
+ let agentFlags = [];
179
+ if (agentDefinition && nativeResolvable) {
180
+ agentFlags = ['--agent', agentDefinition];
181
+ }
182
+ else if (agentDefinitionPath) {
183
+ agentFlags = ['--system-prompt', agentDefinitionPath];
184
+ }
185
+ else if (systemPrompt) {
186
+ agentFlags = ['--system-prompt', systemPrompt];
187
+ }
188
+ const spawnArgs = [
189
+ '--dangerously-skip-permissions',
190
+ '--dangerously-load-development-channels', 'server:claude-tempo',
191
+ '-n', targetName,
192
+ ...agentFlags,
193
+ ];
194
+ const envVars = {
195
+ [config_2.ENV.ENSEMBLE]: ensemble,
196
+ [config_2.ENV.CONDUCTOR]: isConductor ? 'true' : '',
197
+ [config_2.ENV.PLAYER_NAME]: targetName,
198
+ [config_2.ENV.TEMPORAL_ADDRESS]: temporalAddress,
199
+ [config_2.ENV.TEMPORAL_NAMESPACE]: temporalNamespace,
200
+ };
201
+ if (agentDefinition)
202
+ envVars[config_2.ENV.PLAYER_TYPE] = agentDefinition;
203
+ if (temporalApiKey)
204
+ envVars[config_2.ENV.TEMPORAL_API_KEY] = temporalApiKey;
205
+ if (temporalTlsCertPath)
206
+ envVars[config_2.ENV.TEMPORAL_TLS_CERT_PATH] = temporalTlsCertPath;
207
+ if (temporalTlsKeyPath)
208
+ envVars[config_2.ENV.TEMPORAL_TLS_KEY_PATH] = temporalTlsKeyPath;
209
+ const { pid } = (0, spawn_1.spawnInTerminal)(spawnArgs, workDir, envVars);
210
+ log(`Spawned claude process (pid ${pid}) in ${workDir} as "${targetName}"`);
211
+ }
212
+ return { success: true };
213
+ }
214
+ catch (err) {
215
+ throw activity_1.ApplicationFailure.nonRetryable(`Failed to spawn process for "${targetName}": ${err instanceof Error ? err.message : String(err)}`);
216
+ }
217
+ },
218
+ };
219
+ }
@@ -27,7 +27,7 @@ export declare function server(opts: ServerOpts): Promise<void>;
27
27
  interface UpOpts extends CliOverrides {
28
28
  ensemble: string;
29
29
  name?: string;
30
- from?: string;
30
+ lineup?: string;
31
31
  agent: AgentType;
32
32
  }
33
33
  export declare function up(opts: UpOpts): Promise<void>;
@@ -45,6 +45,11 @@ interface StopOpts extends CliOverrides {
45
45
  all?: boolean;
46
46
  }
47
47
  export declare function stop(opts: StopOpts): Promise<void>;
48
+ interface AgentTypesCommandOpts {
49
+ subcommand?: string;
50
+ name?: string;
51
+ }
52
+ export declare function agentTypesCommand(opts: AgentTypesCommandOpts): Promise<void>;
48
53
  interface EnsembleCommandOpts extends CliOverrides {
49
54
  subcommand?: string;
50
55
  name?: string;