agent-tempo 1.3.1 → 1.4.1
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 +39 -5
- package/README.md +6 -2
- 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 +1 -1
- package/dashboard/package.json +1 -1
- 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/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/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 +32 -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 +250 -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 +88 -0
- package/dist/pi/mission-control/board.js +141 -0
- package/dist/pi/mission-control/extension.d.ts +51 -0
- package/dist/pi/mission-control/extension.js +330 -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 +98 -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 +222 -0
- package/dist/pi/pi-types.js +21 -0
- package/dist/pi/probe.d.ts +99 -0
- package/dist/pi/probe.js +179 -0
- package/dist/pi/render-tools.d.ts +17 -0
- package/dist/pi/render-tools.js +56 -0
- package/dist/pi/reset-pump.d.ts +47 -0
- package/dist/pi/reset-pump.js +85 -0
- package/dist/pi/session-seed.d.ts +74 -0
- package/dist/pi/session-seed.js +103 -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/server-tools.d.ts +2 -0
- package/dist/server-tools.js +50 -46
- package/dist/spawn.d.ts +55 -0
- package/dist/spawn.js +72 -0
- 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 +29 -24
- package/dist/tools/coat-check-get.d.ts +2 -2
- package/dist/tools/coat-check-get.js +38 -33
- 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 +38 -33
- 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 +42 -37
- 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 +340 -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 +31 -26
- 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/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/package.json +4 -1
- package/workflow-bundle.js +97 -6
- package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
- package/dist/tools/helpers.d.ts +0 -21
- package/dist/tools/helpers.js +0 -25
package/dist/tools/ensemble.js
CHANGED
|
@@ -35,11 +35,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.DORMANT_THRESHOLD_MS = void 0;
|
|
37
37
|
exports.classifyDormancy = classifyDormancy;
|
|
38
|
-
exports.
|
|
38
|
+
exports.buildEnsembleTool = buildEnsembleTool;
|
|
39
39
|
const zod_1 = require("zod");
|
|
40
40
|
const os = __importStar(require("os"));
|
|
41
41
|
const resolve_1 = require("../activities/resolve");
|
|
42
|
-
const
|
|
42
|
+
const descriptor_1 = require("./descriptor");
|
|
43
43
|
const duration_1 = require("../utils/duration");
|
|
44
44
|
/**
|
|
45
45
|
* Default dormancy threshold (1 hour). Per #563: a `detached` player whose
|
|
@@ -79,75 +79,80 @@ function classifyDormancy(session, now, thresholdMs = exports.DORMANT_THRESHOLD_
|
|
|
79
79
|
}
|
|
80
80
|
return 'active';
|
|
81
81
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return (0, helpers_1.fail)(`Error listing workflows: ${(0, helpers_1.formatError)(err)}`);
|
|
99
|
-
}
|
|
100
|
-
// Apply scope filters
|
|
101
|
-
let ownGitRoot;
|
|
102
|
-
if (scope === 'repo') {
|
|
82
|
+
function buildEnsembleTool(client, config, getPlayerId, ownWorkflowId) {
|
|
83
|
+
return {
|
|
84
|
+
name: 'ensemble',
|
|
85
|
+
description: `Discover active Claude Code sessions in the "${config.ensemble}" ensemble. Returns player IDs, descriptions, and metadata. NOTE: returns tempo-registered players only — does NOT include Claude Code Agent-tool sub-agents (spawned via the Agent tool / subagent_type). Those are ephemeral and process-local; call TaskList separately to enumerate them. Tempo players are addressable via cue; Agent-tool sub-agents are not.`,
|
|
86
|
+
params: {
|
|
87
|
+
scope: zod_1.z.string().optional().describe('Filter scope: "machine" (same hostname), "repo" (same git root), "all" (default). All scopes are within the current ensemble.'),
|
|
88
|
+
// #563: dormancy filter. Default `show` preserves the pre-#563 listing
|
|
89
|
+
// (everything visible) but groups gone/long-detached players into a
|
|
90
|
+
// separate "Dormant" section. `hide` suppresses dormant entries
|
|
91
|
+
// entirely; `show-only` is the inverse, useful for cleanup workflows.
|
|
92
|
+
dormant: zod_1.z.enum(['show', 'hide', 'show-only']).optional().describe('Dormancy filter: "show" (default — group dormant in a separate section), "hide" (suppress dormant entries), "show-only" (only show dormant). A player is dormant when phase=gone, or phase=detached with no activity in the last hour.'),
|
|
93
|
+
},
|
|
94
|
+
handler: async (args) => {
|
|
95
|
+
const scope = (args.scope ?? 'all');
|
|
96
|
+
const dormantFilter = (args.dormant ?? 'show');
|
|
97
|
+
let sessions;
|
|
103
98
|
try {
|
|
104
|
-
|
|
105
|
-
const ownMeta = await ownHandle.query('getMetadata');
|
|
106
|
-
ownGitRoot = ownMeta.gitRoot;
|
|
99
|
+
sessions = await (0, resolve_1.scanEnsembleSessions)(client, config.ensemble);
|
|
107
100
|
}
|
|
108
|
-
catch {
|
|
109
|
-
|
|
101
|
+
catch (err) {
|
|
102
|
+
return (0, descriptor_1.fail)(`Error listing workflows: ${(0, descriptor_1.formatError)(err)}`);
|
|
110
103
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (scope === '
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
isYou: s.playerId === getPlayerId(),
|
|
123
|
-
dormancy: classifyDormancy(s, now),
|
|
124
|
-
}));
|
|
125
|
-
const active = enriched.filter((p) => p.dormancy === 'active');
|
|
126
|
-
const dormant = enriched.filter((p) => p.dormancy === 'dormant');
|
|
127
|
-
if (active.length === 0 && dormant.length === 0) {
|
|
128
|
-
return (0, helpers_1.ok)('No active sessions found.');
|
|
129
|
-
}
|
|
130
|
-
// #563 summary line — surface both counts so operators can see what's
|
|
131
|
-
// being hidden behind the dormant filter without re-running.
|
|
132
|
-
const summary = `**${config.ensemble}**: ${active.length} active, ${dormant.length} dormant`;
|
|
133
|
-
const sections = [summary];
|
|
134
|
-
const showActive = dormantFilter !== 'show-only';
|
|
135
|
-
const showDormant = dormantFilter !== 'hide';
|
|
136
|
-
if (showActive) {
|
|
137
|
-
if (active.length > 0) {
|
|
138
|
-
sections.push(`\n=== Active (${active.length}) ===\n`);
|
|
139
|
-
sections.push(active.map((p) => renderPlayerLine(p, now, false)).join('\n\n'));
|
|
104
|
+
// Apply scope filters
|
|
105
|
+
let ownGitRoot;
|
|
106
|
+
if (scope === 'repo') {
|
|
107
|
+
try {
|
|
108
|
+
const ownHandle = client.workflow.getHandle(ownWorkflowId);
|
|
109
|
+
const ownMeta = await ownHandle.query('getMetadata');
|
|
110
|
+
ownGitRoot = ownMeta.gitRoot;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Can't determine own git root — skip repo filtering
|
|
114
|
+
}
|
|
140
115
|
}
|
|
141
|
-
|
|
142
|
-
|
|
116
|
+
const scoped = sessions.filter((s) => {
|
|
117
|
+
if (scope === 'machine' && s.hostname !== os.hostname())
|
|
118
|
+
return false;
|
|
119
|
+
if (scope === 'repo' && ownGitRoot && s.gitRoot !== ownGitRoot)
|
|
120
|
+
return false;
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
const enriched = scoped.map((s) => ({
|
|
125
|
+
...s,
|
|
126
|
+
isYou: s.playerId === getPlayerId(),
|
|
127
|
+
dormancy: classifyDormancy(s, now),
|
|
128
|
+
}));
|
|
129
|
+
const active = enriched.filter((p) => p.dormancy === 'active');
|
|
130
|
+
const dormant = enriched.filter((p) => p.dormancy === 'dormant');
|
|
131
|
+
if (active.length === 0 && dormant.length === 0) {
|
|
132
|
+
return (0, descriptor_1.ok)('No active sessions found.');
|
|
143
133
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
sections
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
134
|
+
// #563 summary line — surface both counts so operators can see what's
|
|
135
|
+
// being hidden behind the dormant filter without re-running.
|
|
136
|
+
const summary = `**${config.ensemble}**: ${active.length} active, ${dormant.length} dormant`;
|
|
137
|
+
const sections = [summary];
|
|
138
|
+
const showActive = dormantFilter !== 'show-only';
|
|
139
|
+
const showDormant = dormantFilter !== 'hide';
|
|
140
|
+
if (showActive) {
|
|
141
|
+
if (active.length > 0) {
|
|
142
|
+
sections.push(`\n=== Active (${active.length}) ===\n`);
|
|
143
|
+
sections.push(active.map((p) => renderPlayerLine(p, now, false)).join('\n\n'));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
sections.push('\n=== Active (0) ===\n(none)');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (showDormant && dormant.length > 0) {
|
|
150
|
+
sections.push(`\n=== Dormant (${dormant.length}) — last seen >1h ago or gone ===\n`);
|
|
151
|
+
sections.push(dormant.map((p) => renderPlayerLine(p, now, true)).join('\n\n'));
|
|
152
|
+
}
|
|
153
|
+
return (0, descriptor_1.ok)(sections.join('\n'));
|
|
154
|
+
},
|
|
155
|
+
};
|
|
151
156
|
}
|
|
152
157
|
/**
|
|
153
158
|
* Render one player as the multi-line block historically emitted by
|
|
@@ -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 buildEvaluateGateTool(handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor;
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildEvaluateGateTool = buildEvaluateGateTool;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const descriptor_1 = require("./descriptor");
|
|
6
|
+
const signals_1 = require("../workflows/signals");
|
|
6
7
|
const validation_1 = require("../utils/validation");
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
8
|
+
function buildEvaluateGateTool(handle, getPlayerId) {
|
|
9
|
+
return {
|
|
10
|
+
name: 'evaluate_gate',
|
|
11
|
+
description: 'Mark one or more criteria on a quality gate as passed or failed. Conductor only.',
|
|
12
|
+
params: {
|
|
13
|
+
task: zod_1.z.string().max(validation_1.GATE_TASK_MAX).describe('The task name of the gate to evaluate'),
|
|
14
|
+
evaluations: zod_1.z.array(zod_1.z.object({
|
|
15
|
+
index: zod_1.z.number().int().min(0).describe('Zero-based index of the criterion'),
|
|
16
|
+
status: zod_1.z.enum(['passed', 'failed']).describe('Whether this criterion passed or failed'),
|
|
17
|
+
notes: zod_1.z.string().max(validation_1.GATE_NOTES_MAX).optional().describe('Optional notes explaining the evaluation'),
|
|
18
|
+
})).min(1).describe('List of criterion evaluations'),
|
|
19
|
+
},
|
|
20
|
+
handler: async (args) => {
|
|
21
|
+
const { task, evaluations } = args;
|
|
22
|
+
try {
|
|
23
|
+
await handle.signal(signals_1.evaluateGateCriteriaSignal, {
|
|
24
|
+
task,
|
|
25
|
+
evaluations,
|
|
26
|
+
evaluatedBy: getPlayerId(),
|
|
27
|
+
});
|
|
28
|
+
const summary = evaluations
|
|
29
|
+
.map((ev) => ` ${ev.index}: ${ev.status === 'passed' ? '\u2705' : '\u274c'} ${ev.status}${ev.notes ? ` — ${ev.notes}` : ''}`)
|
|
30
|
+
.join('\n');
|
|
31
|
+
return (0, descriptor_1.ok)(`Evaluated ${evaluations.length} criteria on gate **${task}**:\n${summary}`);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
return (0, descriptor_1.fail)(`Failed to evaluate gate: ${(0, descriptor_1.formatError)(err)}`);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
32
38
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
1
|
import { WorkflowHandle, Client } from '@temporalio/client';
|
|
3
2
|
import { Config } from '../config';
|
|
3
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
4
4
|
/**
|
|
5
5
|
* Return type is monomorphic by design: `{ content, savedAt, savedBy } | null`.
|
|
6
6
|
* ADR 0011 §Alternatives explicitly rejected a `list: boolean` flag because
|
|
@@ -10,4 +10,4 @@ import { Config } from '../config';
|
|
|
10
10
|
* playerStateKeys`; v2 can graduate a dedicated `list_state` MCP tool if
|
|
11
11
|
* telemetry shows real demand.
|
|
12
12
|
*/
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function buildFetchStateTool(client: Client, config: Config, handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildFetchStateTool = buildFetchStateTool;
|
|
4
4
|
/**
|
|
5
5
|
* `fetch_state` — read a saved state slot for self or any peer in the
|
|
6
6
|
* ensemble (#334 PR-1, ADR 0011).
|
|
@@ -26,7 +26,7 @@ exports.registerFetchStateTool = registerFetchStateTool;
|
|
|
26
26
|
const zod_1 = require("zod");
|
|
27
27
|
const resolve_1 = require("./resolve");
|
|
28
28
|
const signals_1 = require("../workflows/signals");
|
|
29
|
-
const
|
|
29
|
+
const descriptor_1 = require("./descriptor");
|
|
30
30
|
const validation_1 = require("../utils/validation");
|
|
31
31
|
/**
|
|
32
32
|
* Return type is monomorphic by design: `{ content, savedAt, savedBy } | null`.
|
|
@@ -37,42 +37,47 @@ const validation_1 = require("../utils/validation");
|
|
|
37
37
|
* playerStateKeys`; v2 can graduate a dedicated `list_state` MCP tool if
|
|
38
38
|
* telemetry shows real demand.
|
|
39
39
|
*/
|
|
40
|
-
function
|
|
41
|
-
|
|
40
|
+
function buildFetchStateTool(client, config, handle, getPlayerId) {
|
|
41
|
+
return {
|
|
42
|
+
name: 'fetch_state',
|
|
43
|
+
description: `Read a saved-state slot for yourself or a peer. Defaults to your own "${validation_1.PLAYER_STATE_DEFAULT_KEY}" slot.
|
|
42
44
|
|
|
43
|
-
Pass \`playerId\` to read a peer's slot (any player in the ensemble can read any other player's state — audit identity is recorded on each slot via \`savedBy\`). Returns a "(no state saved …)" message when the slot is empty.`,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Self-targeted reads use the cached own-session handle directly;
|
|
58
|
-
// peer reads go through `resolveSession` (the codebase convention,
|
|
59
|
-
// not a raw `client.workflow.getHandle`) so workflow-id format
|
|
60
|
-
// changes only need to be tracked in one place.
|
|
61
|
-
const targetHandle = targetId === getPlayerId()
|
|
62
|
-
? handle
|
|
63
|
-
: await (0, resolve_1.resolveSession)(client, config.ensemble, targetId);
|
|
64
|
-
if (!targetHandle) {
|
|
65
|
-
return (0, helpers_1.fail)(`No session found with name "${targetId}".`);
|
|
45
|
+
Pass \`playerId\` to read a peer's slot (any player in the ensemble can read any other player's state — audit identity is recorded on each slot via \`savedBy\`). Returns a "(no state saved …)" message when the slot is empty.`,
|
|
46
|
+
params: {
|
|
47
|
+
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}").`),
|
|
48
|
+
playerId: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional().describe('Target player name (default: self).'),
|
|
49
|
+
},
|
|
50
|
+
handler: async (args) => {
|
|
51
|
+
const { key, playerId } = args;
|
|
52
|
+
const targetId = playerId ?? getPlayerId();
|
|
53
|
+
// Validate any explicit playerId — `getPlayerId()` is trusted (set by
|
|
54
|
+
// the MCP server) and skips this check.
|
|
55
|
+
if (playerId !== undefined) {
|
|
56
|
+
const nameError = (0, validation_1.validatePlayerName)(playerId);
|
|
57
|
+
if (nameError)
|
|
58
|
+
return (0, descriptor_1.fail)(nameError);
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
try {
|
|
61
|
+
// Self-targeted reads use the cached own-session handle directly;
|
|
62
|
+
// peer reads go through `resolveSession` (the codebase convention,
|
|
63
|
+
// not a raw `client.workflow.getHandle`) so workflow-id format
|
|
64
|
+
// changes only need to be tracked in one place.
|
|
65
|
+
const targetHandle = targetId === getPlayerId()
|
|
66
|
+
? handle
|
|
67
|
+
: await (0, resolve_1.resolveSession)(client, config.ensemble, targetId);
|
|
68
|
+
if (!targetHandle) {
|
|
69
|
+
return (0, descriptor_1.fail)(`No session found with name "${targetId}".`);
|
|
70
|
+
}
|
|
71
|
+
const slotKey = key ?? validation_1.PLAYER_STATE_DEFAULT_KEY;
|
|
72
|
+
const result = await targetHandle.query(signals_1.playerStateQuery, { key: slotKey });
|
|
73
|
+
if (!result) {
|
|
74
|
+
return (0, descriptor_1.ok)(`(no state saved at slot "${slotKey}" for ${targetId})`);
|
|
75
|
+
}
|
|
76
|
+
return (0, descriptor_1.ok)(`Slot **"${slotKey}"** — saved by **${result.savedBy}** at ${result.savedAt}\n\n${result.content}`);
|
|
71
77
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
+
catch (err) {
|
|
79
|
+
return (0, descriptor_1.fail)(`Failed to fetch state: ${(0, descriptor_1.formatError)(err)}`);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
78
83
|
}
|
package/dist/tools/gates.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 buildGatesTool(handle: WorkflowHandle): TempoToolDescriptor;
|
package/dist/tools/gates.js
CHANGED
|
@@ -1,41 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildGatesTool = buildGatesTool;
|
|
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
|
-
|
|
20
|
-
|
|
7
|
+
function buildGatesTool(handle) {
|
|
8
|
+
return {
|
|
9
|
+
name: 'gates',
|
|
10
|
+
description: 'List quality gates and their status. Optionally filter by task name or status. Conductor only.',
|
|
11
|
+
params: {
|
|
12
|
+
task: zod_1.z.string().max(validation_1.GATE_TASK_MAX).optional().describe('Filter by specific task name'),
|
|
13
|
+
status: zod_1.z.enum(['open', 'passed', 'failed']).optional().describe('Filter by gate status'),
|
|
14
|
+
},
|
|
15
|
+
handler: async (args) => {
|
|
16
|
+
const { task, status } = args;
|
|
17
|
+
try {
|
|
18
|
+
const gates = await handle.query('qualityGates');
|
|
19
|
+
let filtered = gates;
|
|
20
|
+
if (task) {
|
|
21
|
+
filtered = filtered.filter((g) => g.task === task);
|
|
22
|
+
}
|
|
23
|
+
if (status) {
|
|
24
|
+
filtered = filtered.filter((g) => g.status === status);
|
|
25
|
+
}
|
|
26
|
+
if (filtered.length === 0) {
|
|
27
|
+
return (0, descriptor_1.ok)('No quality gates found matching the filter.');
|
|
28
|
+
}
|
|
29
|
+
const lines = filtered.map((g) => {
|
|
30
|
+
const icon = g.status === 'passed' ? '\u2705' : g.status === 'failed' ? '\u274c' : '\u23f3';
|
|
31
|
+
const criteriaLines = g.criteria.map((c, i) => {
|
|
32
|
+
const cIcon = c.status === 'passed' ? '\u2705' : c.status === 'failed' ? '\u274c' : '\u2b1c';
|
|
33
|
+
const evaluator = c.evaluatedBy ? ` (by ${c.evaluatedBy})` : '';
|
|
34
|
+
const notes = c.notes ? ` — ${c.notes}` : '';
|
|
35
|
+
return ` ${i}. ${cIcon} ${c.text}${evaluator}${notes}`;
|
|
36
|
+
});
|
|
37
|
+
return `${icon} **${g.task}** [${g.status}] (by ${g.createdBy}, ${g.createdAt})\n${criteriaLines.join('\n')}`;
|
|
38
|
+
});
|
|
39
|
+
return (0, descriptor_1.ok)(`${filtered.length} quality gate${filtered.length === 1 ? '' : 's'}:\n\n${lines.join('\n\n')}`);
|
|
21
40
|
}
|
|
22
|
-
|
|
23
|
-
return (0,
|
|
41
|
+
catch (err) {
|
|
42
|
+
return (0, descriptor_1.fail)(`Failed to query gates: ${(0, descriptor_1.formatError)(err)}`);
|
|
24
43
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const criteriaLines = g.criteria.map((c, i) => {
|
|
28
|
-
const cIcon = c.status === 'passed' ? '\u2705' : c.status === 'failed' ? '\u274c' : '\u2b1c';
|
|
29
|
-
const evaluator = c.evaluatedBy ? ` (by ${c.evaluatedBy})` : '';
|
|
30
|
-
const notes = c.notes ? ` — ${c.notes}` : '';
|
|
31
|
-
return ` ${i}. ${cIcon} ${c.text}${evaluator}${notes}`;
|
|
32
|
-
});
|
|
33
|
-
return `${icon} **${g.task}** [${g.status}] (by ${g.createdBy}, ${g.createdAt})\n${criteriaLines.join('\n')}`;
|
|
34
|
-
});
|
|
35
|
-
return (0, helpers_1.ok)(`${filtered.length} quality gate${filtered.length === 1 ? '' : 's'}:\n\n${lines.join('\n\n')}`);
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
return (0, helpers_1.fail)(`Failed to query gates: ${(0, helpers_1.formatError)(err)}`);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
44
|
+
},
|
|
45
|
+
};
|
|
41
46
|
}
|
package/dist/tools/hosts.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 buildHostsTool(client: Client, config: Config): TempoToolDescriptor;
|
package/dist/tools/hosts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildHostsTool = buildHostsTool;
|
|
4
4
|
/**
|
|
5
5
|
* `hosts` — MCP tool for surfacing daemons polling the Temporal namespace (#274).
|
|
6
6
|
*
|
|
@@ -16,25 +16,30 @@ exports.registerHostsTool = registerHostsTool;
|
|
|
16
16
|
* Thin wrapper — all the logic is in `listHosts`.
|
|
17
17
|
*/
|
|
18
18
|
const zod_1 = require("zod");
|
|
19
|
-
const
|
|
19
|
+
const descriptor_1 = require("./descriptor");
|
|
20
20
|
const hosts_1 = require("../utils/hosts");
|
|
21
21
|
const format_hosts_1 = require("../utils/format-hosts");
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
function buildHostsTool(client, config) {
|
|
23
|
+
return {
|
|
24
|
+
name: 'hosts',
|
|
25
|
+
description: 'Show all daemons polling this Temporal namespace, with their advertised capabilities. Returns liveness (live/stale), recruit-readiness, and each daemon\'s profile (default agent, available player types, platform) when it signaled one at boot. Read-only diagnostic.',
|
|
26
|
+
params: {
|
|
27
|
+
includeStale: zod_1.z.boolean().optional().describe('Include hosts not seen in the last minute (default: false).'),
|
|
28
|
+
force: zod_1.z.boolean().optional().describe('Bypass the 3-second result cache (default: false).'),
|
|
29
|
+
},
|
|
30
|
+
handler: async (args) => {
|
|
31
|
+
const { includeStale, force } = args;
|
|
32
|
+
try {
|
|
33
|
+
const hosts = await (0, hosts_1.listHosts)(client, {
|
|
34
|
+
force: Boolean(force),
|
|
35
|
+
namespace: config.temporalNamespace,
|
|
36
|
+
taskQueue: config.taskQueue,
|
|
37
|
+
});
|
|
38
|
+
return (0, descriptor_1.ok)((0, format_hosts_1.formatHostList)(hosts, { includeStale: Boolean(includeStale) }));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return (0, descriptor_1.fail)(`Failed to list hosts: ${(0, descriptor_1.formatError)(err)}`);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
40
45
|
}
|
package/dist/tools/listen.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 buildListenTool(handle: WorkflowHandle): TempoToolDescriptor;
|
package/dist/tools/listen.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
exports.buildListenTool = buildListenTool;
|
|
4
|
+
const descriptor_1 = require("./descriptor");
|
|
5
|
+
function buildListenTool(handle) {
|
|
6
|
+
return {
|
|
7
|
+
name: 'listen',
|
|
8
|
+
description: 'Check for pending messages from other sessions. Use this if you want to manually check for new messages.',
|
|
9
|
+
params: {},
|
|
10
|
+
handler: async () => {
|
|
11
|
+
try {
|
|
12
|
+
const messages = await handle.query('pendingMessages');
|
|
13
|
+
if (messages.length === 0) {
|
|
14
|
+
return (0, descriptor_1.ok)('No pending messages.');
|
|
15
|
+
}
|
|
16
|
+
// Mark messages as delivered
|
|
17
|
+
const ids = messages.map((m) => m.id);
|
|
18
|
+
await handle.signal('markDelivered', ids);
|
|
19
|
+
const lines = messages.map((m) => `**${m.from}** (${m.timestamp}):\n${m.text}`);
|
|
20
|
+
return (0, descriptor_1.ok)(lines.join('\n\n'));
|
|
11
21
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
catch (err) {
|
|
19
|
-
return (0, helpers_1.fail)(`Failed to check messages: ${(0, helpers_1.formatError)(err)}`);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
+
catch (err) {
|
|
23
|
+
return (0, descriptor_1.fail)(`Failed to check messages: ${(0, descriptor_1.formatError)(err)}`);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
22
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
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 { AgentType } from '../types';
|
|
5
|
-
|
|
4
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
5
|
+
export declare function buildLoadLineupTool(client: Client, config: Config, getPlayerId: () => string, ownAgentType?: AgentType, handle?: WorkflowHandle, setPlayerId?: (id: string) => void, isConductor?: boolean): TempoToolDescriptor;
|