agent-tempo 2.0.0-beta.1 → 2.0.0-beta.2
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/dashboard/package.json +1 -1
- package/dist/activities/maestro.js +3 -0
- package/dist/activities/outbox.js +12 -0
- package/dist/activities/resolve.d.ts +18 -7
- package/dist/activities/resolve.js +58 -46
- package/dist/adapters/claude-code/adapter.js +7 -0
- package/dist/cli/command-center-command.js +15 -1
- package/dist/cli/help-text.js +2 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +14 -0
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +38 -1
- package/dist/daemon.js +29 -0
- package/dist/http/event-types.d.ts +33 -0
- package/dist/http/server.js +10 -0
- package/dist/http/snapshot.js +3 -0
- package/dist/observability/nondeterminism-alarm.d.ts +113 -0
- package/dist/observability/nondeterminism-alarm.js +162 -0
- package/dist/server-tools.js +30 -29
- package/dist/server.js +42 -0
- package/dist/tools/action-guard.d.ts +23 -0
- package/dist/tools/action-guard.js +33 -0
- package/dist/tools/coat-check.d.ts +20 -0
- package/dist/tools/coat-check.js +129 -0
- package/dist/tools/gate.d.ts +13 -0
- package/dist/tools/gate.js +88 -0
- package/dist/tools/schedule.d.ts +18 -0
- package/dist/tools/schedule.js +93 -1
- package/dist/tools/stage.d.ts +17 -0
- package/dist/tools/stage.js +85 -1
- package/dist/tools/state.d.ts +14 -0
- package/dist/tools/state.js +95 -0
- package/dist/types.d.ts +39 -1
- package/dist/utils/orphan-guard.d.ts +37 -0
- package/dist/utils/orphan-guard.js +26 -0
- package/dist/utils/search-attributes.d.ts +1 -0
- package/dist/utils/search-attributes.js +9 -0
- package/dist/workflows/session.js +193 -43
- package/package.json +1 -1
- package/workflow-bundle.js +241 -45
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { WorkflowHandle } from '@temporalio/client';
|
|
2
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
3
|
+
/**
|
|
4
|
+
* Canonical `gate` tool. Dispatches on `action` (define | list); `evaluate_gate`
|
|
5
|
+
* is intentionally excluded (separate tool).
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildGateTool(handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor;
|
|
8
|
+
/**
|
|
9
|
+
* Legacy forwarding aliases — `quality_gate` → define, `gates` → list. Each
|
|
10
|
+
* keeps its exact original schema + handler; description gains a deprecation
|
|
11
|
+
* note. Explicit object literals (see §6 drift note).
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildGateAliasTools(handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor[];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildGateTool = buildGateTool;
|
|
4
|
+
exports.buildGateAliasTools = buildGateAliasTools;
|
|
5
|
+
/**
|
|
6
|
+
* `gate` — canonical multi-action quality-gate tool (#793 merge, PARTIAL).
|
|
7
|
+
*
|
|
8
|
+
* Merges the two gate-DEFINITION tools (`quality_gate` → define, `gates` → list)
|
|
9
|
+
* into ONE canonical tool. The canonical name is **net-new**, so `action` is
|
|
10
|
+
* REQUIRED.
|
|
11
|
+
*
|
|
12
|
+
* PARTIAL merge: `evaluate_gate` STAYS A SEPARATE TOOL and is NOT folded in.
|
|
13
|
+
* Rationale (brief §3⑤): evaluate is a distinct runtime *operation* — record
|
|
14
|
+
* pass/fail + notes against criteria — not CRUD on the gate *definition*. The
|
|
15
|
+
* define/list-the-gate vs act-on-the-gate line is the partial boundary.
|
|
16
|
+
*
|
|
17
|
+
* Legacy tools stay registered as forwarding aliases ({@link buildGateAliasTools});
|
|
18
|
+
* both paths reuse the legacy handler bodies. Conductor-only (gated in
|
|
19
|
+
* server-tools.ts alongside the other gate/stage tools).
|
|
20
|
+
*/
|
|
21
|
+
const zod_1 = require("zod");
|
|
22
|
+
const descriptor_1 = require("./descriptor");
|
|
23
|
+
const action_guard_1 = require("./action-guard");
|
|
24
|
+
const validation_1 = require("../utils/validation");
|
|
25
|
+
const quality_gate_1 = require("./quality-gate");
|
|
26
|
+
const gates_1 = require("./gates");
|
|
27
|
+
/**
|
|
28
|
+
* Canonical `gate` tool. Dispatches on `action` (define | list); `evaluate_gate`
|
|
29
|
+
* is intentionally excluded (separate tool).
|
|
30
|
+
*/
|
|
31
|
+
function buildGateTool(handle, getPlayerId) {
|
|
32
|
+
const define = (0, quality_gate_1.buildQualityGateTool)(handle, getPlayerId);
|
|
33
|
+
const list = (0, gates_1.buildGatesTool)(handle);
|
|
34
|
+
return {
|
|
35
|
+
name: 'gate',
|
|
36
|
+
description: 'Define and inspect quality gates for a task (conductor only). ' +
|
|
37
|
+
'action="define" sets/replaces a gate (task + criteria); ' +
|
|
38
|
+
'action="list" shows gates and per-criterion status (optional task/status filters). ' +
|
|
39
|
+
'To record pass/fail on criteria, use the separate `evaluate_gate` tool.',
|
|
40
|
+
params: {
|
|
41
|
+
action: zod_1.z.enum(['define', 'list']).describe('Which gate operation to perform'),
|
|
42
|
+
// define / list (filter):
|
|
43
|
+
task: zod_1.z.string().max(validation_1.GATE_TASK_MAX).optional().describe('define: the task this gate guards (required). list: optional task filter.'),
|
|
44
|
+
// define:
|
|
45
|
+
criteria: zod_1.z.array(zod_1.z.string().max(validation_1.GATE_CRITERION_TEXT_MAX)).min(1).max(validation_1.GATE_CRITERIA_MAX).optional().describe('define: the list of criteria that must pass (required)'),
|
|
46
|
+
// list:
|
|
47
|
+
status: zod_1.z.enum(['open', 'passed', 'failed']).optional().describe('list: optional status filter'),
|
|
48
|
+
},
|
|
49
|
+
handler: async (args) => {
|
|
50
|
+
const action = args.action;
|
|
51
|
+
switch (action) {
|
|
52
|
+
case 'define': {
|
|
53
|
+
const m = (0, action_guard_1.firstMissing)(args, ['task', 'criteria']);
|
|
54
|
+
if (m)
|
|
55
|
+
return (0, descriptor_1.fail)(`gate action="define" requires "${m}".`);
|
|
56
|
+
return define.handler(args);
|
|
57
|
+
}
|
|
58
|
+
case 'list':
|
|
59
|
+
return list.handler(args);
|
|
60
|
+
default:
|
|
61
|
+
return (0, descriptor_1.fail)(`Unknown gate action: ${String(action)}. Expected define | list.`);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Legacy forwarding aliases — `quality_gate` → define, `gates` → list. Each
|
|
68
|
+
* keeps its exact original schema + handler; description gains a deprecation
|
|
69
|
+
* note. Explicit object literals (see §6 drift note).
|
|
70
|
+
*/
|
|
71
|
+
function buildGateAliasTools(handle, getPlayerId) {
|
|
72
|
+
const define = (0, quality_gate_1.buildQualityGateTool)(handle, getPlayerId);
|
|
73
|
+
const list = (0, gates_1.buildGatesTool)(handle);
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
name: 'quality_gate',
|
|
77
|
+
description: 'DEPRECATED — use `gate` with action="define". ' + define.description,
|
|
78
|
+
params: define.params,
|
|
79
|
+
handler: define.handler,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'gates',
|
|
83
|
+
description: 'DEPRECATED — use `gate` with action="list". ' + list.description,
|
|
84
|
+
params: list.params,
|
|
85
|
+
handler: list.handler,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
package/dist/tools/schedule.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { Client } from '@temporalio/client';
|
|
2
2
|
import { Config } from '../config';
|
|
3
3
|
import { type TempoToolDescriptor } from './descriptor';
|
|
4
|
+
/**
|
|
5
|
+
* Canonical `schedule` tool (#793 merge) — **reused** name, so `action` defaults
|
|
6
|
+
* to `'create'` for backward-compat (existing callers omit `action` → the legacy
|
|
7
|
+
* bare-`schedule` create behaviour). Actions: `create | cancel | list`.
|
|
8
|
+
*
|
|
9
|
+
* The rich one-of timing validation lives in the create handler (reused
|
|
10
|
+
* verbatim); `cancel` reuses `unschedule`, `list` reuses `schedules`. The legacy
|
|
11
|
+
* `unschedule` / `schedules` names stay registered as forwarding aliases
|
|
12
|
+
* ({@link buildScheduleAliasTools}); there is no alias for `create` — the bare
|
|
13
|
+
* `schedule` IS create.
|
|
14
|
+
*/
|
|
4
15
|
export declare function buildScheduleTool(client: Client, config: Config, getPlayerId: () => string): TempoToolDescriptor;
|
|
16
|
+
/**
|
|
17
|
+
* Legacy forwarding aliases — `unschedule` → cancel, `schedules` → list. Each
|
|
18
|
+
* keeps its exact original schema + handler; description gains a deprecation
|
|
19
|
+
* note. No alias for `create` — the bare `schedule` IS create. Explicit object
|
|
20
|
+
* literals (see #793 brief §6 drift note).
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildScheduleAliasTools(client: Client, config: Config): TempoToolDescriptor[];
|
package/dist/tools/schedule.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildScheduleTool = buildScheduleTool;
|
|
4
|
+
exports.buildScheduleAliasTools = buildScheduleAliasTools;
|
|
4
5
|
const zod_1 = require("zod");
|
|
5
6
|
const croner_1 = require("croner");
|
|
6
7
|
const client_1 = require("@temporalio/client");
|
|
@@ -8,9 +9,18 @@ const config_1 = require("../config");
|
|
|
8
9
|
const duration_1 = require("../utils/duration");
|
|
9
10
|
const resolve_1 = require("./resolve");
|
|
10
11
|
const descriptor_1 = require("./descriptor");
|
|
12
|
+
const action_guard_1 = require("./action-guard");
|
|
13
|
+
const unschedule_1 = require("./unschedule");
|
|
14
|
+
const schedules_1 = require("./schedules");
|
|
11
15
|
const validation_1 = require("../utils/validation");
|
|
12
16
|
const log = (...args) => console.error('[agent-tempo:schedule]', ...args);
|
|
13
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Internal create-only descriptor (the legacy bare-`schedule` behaviour). Its
|
|
19
|
+
* `.handler` is reused verbatim by the canonical {@link buildScheduleTool}
|
|
20
|
+
* dispatch under `action="create"`. Not exported — the canonical tool is the
|
|
21
|
+
* public surface.
|
|
22
|
+
*/
|
|
23
|
+
function buildScheduleCreateTool(client, config, getPlayerId) {
|
|
14
24
|
return {
|
|
15
25
|
name: 'schedule',
|
|
16
26
|
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.',
|
|
@@ -155,3 +165,85 @@ function buildScheduleTool(client, config, getPlayerId) {
|
|
|
155
165
|
},
|
|
156
166
|
};
|
|
157
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Canonical `schedule` tool (#793 merge) — **reused** name, so `action` defaults
|
|
170
|
+
* to `'create'` for backward-compat (existing callers omit `action` → the legacy
|
|
171
|
+
* bare-`schedule` create behaviour). Actions: `create | cancel | list`.
|
|
172
|
+
*
|
|
173
|
+
* The rich one-of timing validation lives in the create handler (reused
|
|
174
|
+
* verbatim); `cancel` reuses `unschedule`, `list` reuses `schedules`. The legacy
|
|
175
|
+
* `unschedule` / `schedules` names stay registered as forwarding aliases
|
|
176
|
+
* ({@link buildScheduleAliasTools}); there is no alias for `create` — the bare
|
|
177
|
+
* `schedule` IS create.
|
|
178
|
+
*/
|
|
179
|
+
function buildScheduleTool(client, config, getPlayerId) {
|
|
180
|
+
const create = buildScheduleCreateTool(client, config, getPlayerId);
|
|
181
|
+
const cancel = (0, unschedule_1.buildUnscheduleTool)(client, config);
|
|
182
|
+
const list = (0, schedules_1.buildSchedulesTool)(client, config);
|
|
183
|
+
return {
|
|
184
|
+
name: 'schedule',
|
|
185
|
+
description: 'Schedule messages to players (one-shot, delay, recurring, or cron). ' +
|
|
186
|
+
'action="create" (default) schedules a message (name+message+target + one timing of at/delay/every/cron); ' +
|
|
187
|
+
'action="cancel" removes a named schedule; ' +
|
|
188
|
+
'action="list" shows all active schedules.',
|
|
189
|
+
params: {
|
|
190
|
+
action: zod_1.z.enum(['create', 'cancel', 'list']).optional().describe('Which schedule operation to perform (defaults to "create" when omitted)'),
|
|
191
|
+
// create:
|
|
192
|
+
name: zod_1.z.string().max(validation_1.SCHEDULE_NAME_MAX).optional().describe('create/cancel: unique name for this schedule'),
|
|
193
|
+
message: zod_1.z.string().max(validation_1.SCHEDULE_MESSAGE_MAX).optional().describe('create: the message to deliver'),
|
|
194
|
+
target: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional().describe('create: player to deliver to ("self" = this session)'),
|
|
195
|
+
at: zod_1.z.string().optional().describe('create: ISO datetime for one-shot delivery'),
|
|
196
|
+
delay: zod_1.z.string().optional().describe('create: duration until first delivery (e.g. "10m", "2h", "1d")'),
|
|
197
|
+
every: zod_1.z.string().optional().describe('create: recurring interval (e.g. "5m", "1h")'),
|
|
198
|
+
cron: zod_1.z.string().max(validation_1.CRON_EXPRESSION_MAX).optional().describe('create: cron expression. Mutually exclusive with at/delay/every.'),
|
|
199
|
+
timezone: zod_1.z.string().optional().describe('create: IANA timezone for cron evaluation (default UTC). Only with cron.'),
|
|
200
|
+
until: zod_1.z.string().optional().describe('create: ISO datetime — stop recurring after this time'),
|
|
201
|
+
count: zod_1.z.number().optional().describe('create: max number of deliveries for recurring schedules'),
|
|
202
|
+
},
|
|
203
|
+
handler: async (args) => {
|
|
204
|
+
const action = args.action ?? 'create';
|
|
205
|
+
switch (action) {
|
|
206
|
+
case 'create': {
|
|
207
|
+
const m = (0, action_guard_1.firstMissing)(args, ['name', 'message', 'target']);
|
|
208
|
+
if (m)
|
|
209
|
+
return (0, descriptor_1.fail)(`schedule action="create" requires "${m}".`);
|
|
210
|
+
return create.handler(args);
|
|
211
|
+
}
|
|
212
|
+
case 'cancel': {
|
|
213
|
+
const m = (0, action_guard_1.firstMissing)(args, ['name']);
|
|
214
|
+
if (m)
|
|
215
|
+
return (0, descriptor_1.fail)(`schedule action="cancel" requires "${m}".`);
|
|
216
|
+
return cancel.handler(args);
|
|
217
|
+
}
|
|
218
|
+
case 'list':
|
|
219
|
+
return list.handler(args);
|
|
220
|
+
default:
|
|
221
|
+
return (0, descriptor_1.fail)(`Unknown schedule action: ${String(action)}. Expected create | cancel | list.`);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Legacy forwarding aliases — `unschedule` → cancel, `schedules` → list. Each
|
|
228
|
+
* keeps its exact original schema + handler; description gains a deprecation
|
|
229
|
+
* note. No alias for `create` — the bare `schedule` IS create. Explicit object
|
|
230
|
+
* literals (see #793 brief §6 drift note).
|
|
231
|
+
*/
|
|
232
|
+
function buildScheduleAliasTools(client, config) {
|
|
233
|
+
const cancel = (0, unschedule_1.buildUnscheduleTool)(client, config);
|
|
234
|
+
const list = (0, schedules_1.buildSchedulesTool)(client, config);
|
|
235
|
+
return [
|
|
236
|
+
{
|
|
237
|
+
name: 'unschedule',
|
|
238
|
+
description: 'DEPRECATED — use `schedule` with action="cancel". ' + cancel.description,
|
|
239
|
+
params: cancel.params,
|
|
240
|
+
handler: cancel.handler,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'schedules',
|
|
244
|
+
description: 'DEPRECATED — use `schedule` with action="list". ' + list.description,
|
|
245
|
+
params: list.params,
|
|
246
|
+
handler: list.handler,
|
|
247
|
+
},
|
|
248
|
+
];
|
|
249
|
+
}
|
package/dist/tools/stage.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
import { WorkflowHandle } from '@temporalio/client';
|
|
2
2
|
import { type TempoToolDescriptor } from './descriptor';
|
|
3
|
+
/**
|
|
4
|
+
* Canonical `stage` tool (#793 merge) — **reused** name, so `action` defaults to
|
|
5
|
+
* `'create'` for backward-compat. Actions: `create | list | cancel` (all CRUD
|
|
6
|
+
* peers on the same StageEntry — see #793 brief §4). Conductor-only (gated in
|
|
7
|
+
* server-tools.ts).
|
|
8
|
+
*
|
|
9
|
+
* The legacy `stages` / `cancel_stage` names stay registered as forwarding
|
|
10
|
+
* aliases ({@link buildStageAliasTools}); there is no alias for `create` — the
|
|
11
|
+
* bare `stage` IS create.
|
|
12
|
+
*/
|
|
3
13
|
export declare function buildStageTool(handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor;
|
|
14
|
+
/**
|
|
15
|
+
* Legacy forwarding aliases — `stages` → list, `cancel_stage` → cancel. Each
|
|
16
|
+
* keeps its exact original schema + handler; description gains a deprecation
|
|
17
|
+
* note. No alias for `create` — the bare `stage` IS create. Explicit object
|
|
18
|
+
* literals (see #793 brief §6 drift note).
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildStageAliasTools(handle: WorkflowHandle): TempoToolDescriptor[];
|
package/dist/tools/stage.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildStageTool = buildStageTool;
|
|
4
|
+
exports.buildStageAliasTools = buildStageAliasTools;
|
|
4
5
|
const zod_1 = require("zod");
|
|
5
6
|
const descriptor_1 = require("./descriptor");
|
|
7
|
+
const action_guard_1 = require("./action-guard");
|
|
8
|
+
const stages_1 = require("./stages");
|
|
9
|
+
const cancel_stage_1 = require("./cancel-stage");
|
|
6
10
|
const validation_1 = require("../utils/validation");
|
|
7
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Internal create-only descriptor (the legacy bare-`stage` behaviour). Its
|
|
13
|
+
* `.handler` is reused verbatim by the canonical {@link buildStageTool} under
|
|
14
|
+
* `action="create"`. Not exported — the canonical tool is the public surface.
|
|
15
|
+
*/
|
|
16
|
+
function buildStageCreateTool(handle, getPlayerId) {
|
|
8
17
|
return {
|
|
9
18
|
name: 'stage',
|
|
10
19
|
description: 'Create a pipeline stage tracking N players. When all players report, a completion message is auto-injected. Conductor only.',
|
|
@@ -31,3 +40,78 @@ function buildStageTool(handle, getPlayerId) {
|
|
|
31
40
|
},
|
|
32
41
|
};
|
|
33
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Canonical `stage` tool (#793 merge) — **reused** name, so `action` defaults to
|
|
45
|
+
* `'create'` for backward-compat. Actions: `create | list | cancel` (all CRUD
|
|
46
|
+
* peers on the same StageEntry — see #793 brief §4). Conductor-only (gated in
|
|
47
|
+
* server-tools.ts).
|
|
48
|
+
*
|
|
49
|
+
* The legacy `stages` / `cancel_stage` names stay registered as forwarding
|
|
50
|
+
* aliases ({@link buildStageAliasTools}); there is no alias for `create` — the
|
|
51
|
+
* bare `stage` IS create.
|
|
52
|
+
*/
|
|
53
|
+
function buildStageTool(handle, getPlayerId) {
|
|
54
|
+
const create = buildStageCreateTool(handle, getPlayerId);
|
|
55
|
+
const list = (0, stages_1.buildStagesTool)(handle);
|
|
56
|
+
const cancel = (0, cancel_stage_1.buildCancelStageTool)(handle);
|
|
57
|
+
return {
|
|
58
|
+
name: 'stage',
|
|
59
|
+
description: 'Track a fan-out/fan-in pipeline stage across N players (conductor only). ' +
|
|
60
|
+
'action="create" (default) opens a stage (name + players[, failurePolicy]); ' +
|
|
61
|
+
'action="list" shows all stages and per-player report status; ' +
|
|
62
|
+
'action="cancel" cancels a named stage.',
|
|
63
|
+
params: {
|
|
64
|
+
action: zod_1.z.enum(['create', 'list', 'cancel']).optional().describe('Which stage operation to perform (defaults to "create" when omitted)'),
|
|
65
|
+
// create / cancel:
|
|
66
|
+
name: zod_1.z.string().max(validation_1.STAGE_NAME_MAX).optional().describe('create/cancel: the stage name'),
|
|
67
|
+
// create:
|
|
68
|
+
players: zod_1.z.array(zod_1.z.string().regex(validation_1.PLAYER_NAME_REGEX)).min(1).max(validation_1.STAGE_PLAYERS_MAX).optional().describe('create: player names to track in this stage'),
|
|
69
|
+
failurePolicy: zod_1.z.enum(['halt', 'continue']).optional().describe('create: "halt" (default) fails the stage on a blocker; "continue" waits for all players.'),
|
|
70
|
+
},
|
|
71
|
+
handler: async (args) => {
|
|
72
|
+
const action = args.action ?? 'create';
|
|
73
|
+
switch (action) {
|
|
74
|
+
case 'create': {
|
|
75
|
+
const m = (0, action_guard_1.firstMissing)(args, ['name', 'players']);
|
|
76
|
+
if (m)
|
|
77
|
+
return (0, descriptor_1.fail)(`stage action="create" requires "${m}".`);
|
|
78
|
+
return create.handler(args);
|
|
79
|
+
}
|
|
80
|
+
case 'list':
|
|
81
|
+
return list.handler(args);
|
|
82
|
+
case 'cancel': {
|
|
83
|
+
const m = (0, action_guard_1.firstMissing)(args, ['name']);
|
|
84
|
+
if (m)
|
|
85
|
+
return (0, descriptor_1.fail)(`stage action="cancel" requires "${m}".`);
|
|
86
|
+
return cancel.handler(args);
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return (0, descriptor_1.fail)(`Unknown stage action: ${String(action)}. Expected create | list | cancel.`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Legacy forwarding aliases — `stages` → list, `cancel_stage` → cancel. Each
|
|
96
|
+
* keeps its exact original schema + handler; description gains a deprecation
|
|
97
|
+
* note. No alias for `create` — the bare `stage` IS create. Explicit object
|
|
98
|
+
* literals (see #793 brief §6 drift note).
|
|
99
|
+
*/
|
|
100
|
+
function buildStageAliasTools(handle) {
|
|
101
|
+
const list = (0, stages_1.buildStagesTool)(handle);
|
|
102
|
+
const cancel = (0, cancel_stage_1.buildCancelStageTool)(handle);
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
name: 'stages',
|
|
106
|
+
description: 'DEPRECATED — use `stage` with action="list". ' + list.description,
|
|
107
|
+
params: list.params,
|
|
108
|
+
handler: list.handler,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'cancel_stage',
|
|
112
|
+
description: 'DEPRECATED — use `stage` with action="cancel". ' + cancel.description,
|
|
113
|
+
params: cancel.params,
|
|
114
|
+
handler: cancel.handler,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Client, WorkflowHandle } from '@temporalio/client';
|
|
2
|
+
import { Config } from '../config';
|
|
3
|
+
import { type TempoToolDescriptor } from './descriptor';
|
|
4
|
+
/**
|
|
5
|
+
* Canonical `state` tool. Dispatches on `action`; per-action required fields are
|
|
6
|
+
* runtime-guarded.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildStateTool(client: Client, config: Config, handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor;
|
|
9
|
+
/**
|
|
10
|
+
* Legacy forwarding aliases — `save_state` / `fetch_state` / `clear_state`.
|
|
11
|
+
* Each keeps its exact original schema + handler; description gains a
|
|
12
|
+
* deprecation note. Explicit object literals (see §6 drift note).
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildStateAliasTools(client: Client, config: Config, handle: WorkflowHandle, getPlayerId: () => string): TempoToolDescriptor[];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildStateTool = buildStateTool;
|
|
4
|
+
exports.buildStateAliasTools = buildStateAliasTools;
|
|
5
|
+
/**
|
|
6
|
+
* `state` — canonical multi-action player saveable-state tool (#793 merge).
|
|
7
|
+
*
|
|
8
|
+
* Merges the three legacy tools (`save_state` / `fetch_state` / `clear_state`)
|
|
9
|
+
* into ONE canonical tool with a flat `{ action, ...optional fields }` shape
|
|
10
|
+
* (see docs/design/793-tool-family-merge-brief.md §2). The canonical name is
|
|
11
|
+
* **net-new**, so `action` is REQUIRED.
|
|
12
|
+
*
|
|
13
|
+
* Legacy tools stay registered as forwarding aliases
|
|
14
|
+
* ({@link buildStateAliasTools}); both paths reuse the legacy handler bodies, so
|
|
15
|
+
* behaviour (owner-only write, self-or-peer read, idempotent clear) is identical.
|
|
16
|
+
*/
|
|
17
|
+
const zod_1 = require("zod");
|
|
18
|
+
const descriptor_1 = require("./descriptor");
|
|
19
|
+
const action_guard_1 = require("./action-guard");
|
|
20
|
+
const validation_1 = require("../utils/validation");
|
|
21
|
+
const save_state_1 = require("./save-state");
|
|
22
|
+
const fetch_state_1 = require("./fetch-state");
|
|
23
|
+
const clear_state_1 = require("./clear-state");
|
|
24
|
+
/**
|
|
25
|
+
* Canonical `state` tool. Dispatches on `action`; per-action required fields are
|
|
26
|
+
* runtime-guarded.
|
|
27
|
+
*/
|
|
28
|
+
function buildStateTool(client, config, handle, getPlayerId) {
|
|
29
|
+
const save = (0, save_state_1.buildSaveStateTool)(handle, getPlayerId);
|
|
30
|
+
const fetch = (0, fetch_state_1.buildFetchStateTool)(client, config, handle, getPlayerId);
|
|
31
|
+
const clear = (0, clear_state_1.buildClearStateTool)(handle);
|
|
32
|
+
return {
|
|
33
|
+
name: 'state',
|
|
34
|
+
description: 'Curated per-player state slots that survive restart (you choose what context persists). ' +
|
|
35
|
+
'action="save" writes your own slot (content[, key]); ' +
|
|
36
|
+
'action="fetch" reads a slot for yourself or a peer (key/playerId, defaults to your own "main"); ' +
|
|
37
|
+
'action="clear" empties one of your slots. save/clear are owner-only.',
|
|
38
|
+
params: {
|
|
39
|
+
action: zod_1.z.enum(['save', 'fetch', 'clear']).describe('Which state operation to perform'),
|
|
40
|
+
// save:
|
|
41
|
+
content: zod_1.z.string().min(1).max(validation_1.PLAYER_STATE_CONTENT_MAX).optional().describe('save: the state body to store (≤32 KiB)'),
|
|
42
|
+
// save / fetch / clear:
|
|
43
|
+
key: zod_1.z.string().regex(validation_1.PLAYER_STATE_KEY_REGEX).max(validation_1.PLAYER_STATE_KEY_MAX).optional().describe('Slot key (defaults to "main")'),
|
|
44
|
+
// fetch:
|
|
45
|
+
playerId: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional().describe('fetch: peer to read from (defaults to yourself)'),
|
|
46
|
+
},
|
|
47
|
+
handler: async (args) => {
|
|
48
|
+
const action = args.action;
|
|
49
|
+
switch (action) {
|
|
50
|
+
case 'save': {
|
|
51
|
+
const m = (0, action_guard_1.firstMissing)(args, ['content']);
|
|
52
|
+
if (m)
|
|
53
|
+
return (0, descriptor_1.fail)(`state action="save" requires "${m}".`);
|
|
54
|
+
return save.handler(args);
|
|
55
|
+
}
|
|
56
|
+
case 'fetch':
|
|
57
|
+
return fetch.handler(args);
|
|
58
|
+
case 'clear':
|
|
59
|
+
return clear.handler(args);
|
|
60
|
+
default:
|
|
61
|
+
return (0, descriptor_1.fail)(`Unknown state action: ${String(action)}. Expected save | fetch | clear.`);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Legacy forwarding aliases — `save_state` / `fetch_state` / `clear_state`.
|
|
68
|
+
* Each keeps its exact original schema + handler; description gains a
|
|
69
|
+
* deprecation note. Explicit object literals (see §6 drift note).
|
|
70
|
+
*/
|
|
71
|
+
function buildStateAliasTools(client, config, handle, getPlayerId) {
|
|
72
|
+
const save = (0, save_state_1.buildSaveStateTool)(handle, getPlayerId);
|
|
73
|
+
const fetch = (0, fetch_state_1.buildFetchStateTool)(client, config, handle, getPlayerId);
|
|
74
|
+
const clear = (0, clear_state_1.buildClearStateTool)(handle);
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
name: 'save_state',
|
|
78
|
+
description: 'DEPRECATED — use `state` with action="save". ' + save.description,
|
|
79
|
+
params: save.params,
|
|
80
|
+
handler: save.handler,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'fetch_state',
|
|
84
|
+
description: 'DEPRECATED — use `state` with action="fetch". ' + fetch.description,
|
|
85
|
+
params: fetch.params,
|
|
86
|
+
handler: fetch.handler,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'clear_state',
|
|
90
|
+
description: 'DEPRECATED — use `state` with action="clear". ' + clear.description,
|
|
91
|
+
params: clear.params,
|
|
92
|
+
handler: clear.handler,
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -57,6 +57,19 @@ export interface AdapterDescriptor {
|
|
|
57
57
|
blocksOnLLMTurn: boolean;
|
|
58
58
|
/** Heartbeat cadence in milliseconds. Interactive: 60_000; SDK: 30_000. */
|
|
59
59
|
heartbeatMs: number;
|
|
60
|
+
/**
|
|
61
|
+
* #704 — True iff the adapter can park pre-attach on a blocking launch-time
|
|
62
|
+
* dialog (the interactive `claude-code` dev-channels acknowledgment, which
|
|
63
|
+
* requires a human click). When true, the booting attach-timeout watchdog is
|
|
64
|
+
* DISARMED for this adapter: a recruit waiting on a human is not a hang the
|
|
65
|
+
* watchdog should false-kill (an operator-away false-kill is worse than the
|
|
66
|
+
* hang — see docs/design/704-is-demo-companion-brief.md). Headless adapters
|
|
67
|
+
* (no dialog) omit this / set false → the watchdog arms. The property is the
|
|
68
|
+
* contract, not the adapter name: a future interactive adapter inherits the
|
|
69
|
+
* disarm by setting it. Interactive arming returns once #890 dissolves the
|
|
70
|
+
* dialog. Optional; absent ⇒ falsy ⇒ armed.
|
|
71
|
+
*/
|
|
72
|
+
canBlockOnDialog?: boolean;
|
|
60
73
|
}
|
|
61
74
|
/**
|
|
62
75
|
* Reason an attachment detached or was detached. Used for audit and UX messaging.
|
|
@@ -85,7 +98,7 @@ export interface AdapterDescriptor {
|
|
|
85
98
|
* When not opted in, the base class fires the reason as terminal just like any
|
|
86
99
|
* other non-recoverable lease loss.
|
|
87
100
|
*/
|
|
88
|
-
export type DetachReason = 'user-stop' | 'restart' | 'heartbeat-timeout' | 'superseded' | 'agent-exited' | 'spawn-failed' | 'destroy' | 'force' | 'reconnect-exhausted' | 'continued-as-new';
|
|
101
|
+
export type DetachReason = 'user-stop' | 'restart' | 'heartbeat-timeout' | 'superseded' | 'agent-exited' | 'spawn-failed' | 'destroy' | 'force' | 'reconnect-exhausted' | 'continued-as-new' | 'boot-timeout';
|
|
89
102
|
/**
|
|
90
103
|
* Workflow-emitted directive to the attached adapter. Delivered via {@link AttachmentInfo}
|
|
91
104
|
* polling (no reverse-RPC surface in Temporal).
|
|
@@ -178,6 +191,23 @@ export interface SessionMetadata {
|
|
|
178
191
|
playerTypeDescription?: string;
|
|
179
192
|
/** Player ID of who recruited this player. */
|
|
180
193
|
recruitedBy?: string;
|
|
194
|
+
/**
|
|
195
|
+
* #704 — resolved at spawn time from the adapter descriptor's
|
|
196
|
+
* {@link AdapterDescriptor.canBlockOnDialog}. Threaded onto durable metadata
|
|
197
|
+
* (and carried across continueAsNew) so the session workflow can decide
|
|
198
|
+
* whether to ARM the booting attach-timeout watchdog WITHOUT importing the
|
|
199
|
+
* client-side adapter registry (workflows are sandboxed). True ⇒ interactive
|
|
200
|
+
* adapter that can park on a launch dialog ⇒ watchdog DISARMED. Absent/false
|
|
201
|
+
* ⇒ headless ⇒ ARMED.
|
|
202
|
+
*/
|
|
203
|
+
canBlockOnDialog?: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* #704 — optional override (ms) for the booting attach-timeout deadline,
|
|
206
|
+
* resolved from `AGENT_TEMPO_BOOTING_DEADLINE_MS` at spawn time. Workflows
|
|
207
|
+
* can't read process.env, so the override rides durable metadata. Absent ⇒
|
|
208
|
+
* the workflow's 180s default applies.
|
|
209
|
+
*/
|
|
210
|
+
bootingDeadlineMs?: number;
|
|
181
211
|
/** Worktree path if this session was spawned in an isolated worktree. */
|
|
182
212
|
worktreePath?: string;
|
|
183
213
|
/**
|
|
@@ -874,6 +904,14 @@ export interface MaestroPlayerInfo {
|
|
|
874
904
|
* surfaces that want to show "last active 12s ago" per player.
|
|
875
905
|
*/
|
|
876
906
|
lastActivityAt?: string;
|
|
907
|
+
/**
|
|
908
|
+
* #886 slice 2 — `true` when this row came from a DEGRADED observation scan
|
|
909
|
+
* (the workflow was listed but its metadata extraction failed, so non-identity
|
|
910
|
+
* fields are best-effort blanks). Surfaces to the dashboard/board so the
|
|
911
|
+
* player renders as "uncertain" rather than vanishing — preventing roster
|
|
912
|
+
* flapping (contra #777). Absent / `false` on healthy rows.
|
|
913
|
+
*/
|
|
914
|
+
degraded?: boolean;
|
|
877
915
|
}
|
|
878
916
|
/** A message relayed through the global Maestro for dashboard visibility. */
|
|
879
917
|
export interface MaestroRelayMessage {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #704 Item 1b — late-orphan self-tombstone discriminator (pure).
|
|
3
|
+
*
|
|
4
|
+
* A recruited process can launch LONG after its recruit was cancelled (a slow /
|
|
5
|
+
* wedged cold start). When it finally boots and reaches the workflow-start site
|
|
6
|
+
* (`server.ts`), this predicate decides whether it is an ORPHAN of a cancelled
|
|
7
|
+
* recruit (→ self-exit, do not re-register) or a legitimate (re-)start (→ proceed).
|
|
8
|
+
*
|
|
9
|
+
* The discriminator is the **running-run × close-reason PAIR**:
|
|
10
|
+
* - If a RUNNING run exists for the derived id, this process is NOT an orphan —
|
|
11
|
+
* every managed re-creation (recruit / restart / migrate / up) pre-creates a
|
|
12
|
+
* RUNNING run before its process boots, so a legit reuse is seen as RUNNING and
|
|
13
|
+
* simply attaches via `USE_EXISTING`. Never self-exit.
|
|
14
|
+
* - Otherwise, the most-recent run is CLOSED. Self-exit ONLY when it closed with a
|
|
15
|
+
* typed tombstone reason (`destroyed` | `boot-timeout`) within a generous TTL.
|
|
16
|
+
* The TTL bounds a stale tombstone so a much-later legit manual reuse of the
|
|
17
|
+
* same name isn't blocked forever (the observed orphan spawn delay was ~100min).
|
|
18
|
+
*
|
|
19
|
+
* Pure + dependency-free so it unit-tests without a live Temporal env; `server.ts`
|
|
20
|
+
* supplies the `describe()`-derived inputs.
|
|
21
|
+
*/
|
|
22
|
+
export interface OrphanTombstoneInput {
|
|
23
|
+
/** `describe().status.name` — e.g. `'RUNNING'`, `'COMPLETED'`, `'TERMINATED'`. */
|
|
24
|
+
statusName: string;
|
|
25
|
+
/** `describe().memo?.[MEMO_KEYS.closeReason]` — the typed close reason, if any. */
|
|
26
|
+
closeReason: unknown;
|
|
27
|
+
/** `describe().closeTime?.getTime() ?? 0` — ms epoch of run close, `0` if open/unknown. */
|
|
28
|
+
closeTimeMs: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns true iff the booting process should self-exit as a late orphan.
|
|
32
|
+
*
|
|
33
|
+
* @param input the `describe()`-derived run state for the derived workflow id
|
|
34
|
+
* @param ttlMs how long a tombstone is honored (ms); `> 0`
|
|
35
|
+
* @param nowMs current wall-clock ms (`Date.now()`)
|
|
36
|
+
*/
|
|
37
|
+
export declare function shouldSelfExitAsOrphan(input: OrphanTombstoneInput, ttlMs: number, nowMs: number): boolean;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shouldSelfExitAsOrphan = shouldSelfExitAsOrphan;
|
|
4
|
+
/** Close reasons that tombstone a derived id against orphan re-registration. */
|
|
5
|
+
const TOMBSTONE_REASONS = new Set(['destroyed', 'boot-timeout']);
|
|
6
|
+
/**
|
|
7
|
+
* Returns true iff the booting process should self-exit as a late orphan.
|
|
8
|
+
*
|
|
9
|
+
* @param input the `describe()`-derived run state for the derived workflow id
|
|
10
|
+
* @param ttlMs how long a tombstone is honored (ms); `> 0`
|
|
11
|
+
* @param nowMs current wall-clock ms (`Date.now()`)
|
|
12
|
+
*/
|
|
13
|
+
function shouldSelfExitAsOrphan(input, ttlMs, nowMs) {
|
|
14
|
+
// A running run means a legit (re-)creation already exists — attach, don't exit.
|
|
15
|
+
if (input.statusName === 'RUNNING')
|
|
16
|
+
return false;
|
|
17
|
+
// Only a typed tombstone close-reason qualifies.
|
|
18
|
+
if (typeof input.closeReason !== 'string' || !TOMBSTONE_REASONS.has(input.closeReason)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
// Bound staleness: a tombstone older than the TTL is released so a legit
|
|
22
|
+
// much-later reuse of the same derived id isn't blocked.
|
|
23
|
+
if (!(input.closeTimeMs > 0))
|
|
24
|
+
return false;
|
|
25
|
+
return nowMs - input.closeTimeMs <= ttlMs;
|
|
26
|
+
}
|
|
@@ -61,6 +61,7 @@ export declare const MEMO_KEYS: {
|
|
|
61
61
|
readonly agentType: "AgentTempoAgentType";
|
|
62
62
|
readonly gitBranch: "AgentTempoGitBranch";
|
|
63
63
|
readonly protocol: "AgentTempoProtocol";
|
|
64
|
+
readonly closeReason: "AgentTempoCloseReason";
|
|
64
65
|
};
|
|
65
66
|
/**
|
|
66
67
|
* Escape a value for use in Temporal visibility query strings — strips
|
|
@@ -43,6 +43,15 @@ exports.MEMO_KEYS = {
|
|
|
43
43
|
// Memo, not a search attribute: avoids a 6th Keyword (the #747 SA-diet keeps
|
|
44
44
|
// us at 5/10) and needs no operator registration. See constants.PROTOCOL_VERSION.
|
|
45
45
|
protocol: 'AgentTempoProtocol',
|
|
46
|
+
// #704 — typed terminal close-reason stamped on a workflow's memo when it
|
|
47
|
+
// COMPLETEs via `destroy` (`'destroyed'`) or the booting attach-timeout
|
|
48
|
+
// watchdog (`'boot-timeout'`). Survives completion and is readable via
|
|
49
|
+
// `describe().memo`. The bootstrap orphan-guard (`server.ts`) reads it to
|
|
50
|
+
// self-tombstone a late-launching orphan process whose run was cancelled,
|
|
51
|
+
// discriminated by the running-run × close-reason PAIR. A string memo (not a
|
|
52
|
+
// search attribute): avoids a 6th Keyword (the #747 SA-diet keeps us at 5/10)
|
|
53
|
+
// and needs no operator registration.
|
|
54
|
+
closeReason: 'AgentTempoCloseReason',
|
|
46
55
|
};
|
|
47
56
|
/**
|
|
48
57
|
* Escape a value for use in Temporal visibility query strings — strips
|