agent-tempo 1.4.1 → 1.4.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/client/core.js +18 -0
- package/dist/client/interface.d.ts +15 -0
- package/dist/http/writes.d.ts +1 -1
- package/dist/http/writes.js +14 -0
- package/dist/pi/mission-control/actions.d.ts +1 -0
- package/dist/pi/mission-control/actions.js +3 -0
- package/dist/pi/mission-control/extension.js +5 -7
- package/dist/pi/pi-types.d.ts +17 -6
- package/dist/pi/render-tools.d.ts +5 -5
- package/dist/pi/render-tools.js +16 -7
- package/dist/tui/index.js +1 -0
- package/package.json +1 -1
package/dashboard/package.json
CHANGED
package/dist/client/core.js
CHANGED
|
@@ -741,6 +741,24 @@ function createTempoClientCore(client, opts = {}) {
|
|
|
741
741
|
entryId,
|
|
742
742
|
};
|
|
743
743
|
},
|
|
744
|
+
async reset(ensemble, playerId, reason) {
|
|
745
|
+
// H5b: HTTP-route counterpart to the `reset` MCP tool (D14). Enqueues the
|
|
746
|
+
// SAME `'reset'` outbox entry on the maestro outbox — no new wire. D14:
|
|
747
|
+
// reset is clean-wipe only (always `fresh: true`); `invokerPlayerId:
|
|
748
|
+
// 'maestro'` is the operator identity, surfaced to the wiped session as
|
|
749
|
+
// `requestedBy`. The caller (HTTP handler) ensures the maestro exists.
|
|
750
|
+
const maestroId = (0, config_1.sessionWorkflowId)(ensemble, 'maestro');
|
|
751
|
+
const h = handle(maestroId);
|
|
752
|
+
const entry = {
|
|
753
|
+
type: 'reset',
|
|
754
|
+
targetPlayerId: playerId,
|
|
755
|
+
invokerPlayerId: 'maestro',
|
|
756
|
+
fresh: true,
|
|
757
|
+
...(reason !== undefined ? { reason } : {}),
|
|
758
|
+
};
|
|
759
|
+
const entryId = await h.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
|
|
760
|
+
return { playerId, entryId };
|
|
761
|
+
},
|
|
744
762
|
async detach(ensemble, playerId, deadlineMs = 5_000) {
|
|
745
763
|
const maestroId = (0, config_1.sessionWorkflowId)(ensemble, 'maestro');
|
|
746
764
|
const h = handle(maestroId);
|
|
@@ -97,6 +97,12 @@ export interface RestartClientResult {
|
|
|
97
97
|
/** Outbox entry id; callers can poll `submitOutbox` history or `outboxQuery` for status. */
|
|
98
98
|
entryId: string;
|
|
99
99
|
}
|
|
100
|
+
export interface ResetClientResult {
|
|
101
|
+
/** Player the reset (clean-wipe) was queued for. */
|
|
102
|
+
playerId: string;
|
|
103
|
+
/** Outbox entry id; callers can poll `submitOutbox` history or `outboxQuery` for status. */
|
|
104
|
+
entryId: string;
|
|
105
|
+
}
|
|
100
106
|
/** Per-target outcome returned by `shutdown`. */
|
|
101
107
|
export interface EnsembleShutdownDetail {
|
|
102
108
|
playerId: string;
|
|
@@ -294,6 +300,15 @@ export interface TempoClientCore {
|
|
|
294
300
|
release(ensemble: string, playerId?: string): Promise<ReleaseClientResult>;
|
|
295
301
|
/** PR-D: Restart a player — §8.2 algorithm. Works on any non-`gone` phase. */
|
|
296
302
|
restart(ensemble: string, playerId: string, opts?: RestartClientOpts): Promise<RestartClientResult>;
|
|
303
|
+
/**
|
|
304
|
+
* H5b: Clean-wipe a player's conversation context (D14 reset) via the maestro
|
|
305
|
+
* outbox — the HTTP-route counterpart to the `reset` MCP tool. Always
|
|
306
|
+
* `fresh: true`; the operator identity (`invokerPlayerId: 'maestro'`) is
|
|
307
|
+
* surfaced to the wiped session. Reuses the existing reset machinery — no new
|
|
308
|
+
* wire. The caller must ensure the maestro session exists first (the daemon
|
|
309
|
+
* HTTP handler does, mirroring `cue`).
|
|
310
|
+
*/
|
|
311
|
+
reset(ensemble: string, playerId: string, reason?: string): Promise<ResetClientResult>;
|
|
297
312
|
/** PR-D: Gracefully detach a player's adapter. Workflow survives in `detached`. */
|
|
298
313
|
detach(ensemble: string, playerId: string, deadlineMs?: number): Promise<void>;
|
|
299
314
|
/**
|
package/dist/http/writes.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export { WRITE_BODY_MAX };
|
|
|
42
42
|
* mutations. Bodies are uniform `{ playerId, reason? }` (plus per-action
|
|
43
43
|
* extras); the ensemble lives in the URL.
|
|
44
44
|
*/
|
|
45
|
-
export declare const WRITE_ACTIONS: readonly ["cue", "pause", "play", "release", "recruit", "restart", "destroy", "detach", "recall"];
|
|
45
|
+
export declare const WRITE_ACTIONS: readonly ["cue", "pause", "play", "release", "recruit", "restart", "reset", "destroy", "detach", "recall"];
|
|
46
46
|
export type WriteAction = (typeof WRITE_ACTIONS)[number];
|
|
47
47
|
/** Type guard — narrows an arbitrary string to a known `WriteAction`. */
|
|
48
48
|
export declare function isWriteAction(s: string): s is WriteAction;
|
package/dist/http/writes.js
CHANGED
|
@@ -26,6 +26,7 @@ exports.WRITE_ACTIONS = [
|
|
|
26
26
|
'release',
|
|
27
27
|
'recruit',
|
|
28
28
|
'restart',
|
|
29
|
+
'reset',
|
|
29
30
|
'destroy',
|
|
30
31
|
'detach',
|
|
31
32
|
'recall',
|
|
@@ -62,6 +63,7 @@ async function handleWriteRoute(req, res, client, ensemble, action) {
|
|
|
62
63
|
case 'release': return await handleRelease(res, client, ensemble, body);
|
|
63
64
|
case 'recruit': return await handleRecruit(res, client, ensemble, body);
|
|
64
65
|
case 'restart': return await handleRestart(res, client, ensemble, body);
|
|
66
|
+
case 'reset': return await handleReset(res, client, ensemble, body);
|
|
65
67
|
case 'destroy': return await handleDestroy(res, client, ensemble, body);
|
|
66
68
|
case 'detach': return await handleDetach(res, client, ensemble, body);
|
|
67
69
|
case 'recall': return await handleRecall(res, client, ensemble, body);
|
|
@@ -169,6 +171,18 @@ async function handleRestart(res, client, ensemble, body) {
|
|
|
169
171
|
const result = await client.restart(ensemble, playerId);
|
|
170
172
|
(0, responses_1.jsonResponse)(res, 202, result);
|
|
171
173
|
}
|
|
174
|
+
async function handleReset(res, client, ensemble, body) {
|
|
175
|
+
const playerId = (0, body_1.requirePlayerId)(res, body);
|
|
176
|
+
if (!playerId)
|
|
177
|
+
return;
|
|
178
|
+
const reason = (0, body_1.stringField)(body, 'reason');
|
|
179
|
+
// Reset (D14 clean-wipe) enqueues on the maestro outbox — ensure the maestro
|
|
180
|
+
// exists first (like `cue`) so a reset before it's up doesn't 500. Idempotent
|
|
181
|
+
// (USE_EXISTING). 202 + the queued entry id, mirroring `restart`.
|
|
182
|
+
await client.ensureMaestroSession(ensemble);
|
|
183
|
+
const result = await client.reset(ensemble, playerId, reason);
|
|
184
|
+
(0, responses_1.jsonResponse)(res, 202, result);
|
|
185
|
+
}
|
|
172
186
|
async function handleDestroy(res, client, ensemble, body) {
|
|
173
187
|
const playerId = (0, body_1.requirePlayerId)(res, body);
|
|
174
188
|
if (!playerId)
|
|
@@ -42,6 +42,7 @@ export declare class MissionControlActions {
|
|
|
42
42
|
play(release?: boolean): Promise<ActionResult>;
|
|
43
43
|
restart(playerId: string, reason?: string): Promise<ActionResult>;
|
|
44
44
|
destroy(playerId: string, reason?: string): Promise<ActionResult>;
|
|
45
|
+
reset(playerId: string, reason?: string): Promise<ActionResult>;
|
|
45
46
|
gateArm(playerId: string): Promise<ActionResult>;
|
|
46
47
|
gateDisarm(playerId: string): Promise<ActionResult>;
|
|
47
48
|
gateDecide(playerId: string, requestId: string, decision: 'allow' | 'deny'): Promise<ActionResult>;
|
|
@@ -84,6 +84,9 @@ class MissionControlActions {
|
|
|
84
84
|
destroy(playerId, reason) {
|
|
85
85
|
return this.post(`/v1/ensembles/${this.ens()}/destroy`, { playerId, ...(reason ? { reason } : {}) });
|
|
86
86
|
}
|
|
87
|
+
reset(playerId, reason) {
|
|
88
|
+
return this.post(`/v1/ensembles/${this.ens()}/reset`, { playerId, ...(reason ? { reason } : {}) });
|
|
89
|
+
}
|
|
87
90
|
// ── Operator gate plane (T3) ──
|
|
88
91
|
gateArm(playerId) {
|
|
89
92
|
return this.post(`/v1/players/${this.player(playerId)}/gate-arm`, {});
|
|
@@ -161,15 +161,13 @@ class Controller {
|
|
|
161
161
|
this.report(ctx, `destroy ${p}`, await this.actions.destroy(p, reason || undefined));
|
|
162
162
|
}
|
|
163
163
|
async cmdReset(args, ctx) {
|
|
164
|
-
|
|
164
|
+
// H5b: real POST /v1/ensembles/:e/reset (D14 clean-wipe) — mirrors cmdRestart.
|
|
165
|
+
const [p, reason] = Controller.splitFirst(args);
|
|
165
166
|
if (!p) {
|
|
166
|
-
this.notify(ctx, 'Usage: /reset <player>');
|
|
167
|
+
this.notify(ctx, 'Usage: /reset <player> [reason]');
|
|
167
168
|
return;
|
|
168
169
|
}
|
|
169
|
-
|
|
170
|
-
// rather than silently fail. Wiring a POST /v1/ensembles/:e/reset is a daemon
|
|
171
|
-
// follow-up (flagged to the conductor).
|
|
172
|
-
this.notify(ctx, `reset ${p}: not available over the daemon HTTP surface yet (MCP/outbox only). Flagged for a daemon route.`);
|
|
170
|
+
this.report(ctx, `reset ${p}`, await this.actions.reset(p, reason || undefined));
|
|
173
171
|
}
|
|
174
172
|
async cmdArm(args, ctx) {
|
|
175
173
|
const [p, mode] = Controller.splitFirst(args);
|
|
@@ -320,7 +318,7 @@ function createMissionControlExtension(deps = {}) {
|
|
|
320
318
|
pi.registerCommand('play', { description: 'Resume the ensemble (/play [release])', handler: (a, ctx) => ctrl.cmdPlay(a, ctx) });
|
|
321
319
|
pi.registerCommand('restart', { description: 'Restart a player (/restart <player> [reason])', handler: (a, ctx) => ctrl.cmdRestart(a, ctx) });
|
|
322
320
|
pi.registerCommand('destroy', { description: 'Destroy a player (/destroy <player> [reason])', handler: (a, ctx) => ctrl.cmdDestroy(a, ctx) });
|
|
323
|
-
pi.registerCommand('reset', { description: 'Clean-wipe a player (/reset <player>)', handler: (a, ctx) => ctrl.cmdReset(a, ctx) });
|
|
321
|
+
pi.registerCommand('reset', { description: 'Clean-wipe a player (/reset <player> [reason])', handler: (a, ctx) => ctrl.cmdReset(a, ctx) });
|
|
324
322
|
pi.registerCommand('arm', { description: 'Arm/disarm the operator gate for a player (/arm <player> [off])', handler: (a, ctx) => ctrl.cmdArm(a, ctx) });
|
|
325
323
|
pi.registerCommand('gate', { description: 'Decide a gate request for the tailed player (/gate <reqId> allow|deny)', handler: (a, ctx) => ctrl.cmdGate(a, ctx) });
|
|
326
324
|
};
|
package/dist/pi/pi-types.d.ts
CHANGED
|
@@ -179,14 +179,25 @@ export interface PiToolCallResult {
|
|
|
179
179
|
reason?: string;
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
182
|
-
* Pi tool result
|
|
183
|
-
* (
|
|
184
|
-
* is
|
|
182
|
+
* Pi tool result — a Pi-free structural mirror of the real `AgentToolResult`
|
|
183
|
+
* (#653, 1.4.2). The Phase-0 `{ output, isError }` guess was WRONG: Pi's real
|
|
184
|
+
* `AgentToolResult` is `{ content: (TextContent|ImageContent)[]; details; terminate? }`
|
|
185
|
+
* (pi-agent-core types.d.ts:305), so `{ output, isError }` made every native
|
|
186
|
+
* agent-tempo tool result malformed for a Pi model (no `content[]` — the
|
|
187
|
+
* RESULT-shape mirror of #651's INPUT-shape bug).
|
|
188
|
+
*
|
|
189
|
+
* We only ever emit TEXT content. Errors are NOT content-encoded — they are
|
|
190
|
+
* THROWN from `execute` (Pi's sanctioned error path; the loop catches → sets
|
|
191
|
+
* isError + folds the message into content). So this success-shape carries no
|
|
192
|
+
* error flag. See `toPiResult` / `renderToPi` in render-tools.ts.
|
|
185
193
|
*/
|
|
186
194
|
export interface PiToolResult {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
content: Array<{
|
|
196
|
+
type: 'text';
|
|
197
|
+
text: string;
|
|
198
|
+
}>;
|
|
199
|
+
details: Record<string, unknown>;
|
|
200
|
+
terminate?: boolean;
|
|
190
201
|
}
|
|
191
202
|
/**
|
|
192
203
|
* Pi native tool definition. `parameters` is a TypeBox schema (NOT zod) — see
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { TempoToolDescriptor, TempoToolResult } from '../tools/descriptor';
|
|
2
2
|
import type { ExtensionAPI, PiToolResult } from './pi-types';
|
|
3
3
|
/**
|
|
4
|
-
* Map a neutral {@link TempoToolResult} onto Pi's `AgentToolResult`
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `
|
|
4
|
+
* Map a SUCCESSFUL neutral {@link TempoToolResult} onto Pi's `AgentToolResult`
|
|
5
|
+
* shape (#653, 1.4.2): the text becomes a single text-content block. ERRORS are
|
|
6
|
+
* NOT mapped here — they are thrown from `execute` (Pi's sanctioned error path),
|
|
7
|
+
* so callers must check `r.isError` BEFORE calling this. `details` is an empty
|
|
8
|
+
* object (we surface no structured details); `terminate` is left unset.
|
|
9
9
|
*/
|
|
10
10
|
export declare function toPiResult(r: TempoToolResult): PiToolResult;
|
|
11
11
|
/**
|
package/dist/pi/render-tools.js
CHANGED
|
@@ -24,14 +24,14 @@ exports.renderToPi = renderToPi;
|
|
|
24
24
|
*/
|
|
25
25
|
const zod_to_typebox_1 = require("./zod-to-typebox");
|
|
26
26
|
/**
|
|
27
|
-
* Map a neutral {@link TempoToolResult} onto Pi's `AgentToolResult`
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* `
|
|
27
|
+
* Map a SUCCESSFUL neutral {@link TempoToolResult} onto Pi's `AgentToolResult`
|
|
28
|
+
* shape (#653, 1.4.2): the text becomes a single text-content block. ERRORS are
|
|
29
|
+
* NOT mapped here — they are thrown from `execute` (Pi's sanctioned error path),
|
|
30
|
+
* so callers must check `r.isError` BEFORE calling this. `details` is an empty
|
|
31
|
+
* object (we surface no structured details); `terminate` is left unset.
|
|
32
32
|
*/
|
|
33
33
|
function toPiResult(r) {
|
|
34
|
-
return
|
|
34
|
+
return { content: [{ type: 'text', text: r.text }], details: {} };
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* Register every descriptor onto the Pi extension API. The TypeBox schema is
|
|
@@ -50,7 +50,16 @@ function renderToPi(pi, descriptors) {
|
|
|
50
50
|
// toolCallId string (1st) and hand `params` to the descriptor handler.
|
|
51
51
|
// (Passing the 1st positional was the v1.4.0 arg-order bug: handlers got the
|
|
52
52
|
// toolCallId string instead of params.) See PiToolDefinition.execute.
|
|
53
|
-
execute: async (_toolCallId, params) =>
|
|
53
|
+
execute: async (_toolCallId, params) => {
|
|
54
|
+
const r = await d.handler(params);
|
|
55
|
+
// Pi's sanctioned error path (#653): THROW on failure — the agent loop
|
|
56
|
+
// catches it → createErrorToolResult (message → content) + isError:true.
|
|
57
|
+
// Content-encoding an error WITHOUT throwing would make the model think
|
|
58
|
+
// the tool SUCCEEDED (Pi types.d.ts:327). So errors never reach toPiResult.
|
|
59
|
+
if (r.isError)
|
|
60
|
+
throw new Error(r.text);
|
|
61
|
+
return toPiResult(r);
|
|
62
|
+
},
|
|
54
63
|
});
|
|
55
64
|
}
|
|
56
65
|
}
|
package/dist/tui/index.js
CHANGED