agent-tempo 1.7.0 → 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/CLAUDE.md +10 -13
- package/README.md +21 -15
- package/dashboard/dist/assets/index-BbR7zIdK.js.map +1 -1
- package/dashboard/package.json +1 -1
- package/dist/activities/hard-terminate.d.ts +2 -2
- package/dist/activities/hard-terminate.js +3 -9
- package/dist/activities/maestro.d.ts +8 -13
- package/dist/activities/maestro.js +3 -10
- package/dist/activities/outbox.js +16 -2
- package/dist/activities/resolve.d.ts +18 -7
- package/dist/activities/resolve.js +58 -46
- package/dist/adapters/base.js +3 -0
- package/dist/adapters/claude-code/adapter.js +7 -0
- package/dist/cli/command-center-command.js +15 -1
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.js +79 -11
- package/dist/cli/daemon-command.js +19 -33
- package/dist/cli/help-text.js +9 -7
- package/dist/cli/home-command.d.ts +26 -0
- package/dist/cli/home-command.js +156 -0
- package/dist/cli/removed-verbs.d.ts +0 -4
- package/dist/cli/removed-verbs.js +23 -12
- package/dist/cli/sa-preflight.d.ts +0 -14
- package/dist/cli/sa-preflight.js +1 -17
- package/dist/cli/startup.d.ts +1 -10
- package/dist/cli/startup.js +0 -40
- package/dist/cli.js +34 -80
- package/dist/client/core.js +13 -15
- package/dist/client/index.d.ts +2 -1
- package/dist/client/interface.d.ts +5 -14
- package/dist/client/with-spawn.d.ts +9 -4
- package/dist/client/with-spawn.js +7 -10
- package/dist/config.d.ts +14 -13
- package/dist/config.js +14 -0
- package/dist/constants.d.ts +50 -0
- package/dist/constants.js +60 -1
- package/dist/daemon.js +81 -0
- package/dist/http/auth.d.ts +3 -21
- package/dist/http/auth.js +10 -31
- package/dist/http/event-types.d.ts +38 -4
- package/dist/http/orphans.d.ts +3 -3
- package/dist/http/orphans.js +3 -3
- package/dist/http/server.d.ts +0 -5
- package/dist/http/server.js +79 -29
- package/dist/http/snapshot.js +3 -0
- package/dist/http/writes.d.ts +1 -1
- package/dist/http/writes.js +25 -2
- package/dist/observability/nondeterminism-alarm.d.ts +113 -0
- package/dist/observability/nondeterminism-alarm.js +162 -0
- package/dist/palette/index.d.ts +6 -7
- package/dist/palette/index.js +6 -7
- package/dist/pi/mission-control/actions.d.ts +67 -1
- package/dist/pi/mission-control/actions.js +61 -0
- package/dist/pi/mission-control/extension.d.ts +77 -0
- package/dist/pi/mission-control/extension.js +171 -0
- package/dist/pi/workflow-client.d.ts +14 -16
- package/dist/pi/workflow-client.js +18 -44
- 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 +54 -7
- package/dist/upgrade/boot-guard.d.ts +95 -0
- package/dist/upgrade/boot-guard.js +88 -0
- package/dist/upgrade/from-upgrade.d.ts +95 -0
- package/dist/upgrade/from-upgrade.js +330 -0
- package/dist/upgrade/phase-engine.js +4 -1
- package/dist/utils/duration.d.ts +3 -6
- package/dist/utils/duration.js +3 -6
- package/dist/utils/format-hosts.d.ts +1 -1
- package/dist/utils/format-hosts.js +0 -2
- package/dist/utils/grpc-shutdown-guard.js +0 -4
- package/dist/utils/hosts.d.ts +4 -10
- package/dist/utils/hosts.js +12 -28
- package/dist/utils/orphan-guard.d.ts +37 -0
- package/dist/utils/orphan-guard.js +26 -0
- package/dist/utils/search-attributes.d.ts +28 -18
- package/dist/utils/search-attributes.js +48 -18
- package/dist/utils/visibility-deadline.d.ts +16 -0
- package/dist/utils/visibility-deadline.js +16 -0
- package/dist/workflows/maestro-signals.d.ts +0 -13
- package/dist/workflows/maestro-signals.js +6 -14
- package/dist/workflows/maestro.js +27 -32
- package/dist/workflows/scheduler.js +7 -1
- package/dist/workflows/session.js +237 -153
- package/dist/workflows/signals.d.ts +9 -1
- package/dist/workflows/signals.js +8 -6
- package/package.json +1 -6
- package/workflow-bundle.js +478 -225
- package/dist/cli/legacy-migration.d.ts +0 -35
- package/dist/cli/legacy-migration.js +0 -341
- package/dist/client/ensure-conductor-spawned.d.ts +0 -35
- package/dist/client/ensure-conductor-spawned.js +0 -48
- package/dist/tui/App.d.ts +0 -85
- package/dist/tui/App.js +0 -1791
- package/dist/tui/bootstrap-types.d.ts +0 -46
- package/dist/tui/bootstrap-types.js +0 -7
- package/dist/tui/commands.d.ts +0 -71
- package/dist/tui/commands.js +0 -1375
- package/dist/tui/components/ChatView.d.ts +0 -35
- package/dist/tui/components/ChatView.js +0 -54
- package/dist/tui/components/CommandPalette.d.ts +0 -21
- package/dist/tui/components/CommandPalette.js +0 -67
- package/dist/tui/components/ConversationStream.d.ts +0 -114
- package/dist/tui/components/ConversationStream.js +0 -307
- package/dist/tui/components/CreateEnsembleWizard.d.ts +0 -19
- package/dist/tui/components/CreateEnsembleWizard.js +0 -223
- package/dist/tui/components/DestroyConfirmModal.d.ts +0 -17
- package/dist/tui/components/DestroyConfirmModal.js +0 -62
- package/dist/tui/components/ErrorView.d.ts +0 -31
- package/dist/tui/components/ErrorView.js +0 -129
- package/dist/tui/components/HomeView.d.ts +0 -54
- package/dist/tui/components/HomeView.js +0 -306
- package/dist/tui/components/LoadLineupModal.d.ts +0 -18
- package/dist/tui/components/LoadLineupModal.js +0 -79
- package/dist/tui/components/MainView.d.ts +0 -21
- package/dist/tui/components/MainView.js +0 -107
- package/dist/tui/components/NewEnsembleModal.d.ts +0 -9
- package/dist/tui/components/NewEnsembleModal.js +0 -73
- package/dist/tui/components/Picker.d.ts +0 -23
- package/dist/tui/components/Picker.js +0 -70
- package/dist/tui/components/PlayerDetailView.d.ts +0 -26
- package/dist/tui/components/PlayerDetailView.js +0 -118
- package/dist/tui/components/PromptArea.d.ts +0 -50
- package/dist/tui/components/PromptArea.js +0 -303
- package/dist/tui/components/RecruitWizard.d.ts +0 -17
- package/dist/tui/components/RecruitWizard.js +0 -221
- package/dist/tui/components/RestoreConfirmModal.d.ts +0 -18
- package/dist/tui/components/RestoreConfirmModal.js +0 -71
- package/dist/tui/components/ScheduleWizard.d.ts +0 -19
- package/dist/tui/components/ScheduleWizard.js +0 -259
- package/dist/tui/components/Splash.d.ts +0 -23
- package/dist/tui/components/Splash.js +0 -221
- package/dist/tui/components/StatusBar.d.ts +0 -48
- package/dist/tui/components/StatusBar.js +0 -128
- package/dist/tui/components/StatusOverlay.d.ts +0 -15
- package/dist/tui/components/StatusOverlay.js +0 -76
- package/dist/tui/components/TitleBar.d.ts +0 -10
- package/dist/tui/components/TitleBar.js +0 -21
- package/dist/tui/index.d.ts +0 -15
- package/dist/tui/index.js +0 -162
- package/dist/tui/ink-context.d.ts +0 -18
- package/dist/tui/ink-context.js +0 -59
- package/dist/tui/ink-loader.d.ts +0 -26
- package/dist/tui/ink-loader.js +0 -42
- package/dist/tui/removed-commands.d.ts +0 -9
- package/dist/tui/removed-commands.js +0 -22
- package/dist/tui/sse-handler.d.ts +0 -52
- package/dist/tui/sse-handler.js +0 -157
- package/dist/tui/store.d.ts +0 -598
- package/dist/tui/store.js +0 -753
- package/dist/tui/utils/format.d.ts +0 -56
- package/dist/tui/utils/format.js +0 -155
- package/dist/tui/utils/fullscreen.d.ts +0 -23
- package/dist/tui/utils/fullscreen.js +0 -71
- package/dist/tui/utils/history.d.ts +0 -10
- package/dist/tui/utils/history.js +0 -85
- package/dist/tui/utils/platform.d.ts +0 -45
- package/dist/tui/utils/platform.js +0 -258
- package/dist/tui/utils/theme.d.ts +0 -21
- package/dist/tui/utils/theme.js +0 -24
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnswerEntry } from '../../types';
|
|
1
|
+
import type { AnswerEntry, HostInfo, AttachmentInfo, ScheduleEntry, Message, SentMessage } from '../../types';
|
|
2
2
|
import type { EnsembleSummary } from '../../client/interface';
|
|
3
3
|
/** Env var holding the daemon admin (T3) token (writes + inner tail). */
|
|
4
4
|
export declare const ADMIN_TOKEN_ENV = "AGENT_TEMPO_HTTP_ADMIN_TOKEN";
|
|
@@ -104,9 +104,75 @@ export declare class MissionControlActions {
|
|
|
104
104
|
ok: false;
|
|
105
105
|
error: string;
|
|
106
106
|
}>;
|
|
107
|
+
/**
|
|
108
|
+
* List connected hosts (`GET /v1/hosts`, Tier-1 read) — the cluster host
|
|
109
|
+
* registry + freshness + capability profile, the same payload the dashboard
|
|
110
|
+
* `/hosts` view and the CLI `agent-tempo hosts` consume. NOT ensemble-scoped
|
|
111
|
+
* (the host registry is cluster-global), so it ignores the bound ensemble.
|
|
112
|
+
*/
|
|
113
|
+
listHosts(): Promise<{
|
|
114
|
+
ok: true;
|
|
115
|
+
hosts: HostInfo[];
|
|
116
|
+
} | {
|
|
117
|
+
ok: false;
|
|
118
|
+
error: string;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* A player's V2 attachment-lifecycle snapshot (`GET /v1/ensembles/:e/attachment-info`,
|
|
122
|
+
* Tier-1 read). Thin shim over `client.attachmentInfo` — the same payload the
|
|
123
|
+
* MCP `attachment_info` tool and CLI `attachment-info` render via the shared
|
|
124
|
+
* `formatAttachmentInfoForDisplay`. The player is a `?playerId=` query param.
|
|
125
|
+
*/
|
|
126
|
+
attachmentInfo(playerId: string): Promise<{
|
|
127
|
+
ok: true;
|
|
128
|
+
info: AttachmentInfo;
|
|
129
|
+
} | {
|
|
130
|
+
ok: false;
|
|
131
|
+
error: string;
|
|
132
|
+
}>;
|
|
133
|
+
/**
|
|
134
|
+
* A player's full message timeline (received + sent). Hits the existing
|
|
135
|
+
* `POST /v1/ensembles/:e/recall` route with `{ full: true }` so the daemon
|
|
136
|
+
* returns the raw `{ received, sent }` arrays (the default count-only shape
|
|
137
|
+
* stays for the dashboard). Thin shim over `client.recall`; the caller feeds
|
|
138
|
+
* the result through the shared `buildTimeline`/`formatRecall` helpers. POST
|
|
139
|
+
* (not GET) because recall groups with the per-player action surface (T2).
|
|
140
|
+
*/
|
|
141
|
+
recall(playerId: string): Promise<{
|
|
142
|
+
ok: true;
|
|
143
|
+
received: Message[];
|
|
144
|
+
sent: SentMessage[];
|
|
145
|
+
} | {
|
|
146
|
+
ok: false;
|
|
147
|
+
error: string;
|
|
148
|
+
}>;
|
|
149
|
+
/**
|
|
150
|
+
* Active schedules for the bound ensemble (`GET /v1/ensembles/:e/schedules`,
|
|
151
|
+
* Tier-1 read). Thin shim over `client.getSchedules` — the same `ScheduleEntry[]`
|
|
152
|
+
* the MCP `schedules` tool and CLI render. Empty when no scheduler is running.
|
|
153
|
+
*/
|
|
154
|
+
listSchedules(): Promise<{
|
|
155
|
+
ok: true;
|
|
156
|
+
schedules: ScheduleEntry[];
|
|
157
|
+
} | {
|
|
158
|
+
ok: false;
|
|
159
|
+
error: string;
|
|
160
|
+
}>;
|
|
107
161
|
cue(to: string, message: string): Promise<ActionResult>;
|
|
108
162
|
pause(): Promise<ActionResult>;
|
|
109
163
|
play(release?: boolean): Promise<ActionResult>;
|
|
164
|
+
/**
|
|
165
|
+
* Free HELD players via the dedicated `POST /v1/ensembles/:e/release` route
|
|
166
|
+
* (#742 gap 8 — `/go`). Optional `playerId` releases ONE held player; omitted
|
|
167
|
+
* releases all held in the ensemble. Distinct from `play(release:true)`, which
|
|
168
|
+
* ALSO clears the PAUSE axis — `/go` frees holds without touching pause.
|
|
169
|
+
*/
|
|
170
|
+
release(playerId?: string): Promise<ActionResult>;
|
|
171
|
+
/**
|
|
172
|
+
* Cancel a named schedule (`POST /v1/ensembles/:e/unschedule`, #742 gap —
|
|
173
|
+
* `/unschedule` & `/schedule delete`). Thin shim over `client.cancelSchedule`.
|
|
174
|
+
*/
|
|
175
|
+
unschedule(name: string): Promise<ActionResult>;
|
|
110
176
|
restart(playerId: string, reason?: string): Promise<ActionResult>;
|
|
111
177
|
destroy(playerId: string, reason?: string): Promise<ActionResult>;
|
|
112
178
|
reset(playerId: string, reason?: string): Promise<ActionResult>;
|
|
@@ -211,6 +211,51 @@ class MissionControlActions {
|
|
|
211
211
|
const res = await this.getJson('/v1/ensembles');
|
|
212
212
|
return res.ok ? { ok: true, ensembles: res.data } : res;
|
|
213
213
|
}
|
|
214
|
+
// ── Read surface: hosts (#742 gap 5) ──
|
|
215
|
+
/**
|
|
216
|
+
* List connected hosts (`GET /v1/hosts`, Tier-1 read) — the cluster host
|
|
217
|
+
* registry + freshness + capability profile, the same payload the dashboard
|
|
218
|
+
* `/hosts` view and the CLI `agent-tempo hosts` consume. NOT ensemble-scoped
|
|
219
|
+
* (the host registry is cluster-global), so it ignores the bound ensemble.
|
|
220
|
+
*/
|
|
221
|
+
async listHosts() {
|
|
222
|
+
const res = await this.getJson('/v1/hosts');
|
|
223
|
+
return res.ok ? { ok: true, hosts: res.data } : res;
|
|
224
|
+
}
|
|
225
|
+
// ── Read surface: attachment-info (#742 gap — /attachment-info) ──
|
|
226
|
+
/**
|
|
227
|
+
* A player's V2 attachment-lifecycle snapshot (`GET /v1/ensembles/:e/attachment-info`,
|
|
228
|
+
* Tier-1 read). Thin shim over `client.attachmentInfo` — the same payload the
|
|
229
|
+
* MCP `attachment_info` tool and CLI `attachment-info` render via the shared
|
|
230
|
+
* `formatAttachmentInfoForDisplay`. The player is a `?playerId=` query param.
|
|
231
|
+
*/
|
|
232
|
+
async attachmentInfo(playerId) {
|
|
233
|
+
const res = await this.getJson(`/v1/ensembles/${this.ens()}/attachment-info?playerId=${encodeURIComponent(playerId)}`);
|
|
234
|
+
return res.ok ? { ok: true, info: res.data } : res;
|
|
235
|
+
}
|
|
236
|
+
// ── Read surface: recall (#742 gap — /recall) ──
|
|
237
|
+
/**
|
|
238
|
+
* A player's full message timeline (received + sent). Hits the existing
|
|
239
|
+
* `POST /v1/ensembles/:e/recall` route with `{ full: true }` so the daemon
|
|
240
|
+
* returns the raw `{ received, sent }` arrays (the default count-only shape
|
|
241
|
+
* stays for the dashboard). Thin shim over `client.recall`; the caller feeds
|
|
242
|
+
* the result through the shared `buildTimeline`/`formatRecall` helpers. POST
|
|
243
|
+
* (not GET) because recall groups with the per-player action surface (T2).
|
|
244
|
+
*/
|
|
245
|
+
async recall(playerId) {
|
|
246
|
+
const res = await this.postJson(`/v1/ensembles/${this.ens()}/recall`, { playerId, full: true });
|
|
247
|
+
return res.ok ? { ok: true, received: res.data.received, sent: res.data.sent } : res;
|
|
248
|
+
}
|
|
249
|
+
// ── Read surface: schedules (#742 gap — /schedule list) ──
|
|
250
|
+
/**
|
|
251
|
+
* Active schedules for the bound ensemble (`GET /v1/ensembles/:e/schedules`,
|
|
252
|
+
* Tier-1 read). Thin shim over `client.getSchedules` — the same `ScheduleEntry[]`
|
|
253
|
+
* the MCP `schedules` tool and CLI render. Empty when no scheduler is running.
|
|
254
|
+
*/
|
|
255
|
+
async listSchedules() {
|
|
256
|
+
const res = await this.getJson(`/v1/ensembles/${this.ens()}/schedules`);
|
|
257
|
+
return res.ok ? { ok: true, schedules: res.data } : res;
|
|
258
|
+
}
|
|
214
259
|
// ── Ensemble write surface (T2) ──
|
|
215
260
|
cue(to, message) {
|
|
216
261
|
// #822 — parse the deliverability hint so the board warns on a detached/gone
|
|
@@ -223,6 +268,22 @@ class MissionControlActions {
|
|
|
223
268
|
play(release) {
|
|
224
269
|
return this.post(`/v1/ensembles/${this.ens()}/play`, release ? { release } : {});
|
|
225
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Free HELD players via the dedicated `POST /v1/ensembles/:e/release` route
|
|
273
|
+
* (#742 gap 8 — `/go`). Optional `playerId` releases ONE held player; omitted
|
|
274
|
+
* releases all held in the ensemble. Distinct from `play(release:true)`, which
|
|
275
|
+
* ALSO clears the PAUSE axis — `/go` frees holds without touching pause.
|
|
276
|
+
*/
|
|
277
|
+
release(playerId) {
|
|
278
|
+
return this.post(`/v1/ensembles/${this.ens()}/release`, playerId ? { playerId } : {});
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Cancel a named schedule (`POST /v1/ensembles/:e/unschedule`, #742 gap —
|
|
282
|
+
* `/unschedule` & `/schedule delete`). Thin shim over `client.cancelSchedule`.
|
|
283
|
+
*/
|
|
284
|
+
unschedule(name) {
|
|
285
|
+
return this.post(`/v1/ensembles/${this.ens()}/unschedule`, { name });
|
|
286
|
+
}
|
|
226
287
|
restart(playerId, reason) {
|
|
227
288
|
return this.post(`/v1/ensembles/${this.ens()}/restart`, { playerId, ...(reason ? { reason } : {}) });
|
|
228
289
|
}
|
|
@@ -194,6 +194,83 @@ export declare class Controller {
|
|
|
194
194
|
/** First whitespace-delimited token + the remainder. */
|
|
195
195
|
private static splitFirst;
|
|
196
196
|
cmdPlayers(ctx: McExtensionContext): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Connected-host listing — reuses the shared `formatHostList` formatter (the
|
|
199
|
+
* same text the CLI `hosts` and MCP `hosts` tool render). `includeStale`
|
|
200
|
+
* surfaces hosts past their liveness window (`/hosts --all`). Returns the
|
|
201
|
+
* text so a future planner tool can reuse it (same shape as
|
|
202
|
+
* {@link ensemblesText}). NOT ensemble-scoped — the host registry is global.
|
|
203
|
+
*/
|
|
204
|
+
hostsText(includeStale: boolean): Promise<{
|
|
205
|
+
ok: true;
|
|
206
|
+
text: string;
|
|
207
|
+
} | {
|
|
208
|
+
ok: false;
|
|
209
|
+
error: string;
|
|
210
|
+
}>;
|
|
211
|
+
cmdHosts(args: string, ctx: McExtensionContext): Promise<void>;
|
|
212
|
+
/**
|
|
213
|
+
* Full board detail — the human counterpart to the `observe_board` planner
|
|
214
|
+
* tool. Renders the live `BoardModel` (already kept current by the coarse SSE
|
|
215
|
+
* stream), so there's no extra fetch: the board IS the snapshot. Richer than
|
|
216
|
+
* the TUI `/status` overlay (phase glyphs + currentTool + context% + tail).
|
|
217
|
+
*/
|
|
218
|
+
cmdStatus(ctx: McExtensionContext): void;
|
|
219
|
+
/**
|
|
220
|
+
* `/go [player]` — free HELD players. Uses the dedicated `release` route so
|
|
221
|
+
* it frees holds WITHOUT clearing the PAUSE axis (that's `/play` / `/unpause`).
|
|
222
|
+
* No arg → release all held in the ensemble; `/go <player>` releases one.
|
|
223
|
+
*/
|
|
224
|
+
cmdGo(args: string, ctx: McExtensionContext): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* A player's attachment-lifecycle snapshot — reuses the shared
|
|
227
|
+
* `formatAttachmentInfoForDisplay` (the same text the CLI `attachment-info`
|
|
228
|
+
* and MCP `attachment_info` tool render). Returns the text so a future planner
|
|
229
|
+
* tool can reuse it (same shape as {@link hostsText}).
|
|
230
|
+
*/
|
|
231
|
+
attachmentInfoText(playerId: string): Promise<{
|
|
232
|
+
ok: true;
|
|
233
|
+
text: string;
|
|
234
|
+
} | {
|
|
235
|
+
ok: false;
|
|
236
|
+
error: string;
|
|
237
|
+
}>;
|
|
238
|
+
cmdAttachmentInfo(args: string, ctx: McExtensionContext): Promise<void>;
|
|
239
|
+
/**
|
|
240
|
+
* A player's message timeline — reuses the shared `buildTimeline`/`formatRecall`
|
|
241
|
+
* helpers (the same render the CLI `recall` and MCP `recall` tool produce).
|
|
242
|
+
* Includes sent messages (operator wants the full picture). Pulls the full
|
|
243
|
+
* timeline via the `{ full: true }` recall route, NOT the count-only default.
|
|
244
|
+
*/
|
|
245
|
+
recallText(playerId: string): Promise<{
|
|
246
|
+
ok: true;
|
|
247
|
+
text: string;
|
|
248
|
+
} | {
|
|
249
|
+
ok: false;
|
|
250
|
+
error: string;
|
|
251
|
+
}>;
|
|
252
|
+
cmdRecall(args: string, ctx: McExtensionContext): Promise<void>;
|
|
253
|
+
/**
|
|
254
|
+
* Active-schedule listing — renders each {@link ScheduleEntry} in the same
|
|
255
|
+
* shape the MCP `schedules` tool produces (name → target | recurrence | next).
|
|
256
|
+
*/
|
|
257
|
+
schedulesText(): Promise<{
|
|
258
|
+
ok: true;
|
|
259
|
+
text: string;
|
|
260
|
+
} | {
|
|
261
|
+
ok: false;
|
|
262
|
+
error: string;
|
|
263
|
+
}>;
|
|
264
|
+
/**
|
|
265
|
+
* `/schedule [list | delete <name>]` — list active schedules (default) or
|
|
266
|
+
* cancel one. CREATE is intentionally NOT here: there is no thin-shim create
|
|
267
|
+
* method on the client (the MCP `schedule` tool signals the scheduler workflow
|
|
268
|
+
* directly), so create stays on the MCP tool / CLI per the architect's
|
|
269
|
+
* thin-shim ruling.
|
|
270
|
+
*/
|
|
271
|
+
cmdSchedule(args: string, ctx: McExtensionContext): Promise<void>;
|
|
272
|
+
/** `/unschedule <name>` — alias for `/schedule delete <name>`. */
|
|
273
|
+
cmdUnschedule(args: string, ctx: McExtensionContext): Promise<void>;
|
|
197
274
|
/**
|
|
198
275
|
* One-line ensembles listing — `▶` marks the board's current binding. Shared
|
|
199
276
|
* by the `/ensembles` command and the `list_ensembles` planner tool. Returns
|
|
@@ -74,6 +74,9 @@ const inner_tail_1 = require("./inner-tail");
|
|
|
74
74
|
const ensure_infra_1 = require("../../cli/ensure-infra");
|
|
75
75
|
const zod_to_typebox_1 = require("../zod-to-typebox");
|
|
76
76
|
const validation_1 = require("../../utils/validation");
|
|
77
|
+
const format_hosts_1 = require("../../utils/format-hosts");
|
|
78
|
+
const attachment_format_1 = require("../../utils/attachment-format");
|
|
79
|
+
const recall_format_1 = require("../../utils/recall-format");
|
|
77
80
|
/** The durable conductor a `/handoff` targets by default (matches catalog's conductorName default). */
|
|
78
81
|
const DEFAULT_CONDUCTOR = 'conductor';
|
|
79
82
|
/**
|
|
@@ -96,6 +99,34 @@ const ASK_POLL_INTERVAL_MS = 1_000;
|
|
|
96
99
|
function mintQuestionId() {
|
|
97
100
|
return `q-${crypto.randomUUID()}`;
|
|
98
101
|
}
|
|
102
|
+
/** Compact recurrence label for a schedule (ms interval → "5m"/"1h"/…). */
|
|
103
|
+
function formatScheduleInterval(ms) {
|
|
104
|
+
if (ms >= 86_400_000)
|
|
105
|
+
return `${ms / 86_400_000}d`;
|
|
106
|
+
if (ms >= 3_600_000)
|
|
107
|
+
return `${ms / 3_600_000}h`;
|
|
108
|
+
if (ms >= 60_000)
|
|
109
|
+
return `${ms / 60_000}m`;
|
|
110
|
+
return `${ms / 1000}s`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* #742 — render one {@link ScheduleEntry} for the board, mirroring the MCP
|
|
114
|
+
* `schedules` tool line shape (name → target | recurrence (bounds) | next + msg
|
|
115
|
+
* preview). Kept module-local + pure so the controller test can assert it.
|
|
116
|
+
*/
|
|
117
|
+
function formatScheduleLine(s) {
|
|
118
|
+
const recur = s.cronExpression
|
|
119
|
+
? `cron: ${s.cronExpression} (${s.timezone || 'UTC'})`
|
|
120
|
+
: s.interval ? `every ${formatScheduleInterval(s.interval)}` : 'one-shot';
|
|
121
|
+
const bounds = [];
|
|
122
|
+
if (s.until)
|
|
123
|
+
bounds.push(`until ${s.until}`);
|
|
124
|
+
if (s.remainingCount != null)
|
|
125
|
+
bounds.push(`${s.firedCount}/${s.firedCount + s.remainingCount} fired`);
|
|
126
|
+
const boundsStr = bounds.length ? ` (${bounds.join(', ')})` : '';
|
|
127
|
+
const msgPreview = s.message.length > 60 ? s.message.slice(0, 57) + '...' : s.message;
|
|
128
|
+
return `• ${s.name} → ${s.target} | ${recur}${boundsStr} | next: ${s.nextFireAt}\n msg: ${msgPreview}`;
|
|
129
|
+
}
|
|
99
130
|
/**
|
|
100
131
|
* #700 P2 — build the planner-wake injection from a resolved `answer` SSE event.
|
|
101
132
|
* Pure (testable). The planner has no inbox; the SSE `answer` event is turned
|
|
@@ -344,6 +375,139 @@ class Controller {
|
|
|
344
375
|
const ids = (0, board_1.sortedPlayerIds)(this.model);
|
|
345
376
|
this.notify(ctx, ids.length ? `Players (${ids.length}): ${ids.join(', ')}` : 'No players in the ensemble.');
|
|
346
377
|
}
|
|
378
|
+
// ── Hosts (#742 gap 5) ──
|
|
379
|
+
/**
|
|
380
|
+
* Connected-host listing — reuses the shared `formatHostList` formatter (the
|
|
381
|
+
* same text the CLI `hosts` and MCP `hosts` tool render). `includeStale`
|
|
382
|
+
* surfaces hosts past their liveness window (`/hosts --all`). Returns the
|
|
383
|
+
* text so a future planner tool can reuse it (same shape as
|
|
384
|
+
* {@link ensemblesText}). NOT ensemble-scoped — the host registry is global.
|
|
385
|
+
*/
|
|
386
|
+
async hostsText(includeStale) {
|
|
387
|
+
const r = await this.actions.listHosts();
|
|
388
|
+
if (!r.ok)
|
|
389
|
+
return r;
|
|
390
|
+
return { ok: true, text: (0, format_hosts_1.formatHostList)(r.hosts, { includeStale }) };
|
|
391
|
+
}
|
|
392
|
+
async cmdHosts(args, ctx) {
|
|
393
|
+
const includeStale = /(?:^|\s)(?:--all|-a)(?:\s|$)/.test(args);
|
|
394
|
+
const r = await this.hostsText(includeStale);
|
|
395
|
+
this.notify(ctx, r.ok ? r.text : `hosts failed: ${r.error}`);
|
|
396
|
+
}
|
|
397
|
+
// ── Status (#742 gap 9) ──
|
|
398
|
+
/**
|
|
399
|
+
* Full board detail — the human counterpart to the `observe_board` planner
|
|
400
|
+
* tool. Renders the live `BoardModel` (already kept current by the coarse SSE
|
|
401
|
+
* stream), so there's no extra fetch: the board IS the snapshot. Richer than
|
|
402
|
+
* the TUI `/status` overlay (phase glyphs + currentTool + context% + tail).
|
|
403
|
+
*/
|
|
404
|
+
cmdStatus(ctx) {
|
|
405
|
+
this.notify(ctx, (0, render_1.renderBoard)(this.model, this.localHost).join('\n'));
|
|
406
|
+
}
|
|
407
|
+
// ── Release / go (#742 gap 8) ──
|
|
408
|
+
/**
|
|
409
|
+
* `/go [player]` — free HELD players. Uses the dedicated `release` route so
|
|
410
|
+
* it frees holds WITHOUT clearing the PAUSE axis (that's `/play` / `/unpause`).
|
|
411
|
+
* No arg → release all held in the ensemble; `/go <player>` releases one.
|
|
412
|
+
*/
|
|
413
|
+
async cmdGo(args, ctx) {
|
|
414
|
+
const player = args.trim().split(/\s+/).filter(Boolean)[0];
|
|
415
|
+
const label = player ? `go (release ${player})` : 'go (release held)';
|
|
416
|
+
this.report(ctx, label, await this.actions.release(player || undefined));
|
|
417
|
+
}
|
|
418
|
+
// ── Attachment-info (#742 — /attachment-info) ──
|
|
419
|
+
/**
|
|
420
|
+
* A player's attachment-lifecycle snapshot — reuses the shared
|
|
421
|
+
* `formatAttachmentInfoForDisplay` (the same text the CLI `attachment-info`
|
|
422
|
+
* and MCP `attachment_info` tool render). Returns the text so a future planner
|
|
423
|
+
* tool can reuse it (same shape as {@link hostsText}).
|
|
424
|
+
*/
|
|
425
|
+
async attachmentInfoText(playerId) {
|
|
426
|
+
const r = await this.actions.attachmentInfo(playerId);
|
|
427
|
+
if (!r.ok)
|
|
428
|
+
return r;
|
|
429
|
+
return { ok: true, text: (0, attachment_format_1.formatAttachmentInfoForDisplay)(playerId, r.info).join('\n') };
|
|
430
|
+
}
|
|
431
|
+
async cmdAttachmentInfo(args, ctx) {
|
|
432
|
+
const player = args.trim().split(/\s+/).filter(Boolean)[0];
|
|
433
|
+
if (!player) {
|
|
434
|
+
this.notify(ctx, 'Usage: /attachment-info <player>');
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const r = await this.attachmentInfoText(player);
|
|
438
|
+
this.notify(ctx, r.ok ? r.text : `attachment-info failed: ${r.error}`);
|
|
439
|
+
}
|
|
440
|
+
// ── Recall (#742 — /recall) ──
|
|
441
|
+
/**
|
|
442
|
+
* A player's message timeline — reuses the shared `buildTimeline`/`formatRecall`
|
|
443
|
+
* helpers (the same render the CLI `recall` and MCP `recall` tool produce).
|
|
444
|
+
* Includes sent messages (operator wants the full picture). Pulls the full
|
|
445
|
+
* timeline via the `{ full: true }` recall route, NOT the count-only default.
|
|
446
|
+
*/
|
|
447
|
+
async recallText(playerId) {
|
|
448
|
+
const r = await this.actions.recall(playerId);
|
|
449
|
+
if (!r.ok)
|
|
450
|
+
return r;
|
|
451
|
+
const timeline = (0, recall_format_1.buildTimeline)(r.received, r.sent, true);
|
|
452
|
+
return { ok: true, text: (0, recall_format_1.formatRecall)(timeline).text };
|
|
453
|
+
}
|
|
454
|
+
async cmdRecall(args, ctx) {
|
|
455
|
+
const player = args.trim().split(/\s+/).filter(Boolean)[0];
|
|
456
|
+
if (!player) {
|
|
457
|
+
this.notify(ctx, 'Usage: /recall <player>');
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const r = await this.recallText(player);
|
|
461
|
+
this.notify(ctx, r.ok ? r.text : `recall failed: ${r.error}`);
|
|
462
|
+
}
|
|
463
|
+
// ── Schedules (#742 — /schedule, /unschedule) ──
|
|
464
|
+
/**
|
|
465
|
+
* Active-schedule listing — renders each {@link ScheduleEntry} in the same
|
|
466
|
+
* shape the MCP `schedules` tool produces (name → target | recurrence | next).
|
|
467
|
+
*/
|
|
468
|
+
async schedulesText() {
|
|
469
|
+
const r = await this.actions.listSchedules();
|
|
470
|
+
if (!r.ok)
|
|
471
|
+
return r;
|
|
472
|
+
if (r.schedules.length === 0)
|
|
473
|
+
return { ok: true, text: 'No active schedules.' };
|
|
474
|
+
return { ok: true, text: r.schedules.map(formatScheduleLine).join('\n') };
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* `/schedule [list | delete <name>]` — list active schedules (default) or
|
|
478
|
+
* cancel one. CREATE is intentionally NOT here: there is no thin-shim create
|
|
479
|
+
* method on the client (the MCP `schedule` tool signals the scheduler workflow
|
|
480
|
+
* directly), so create stays on the MCP tool / CLI per the architect's
|
|
481
|
+
* thin-shim ruling.
|
|
482
|
+
*/
|
|
483
|
+
async cmdSchedule(args, ctx) {
|
|
484
|
+
const [sub, rest] = Controller.splitFirst(args);
|
|
485
|
+
if (sub === 'delete' || sub === 'rm') {
|
|
486
|
+
const name = rest.trim().split(/\s+/).filter(Boolean)[0];
|
|
487
|
+
if (!name) {
|
|
488
|
+
this.notify(ctx, 'Usage: /schedule delete <name>');
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
this.report(ctx, `unschedule ${name}`, await this.actions.unschedule(name));
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (sub === 'create' || sub === 'new' || sub === 'add') {
|
|
495
|
+
this.notify(ctx, 'Schedule creation runs through the MCP `schedule` tool / `agent-tempo schedule` CLI — the board lists and deletes only.');
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
// Default (no arg or `list`) → list.
|
|
499
|
+
const r = await this.schedulesText();
|
|
500
|
+
this.notify(ctx, r.ok ? r.text : `schedules failed: ${r.error}`);
|
|
501
|
+
}
|
|
502
|
+
/** `/unschedule <name>` — alias for `/schedule delete <name>`. */
|
|
503
|
+
async cmdUnschedule(args, ctx) {
|
|
504
|
+
const name = args.trim().split(/\s+/).filter(Boolean)[0];
|
|
505
|
+
if (!name) {
|
|
506
|
+
this.notify(ctx, 'Usage: /unschedule <name>');
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
this.report(ctx, `unschedule ${name}`, await this.actions.unschedule(name));
|
|
510
|
+
}
|
|
347
511
|
// ── Multi-ensemble home (#790) ──
|
|
348
512
|
/**
|
|
349
513
|
* One-line ensembles listing — `▶` marks the board's current binding. Shared
|
|
@@ -1086,6 +1250,13 @@ function createMissionControlExtension(deps = {}) {
|
|
|
1086
1250
|
pi.on('session_shutdown', () => teardown());
|
|
1087
1251
|
// Operator commands (display-only widget → slash-commands drive everything).
|
|
1088
1252
|
pi.registerCommand('players', { description: 'List ensemble players', handler: (_a, ctx) => ctrl.cmdPlayers(ctx) });
|
|
1253
|
+
pi.registerCommand('hosts', { description: 'List connected hosts (/hosts [--all])', handler: (a, ctx) => ctrl.cmdHosts(a, ctx) });
|
|
1254
|
+
pi.registerCommand('status', { description: 'Show the full board detail', handler: (_a, ctx) => ctrl.cmdStatus(ctx) });
|
|
1255
|
+
pi.registerCommand('go', { description: 'Release HELD players (/go [player])', handler: (a, ctx) => ctrl.cmdGo(a, ctx) });
|
|
1256
|
+
pi.registerCommand('attachment-info', { description: 'Show a player\'s attachment lifecycle (/attachment-info <player>)', handler: (a, ctx) => ctrl.cmdAttachmentInfo(a, ctx) });
|
|
1257
|
+
pi.registerCommand('recall', { description: 'Show a player\'s message timeline (/recall <player>)', handler: (a, ctx) => ctrl.cmdRecall(a, ctx) });
|
|
1258
|
+
pi.registerCommand('schedule', { description: 'List or delete schedules (/schedule [list | delete <name>])', handler: (a, ctx) => ctrl.cmdSchedule(a, ctx) });
|
|
1259
|
+
pi.registerCommand('unschedule', { description: 'Cancel a schedule (/unschedule <name>)', handler: (a, ctx) => ctrl.cmdUnschedule(a, ctx) });
|
|
1089
1260
|
// #790 — multi-ensemble home: list + re-bind.
|
|
1090
1261
|
pi.registerCommand('ensembles', { description: 'List all ensembles (▶ marks the bound one)', handler: (_a, ctx) => ctrl.cmdEnsembles(ctx) });
|
|
1091
1262
|
pi.registerCommand('ensemble', { description: 'Switch the board to another ensemble (/ensemble <name> [--force])', handler: (a, ctx) => ctrl.cmdEnsemble(a, ctx) });
|
|
@@ -153,26 +153,24 @@ export declare class PiWorkflowClient {
|
|
|
153
153
|
fetchPending(): Promise<Message[]>;
|
|
154
154
|
/** Acknowledge delivered cue ids so the workflow stops re-serving them. */
|
|
155
155
|
ackDelivered(messageIds: string[]): Promise<void>;
|
|
156
|
-
/**
|
|
157
|
-
* `true` until a `pendingIntake` query is rejected as unregistered — i.e.
|
|
158
|
-
* the session workflow predates #750. Cached so the pump pays the failed
|
|
159
|
-
* probe ONCE, not every tick, then sticks to the legacy two-query path for
|
|
160
|
-
* the lifetime of this client. Known cost-only staleness: the Pi client
|
|
161
|
-
* handle follows continueAsNew transparently, so a pre-#750 workflow that
|
|
162
|
-
* CANs onto a #750+ worker bundle MID-SESSION keeps paying 2 queries/tick
|
|
163
|
-
* here until the extension process restarts (fresh client → fresh probe).
|
|
164
|
-
* Benign and self-healing — correctness is unaffected either way.
|
|
165
|
-
*/
|
|
166
|
-
private combinedIntakeSupported;
|
|
167
156
|
/**
|
|
168
157
|
* One-query intake: undelivered cues + the pending reset together —
|
|
169
|
-
* halves the pump's idle Temporal actions (2 → 1 query/tick).
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
158
|
+
* halves the pump's idle Temporal actions (2 → 1 query/tick).
|
|
159
|
+
*
|
|
160
|
+
* 2.0 (#788): the pre-#750 two-query fallback (`combinedIntakeSupported`
|
|
161
|
+
* probe + the legacy `pendingMessages` + `pendingReset` pair) is gone — the
|
|
162
|
+
* A2 clean cutover (guarded by #786) guarantees every 2.0 session workflow
|
|
163
|
+
* serves the `pendingIntake` handler, so the fallback was dead.
|
|
173
164
|
*/
|
|
174
165
|
fetchIntake(): Promise<PendingIntake>;
|
|
175
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Poll the workflow's single-slot pending reset (null = none).
|
|
168
|
+
*
|
|
169
|
+
* 2.0 (#788): the standalone `pendingReset` query was removed — derive the
|
|
170
|
+
* reset from the combined `pendingIntake` query. Only the cue-pump's
|
|
171
|
+
* no-`intakeSource` fallback path reaches this; the primary pump path reads
|
|
172
|
+
* `pendingReset` straight off `fetchIntake`'s result.
|
|
173
|
+
*/
|
|
176
174
|
fetchPendingReset(): Promise<PendingReset | null>;
|
|
177
175
|
/**
|
|
178
176
|
* Ack a performed reset by id — the workflow clears the slot ONLY if the id
|
|
@@ -23,6 +23,7 @@ const action_counters_1 = require("../utils/action-counters");
|
|
|
23
23
|
const search_attributes_1 = require("../utils/search-attributes");
|
|
24
24
|
const config_1 = require("../config");
|
|
25
25
|
const signals_1 = require("../workflows/signals");
|
|
26
|
+
const constants_1 = require("../constants");
|
|
26
27
|
/** Adapter identity advertised on claim. Pi interactive players are `interactive`-class. */
|
|
27
28
|
const PI_ADAPTER_ID = 'pi';
|
|
28
29
|
/**
|
|
@@ -160,6 +161,7 @@ class PiWorkflowClient {
|
|
|
160
161
|
adapterId: PI_ADAPTER_ID,
|
|
161
162
|
adapterClass: 'interactive',
|
|
162
163
|
leaseMs: this.leaseMs,
|
|
164
|
+
protocolVersion: constants_1.PROTOCOL_VERSION, // #786 — 2.0 wire handshake
|
|
163
165
|
...(this.expectedAttachmentId ? { expectedAttachmentId: this.expectedAttachmentId } : {}),
|
|
164
166
|
},
|
|
165
167
|
],
|
|
@@ -286,57 +288,29 @@ class PiWorkflowClient {
|
|
|
286
288
|
await handle.signal(signals_1.markDeliveredSignal, messageIds);
|
|
287
289
|
}
|
|
288
290
|
// ── Combined intake (T0.3 of #747, #750) ──
|
|
289
|
-
/**
|
|
290
|
-
* `true` until a `pendingIntake` query is rejected as unregistered — i.e.
|
|
291
|
-
* the session workflow predates #750. Cached so the pump pays the failed
|
|
292
|
-
* probe ONCE, not every tick, then sticks to the legacy two-query path for
|
|
293
|
-
* the lifetime of this client. Known cost-only staleness: the Pi client
|
|
294
|
-
* handle follows continueAsNew transparently, so a pre-#750 workflow that
|
|
295
|
-
* CANs onto a #750+ worker bundle MID-SESSION keeps paying 2 queries/tick
|
|
296
|
-
* here until the extension process restarts (fresh client → fresh probe).
|
|
297
|
-
* Benign and self-healing — correctness is unaffected either way.
|
|
298
|
-
*/
|
|
299
|
-
combinedIntakeSupported = true;
|
|
300
291
|
/**
|
|
301
292
|
* One-query intake: undelivered cues + the pending reset together —
|
|
302
|
-
* halves the pump's idle Temporal actions (2 → 1 query/tick).
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
293
|
+
* halves the pump's idle Temporal actions (2 → 1 query/tick).
|
|
294
|
+
*
|
|
295
|
+
* 2.0 (#788): the pre-#750 two-query fallback (`combinedIntakeSupported`
|
|
296
|
+
* probe + the legacy `pendingMessages` + `pendingReset` pair) is gone — the
|
|
297
|
+
* A2 clean cutover (guarded by #786) guarantees every 2.0 session workflow
|
|
298
|
+
* serves the `pendingIntake` handler, so the fallback was dead.
|
|
306
299
|
*/
|
|
307
300
|
async fetchIntake() {
|
|
308
|
-
|
|
309
|
-
const handle = this.requireHandle();
|
|
310
|
-
try {
|
|
311
|
-
const intake = await handle.query(signals_1.pendingIntakeQuery);
|
|
312
|
-
// Shape-guard: a real server either serves the query or rejects it
|
|
313
|
-
// as unregistered, but belt-and-braces against converter quirks /
|
|
314
|
-
// test doubles — a malformed result downgrades to the legacy pair
|
|
315
|
-
// rather than crashing the pump tick.
|
|
316
|
-
if (intake && Array.isArray(intake.messages))
|
|
317
|
-
return intake;
|
|
318
|
-
this.combinedIntakeSupported = false;
|
|
319
|
-
log('pendingIntake returned a malformed result — falling back to the legacy pair');
|
|
320
|
-
}
|
|
321
|
-
catch (err) {
|
|
322
|
-
if (!isQueryNotRegisteredError(err))
|
|
323
|
-
throw err;
|
|
324
|
-
this.combinedIntakeSupported = false;
|
|
325
|
-
log('pendingIntake query not registered on this workflow (pre-#750 build) — ' +
|
|
326
|
-
'falling back to the legacy pendingMessages + pendingReset pair');
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
const [messages, pendingReset] = await Promise.all([
|
|
330
|
-
this.fetchPending(),
|
|
331
|
-
this.fetchPendingReset(),
|
|
332
|
-
]);
|
|
333
|
-
return { messages, pendingReset };
|
|
301
|
+
return this.requireHandle().query(signals_1.pendingIntakeQuery);
|
|
334
302
|
}
|
|
335
303
|
// ── Reset intake (3d D14) ──
|
|
336
|
-
/**
|
|
304
|
+
/**
|
|
305
|
+
* Poll the workflow's single-slot pending reset (null = none).
|
|
306
|
+
*
|
|
307
|
+
* 2.0 (#788): the standalone `pendingReset` query was removed — derive the
|
|
308
|
+
* reset from the combined `pendingIntake` query. Only the cue-pump's
|
|
309
|
+
* no-`intakeSource` fallback path reaches this; the primary pump path reads
|
|
310
|
+
* `pendingReset` straight off `fetchIntake`'s result.
|
|
311
|
+
*/
|
|
337
312
|
async fetchPendingReset() {
|
|
338
|
-
|
|
339
|
-
return handle.query(signals_1.pendingResetQuery);
|
|
313
|
+
return (await this.requireHandle().query(signals_1.pendingIntakeQuery)).pendingReset;
|
|
340
314
|
}
|
|
341
315
|
/**
|
|
342
316
|
* Ack a performed reset by id — the workflow clears the slot ONLY if the id
|