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.
- package/CLAUDE.md +253 -219
- package/LICENSE +21 -21
- package/README.md +293 -289
- package/assets/icon-dark.svg +9 -9
- package/assets/icon.svg +9 -9
- package/assets/logo-dark.svg +11 -11
- package/assets/logo-light.svg +11 -11
- package/dashboard/README.md +91 -91
- package/dashboard/dist/assets/{index-D6Xyje_n.js → index-jmYe6rmS.js} +2 -2
- package/dashboard/dist/assets/index-jmYe6rmS.js.map +1 -0
- package/dashboard/dist/index.html +20 -20
- package/dashboard/package.json +47 -47
- package/dist/activities/outbox.d.ts +30 -1
- package/dist/activities/outbox.js +96 -3
- package/dist/adapters/base.js +5 -0
- package/dist/adapters/copilot/adapter.js +12 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/pi/adapter.d.ts +2 -0
- package/dist/adapters/pi/adapter.js +43 -0
- package/dist/adapters/pi/index.d.ts +16 -0
- package/dist/adapters/pi/index.js +10 -0
- package/dist/cli/global-wrapper.d.ts +19 -0
- package/dist/cli/global-wrapper.js +169 -0
- package/dist/cli/help-text.js +97 -97
- package/dist/cli/startup.js +11 -0
- package/dist/cli/upgrade-command.js +81 -81
- package/dist/cli.js +12 -0
- package/dist/client/core.js +9 -2
- package/dist/client/interface.d.ts +6 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +74 -0
- package/dist/daemon.js +37 -1
- package/dist/http/aggregate.d.ts +22 -1
- package/dist/http/aggregate.js +41 -0
- package/dist/http/auth.d.ts +94 -8
- package/dist/http/auth.js +93 -9
- package/dist/http/body.d.ts +4 -1
- package/dist/http/body.js +6 -3
- package/dist/http/event-bus.js +1 -0
- package/dist/http/event-types.d.ts +34 -2
- package/dist/http/event-types.js +1 -0
- package/dist/http/gate-audit.d.ts +12 -0
- package/dist/http/gate-audit.js +95 -0
- package/dist/http/gate-registry.d.ts +167 -0
- package/dist/http/gate-registry.js +163 -0
- package/dist/http/gate-routes.d.ts +48 -0
- package/dist/http/gate-routes.js +102 -0
- package/dist/http/ingest-registry.d.ts +30 -0
- package/dist/http/ingest-registry.js +108 -0
- package/dist/http/inner-loop-routes.d.ts +66 -0
- package/dist/http/inner-loop-routes.js +182 -0
- package/dist/http/inner-loop.d.ts +92 -0
- package/dist/http/inner-loop.js +155 -0
- package/dist/http/server.d.ts +38 -3
- package/dist/http/server.js +211 -6
- package/dist/http/snapshot.d.ts +6 -0
- package/dist/http/snapshot.js +6 -0
- package/dist/pi/cue-pump.d.ts +61 -0
- package/dist/pi/cue-pump.js +95 -0
- package/dist/pi/extension.d.ts +45 -0
- package/dist/pi/extension.js +407 -0
- package/dist/pi/gate-client.d.ts +54 -0
- package/dist/pi/gate-client.js +136 -0
- package/dist/pi/headless.d.ts +85 -0
- package/dist/pi/headless.js +224 -0
- package/dist/pi/index.d.ts +28 -0
- package/dist/pi/index.js +43 -0
- package/dist/pi/inner-loop-client.d.ts +67 -0
- package/dist/pi/inner-loop-client.js +164 -0
- package/dist/pi/inner-loop-publisher.d.ts +187 -0
- package/dist/pi/inner-loop-publisher.js +236 -0
- package/dist/pi/lazy-proxy.d.ts +37 -0
- package/dist/pi/lazy-proxy.js +55 -0
- package/dist/pi/mission-control/actions.d.ts +48 -0
- package/dist/pi/mission-control/actions.js +98 -0
- package/dist/pi/mission-control/board.d.ts +53 -0
- package/dist/pi/mission-control/board.js +104 -0
- package/dist/pi/mission-control/extension.d.ts +44 -0
- package/dist/pi/mission-control/extension.js +251 -0
- package/dist/pi/mission-control/index.d.ts +15 -0
- package/dist/pi/mission-control/index.js +32 -0
- package/dist/pi/mission-control/inner-tail.d.ts +48 -0
- package/dist/pi/mission-control/inner-tail.js +76 -0
- package/dist/pi/mission-control/pi-ui.d.ts +43 -0
- package/dist/pi/mission-control/pi-ui.js +10 -0
- package/dist/pi/mission-control/render.d.ts +6 -0
- package/dist/pi/mission-control/render.js +95 -0
- package/dist/pi/phase-driver.d.ts +74 -0
- package/dist/pi/phase-driver.js +122 -0
- package/dist/pi/pi-types.d.ts +208 -0
- package/dist/pi/pi-types.js +21 -0
- package/dist/pi/probe.d.ts +80 -0
- package/dist/pi/probe.js +154 -0
- package/dist/pi/render-tools.d.ts +17 -0
- package/dist/pi/render-tools.js +51 -0
- package/dist/pi/reset-pump.d.ts +47 -0
- package/dist/pi/reset-pump.js +85 -0
- package/dist/pi/tool-capability.d.ts +60 -0
- package/dist/pi/tool-capability.js +156 -0
- package/dist/pi/workflow-client.d.ts +158 -0
- package/dist/pi/workflow-client.js +289 -0
- package/dist/pi/zod-to-typebox.d.ts +74 -0
- package/dist/pi/zod-to-typebox.js +191 -0
- package/dist/scripts/verify-daemon-isolation-guard.js +24 -24
- package/dist/server-tools.d.ts +2 -0
- package/dist/server-tools.js +50 -46
- package/dist/server.js +4 -0
- package/dist/spawn.d.ts +55 -0
- package/dist/spawn.js +84 -12
- package/dist/tools/agent-types.d.ts +2 -2
- package/dist/tools/agent-types.js +22 -17
- package/dist/tools/attachment-info.d.ts +2 -2
- package/dist/tools/attachment-info.js +38 -33
- package/dist/tools/broadcast.d.ts +2 -2
- package/dist/tools/broadcast.js +69 -64
- package/dist/tools/cancel-stage.d.ts +2 -2
- package/dist/tools/cancel-stage.js +20 -15
- package/dist/tools/clear-state.d.ts +2 -2
- package/dist/tools/clear-state.js +25 -20
- package/dist/tools/coat-check-evict.d.ts +2 -2
- package/dist/tools/coat-check-evict.js +30 -25
- package/dist/tools/coat-check-get.d.ts +2 -2
- package/dist/tools/coat-check-get.js +39 -34
- package/dist/tools/coat-check-list.d.ts +2 -2
- package/dist/tools/coat-check-list.js +48 -43
- package/dist/tools/coat-check-put.d.ts +2 -2
- package/dist/tools/coat-check-put.js +41 -36
- package/dist/tools/cue.d.ts +2 -2
- package/dist/tools/cue.js +57 -52
- package/dist/tools/descriptor.d.ts +72 -0
- package/dist/tools/descriptor.js +39 -0
- package/dist/tools/destroy.d.ts +2 -2
- package/dist/tools/destroy.js +153 -148
- package/dist/tools/ensemble.d.ts +2 -2
- package/dist/tools/ensemble.js +71 -66
- package/dist/tools/evaluate-gate.d.ts +2 -2
- package/dist/tools/evaluate-gate.js +33 -27
- package/dist/tools/fetch-state.d.ts +2 -2
- package/dist/tools/fetch-state.js +43 -38
- package/dist/tools/gates.d.ts +2 -2
- package/dist/tools/gates.js +39 -34
- package/dist/tools/hosts.d.ts +2 -2
- package/dist/tools/hosts.js +25 -20
- package/dist/tools/listen.d.ts +2 -2
- package/dist/tools/listen.js +23 -18
- package/dist/tools/load-lineup.d.ts +2 -2
- package/dist/tools/load-lineup.js +324 -319
- package/dist/tools/migrate.d.ts +2 -2
- package/dist/tools/migrate.js +45 -40
- package/dist/tools/pause.d.ts +2 -2
- package/dist/tools/pause.js +34 -29
- package/dist/tools/play.d.ts +2 -2
- package/dist/tools/play.js +53 -48
- package/dist/tools/quality-gate.d.ts +2 -2
- package/dist/tools/quality-gate.js +26 -21
- package/dist/tools/recall.d.ts +2 -2
- package/dist/tools/recall.js +32 -27
- package/dist/tools/recruit.d.ts +2 -2
- package/dist/tools/recruit.js +325 -256
- package/dist/tools/release.d.ts +2 -2
- package/dist/tools/release.js +85 -80
- package/dist/tools/report.d.ts +2 -2
- package/dist/tools/report.js +28 -23
- package/dist/tools/reset.d.ts +3 -0
- package/dist/tools/reset.js +51 -0
- package/dist/tools/restart.d.ts +2 -2
- package/dist/tools/restart.js +51 -46
- package/dist/tools/restore.d.ts +2 -2
- package/dist/tools/restore.js +76 -71
- package/dist/tools/save-lineup.d.ts +2 -2
- package/dist/tools/save-lineup.js +32 -27
- package/dist/tools/save-state.d.ts +2 -2
- package/dist/tools/save-state.js +43 -38
- package/dist/tools/schedule.d.ts +2 -2
- package/dist/tools/schedule.js +133 -128
- package/dist/tools/schedules.d.ts +2 -2
- package/dist/tools/schedules.js +41 -36
- package/dist/tools/set-ensemble-description.d.ts +2 -2
- package/dist/tools/set-ensemble-description.js +26 -21
- package/dist/tools/set-name.d.ts +2 -2
- package/dist/tools/set-name.js +38 -33
- package/dist/tools/set-part.d.ts +2 -2
- package/dist/tools/set-part.js +20 -15
- package/dist/tools/shutdown.d.ts +2 -2
- package/dist/tools/shutdown.js +39 -34
- package/dist/tools/stage.d.ts +2 -2
- package/dist/tools/stage.js +28 -23
- package/dist/tools/stages.d.ts +2 -2
- package/dist/tools/stages.js +36 -31
- package/dist/tools/unschedule.d.ts +2 -2
- package/dist/tools/unschedule.js +30 -25
- package/dist/tools/who-am-i.d.ts +2 -2
- package/dist/tools/who-am-i.js +36 -31
- package/dist/tools/worktree.d.ts +2 -2
- package/dist/tools/worktree.js +134 -129
- package/dist/tui/index.js +6 -6
- package/dist/types.d.ts +47 -2
- package/dist/types.js +1 -1
- package/dist/utils/default-part.js +1 -0
- package/dist/utils/grpc-shutdown-guard.d.ts +52 -0
- package/dist/utils/grpc-shutdown-guard.js +88 -0
- package/dist/utils/sdk-probe.d.ts +23 -0
- package/dist/utils/sdk-probe.js +46 -7
- package/dist/worker.d.ts +3 -1
- package/dist/worker.js +6 -2
- package/dist/workflows/session.js +70 -2
- package/dist/workflows/signals.d.ts +32 -2
- package/dist/workflows/signals.js +25 -2
- package/examples/agents/tempo-composer.md +56 -56
- package/examples/agents/tempo-conductor.md +117 -117
- package/examples/agents/tempo-critic.md +73 -73
- package/examples/agents/tempo-improv.md +74 -74
- package/examples/agents/tempo-liner.md +75 -75
- package/examples/agents/tempo-roadie.md +61 -61
- package/examples/agents/tempo-soloist.md +71 -71
- package/examples/agents/tempo-tuner.md +94 -94
- package/examples/ensembles/tempo-big-band.yaml +146 -146
- package/examples/ensembles/tempo-dev-team.yaml +58 -58
- package/examples/ensembles/tempo-headless-jam.yaml +77 -77
- package/examples/ensembles/tempo-jam-session.yaml +41 -41
- package/examples/ensembles/tempo-mock-jam.yaml +79 -79
- package/examples/ensembles/tempo-review-squad.yaml +32 -32
- package/package.json +176 -173
- package/packaging/launchd/com.agent.tempo.plist +46 -46
- package/packaging/systemd/agent-tempo.service +32 -32
- package/packaging/windows/install-task.ps1 +71 -71
- package/scenarios/conductor-recruit-mock.yaml +33 -33
- package/scenarios/echo-roundtrip.yaml +15 -15
- package/scenarios/multi-player-handoff.yaml +38 -38
- package/scenarios/recruit-cascade.yaml +38 -38
- package/scenarios/two-player-conversation.yaml +33 -33
- package/workflow-bundle.js +97 -6
- package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
- package/dist/activities/claude-stop.d.ts +0 -21
- package/dist/activities/claude-stop.js +0 -94
- package/dist/channel.d.ts +0 -3
- package/dist/channel.js +0 -48
- package/dist/copilot-bridge.d.ts +0 -22
- package/dist/copilot-bridge.js +0 -565
- package/dist/scripts/258-spotcheck.js +0 -303
- package/dist/tools/detach.d.ts +0 -4
- package/dist/tools/detach.js +0 -45
- package/dist/tools/encore.d.ts +0 -4
- package/dist/tools/encore.js +0 -31
- package/dist/tools/helpers.d.ts +0 -21
- package/dist/tools/helpers.js +0 -25
- package/dist/tools/pause-ensemble.d.ts +0 -4
- package/dist/tools/pause-ensemble.js +0 -58
- package/dist/tools/resume-ensemble.d.ts +0 -4
- package/dist/tools/resume-ensemble.js +0 -79
- package/dist/tools/stop.d.ts +0 -4
- package/dist/tools/stop.js +0 -29
- package/dist/tui/client.d.ts +0 -6
- package/dist/tui/client.js +0 -9
- package/dist/tui/components/ActivityLog.d.ts +0 -16
- package/dist/tui/components/ActivityLog.js +0 -36
- package/dist/tui/components/CommandOverlay.d.ts +0 -15
- package/dist/tui/components/CommandOverlay.js +0 -34
- package/dist/tui/components/ConductorChat.d.ts +0 -16
- package/dist/tui/components/ConductorChat.js +0 -32
- package/dist/tui/components/EnsembleListView.d.ts +0 -14
- package/dist/tui/components/EnsembleListView.js +0 -32
- package/dist/tui/components/EnsemblePanel.d.ts +0 -12
- package/dist/tui/components/EnsemblePanel.js +0 -40
- package/dist/tui/components/InputBar.d.ts +0 -13
- package/dist/tui/components/InputBar.js +0 -58
- package/dist/tui/components/ScheduleOverlay.d.ts +0 -13
- package/dist/tui/components/ScheduleOverlay.js +0 -113
- package/dist/tui/components/TopBar.d.ts +0 -12
- package/dist/tui/components/TopBar.js +0 -15
- package/dist/tui/core-api.d.ts +0 -26
- package/dist/tui/core-api.js +0 -67
- package/dist/tui/hooks/useEnsembleDiscovery.d.ts +0 -3
- package/dist/tui/hooks/useEnsembleDiscovery.js +0 -30
- package/dist/tui/hooks/useMaestroPoller.d.ts +0 -3
- package/dist/tui/hooks/useMaestroPoller.js +0 -36
- package/dist/tui/hooks/useSendCommand.d.ts +0 -7
- package/dist/tui/hooks/useSendCommand.js +0 -29
- package/dist/utils/bg-preflight.d.ts +0 -25
- package/dist/utils/bg-preflight.js +0 -154
package/dist/spawn.js
CHANGED
|
@@ -12,6 +12,7 @@ exports.spawnCopilotBridge = spawnCopilotBridge;
|
|
|
12
12
|
exports.spawnMockAdapter = spawnMockAdapter;
|
|
13
13
|
exports.spawnClaudeApiAdapter = spawnClaudeApiAdapter;
|
|
14
14
|
exports.spawnOpenCodeAdapter = spawnOpenCodeAdapter;
|
|
15
|
+
exports.spawnPiHeadless = spawnPiHeadless;
|
|
15
16
|
exports.spawnClaudeCodeHeadlessAdapter = spawnClaudeCodeHeadlessAdapter;
|
|
16
17
|
const child_process_1 = require("child_process");
|
|
17
18
|
const fs_1 = require("fs");
|
|
@@ -262,12 +263,12 @@ function spawnInTerminal(claudeArgs, workDir, envVars, options) {
|
|
|
262
263
|
// Append `; exit` so the wrapping shell exits when claude does (clean or killed).
|
|
263
264
|
// Without it, claude exit returns control to the shell prompt and the tab lingers —
|
|
264
265
|
// parity with the Windows WT `closeOnExit: 'always'` + parent-walk fix from #166.
|
|
265
|
-
const osaScript = `
|
|
266
|
-
tell application "Ghostty"
|
|
267
|
-
set cfg to new surface configuration
|
|
268
|
-
set initial working directory of cfg to ${JSON.stringify(workDir)}
|
|
269
|
-
set initial input of cfg to ${JSON.stringify(claudeInvocation + '; exit\n')}
|
|
270
|
-
set win to new window with configuration cfg
|
|
266
|
+
const osaScript = `
|
|
267
|
+
tell application "Ghostty"
|
|
268
|
+
set cfg to new surface configuration
|
|
269
|
+
set initial working directory of cfg to ${JSON.stringify(workDir)}
|
|
270
|
+
set initial input of cfg to ${JSON.stringify(claudeInvocation + '; exit\n')}
|
|
271
|
+
set win to new window with configuration cfg
|
|
271
272
|
end tell`;
|
|
272
273
|
log('Using Ghostty initial-input path');
|
|
273
274
|
const child = (0, child_process_1.spawn)('osascript', ['-e', osaScript], {
|
|
@@ -285,12 +286,12 @@ function spawnInTerminal(claudeArgs, workDir, envVars, options) {
|
|
|
285
286
|
// so any `"` or `\` in paths/args doesn't break the AppleScript parser. Parity with
|
|
286
287
|
// the Ghostty path above.
|
|
287
288
|
const shellCmd = `cd ${shellQuote(workDir)} && ${claudeInvocation} ; exit`;
|
|
288
|
-
const osaScript = `
|
|
289
|
-
tell application "iTerm2"
|
|
290
|
-
set newWindow to (create window with default profile)
|
|
291
|
-
tell current session of newWindow
|
|
292
|
-
write text ${JSON.stringify(shellCmd)}
|
|
293
|
-
end tell
|
|
289
|
+
const osaScript = `
|
|
290
|
+
tell application "iTerm2"
|
|
291
|
+
set newWindow to (create window with default profile)
|
|
292
|
+
tell current session of newWindow
|
|
293
|
+
write text ${JSON.stringify(shellCmd)}
|
|
294
|
+
end tell
|
|
294
295
|
end tell`;
|
|
295
296
|
log('Using iTerm2 write-text path');
|
|
296
297
|
const child = (0, child_process_1.spawn)('osascript', ['-e', osaScript], {
|
|
@@ -671,6 +672,77 @@ function spawnOpenCodeAdapter(opts) {
|
|
|
671
672
|
log(`Spawned opencode adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}"${opts.model ? ` (model=${opts.model})` : ''}${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
|
|
672
673
|
return { pid: child.pid, logPath, pidPath };
|
|
673
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Resolve the path to the Pi headless adapter entry point. Mirrors
|
|
677
|
+
* {@link resolveOpenCodePath} — dev (ts-node) + prod (compiled .js) both launch
|
|
678
|
+
* the same code through the same `require.main === module` gate.
|
|
679
|
+
*/
|
|
680
|
+
function resolvePiPath() {
|
|
681
|
+
const isDev = __filename.endsWith('.ts');
|
|
682
|
+
if (isDev) {
|
|
683
|
+
return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'pi', 'adapter.ts')] };
|
|
684
|
+
}
|
|
685
|
+
return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'pi', 'adapter.js')] };
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Spawn the headless Pi runtime as a detached subprocess. Pattern matches
|
|
689
|
+
* {@link spawnOpenCodeAdapter} — no TTY, log + PID files, env carries identity +
|
|
690
|
+
* Temporal settings + attachment handoff + the Pi model / continue-session /
|
|
691
|
+
* tool-access knobs. The entry constructs `createAgentSession` with the `src/pi`
|
|
692
|
+
* extension injected inline; the singleton claims + heartbeats + registers tools.
|
|
693
|
+
*/
|
|
694
|
+
function spawnPiHeadless(opts) {
|
|
695
|
+
const { cmd, args } = resolvePiPath();
|
|
696
|
+
const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
|
|
697
|
+
const logName = opts.name || `pi-${Date.now()}`;
|
|
698
|
+
const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
|
|
699
|
+
const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
|
|
700
|
+
(0, fs_1.mkdirSync)(logDirPath, { recursive: true });
|
|
701
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
702
|
+
const toolAccess = opts.toolAccess || 'restricted';
|
|
703
|
+
let child;
|
|
704
|
+
try {
|
|
705
|
+
child = (0, child_process_1.spawn)(cmd, args, {
|
|
706
|
+
cwd: opts.workDir,
|
|
707
|
+
detached: true,
|
|
708
|
+
stdio: ['ignore', logFd, logFd],
|
|
709
|
+
env: {
|
|
710
|
+
...process.env,
|
|
711
|
+
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
712
|
+
[config_1.ENV.PLAYER_NAME]: opts.name,
|
|
713
|
+
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
714
|
+
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
715
|
+
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
716
|
+
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
|
717
|
+
...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
|
|
718
|
+
...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
|
|
719
|
+
// Model selection: recruit-arg → AGENT_TEMPO_PI_MODEL → Pi default.
|
|
720
|
+
...(opts.model ? { [config_1.ENV.PI_MODEL]: opts.model } : {}),
|
|
721
|
+
// Restart-resume: continue the prior Pi conversation.
|
|
722
|
+
...(opts.continueSessionId ? { [config_1.ENV.PI_CONTINUE_SESSION]: opts.continueSessionId } : {}),
|
|
723
|
+
// MD-C: tool-class policy. ALWAYS set (default 'restricted' — the safe
|
|
724
|
+
// unsupervised default + an explicit value for the gate + audit trail).
|
|
725
|
+
[config_1.ENV.TOOL_ACCESS]: toolAccess,
|
|
726
|
+
// 3c Tier-2: per-player ingest token (minted by the daemon outbox).
|
|
727
|
+
// Absent → the inner-loop publisher's HTTP client no-ops (no fine tail).
|
|
728
|
+
...(opts.ingestToken ? { [config_1.ENV.INGEST_TOKEN]: opts.ingestToken } : {}),
|
|
729
|
+
// Attachment handoff — extension renews via claimAttachment(expectedAttachmentId).
|
|
730
|
+
...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
|
|
731
|
+
...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
|
|
732
|
+
...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
child.unref();
|
|
736
|
+
}
|
|
737
|
+
finally {
|
|
738
|
+
(0, fs_1.closeSync)(logFd);
|
|
739
|
+
}
|
|
740
|
+
if (child.pid != null) {
|
|
741
|
+
(0, fs_1.writeFileSync)(pidPath, String(child.pid));
|
|
742
|
+
}
|
|
743
|
+
log(`Spawned pi headless adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}" (toolAccess=${toolAccess})${opts.model ? ` (model=${opts.model})` : ''}${opts.continueSessionId ? ` (continue=${opts.continueSessionId})` : ''}${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
|
|
744
|
+
return { pid: child.pid, logPath, pidPath };
|
|
745
|
+
}
|
|
674
746
|
/**
|
|
675
747
|
* Resolve the path to the claude-code-headless adapter entry point.
|
|
676
748
|
* Mirrors {@link resolveClaudeApiPath} so dev (ts-node) and prod
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function
|
|
1
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
2
|
+
export declare function buildAgentTypesTool(): TempoToolDescriptor;
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
3
|
+
exports.buildAgentTypesTool = buildAgentTypesTool;
|
|
4
|
+
const descriptor_1 = require("./descriptor");
|
|
5
5
|
const agent_types_1 = require("../ensemble/agent-types");
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
function buildAgentTypesTool() {
|
|
7
|
+
return {
|
|
8
|
+
name: 'agent_types',
|
|
9
|
+
description: 'List available player types (agent definitions) that can be used when recruiting',
|
|
10
|
+
params: {},
|
|
11
|
+
handler: async () => {
|
|
12
|
+
const types = (0, agent_types_1.listAgentTypes)();
|
|
13
|
+
if (types.length === 0) {
|
|
14
|
+
return (0, descriptor_1.ok)('No agent types found.');
|
|
15
|
+
}
|
|
16
|
+
const lines = types.map(t => {
|
|
17
|
+
const src = t.source === 'shipped' ? '(shipped)' : t.source === 'user' ? '(user)' : '(project)';
|
|
18
|
+
const tools = t.allowedTools && t.allowedTools.length > 0
|
|
19
|
+
? `\n Allowed tools: ${t.allowedTools.join(', ')}`
|
|
20
|
+
: '';
|
|
21
|
+
return `**${t.name}** ${src}\n ${t.description || 'No description'}${tools}`;
|
|
22
|
+
});
|
|
23
|
+
return (0, descriptor_1.ok)(lines.join('\n\n'));
|
|
24
|
+
},
|
|
25
|
+
};
|
|
21
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
1
|
import { Client } from '@temporalio/client';
|
|
3
2
|
import { Config } from '../config';
|
|
4
|
-
|
|
3
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
4
|
+
export declare function buildAttachmentInfoTool(client: Client, config: Config): TempoToolDescriptor;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildAttachmentInfoTool = buildAttachmentInfoTool;
|
|
4
4
|
/**
|
|
5
5
|
* `attachment_info` — diagnostic query for the V2 attachment lifecycle (design §§2.2, 2.4).
|
|
6
6
|
*
|
|
@@ -11,38 +11,43 @@ exports.registerAttachmentInfoTool = registerAttachmentInfoTool;
|
|
|
11
11
|
const zod_1 = require("zod");
|
|
12
12
|
const resolve_1 = require("./resolve");
|
|
13
13
|
const signals_1 = require("../workflows/signals");
|
|
14
|
-
const
|
|
14
|
+
const descriptor_1 = require("./descriptor");
|
|
15
15
|
const validation_1 = require("../utils/validation");
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
return (0,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
16
|
+
function buildAttachmentInfoTool(client, config) {
|
|
17
|
+
return {
|
|
18
|
+
name: 'attachment_info',
|
|
19
|
+
description: 'Query the V2 attachment lifecycle state of a session — phase (booting/attached/processing/awaiting/draining/detached/gone), current attachment holder (host + adapter + lease expiry), preferred host, and in-flight message count. Read-only diagnostic.',
|
|
20
|
+
params: {
|
|
21
|
+
playerId: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('The player name to inspect'),
|
|
22
|
+
},
|
|
23
|
+
handler: async (args) => {
|
|
24
|
+
const { playerId } = args;
|
|
25
|
+
const nameError = (0, validation_1.validatePlayerName)(playerId);
|
|
26
|
+
if (nameError)
|
|
27
|
+
return (0, descriptor_1.fail)(nameError);
|
|
28
|
+
try {
|
|
29
|
+
const resolved = await (0, resolve_1.resolveSession)(client, config.ensemble, playerId);
|
|
30
|
+
if (!resolved)
|
|
31
|
+
return (0, descriptor_1.fail)(`No session found with name "${playerId}".`);
|
|
32
|
+
const info = await resolved.query(signals_1.attachmentInfoQuery);
|
|
33
|
+
const lines = [
|
|
34
|
+
`**${playerId}** — phase: \`${info.phase}\``,
|
|
35
|
+
`in-flight messages: ${info.inFlightCount}`,
|
|
36
|
+
];
|
|
37
|
+
if (info.currentAttachment) {
|
|
38
|
+
const a = info.currentAttachment;
|
|
39
|
+
lines.push(`attached on: **${a.hostname}** (adapter: ${a.adapterId}/${a.adapterClass}, attachmentId: \`${a.attachmentId.slice(0, 8)}…\`)`);
|
|
40
|
+
lines.push(`lease expires: ${a.expiresAt}`);
|
|
41
|
+
}
|
|
42
|
+
if (info.preferredHost)
|
|
43
|
+
lines.push(`preferred host: ${info.preferredHost}`);
|
|
44
|
+
if (info.processingSince)
|
|
45
|
+
lines.push(`processing since: ${info.processingSince}`);
|
|
46
|
+
return (0, descriptor_1.ok)(lines.join('\n'));
|
|
37
47
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
return (0, helpers_1.fail)(`Failed to query attachment info: ${(0, helpers_1.formatError)(err)}`);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
+
catch (err) {
|
|
49
|
+
return (0, descriptor_1.fail)(`Failed to query attachment info: ${(0, descriptor_1.formatError)(err)}`);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
48
53
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
1
|
import { Client, WorkflowHandle } from '@temporalio/client';
|
|
3
2
|
import { Config } from '../config';
|
|
4
|
-
|
|
3
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
4
|
+
export declare function buildBroadcastTool(client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle): TempoToolDescriptor;
|
package/dist/tools/broadcast.js
CHANGED
|
@@ -1,76 +1,81 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
return (0,
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
exports.buildCancelStageTool = buildCancelStageTool;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const descriptor_1 = require("./descriptor");
|
|
6
6
|
const validation_1 = require("../utils/validation");
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
name:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
17
|
+
const descriptor_1 = require("./descriptor");
|
|
18
18
|
const validation_1 = require("../utils/validation");
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
17
|
+
const descriptor_1 = require("./descriptor");
|
|
18
18
|
const validation_1 = require("../utils/validation");
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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.
|
|
23
|
+
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
}
|