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/tools/schedule.js
CHANGED
|
@@ -1,152 +1,157 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildScheduleTool = buildScheduleTool;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const croner_1 = require("croner");
|
|
6
6
|
const client_1 = require("@temporalio/client");
|
|
7
7
|
const config_1 = require("../config");
|
|
8
8
|
const duration_1 = require("../utils/duration");
|
|
9
9
|
const resolve_1 = require("./resolve");
|
|
10
|
-
const
|
|
10
|
+
const descriptor_1 = require("./descriptor");
|
|
11
11
|
const validation_1 = require("../utils/validation");
|
|
12
12
|
const log = (...args) => console.error('[agent-tempo:schedule]', ...args);
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
name:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
13
|
+
function buildScheduleTool(client, config, getPlayerId) {
|
|
14
|
+
return {
|
|
15
|
+
name: 'schedule',
|
|
16
|
+
description: 'Schedule a message to be sent to a player at a specific time, after a delay, on a recurring interval, or via cron expression.',
|
|
17
|
+
params: {
|
|
18
|
+
name: zod_1.z.string().max(validation_1.SCHEDULE_NAME_MAX).describe('Unique name for this schedule'),
|
|
19
|
+
message: zod_1.z.string().max(validation_1.SCHEDULE_MESSAGE_MAX).describe('The message to deliver'),
|
|
20
|
+
target: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('Player name to deliver to ("self" = this session)'),
|
|
21
|
+
at: zod_1.z.string().optional().describe('ISO datetime for one-shot delivery (e.g. "2026-04-03T20:00:00Z")'),
|
|
22
|
+
delay: zod_1.z.string().optional().describe('Duration until first delivery (e.g. "10m", "2h", "1d")'),
|
|
23
|
+
every: zod_1.z.string().optional().describe('Recurring interval (e.g. "5m", "1h")'),
|
|
24
|
+
cron: zod_1.z.string().max(validation_1.CRON_EXPRESSION_MAX).optional().describe('Cron expression for recurring delivery (e.g. "0 9 * * 1-5" = weekdays at 9am). Mutually exclusive with at/delay/every.'),
|
|
25
|
+
timezone: zod_1.z.string().optional().describe('IANA timezone for cron evaluation (e.g. "America/New_York"). Defaults to UTC. Only used with cron.'),
|
|
26
|
+
until: zod_1.z.string().optional().describe('ISO datetime — stop recurring after this time'),
|
|
27
|
+
count: zod_1.z.number().optional().describe('Max number of deliveries for recurring schedules'),
|
|
28
|
+
},
|
|
29
|
+
handler: async (args) => {
|
|
30
|
+
const { name, message, at, delay, every, cron, timezone, until, count } = args;
|
|
31
|
+
let target = args.target;
|
|
32
|
+
// Resolve "self" to the current player name
|
|
33
|
+
if (target === 'self') {
|
|
34
|
+
target = getPlayerId();
|
|
35
|
+
}
|
|
36
|
+
// Validate target player exists (warn, don't block)
|
|
37
|
+
let targetWarning;
|
|
38
|
+
if (target !== 'all' && target !== 'conductor') {
|
|
39
|
+
try {
|
|
40
|
+
const targetHandle = await (0, resolve_1.resolveSession)(client, config.ensemble, target);
|
|
41
|
+
if (!targetHandle) {
|
|
42
|
+
targetWarning = `Warning: player "${target}" is not currently active. The schedule will be created but may fail to deliver until the player joins.`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Resolution failed — don't block schedule creation
|
|
39
47
|
}
|
|
40
48
|
}
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
// Validate exactly one timing option
|
|
50
|
+
const timingCount = [at, delay, every, cron].filter(Boolean).length;
|
|
51
|
+
if (timingCount !== 1) {
|
|
52
|
+
return (0, descriptor_1.fail)('Provide exactly one timing option: `at`, `delay`, `every`, or `cron`.');
|
|
43
53
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (timingCount !== 1) {
|
|
48
|
-
return (0, helpers_1.fail)('Provide exactly one timing option: `at`, `delay`, `every`, or `cron`.');
|
|
49
|
-
}
|
|
50
|
-
// timezone only valid with cron
|
|
51
|
-
if (timezone && !cron) {
|
|
52
|
-
return (0, helpers_1.fail)('`timezone` can only be used with `cron`.');
|
|
53
|
-
}
|
|
54
|
-
const now = Date.now();
|
|
55
|
-
let nextFireAt;
|
|
56
|
-
let interval;
|
|
57
|
-
if (at) {
|
|
58
|
-
const ts = Date.parse(at);
|
|
59
|
-
if (isNaN(ts)) {
|
|
60
|
-
return (0, helpers_1.fail)(`Invalid ISO datetime for "at": ${at}`);
|
|
54
|
+
// timezone only valid with cron
|
|
55
|
+
if (timezone && !cron) {
|
|
56
|
+
return (0, descriptor_1.fail)('`timezone` can only be used with `cron`.');
|
|
61
57
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
let nextFireAt;
|
|
60
|
+
let interval;
|
|
61
|
+
if (at) {
|
|
62
|
+
const ts = Date.parse(at);
|
|
63
|
+
if (isNaN(ts)) {
|
|
64
|
+
return (0, descriptor_1.fail)(`Invalid ISO datetime for "at": ${at}`);
|
|
65
|
+
}
|
|
66
|
+
nextFireAt = ts;
|
|
68
67
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return (0, helpers_1.fail)(`Invalid or too-short interval for "every": ${every}. Minimum is 10s.`);
|
|
68
|
+
else if (delay) {
|
|
69
|
+
const ms = (0, duration_1.parseDuration)(delay);
|
|
70
|
+
if (ms === null) {
|
|
71
|
+
return (0, descriptor_1.fail)(`Invalid duration for "delay": ${delay}. Use e.g. "30s", "10m", "2h", "1d".`);
|
|
72
|
+
}
|
|
73
|
+
nextFireAt = now + ms;
|
|
76
74
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const job = new croner_1.Cron(cron, { timezone: timezone || 'UTC' });
|
|
84
|
-
const next = job.nextRun();
|
|
85
|
-
if (!next) {
|
|
86
|
-
return (0, helpers_1.fail)(`Cron expression "${cron}" has no upcoming fire time.`);
|
|
75
|
+
else if (every) {
|
|
76
|
+
// every (recurring interval)
|
|
77
|
+
const ms = (0, duration_1.parseDuration)(every);
|
|
78
|
+
if (ms === null || ms < 10_000) {
|
|
79
|
+
return (0, descriptor_1.fail)(`Invalid or too-short interval for "every": ${every}. Minimum is 10s.`);
|
|
87
80
|
}
|
|
88
|
-
nextFireAt =
|
|
81
|
+
nextFireAt = now + ms;
|
|
82
|
+
interval = ms;
|
|
89
83
|
}
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
else {
|
|
85
|
+
// cron (recurring via cron expression)
|
|
86
|
+
try {
|
|
87
|
+
const job = new croner_1.Cron(cron, { timezone: timezone || 'UTC' });
|
|
88
|
+
const next = job.nextRun();
|
|
89
|
+
if (!next) {
|
|
90
|
+
return (0, descriptor_1.fail)(`Cron expression "${cron}" has no upcoming fire time.`);
|
|
91
|
+
}
|
|
92
|
+
nextFireAt = next.getTime();
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
return (0, descriptor_1.fail)(`Invalid cron expression "${cron}": ${(0, descriptor_1.formatError)(err)}`);
|
|
96
|
+
}
|
|
92
97
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
// Parse optional until
|
|
99
|
+
let untilMs;
|
|
100
|
+
if (until) {
|
|
101
|
+
const ts = Date.parse(until);
|
|
102
|
+
if (isNaN(ts)) {
|
|
103
|
+
return (0, descriptor_1.fail)(`Invalid ISO datetime for "until": ${until}`);
|
|
104
|
+
}
|
|
105
|
+
untilMs = ts;
|
|
100
106
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
createdBy: getPlayerId(),
|
|
117
|
-
};
|
|
118
|
-
try {
|
|
119
|
-
const wfId = (0, config_1.schedulerWorkflowId)(config.ensemble);
|
|
120
|
-
// Try to signal the existing scheduler workflow
|
|
107
|
+
const type = cron ? 'cron' : every ? 'interval' : 'once';
|
|
108
|
+
const scheduleEntry = {
|
|
109
|
+
name,
|
|
110
|
+
message,
|
|
111
|
+
target,
|
|
112
|
+
type,
|
|
113
|
+
nextFireAt: new Date(nextFireAt).toISOString(),
|
|
114
|
+
interval,
|
|
115
|
+
cronExpression: cron,
|
|
116
|
+
timezone: cron ? (timezone || 'UTC') : undefined,
|
|
117
|
+
until: untilMs ? new Date(untilMs).toISOString() : undefined,
|
|
118
|
+
remainingCount: count,
|
|
119
|
+
firedCount: 0,
|
|
120
|
+
createdBy: getPlayerId(),
|
|
121
|
+
};
|
|
121
122
|
try {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
const wfId = (0, config_1.schedulerWorkflowId)(config.ensemble);
|
|
124
|
+
// Try to signal the existing scheduler workflow
|
|
125
|
+
try {
|
|
126
|
+
const handle = client.workflow.getHandle(wfId);
|
|
127
|
+
await handle.describe(); // throws if not running
|
|
128
|
+
await handle.signal('addSchedule', scheduleEntry);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Scheduler not running — start it with this schedule as seed
|
|
132
|
+
await client.workflow.start('agentSchedulerWorkflow', {
|
|
133
|
+
workflowId: wfId,
|
|
134
|
+
taskQueue: config.taskQueue,
|
|
135
|
+
args: [{ ensemble: config.ensemble, entries: [scheduleEntry] }],
|
|
136
|
+
workflowIdConflictPolicy: client_1.WorkflowIdConflictPolicy.USE_EXISTING,
|
|
137
|
+
searchAttributes: {
|
|
138
|
+
AgentTempoEnsemble: [config.ensemble],
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
log(`Started scheduler workflow ${wfId}`);
|
|
142
|
+
}
|
|
143
|
+
const fireDate = new Date(nextFireAt).toISOString();
|
|
144
|
+
const recur = cron
|
|
145
|
+
? ` (cron: ${cron}, tz: ${timezone || 'UTC'})`
|
|
146
|
+
: interval
|
|
147
|
+
? ` (repeating every ${every})`
|
|
148
|
+
: ' (one-shot)';
|
|
149
|
+
const msg = `Schedule **${name}** created. Next fire: ${fireDate}${recur}. Target: ${target}.`;
|
|
150
|
+
return (0, descriptor_1.ok)(targetWarning ? `${msg}\n\n⚠ ${targetWarning}` : msg);
|
|
125
151
|
}
|
|
126
|
-
catch {
|
|
127
|
-
|
|
128
|
-
await client.workflow.start('agentSchedulerWorkflow', {
|
|
129
|
-
workflowId: wfId,
|
|
130
|
-
taskQueue: config.taskQueue,
|
|
131
|
-
args: [{ ensemble: config.ensemble, entries: [scheduleEntry] }],
|
|
132
|
-
workflowIdConflictPolicy: client_1.WorkflowIdConflictPolicy.USE_EXISTING,
|
|
133
|
-
searchAttributes: {
|
|
134
|
-
AgentTempoEnsemble: [config.ensemble],
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
log(`Started scheduler workflow ${wfId}`);
|
|
152
|
+
catch (err) {
|
|
153
|
+
return (0, descriptor_1.fail)(`Failed to create schedule: ${(0, descriptor_1.formatError)(err)}`);
|
|
138
154
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
? ` (cron: ${cron}, tz: ${timezone || 'UTC'})`
|
|
142
|
-
: interval
|
|
143
|
-
? ` (repeating every ${every})`
|
|
144
|
-
: ' (one-shot)';
|
|
145
|
-
const msg = `Schedule **${name}** created. Next fire: ${fireDate}${recur}. Target: ${target}.`;
|
|
146
|
-
return (0, helpers_1.ok)(targetWarning ? `${msg}\n\n⚠ ${targetWarning}` : msg);
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
return (0, helpers_1.fail)(`Failed to create schedule: ${(0, helpers_1.formatError)(err)}`);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
155
|
+
},
|
|
156
|
+
};
|
|
152
157
|
}
|
|
@@ -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 buildSchedulesTool(client: Client, config: Config): TempoToolDescriptor;
|
package/dist/tools/schedules.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildSchedulesTool = buildSchedulesTool;
|
|
4
4
|
const client_1 = require("@temporalio/client");
|
|
5
5
|
const config_1 = require("../config");
|
|
6
|
-
const
|
|
6
|
+
const descriptor_1 = require("./descriptor");
|
|
7
7
|
function formatDuration(ms) {
|
|
8
8
|
if (ms >= 86_400_000)
|
|
9
9
|
return `${ms / 86_400_000}d`;
|
|
@@ -13,42 +13,47 @@ function formatDuration(ms) {
|
|
|
13
13
|
return `${ms / 60_000}m`;
|
|
14
14
|
return `${ms / 1000}s`;
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
function buildSchedulesTool(client, config) {
|
|
17
|
+
return {
|
|
18
|
+
name: 'schedules',
|
|
19
|
+
description: 'List all active schedules in this ensemble.',
|
|
20
|
+
params: {},
|
|
21
|
+
handler: async () => {
|
|
22
22
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const wfId = (0, config_1.schedulerWorkflowId)(config.ensemble);
|
|
24
|
+
const handle = client.workflow.getHandle(wfId);
|
|
25
|
+
let schedules;
|
|
26
|
+
try {
|
|
27
|
+
schedules = await handle.query('getSchedules');
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err instanceof client_1.WorkflowNotFoundError) {
|
|
31
|
+
return (0, descriptor_1.ok)('No scheduler running — no schedules exist yet.');
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
28
34
|
}
|
|
29
|
-
|
|
35
|
+
if (schedules.length === 0) {
|
|
36
|
+
return (0, descriptor_1.ok)('No active schedules.');
|
|
37
|
+
}
|
|
38
|
+
const lines = schedules.map((s) => {
|
|
39
|
+
const next = s.nextFireAt; // already ISO string
|
|
40
|
+
const recur = s.cronExpression
|
|
41
|
+
? `cron: ${s.cronExpression} (${s.timezone || 'UTC'})`
|
|
42
|
+
: s.interval ? `every ${formatDuration(s.interval)}` : 'one-shot';
|
|
43
|
+
const bounds = [];
|
|
44
|
+
if (s.until)
|
|
45
|
+
bounds.push(`until ${s.until}`);
|
|
46
|
+
if (s.remainingCount != null)
|
|
47
|
+
bounds.push(`${s.firedCount}/${s.firedCount + s.remainingCount} fired`);
|
|
48
|
+
const boundsStr = bounds.length ? ` (${bounds.join(', ')})` : '';
|
|
49
|
+
const msgPreview = s.message.length > 60 ? s.message.slice(0, 57) + '...' : s.message;
|
|
50
|
+
return `• **${s.name}** → ${s.target} | ${recur}${boundsStr} | next: ${next}\n msg: ${msgPreview}`;
|
|
51
|
+
});
|
|
52
|
+
return (0, descriptor_1.ok)(lines.join('\n'));
|
|
30
53
|
}
|
|
31
|
-
|
|
32
|
-
return (0,
|
|
54
|
+
catch (err) {
|
|
55
|
+
return (0, descriptor_1.fail)(`Failed to query schedules: ${(0, descriptor_1.formatError)(err)}`);
|
|
33
56
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const recur = s.cronExpression
|
|
37
|
-
? `cron: ${s.cronExpression} (${s.timezone || 'UTC'})`
|
|
38
|
-
: s.interval ? `every ${formatDuration(s.interval)}` : 'one-shot';
|
|
39
|
-
const bounds = [];
|
|
40
|
-
if (s.until)
|
|
41
|
-
bounds.push(`until ${s.until}`);
|
|
42
|
-
if (s.remainingCount != null)
|
|
43
|
-
bounds.push(`${s.firedCount}/${s.firedCount + s.remainingCount} fired`);
|
|
44
|
-
const boundsStr = bounds.length ? ` (${bounds.join(', ')})` : '';
|
|
45
|
-
const msgPreview = s.message.length > 60 ? s.message.slice(0, 57) + '...' : s.message;
|
|
46
|
-
return `• **${s.name}** → ${s.target} | ${recur}${boundsStr} | next: ${next}\n msg: ${msgPreview}`;
|
|
47
|
-
});
|
|
48
|
-
return (0, helpers_1.ok)(lines.join('\n'));
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
return (0, helpers_1.fail)(`Failed to query schedules: ${(0, helpers_1.formatError)(err)}`);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
57
|
+
},
|
|
58
|
+
};
|
|
54
59
|
}
|
|
@@ -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 buildSetEnsembleDescriptionTool(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.buildSetEnsembleDescriptionTool = buildSetEnsembleDescriptionTool;
|
|
4
4
|
/**
|
|
5
5
|
* `set_ensemble_description` MCP tool — #399 W1 (Q5.1).
|
|
6
6
|
*
|
|
@@ -12,26 +12,31 @@ exports.registerSetEnsembleDescriptionTool = registerSetEnsembleDescriptionTool;
|
|
|
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
|
|
15
|
+
const descriptor_1 = require("./descriptor");
|
|
16
16
|
const validation_1 = require("../utils/validation");
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
function buildSetEnsembleDescriptionTool(client, config) {
|
|
18
|
+
return {
|
|
19
|
+
name: 'set_ensemble_description',
|
|
20
|
+
description: "Update the ensemble's mission-flavor description (≤100 chars). Surfaces on the dashboard EnsembleCard. Empty string clears it. Refresh at milestone boundaries — don't update for trivial changes.",
|
|
21
|
+
params: {
|
|
22
|
+
description: zod_1.z
|
|
23
|
+
.string()
|
|
24
|
+
.max(validation_1.ENSEMBLE_DESCRIPTION_MAX)
|
|
25
|
+
.describe('Short summary of what the ensemble is currently working on (≤100 chars).'),
|
|
26
|
+
},
|
|
27
|
+
handler: async (args) => {
|
|
28
|
+
const { description } = args;
|
|
29
|
+
try {
|
|
30
|
+
const handle = client.workflow.getHandle((0, config_1.maestroWorkflowId)(config.ensemble));
|
|
31
|
+
await handle.signal(maestro_signals_1.setEnsembleDescriptionSignal.name, description);
|
|
32
|
+
if (description.trim().length === 0) {
|
|
33
|
+
return (0, descriptor_1.ok)(`Ensemble **${config.ensemble}** description cleared.`);
|
|
34
|
+
}
|
|
35
|
+
return (0, descriptor_1.ok)(`Ensemble **${config.ensemble}** description updated: "${description}"`);
|
|
30
36
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
+
catch (err) {
|
|
38
|
+
return (0, descriptor_1.fail)(`Failed to set ensemble description: ${(0, descriptor_1.formatError)(err)}`);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
37
42
|
}
|
package/dist/tools/set-name.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
1
|
import { WorkflowHandle, Client } from '@temporalio/client';
|
|
3
2
|
import { Config } from '../config';
|
|
4
|
-
|
|
3
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
4
|
+
export declare function buildSetNameTool(client: Client, config: Config, handle: WorkflowHandle, getPlayerId: () => string, setPlayerId: (id: string) => void): TempoToolDescriptor;
|
package/dist/tools/set-name.js
CHANGED
|
@@ -1,45 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildSetNameTool = buildSetNameTool;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const config_1 = require("../config");
|
|
6
6
|
const scheduler_signals_1 = require("../workflows/scheduler-signals");
|
|
7
7
|
const resolve_1 = require("./resolve");
|
|
8
|
-
const
|
|
8
|
+
const descriptor_1 = require("./descriptor");
|
|
9
9
|
const validation_1 = require("../utils/validation");
|
|
10
10
|
const log = (...args) => console.error('[agent-tempo:set-name]', ...args);
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
name:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Notify the scheduler to rewrite schedule targets from old name to new name
|
|
11
|
+
function buildSetNameTool(client, config, handle, getPlayerId, setPlayerId) {
|
|
12
|
+
return {
|
|
13
|
+
name: 'set_name',
|
|
14
|
+
description: 'Set a human-readable name for this session. Visible to other players in the ensemble.',
|
|
15
|
+
params: {
|
|
16
|
+
name: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('The name for this session (e.g., "UX", "API", "test-runner")'),
|
|
17
|
+
},
|
|
18
|
+
handler: async (args) => {
|
|
19
|
+
const { name } = args;
|
|
20
|
+
// Validate name to prevent search attribute query injection
|
|
21
|
+
const nameError = (0, validation_1.validatePlayerName)(name);
|
|
22
|
+
if (nameError) {
|
|
23
|
+
return (0, descriptor_1.fail)(nameError);
|
|
24
|
+
}
|
|
25
|
+
// Check if the name is already taken
|
|
26
|
+
const existing = await (0, resolve_1.resolveSession)(client, config.ensemble, name);
|
|
27
|
+
if (existing && existing.workflowId !== handle.workflowId) {
|
|
28
|
+
return (0, descriptor_1.fail)(`Name **${name}** is already taken by another session. Choose a different name.`);
|
|
29
|
+
}
|
|
31
30
|
try {
|
|
32
|
-
const
|
|
33
|
-
await
|
|
31
|
+
const oldName = getPlayerId();
|
|
32
|
+
await handle.signal('setName', name);
|
|
33
|
+
setPlayerId(name);
|
|
34
|
+
// Notify the scheduler to rewrite schedule targets from old name to new name
|
|
35
|
+
try {
|
|
36
|
+
const schedulerHandle = client.workflow.getHandle((0, config_1.schedulerWorkflowId)(config.ensemble));
|
|
37
|
+
await schedulerHandle.signal(scheduler_signals_1.updateScheduleTargetSignal, oldName, name);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Scheduler may not be running — that's fine, no schedules to update
|
|
41
|
+
log(`No scheduler to notify about rename ${oldName} → ${name}`);
|
|
42
|
+
}
|
|
43
|
+
return (0, descriptor_1.ok)(`Session name set to **${name}**. Run \`/rename ${name}\` to match your Claude Code session name.`);
|
|
34
44
|
}
|
|
35
|
-
catch {
|
|
36
|
-
|
|
37
|
-
log(`No scheduler to notify about rename ${oldName} → ${name}`);
|
|
45
|
+
catch (err) {
|
|
46
|
+
return (0, descriptor_1.fail)(`Failed to set name: ${(0, descriptor_1.formatError)(err)}`);
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
catch (err) {
|
|
42
|
-
return (0, helpers_1.fail)(`Failed to set name: ${(0, helpers_1.formatError)(err)}`);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
48
|
+
},
|
|
49
|
+
};
|
|
45
50
|
}
|
package/dist/tools/set-part.d.ts
CHANGED
|
@@ -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 buildSetPartTool(handle: WorkflowHandle): TempoToolDescriptor;
|
package/dist/tools/set-part.js
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildSetPartTool = buildSetPartTool;
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
function buildSetPartTool(handle) {
|
|
8
|
+
return {
|
|
9
|
+
name: 'set_part',
|
|
10
|
+
description: 'Update your description of what you are currently working on. Visible to other sessions via ensemble.',
|
|
11
|
+
params: {
|
|
12
|
+
part: zod_1.z.string().max(validation_1.PART_MAX).describe('A short description of your current work'),
|
|
13
|
+
},
|
|
14
|
+
handler: async (args) => {
|
|
15
|
+
const { part } = args;
|
|
16
|
+
try {
|
|
17
|
+
await handle.signal('setPart', part);
|
|
18
|
+
return (0, descriptor_1.ok)(`Part updated: "${part}"`);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
return (0, descriptor_1.fail)(`Failed to update part: ${(0, descriptor_1.formatError)(err)}`);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
20
25
|
}
|
package/dist/tools/shutdown.d.ts
CHANGED
|
@@ -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 buildShutdownTool(client: Client, config: Config, getPlayerId: () => string): TempoToolDescriptor;
|