agent-tempo 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +253 -219
- package/LICENSE +21 -21
- package/README.md +293 -289
- package/assets/icon-dark.svg +9 -9
- package/assets/icon.svg +9 -9
- package/assets/logo-dark.svg +11 -11
- package/assets/logo-light.svg +11 -11
- package/dashboard/README.md +91 -91
- package/dashboard/dist/assets/{index-D6Xyje_n.js → index-jmYe6rmS.js} +2 -2
- package/dashboard/dist/assets/index-jmYe6rmS.js.map +1 -0
- package/dashboard/dist/index.html +20 -20
- package/dashboard/package.json +47 -47
- package/dist/activities/outbox.d.ts +30 -1
- package/dist/activities/outbox.js +96 -3
- package/dist/adapters/base.js +5 -0
- package/dist/adapters/copilot/adapter.js +12 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/pi/adapter.d.ts +2 -0
- package/dist/adapters/pi/adapter.js +43 -0
- package/dist/adapters/pi/index.d.ts +16 -0
- package/dist/adapters/pi/index.js +10 -0
- package/dist/cli/global-wrapper.d.ts +19 -0
- package/dist/cli/global-wrapper.js +169 -0
- package/dist/cli/help-text.js +97 -97
- package/dist/cli/startup.js +11 -0
- package/dist/cli/upgrade-command.js +81 -81
- package/dist/cli.js +12 -0
- package/dist/client/core.js +9 -2
- package/dist/client/interface.d.ts +6 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +74 -0
- package/dist/daemon.js +37 -1
- package/dist/http/aggregate.d.ts +22 -1
- package/dist/http/aggregate.js +41 -0
- package/dist/http/auth.d.ts +94 -8
- package/dist/http/auth.js +93 -9
- package/dist/http/body.d.ts +4 -1
- package/dist/http/body.js +6 -3
- package/dist/http/event-bus.js +1 -0
- package/dist/http/event-types.d.ts +34 -2
- package/dist/http/event-types.js +1 -0
- package/dist/http/gate-audit.d.ts +12 -0
- package/dist/http/gate-audit.js +95 -0
- package/dist/http/gate-registry.d.ts +167 -0
- package/dist/http/gate-registry.js +163 -0
- package/dist/http/gate-routes.d.ts +48 -0
- package/dist/http/gate-routes.js +102 -0
- package/dist/http/ingest-registry.d.ts +30 -0
- package/dist/http/ingest-registry.js +108 -0
- package/dist/http/inner-loop-routes.d.ts +66 -0
- package/dist/http/inner-loop-routes.js +182 -0
- package/dist/http/inner-loop.d.ts +92 -0
- package/dist/http/inner-loop.js +155 -0
- package/dist/http/server.d.ts +38 -3
- package/dist/http/server.js +211 -6
- package/dist/http/snapshot.d.ts +6 -0
- package/dist/http/snapshot.js +6 -0
- package/dist/pi/cue-pump.d.ts +61 -0
- package/dist/pi/cue-pump.js +95 -0
- package/dist/pi/extension.d.ts +45 -0
- package/dist/pi/extension.js +407 -0
- package/dist/pi/gate-client.d.ts +54 -0
- package/dist/pi/gate-client.js +136 -0
- package/dist/pi/headless.d.ts +85 -0
- package/dist/pi/headless.js +224 -0
- package/dist/pi/index.d.ts +28 -0
- package/dist/pi/index.js +43 -0
- package/dist/pi/inner-loop-client.d.ts +67 -0
- package/dist/pi/inner-loop-client.js +164 -0
- package/dist/pi/inner-loop-publisher.d.ts +187 -0
- package/dist/pi/inner-loop-publisher.js +236 -0
- package/dist/pi/lazy-proxy.d.ts +37 -0
- package/dist/pi/lazy-proxy.js +55 -0
- package/dist/pi/mission-control/actions.d.ts +48 -0
- package/dist/pi/mission-control/actions.js +98 -0
- package/dist/pi/mission-control/board.d.ts +53 -0
- package/dist/pi/mission-control/board.js +104 -0
- package/dist/pi/mission-control/extension.d.ts +44 -0
- package/dist/pi/mission-control/extension.js +251 -0
- package/dist/pi/mission-control/index.d.ts +15 -0
- package/dist/pi/mission-control/index.js +32 -0
- package/dist/pi/mission-control/inner-tail.d.ts +48 -0
- package/dist/pi/mission-control/inner-tail.js +76 -0
- package/dist/pi/mission-control/pi-ui.d.ts +43 -0
- package/dist/pi/mission-control/pi-ui.js +10 -0
- package/dist/pi/mission-control/render.d.ts +6 -0
- package/dist/pi/mission-control/render.js +95 -0
- package/dist/pi/phase-driver.d.ts +74 -0
- package/dist/pi/phase-driver.js +122 -0
- package/dist/pi/pi-types.d.ts +208 -0
- package/dist/pi/pi-types.js +21 -0
- package/dist/pi/probe.d.ts +80 -0
- package/dist/pi/probe.js +154 -0
- package/dist/pi/render-tools.d.ts +17 -0
- package/dist/pi/render-tools.js +51 -0
- package/dist/pi/reset-pump.d.ts +47 -0
- package/dist/pi/reset-pump.js +85 -0
- package/dist/pi/tool-capability.d.ts +60 -0
- package/dist/pi/tool-capability.js +156 -0
- package/dist/pi/workflow-client.d.ts +158 -0
- package/dist/pi/workflow-client.js +289 -0
- package/dist/pi/zod-to-typebox.d.ts +74 -0
- package/dist/pi/zod-to-typebox.js +191 -0
- package/dist/scripts/verify-daemon-isolation-guard.js +24 -24
- package/dist/server-tools.d.ts +2 -0
- package/dist/server-tools.js +50 -46
- package/dist/server.js +4 -0
- package/dist/spawn.d.ts +55 -0
- package/dist/spawn.js +84 -12
- package/dist/tools/agent-types.d.ts +2 -2
- package/dist/tools/agent-types.js +22 -17
- package/dist/tools/attachment-info.d.ts +2 -2
- package/dist/tools/attachment-info.js +38 -33
- package/dist/tools/broadcast.d.ts +2 -2
- package/dist/tools/broadcast.js +69 -64
- package/dist/tools/cancel-stage.d.ts +2 -2
- package/dist/tools/cancel-stage.js +20 -15
- package/dist/tools/clear-state.d.ts +2 -2
- package/dist/tools/clear-state.js +25 -20
- package/dist/tools/coat-check-evict.d.ts +2 -2
- package/dist/tools/coat-check-evict.js +30 -25
- package/dist/tools/coat-check-get.d.ts +2 -2
- package/dist/tools/coat-check-get.js +39 -34
- package/dist/tools/coat-check-list.d.ts +2 -2
- package/dist/tools/coat-check-list.js +48 -43
- package/dist/tools/coat-check-put.d.ts +2 -2
- package/dist/tools/coat-check-put.js +41 -36
- package/dist/tools/cue.d.ts +2 -2
- package/dist/tools/cue.js +57 -52
- package/dist/tools/descriptor.d.ts +72 -0
- package/dist/tools/descriptor.js +39 -0
- package/dist/tools/destroy.d.ts +2 -2
- package/dist/tools/destroy.js +153 -148
- package/dist/tools/ensemble.d.ts +2 -2
- package/dist/tools/ensemble.js +71 -66
- package/dist/tools/evaluate-gate.d.ts +2 -2
- package/dist/tools/evaluate-gate.js +33 -27
- package/dist/tools/fetch-state.d.ts +2 -2
- package/dist/tools/fetch-state.js +43 -38
- package/dist/tools/gates.d.ts +2 -2
- package/dist/tools/gates.js +39 -34
- package/dist/tools/hosts.d.ts +2 -2
- package/dist/tools/hosts.js +25 -20
- package/dist/tools/listen.d.ts +2 -2
- package/dist/tools/listen.js +23 -18
- package/dist/tools/load-lineup.d.ts +2 -2
- package/dist/tools/load-lineup.js +324 -319
- package/dist/tools/migrate.d.ts +2 -2
- package/dist/tools/migrate.js +45 -40
- package/dist/tools/pause.d.ts +2 -2
- package/dist/tools/pause.js +34 -29
- package/dist/tools/play.d.ts +2 -2
- package/dist/tools/play.js +53 -48
- package/dist/tools/quality-gate.d.ts +2 -2
- package/dist/tools/quality-gate.js +26 -21
- package/dist/tools/recall.d.ts +2 -2
- package/dist/tools/recall.js +32 -27
- package/dist/tools/recruit.d.ts +2 -2
- package/dist/tools/recruit.js +325 -256
- package/dist/tools/release.d.ts +2 -2
- package/dist/tools/release.js +85 -80
- package/dist/tools/report.d.ts +2 -2
- package/dist/tools/report.js +28 -23
- package/dist/tools/reset.d.ts +3 -0
- package/dist/tools/reset.js +51 -0
- package/dist/tools/restart.d.ts +2 -2
- package/dist/tools/restart.js +51 -46
- package/dist/tools/restore.d.ts +2 -2
- package/dist/tools/restore.js +76 -71
- package/dist/tools/save-lineup.d.ts +2 -2
- package/dist/tools/save-lineup.js +32 -27
- package/dist/tools/save-state.d.ts +2 -2
- package/dist/tools/save-state.js +43 -38
- package/dist/tools/schedule.d.ts +2 -2
- package/dist/tools/schedule.js +133 -128
- package/dist/tools/schedules.d.ts +2 -2
- package/dist/tools/schedules.js +41 -36
- package/dist/tools/set-ensemble-description.d.ts +2 -2
- package/dist/tools/set-ensemble-description.js +26 -21
- package/dist/tools/set-name.d.ts +2 -2
- package/dist/tools/set-name.js +38 -33
- package/dist/tools/set-part.d.ts +2 -2
- package/dist/tools/set-part.js +20 -15
- package/dist/tools/shutdown.d.ts +2 -2
- package/dist/tools/shutdown.js +39 -34
- package/dist/tools/stage.d.ts +2 -2
- package/dist/tools/stage.js +28 -23
- package/dist/tools/stages.d.ts +2 -2
- package/dist/tools/stages.js +36 -31
- package/dist/tools/unschedule.d.ts +2 -2
- package/dist/tools/unschedule.js +30 -25
- package/dist/tools/who-am-i.d.ts +2 -2
- package/dist/tools/who-am-i.js +36 -31
- package/dist/tools/worktree.d.ts +2 -2
- package/dist/tools/worktree.js +134 -129
- package/dist/tui/index.js +6 -6
- package/dist/types.d.ts +47 -2
- package/dist/types.js +1 -1
- package/dist/utils/default-part.js +1 -0
- package/dist/utils/grpc-shutdown-guard.d.ts +52 -0
- package/dist/utils/grpc-shutdown-guard.js +88 -0
- package/dist/utils/sdk-probe.d.ts +23 -0
- package/dist/utils/sdk-probe.js +46 -7
- package/dist/worker.d.ts +3 -1
- package/dist/worker.js +6 -2
- package/dist/workflows/session.js +70 -2
- package/dist/workflows/signals.d.ts +32 -2
- package/dist/workflows/signals.js +25 -2
- package/examples/agents/tempo-composer.md +56 -56
- package/examples/agents/tempo-conductor.md +117 -117
- package/examples/agents/tempo-critic.md +73 -73
- package/examples/agents/tempo-improv.md +74 -74
- package/examples/agents/tempo-liner.md +75 -75
- package/examples/agents/tempo-roadie.md +61 -61
- package/examples/agents/tempo-soloist.md +71 -71
- package/examples/agents/tempo-tuner.md +94 -94
- package/examples/ensembles/tempo-big-band.yaml +146 -146
- package/examples/ensembles/tempo-dev-team.yaml +58 -58
- package/examples/ensembles/tempo-headless-jam.yaml +77 -77
- package/examples/ensembles/tempo-jam-session.yaml +41 -41
- package/examples/ensembles/tempo-mock-jam.yaml +79 -79
- package/examples/ensembles/tempo-review-squad.yaml +32 -32
- package/package.json +176 -173
- package/packaging/launchd/com.agent.tempo.plist +46 -46
- package/packaging/systemd/agent-tempo.service +32 -32
- package/packaging/windows/install-task.ps1 +71 -71
- package/scenarios/conductor-recruit-mock.yaml +33 -33
- package/scenarios/echo-roundtrip.yaml +15 -15
- package/scenarios/multi-player-handoff.yaml +38 -38
- package/scenarios/recruit-cascade.yaml +38 -38
- package/scenarios/two-player-conversation.yaml +33 -33
- package/workflow-bundle.js +97 -6
- package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
- package/dist/activities/claude-stop.d.ts +0 -21
- package/dist/activities/claude-stop.js +0 -94
- package/dist/channel.d.ts +0 -3
- package/dist/channel.js +0 -48
- package/dist/copilot-bridge.d.ts +0 -22
- package/dist/copilot-bridge.js +0 -565
- package/dist/scripts/258-spotcheck.js +0 -303
- package/dist/tools/detach.d.ts +0 -4
- package/dist/tools/detach.js +0 -45
- package/dist/tools/encore.d.ts +0 -4
- package/dist/tools/encore.js +0 -31
- package/dist/tools/helpers.d.ts +0 -21
- package/dist/tools/helpers.js +0 -25
- package/dist/tools/pause-ensemble.d.ts +0 -4
- package/dist/tools/pause-ensemble.js +0 -58
- package/dist/tools/resume-ensemble.d.ts +0 -4
- package/dist/tools/resume-ensemble.js +0 -79
- package/dist/tools/stop.d.ts +0 -4
- package/dist/tools/stop.js +0 -29
- package/dist/tui/client.d.ts +0 -6
- package/dist/tui/client.js +0 -9
- package/dist/tui/components/ActivityLog.d.ts +0 -16
- package/dist/tui/components/ActivityLog.js +0 -36
- package/dist/tui/components/CommandOverlay.d.ts +0 -15
- package/dist/tui/components/CommandOverlay.js +0 -34
- package/dist/tui/components/ConductorChat.d.ts +0 -16
- package/dist/tui/components/ConductorChat.js +0 -32
- package/dist/tui/components/EnsembleListView.d.ts +0 -14
- package/dist/tui/components/EnsembleListView.js +0 -32
- package/dist/tui/components/EnsemblePanel.d.ts +0 -12
- package/dist/tui/components/EnsemblePanel.js +0 -40
- package/dist/tui/components/InputBar.d.ts +0 -13
- package/dist/tui/components/InputBar.js +0 -58
- package/dist/tui/components/ScheduleOverlay.d.ts +0 -13
- package/dist/tui/components/ScheduleOverlay.js +0 -113
- package/dist/tui/components/TopBar.d.ts +0 -12
- package/dist/tui/components/TopBar.js +0 -15
- package/dist/tui/core-api.d.ts +0 -26
- package/dist/tui/core-api.js +0 -67
- package/dist/tui/hooks/useEnsembleDiscovery.d.ts +0 -3
- package/dist/tui/hooks/useEnsembleDiscovery.js +0 -30
- package/dist/tui/hooks/useMaestroPoller.d.ts +0 -3
- package/dist/tui/hooks/useMaestroPoller.js +0 -36
- package/dist/tui/hooks/useSendCommand.d.ts +0 -7
- package/dist/tui/hooks/useSendCommand.js +0 -29
- package/dist/utils/bg-preflight.d.ts +0 -25
- package/dist/utils/bg-preflight.js +0 -154
package/dist/utils/sdk-probe.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findSdkPackageJson = findSdkPackageJson;
|
|
3
4
|
exports.probeSdkInstall = probeSdkInstall;
|
|
5
|
+
exports.readSdkPackageVersion = readSdkPackageVersion;
|
|
4
6
|
/**
|
|
5
7
|
* Filesystem-walk probe for an installed npm package.
|
|
6
8
|
*
|
|
@@ -20,26 +22,63 @@ exports.probeSdkInstall = probeSdkInstall;
|
|
|
20
22
|
* Used by:
|
|
21
23
|
* - `src/adapters/opencode/adapter.ts` — module-load optional-dep gate
|
|
22
24
|
* - `src/tools/recruit.ts` — recruit pre-flight check
|
|
25
|
+
* - `src/pi/probe.ts` — Pi / Copilot-via-Pi pre-flight (presence + version floor)
|
|
23
26
|
*/
|
|
24
27
|
const fs_1 = require("fs");
|
|
25
28
|
const path_1 = require("path");
|
|
26
29
|
/**
|
|
30
|
+
* Locate an installed package's `package.json` by walking `node_modules`
|
|
31
|
+
* directories upward from `fromDir`. The single source of truth for the
|
|
32
|
+
* filesystem walk — {@link probeSdkInstall} (presence) and
|
|
33
|
+
* {@link readSdkPackageVersion} (version) both build on it.
|
|
34
|
+
*
|
|
27
35
|
* @param pkgName Bare specifier (e.g. `'@opencode-ai/sdk'`).
|
|
28
36
|
* @param fromDir Where to start the walk. Defaults to the caller's
|
|
29
|
-
* `__dirname`-equivalent — pass an explicit value
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* anywhere on the walk up the filesystem.
|
|
37
|
+
* `__dirname`-equivalent — pass an explicit value to anchor elsewhere.
|
|
38
|
+
* @returns The absolute path to `<dir>/node_modules/<pkgName>/package.json`
|
|
39
|
+
* for the first match up the filesystem, or `null` if none is found.
|
|
33
40
|
*/
|
|
34
|
-
function
|
|
41
|
+
function findSdkPackageJson(pkgName, fromDir = __dirname) {
|
|
35
42
|
let dir = fromDir;
|
|
36
43
|
while (true) {
|
|
37
44
|
const candidate = (0, path_1.join)(dir, 'node_modules', pkgName, 'package.json');
|
|
38
45
|
if ((0, fs_1.existsSync)(candidate))
|
|
39
|
-
return
|
|
46
|
+
return candidate;
|
|
40
47
|
const parent = (0, path_1.dirname)(dir);
|
|
41
48
|
if (parent === dir)
|
|
42
|
-
return
|
|
49
|
+
return null;
|
|
43
50
|
dir = parent;
|
|
44
51
|
}
|
|
45
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* @param pkgName Bare specifier (e.g. `'@opencode-ai/sdk'`).
|
|
55
|
+
* @param fromDir Where to start the walk. Defaults to the caller's
|
|
56
|
+
* `__dirname`-equivalent — pass an explicit value if you need to anchor
|
|
57
|
+
* the search elsewhere.
|
|
58
|
+
* @returns `true` if `<dir>/node_modules/<pkgName>/package.json` exists
|
|
59
|
+
* anywhere on the walk up the filesystem.
|
|
60
|
+
*/
|
|
61
|
+
function probeSdkInstall(pkgName, fromDir = __dirname) {
|
|
62
|
+
return findSdkPackageJson(pkgName, fromDir) !== null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Read an installed package's `package.json#version` via the same filesystem
|
|
66
|
+
* walk as {@link probeSdkInstall}. Returns `null` when the package isn't
|
|
67
|
+
* installed, its `package.json` is unreadable, or its `version` field is
|
|
68
|
+
* absent/non-string — callers treat `null` as "version unknown".
|
|
69
|
+
*
|
|
70
|
+
* @param pkgName Bare specifier (e.g. `'@earendil-works/pi-coding-agent'`).
|
|
71
|
+
* @param fromDir Walk start (defaults to this module's `__dirname`).
|
|
72
|
+
*/
|
|
73
|
+
function readSdkPackageVersion(pkgName, fromDir = __dirname) {
|
|
74
|
+
const pkgPath = findSdkPackageJson(pkgName, fromDir);
|
|
75
|
+
if (!pkgPath)
|
|
76
|
+
return null;
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
|
|
79
|
+
return typeof parsed.version === 'string' ? parsed.version : null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Worker } from '@temporalio/worker';
|
|
2
2
|
import { Config } from './config';
|
|
3
|
+
import type { IngestTokenRegistry } from './http/ingest-registry';
|
|
4
|
+
import type { GateRegistry } from './http/gate-registry';
|
|
3
5
|
export interface DualWorkers {
|
|
4
6
|
/** Shared queue worker: workflows + delivery activities + schedule activities */
|
|
5
7
|
sharedWorker: Worker;
|
|
@@ -11,4 +13,4 @@ export interface DualWorkers {
|
|
|
11
13
|
* - Shared queue: workflows + all delivery activities (deliverCue, deliverReport, terminateSession, startRecruitedSession) + schedule activities
|
|
12
14
|
* - Per-host queue: spawnProcess only (routes recruit spawns to the correct machine)
|
|
13
15
|
*/
|
|
14
|
-
export declare function createWorkers(config: Config): Promise<DualWorkers>;
|
|
16
|
+
export declare function createWorkers(config: Config, ingestTokens?: IngestTokenRegistry, gate?: GateRegistry): Promise<DualWorkers>;
|
package/dist/worker.js
CHANGED
|
@@ -93,13 +93,17 @@ async function getWorkflowBundle() {
|
|
|
93
93
|
* - Shared queue: workflows + all delivery activities (deliverCue, deliverReport, terminateSession, startRecruitedSession) + schedule activities
|
|
94
94
|
* - Per-host queue: spawnProcess only (routes recruit spawns to the correct machine)
|
|
95
95
|
*/
|
|
96
|
-
async function createWorkers(config) {
|
|
96
|
+
async function createWorkers(config, ingestTokens, gate) {
|
|
97
97
|
const connection = await (0, connection_1.createTemporalNativeConnection)(config);
|
|
98
98
|
// Create a Client connection for activities that need to interact with Temporal
|
|
99
99
|
const clientConnection = await (0, connection_2.createTemporalConnection)(config);
|
|
100
100
|
const client = new client_1.Client({ connection: clientConnection, namespace: config.temporalNamespace });
|
|
101
101
|
const scheduleActivities = (0, schedule_fire_1.createScheduleActivities)(client);
|
|
102
|
-
|
|
102
|
+
// 3c Tier-2 — thread the daemon's shared IngestTokenRegistry into the outbox
|
|
103
|
+
// activities so the pi spawn branch can mint per-player ingest tokens and the
|
|
104
|
+
// destroy path can revoke them. Same singleton the HTTP server validates
|
|
105
|
+
// against (both run in this daemon process).
|
|
106
|
+
const outboxActivities = (0, outbox_1.createOutboxActivities)(client, config, ingestTokens, gate);
|
|
103
107
|
const maestroActivities = (0, maestro_1.createMaestroActivities)(client);
|
|
104
108
|
const workflowBundle = await getWorkflowBundle();
|
|
105
109
|
const SHUTDOWN_GRACE_TIME = '10s';
|
|
@@ -16,7 +16,7 @@ const attachment_math_1 = require("./attachment-math");
|
|
|
16
16
|
const signals_1 = require("./signals");
|
|
17
17
|
const validation_1 = require("../utils/validation");
|
|
18
18
|
// ── Outbox Activity Proxies ──
|
|
19
|
-
const { deliverCue, deliverReport, terminateSession, startRecruitedSession, releasePlayer, deliverDetach, deliverDestroy, deliverRestart } = (0, workflow_1.proxyActivities)({
|
|
19
|
+
const { deliverCue, deliverReport, terminateSession, startRecruitedSession, releasePlayer, deliverDetach, deliverDestroy, deliverRestart, deliverReset } = (0, workflow_1.proxyActivities)({
|
|
20
20
|
startToCloseTimeout: '30 seconds',
|
|
21
21
|
retry: { maximumAttempts: 3 },
|
|
22
22
|
});
|
|
@@ -117,6 +117,9 @@ async function agentSessionWorkflow(input) {
|
|
|
117
117
|
const messages = input.messages ?? [];
|
|
118
118
|
const sentMessages = input.sentMessages ?? [];
|
|
119
119
|
const outbox = input.outbox ?? [];
|
|
120
|
+
// D14 — pending context-reset flag, polled + acked by the Pi extension. Single
|
|
121
|
+
// slot, latest-wins; survives continue-as-new until the extension acks it.
|
|
122
|
+
let pendingReset = input.pendingReset ?? null;
|
|
120
123
|
let lastActivityTime = workflowNow().getTime();
|
|
121
124
|
let lastOutboundTime = input.lastOutboundTime ?? workflowNow().getTime();
|
|
122
125
|
let lastInboundRRTime = input.lastInboundRRTime ?? 0;
|
|
@@ -128,6 +131,13 @@ async function agentSessionWorkflow(input) {
|
|
|
128
131
|
let activityCount = input.activityCount ?? 0;
|
|
129
132
|
let receivedCount = input.receivedCount ?? 0;
|
|
130
133
|
let sentCount = input.sentCount ?? 0;
|
|
134
|
+
// ── 3c Tier-1 — coarse activity (currentTool + context usage) ──
|
|
135
|
+
// Refreshed by the heartbeat piggyback; surfaced by `getCoarseActivityQuery`.
|
|
136
|
+
// Deliberately volatile/live — NOT carried across continueAsNew (no input
|
|
137
|
+
// field), so a fresh run reports `{currentTool:null}` until the next ~30s
|
|
138
|
+
// heartbeat repopulates it. Acceptable for coarse observability; the live,
|
|
139
|
+
// fine-grained tail is the off-wire /inner side-channel.
|
|
140
|
+
let coarseActivity = { currentTool: null };
|
|
131
141
|
// ── Warm Hold + Pause State ──
|
|
132
142
|
let outboxLocked = input.outboxLocked ?? false;
|
|
133
143
|
let heldMessage = input.heldMessage;
|
|
@@ -308,6 +318,9 @@ async function agentSessionWorkflow(input) {
|
|
|
308
318
|
else if (entry.type === 'release') {
|
|
309
319
|
sentMessages.push({ id: entry.id, to: entry.targetPlayerId, text: '[release requested]', timestamp: entry.createdAt });
|
|
310
320
|
}
|
|
321
|
+
else if (entry.type === 'reset') {
|
|
322
|
+
sentMessages.push({ id: entry.id, to: entry.targetPlayerId, text: '[reset requested]', timestamp: entry.createdAt });
|
|
323
|
+
}
|
|
311
324
|
lastActivityTime = workflowNow().getTime();
|
|
312
325
|
activityCount++;
|
|
313
326
|
lastOutboundTime = workflowNow().getTime();
|
|
@@ -368,6 +381,29 @@ async function agentSessionWorkflow(input) {
|
|
|
368
381
|
lastActivityTime = workflowNow().getTime();
|
|
369
382
|
activityCount++;
|
|
370
383
|
});
|
|
384
|
+
// ── Reset (D14) — set by deliverReset, polled + acked by the Pi extension ──
|
|
385
|
+
(0, workflow_1.setHandler)(signals_1.setPendingResetSignal, (r) => {
|
|
386
|
+
// Latest-wins; stamp requestedAt deterministically (workflowNow, not the activity's clock).
|
|
387
|
+
pendingReset = {
|
|
388
|
+
resetId: r.resetId,
|
|
389
|
+
fresh: r.fresh,
|
|
390
|
+
...(r.reason !== undefined ? { reason: r.reason } : {}),
|
|
391
|
+
...(r.requestedBy !== undefined ? { requestedBy: r.requestedBy } : {}),
|
|
392
|
+
requestedAt: workflowNow().toISOString(),
|
|
393
|
+
};
|
|
394
|
+
lastActivityTime = workflowNow().getTime();
|
|
395
|
+
activityCount++;
|
|
396
|
+
});
|
|
397
|
+
(0, workflow_1.setHandler)(signals_1.pendingResetQuery, () => pendingReset);
|
|
398
|
+
(0, workflow_1.setHandler)(signals_1.ackResetSignal, (resetId) => {
|
|
399
|
+
// Race-safe: only clear if the ack matches the current pending reset, so a
|
|
400
|
+
// newer reset landing during the extension's wipe isn't silently dropped.
|
|
401
|
+
if (pendingReset?.resetId === resetId) {
|
|
402
|
+
pendingReset = null;
|
|
403
|
+
}
|
|
404
|
+
lastActivityTime = workflowNow().getTime();
|
|
405
|
+
activityCount++;
|
|
406
|
+
});
|
|
371
407
|
(0, workflow_1.setHandler)(signals_1.updateMetadataSignal, (update) => {
|
|
372
408
|
if (update.hostname != null)
|
|
373
409
|
input.metadata.hostname = update.hostname;
|
|
@@ -434,6 +470,8 @@ async function agentSessionWorkflow(input) {
|
|
|
434
470
|
leaseMs: currentAttachment.leaseMs,
|
|
435
471
|
};
|
|
436
472
|
});
|
|
473
|
+
// 3c Tier-1 — surface the latest coarse activity for the snapshot fan-out.
|
|
474
|
+
(0, workflow_1.setHandler)(signals_1.getCoarseActivityQuery, () => ({ ...coarseActivity }));
|
|
437
475
|
// ── Hold / Release Handlers ──
|
|
438
476
|
(0, workflow_1.setHandler)(signals_1.releaseHeldSignal, () => {
|
|
439
477
|
if (heldMessage) {
|
|
@@ -807,7 +845,7 @@ async function agentSessionWorkflow(input) {
|
|
|
807
845
|
* `heartbeat` signal — extend the lease. Last-write-wins via the `attachmentId` guard;
|
|
808
846
|
* heartbeats for superseded attachments are ignored.
|
|
809
847
|
*/
|
|
810
|
-
(0, workflow_1.setHandler)(signals_1.heartbeatSignal, ({ attachmentId }) => {
|
|
848
|
+
(0, workflow_1.setHandler)(signals_1.heartbeatSignal, ({ attachmentId, currentTool, contextTokens, contextPercent }) => {
|
|
811
849
|
if (!currentAttachment || currentAttachment.attachmentId !== attachmentId)
|
|
812
850
|
return;
|
|
813
851
|
const now = workflowNow();
|
|
@@ -818,6 +856,16 @@ async function agentSessionWorkflow(input) {
|
|
|
818
856
|
currentAttachment.expiresAt = new Date(now.getTime() + currentAttachment.leaseMs).toISOString();
|
|
819
857
|
lastActivityTime = now.getTime();
|
|
820
858
|
activityCount++;
|
|
859
|
+
// 3c Tier-1 — refresh coarse activity from the heartbeat piggyback. Field-wise
|
|
860
|
+
// merge: only fields the sender included are updated. `currentTool` can be a
|
|
861
|
+
// legitimate `null` (idle), so `!== undefined` distinguishes "sent null" from
|
|
862
|
+
// "not sent" (a non-reporting sender leaves prior coarse intact).
|
|
863
|
+
if (currentTool !== undefined)
|
|
864
|
+
coarseActivity.currentTool = currentTool;
|
|
865
|
+
if (contextTokens !== undefined)
|
|
866
|
+
coarseActivity.contextTokens = contextTokens;
|
|
867
|
+
if (contextPercent !== undefined)
|
|
868
|
+
coarseActivity.contextPercent = contextPercent;
|
|
821
869
|
});
|
|
822
870
|
/**
|
|
823
871
|
* `requestDetach` signal — adapter-initiated graceful detach. Transitions to `draining`;
|
|
@@ -1415,6 +1463,9 @@ async function agentSessionWorkflow(input) {
|
|
|
1415
1463
|
// #131 Phase C — forward to spawnProcess so spawnClaudeApiAdapter
|
|
1416
1464
|
// can plumb it into the subprocess env (AGENT_TEMPO_API_MODEL).
|
|
1417
1465
|
...(entry.model !== undefined ? { model: entry.model } : {}),
|
|
1466
|
+
// Phase 3a / MD-C — forward the headless Pi tool-access policy so
|
|
1467
|
+
// spawnPiHeadless plumbs AGENT_TEMPO_TOOL_ACCESS into the subprocess.
|
|
1468
|
+
...(entry.toolAccess !== undefined ? { toolAccess: entry.toolAccess } : {}),
|
|
1418
1469
|
});
|
|
1419
1470
|
break;
|
|
1420
1471
|
}
|
|
@@ -1468,6 +1519,21 @@ async function agentSessionWorkflow(input) {
|
|
|
1468
1519
|
});
|
|
1469
1520
|
break;
|
|
1470
1521
|
}
|
|
1522
|
+
case 'reset': {
|
|
1523
|
+
// D14: operator/conductor CLEAN-WIPE. Sets a pendingReset flag the
|
|
1524
|
+
// Pi extension polls + acts on (newSession). POLL-delivery, not a
|
|
1525
|
+
// direct subprocess signal. resetId = this outbox entry id (the
|
|
1526
|
+
// extension acks with it). Does NOT route through the MD-G gate.
|
|
1527
|
+
await deliverReset({
|
|
1528
|
+
ensemble: input.metadata.ensemble,
|
|
1529
|
+
targetPlayerId: entry.targetPlayerId,
|
|
1530
|
+
resetId: entry.id,
|
|
1531
|
+
fresh: entry.fresh ?? true,
|
|
1532
|
+
...(entry.reason !== undefined ? { reason: entry.reason } : {}),
|
|
1533
|
+
requestedBy: entry.invokerPlayerId ?? input.metadata.playerId,
|
|
1534
|
+
});
|
|
1535
|
+
break;
|
|
1536
|
+
}
|
|
1471
1537
|
case 'spawn': {
|
|
1472
1538
|
// PR-D: forward the pre-claimed attachment token + pinned runId +
|
|
1473
1539
|
// resolved adapterId to the spawn activity. The child process picks
|
|
@@ -1599,6 +1665,8 @@ async function agentSessionWorkflow(input) {
|
|
|
1599
1665
|
messages: messages.filter((m) => !m.delivered),
|
|
1600
1666
|
sentMessages: sentMessages.slice(-50),
|
|
1601
1667
|
outbox: outbox.filter((e) => e.status === 'pending' || e.status === 'processing'),
|
|
1668
|
+
// D14 — carry an un-acked pending reset across CAN (omit when null).
|
|
1669
|
+
...(pendingReset ? { pendingReset } : {}),
|
|
1602
1670
|
lastInboundRRTime,
|
|
1603
1671
|
lastOutboundTime,
|
|
1604
1672
|
// #399 W2 — counters carried across continueAsNew so the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SessionMetadata, Message, SentMessage, HistoryEntry, OutboxEntry, OutboxEntryInput, QualityGate, WorktreeEntry, StageEntry, AttachmentToken, AttachmentInfo, AdapterClass, DetachReason, OrphanSummary, PlayerStateEntry } from '../types';
|
|
2
|
-
export type { SessionMetadata, SessionInput, Message, Command, PlayerReport, SentMessage, HistoryEntry, OutboxEntry, OutboxEntryInput, OutboxEntryStatus, CueOutboxEntry, RecruitOutboxEntry, ReportOutboxEntry, StopOutboxEntry, ReleaseOutboxEntry, SpawnOutboxEntry, AgentType, QualityGate, QualityGateCriterion, WorktreeEntry, StageEntry, StagePlayerStatus, AttachmentToken, AttachmentInfo, AttachmentPhase, Attachment, AdapterClass, AdapterDescriptor, DetachReason, AdapterDirective, OrphanSummary, PlayerStateEntry, } from '../types';
|
|
1
|
+
import type { SessionMetadata, Message, SentMessage, HistoryEntry, OutboxEntry, OutboxEntryInput, QualityGate, WorktreeEntry, StageEntry, AttachmentToken, AttachmentInfo, AdapterClass, DetachReason, OrphanSummary, PlayerStateEntry, PendingReset } from '../types';
|
|
2
|
+
export type { SessionMetadata, SessionInput, Message, Command, PlayerReport, SentMessage, HistoryEntry, OutboxEntry, OutboxEntryInput, OutboxEntryStatus, CueOutboxEntry, RecruitOutboxEntry, ReportOutboxEntry, StopOutboxEntry, ReleaseOutboxEntry, SpawnOutboxEntry, PendingReset, AgentType, QualityGate, QualityGateCriterion, WorktreeEntry, StageEntry, StagePlayerStatus, AttachmentToken, AttachmentInfo, AttachmentPhase, Attachment, AdapterClass, AdapterDescriptor, DetachReason, AdapterDirective, OrphanSummary, PlayerStateEntry, } from '../types';
|
|
3
3
|
export declare const receiveMessageSignal: import("@temporalio/workflow").SignalDefinition<[{
|
|
4
4
|
from: string;
|
|
5
5
|
text: string;
|
|
@@ -34,6 +34,14 @@ export declare const getMetadataQuery: import("@temporalio/workflow").QueryDefin
|
|
|
34
34
|
export declare const pendingMessagesQuery: import("@temporalio/workflow").QueryDefinition<Message[], [], string>;
|
|
35
35
|
export declare const allMessagesQuery: import("@temporalio/workflow").QueryDefinition<Message[], [], string>;
|
|
36
36
|
export declare const allSentMessagesQuery: import("@temporalio/workflow").QueryDefinition<SentMessage[], [], string>;
|
|
37
|
+
export declare const setPendingResetSignal: import("@temporalio/workflow").SignalDefinition<[{
|
|
38
|
+
resetId: string;
|
|
39
|
+
fresh: boolean;
|
|
40
|
+
reason?: string;
|
|
41
|
+
requestedBy?: string;
|
|
42
|
+
}], string>;
|
|
43
|
+
export declare const pendingResetQuery: import("@temporalio/workflow").QueryDefinition<PendingReset | null, [], string>;
|
|
44
|
+
export declare const ackResetSignal: import("@temporalio/workflow").SignalDefinition<[string], string>;
|
|
37
45
|
/** Release a held session — unlocks the outbox and delivers the stored initial message. */
|
|
38
46
|
export declare const releaseHeldSignal: import("@temporalio/workflow").SignalDefinition<[], "releaseHeld">;
|
|
39
47
|
/** Query whether the session's outbox is locked (warm hold). */
|
|
@@ -138,10 +146,19 @@ export declare const setPreferredHostUpdate: import("@temporalio/common").Update
|
|
|
138
146
|
/**
|
|
139
147
|
* Liveness heartbeat from the adapter. Resets `lastHeartbeatAt` and extends `expiresAt` to
|
|
140
148
|
* `workflow.now() + leaseMs` iff `attachmentId` matches the current attachment; otherwise ignored.
|
|
149
|
+
*
|
|
150
|
+
* 3c Tier-1 (additive, optional, non-breaking): the heartbeat doubles as the
|
|
151
|
+
* coarse-activity piggyback. `currentTool` (null = idle), `contextTokens`, and
|
|
152
|
+
* `contextPercent` (Pi `getContextUsage`, pull-only — no token event exists)
|
|
153
|
+
* refresh the workflow's queryable coarse state read by `getCoarseActivityQuery`.
|
|
154
|
+
* Omitted by senders that don't report coarse — the handler merges field-wise.
|
|
141
155
|
*/
|
|
142
156
|
export declare const heartbeatSignal: import("@temporalio/workflow").SignalDefinition<[{
|
|
143
157
|
attachmentId: string;
|
|
144
158
|
at: string;
|
|
159
|
+
currentTool?: string | null;
|
|
160
|
+
contextTokens?: number;
|
|
161
|
+
contextPercent?: number;
|
|
145
162
|
}], string>;
|
|
146
163
|
/**
|
|
147
164
|
* Adapter-, conductor-, or operator-initiated request to detach gracefully.
|
|
@@ -268,6 +285,19 @@ export declare const getLeaseStateQuery: import("@temporalio/workflow").QueryDef
|
|
|
268
285
|
expiresAt: number | null;
|
|
269
286
|
leaseMs: number | null;
|
|
270
287
|
}, [], string>;
|
|
288
|
+
/**
|
|
289
|
+
* 3c Tier-1 coarse activity — the player's current tool + context-token usage,
|
|
290
|
+
* refreshed by the heartbeat piggyback (`heartbeatSignal`). Read by the snapshot
|
|
291
|
+
* fan-out (`getPlayerWireMeta`) → projected onto `PlayerSummaryV1` → the aggregate
|
|
292
|
+
* poll/diff emits `player.activity`. `currentTool` is `null` when idle/between
|
|
293
|
+
* tools; the context fields are absent when Pi can't report usage (e.g. right
|
|
294
|
+
* after compaction). A live read of volatile state — NOT durable metadata.
|
|
295
|
+
*/
|
|
296
|
+
export declare const getCoarseActivityQuery: import("@temporalio/workflow").QueryDefinition<{
|
|
297
|
+
currentTool: string | null;
|
|
298
|
+
contextTokens?: number;
|
|
299
|
+
contextPercent?: number;
|
|
300
|
+
}, [], string>;
|
|
271
301
|
export declare const testForceContinueAsNewSignal: import("@temporalio/workflow").SignalDefinition<[], "testForceContinueAsNew">;
|
|
272
302
|
export declare const setQualityGateSignal: import("@temporalio/workflow").SignalDefinition<[{
|
|
273
303
|
task: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.stagesQuery = exports.cancelStageSignal = void 0;
|
|
3
|
+
exports.qualityGatesQuery = exports.evaluateGateCriteriaSignal = exports.setQualityGateSignal = exports.testForceContinueAsNewSignal = exports.getCoarseActivityQuery = exports.getLeaseStateQuery = exports.getActivityStateQuery = exports.getMessagingStateQuery = exports.getRunIdQuery = exports.outboxQuery = exports.submitOutboxUpdate = exports.playerStateKeysQuery = exports.playerStateQuery = exports.clearPlayerStateUpdate = exports.savePlayerStateUpdate = exports.orphanSummaryQuery = exports.attachmentInfoQuery = exports.adapterExitedSignal = exports.requestDetachSignal = exports.heartbeatSignal = exports.setPreferredHostUpdate = exports.enqueueSpawnUpdate = exports.forceDetachUpdate = exports.claimAttachmentUpdate = exports.isDestroyedQuery = exports.destroyUpdate = exports.inFlightMessagesQuery = exports.processingEndUpdate = exports.processingStartUpdate = exports.historyQuery = exports.playerReportSignal = exports.commandSignal = exports.pausedQuery = exports.setPausedSignal = exports.outboxLockedQuery = exports.releaseHeldSignal = exports.ackResetSignal = exports.pendingResetQuery = exports.setPendingResetSignal = exports.allSentMessagesQuery = exports.allMessagesQuery = exports.pendingMessagesQuery = exports.getMetadataQuery = exports.getPartQuery = exports.updateMetadataSignal = exports.setNameSignal = exports.markDeliveredSignal = exports.setPartSignal = exports.recordSentMessageSignal = exports.receiveMessageSignal = void 0;
|
|
4
|
+
exports.stagesQuery = exports.cancelStageSignal = exports.setStageSignal = exports.worktreesQuery = exports.removeWorktreeSignal = exports.setWorktreeSignal = void 0;
|
|
5
5
|
const workflow_1 = require("@temporalio/workflow");
|
|
6
6
|
// ── Player Signals ──
|
|
7
7
|
// `isScheduled` + `scheduleName` are set by `src/activities/schedule-fire.ts`
|
|
@@ -27,6 +27,14 @@ exports.getMetadataQuery = (0, workflow_1.defineQuery)('getMetadata');
|
|
|
27
27
|
exports.pendingMessagesQuery = (0, workflow_1.defineQuery)('pendingMessages');
|
|
28
28
|
exports.allMessagesQuery = (0, workflow_1.defineQuery)('allMessages');
|
|
29
29
|
exports.allSentMessagesQuery = (0, workflow_1.defineQuery)('allSentMessages');
|
|
30
|
+
// ── Reset (D14) — context clean-wipe poll-delivery ──
|
|
31
|
+
// `deliverReset` sets the pending flag via `setPendingResetSignal`; the Pi
|
|
32
|
+
// extension polls `pendingResetQuery`, performs the wipe (newSession), then
|
|
33
|
+
// clears it via `ackResetSignal(resetId)`. Single-slot, latest-wins. The
|
|
34
|
+
// workflow stamps `requestedAt` (deterministic). See WIRE-PROTOCOL.md.
|
|
35
|
+
exports.setPendingResetSignal = (0, workflow_1.defineSignal)('setPendingReset');
|
|
36
|
+
exports.pendingResetQuery = (0, workflow_1.defineQuery)('pendingReset');
|
|
37
|
+
exports.ackResetSignal = (0, workflow_1.defineSignal)('ackReset');
|
|
30
38
|
// ── Hold / Release ──
|
|
31
39
|
/** Release a held session — unlocks the outbox and delivers the stored initial message. */
|
|
32
40
|
exports.releaseHeldSignal = (0, workflow_1.defineSignal)('releaseHeld');
|
|
@@ -95,6 +103,12 @@ exports.setPreferredHostUpdate = (0, workflow_1.defineUpdate)('setPreferredHost'
|
|
|
95
103
|
/**
|
|
96
104
|
* Liveness heartbeat from the adapter. Resets `lastHeartbeatAt` and extends `expiresAt` to
|
|
97
105
|
* `workflow.now() + leaseMs` iff `attachmentId` matches the current attachment; otherwise ignored.
|
|
106
|
+
*
|
|
107
|
+
* 3c Tier-1 (additive, optional, non-breaking): the heartbeat doubles as the
|
|
108
|
+
* coarse-activity piggyback. `currentTool` (null = idle), `contextTokens`, and
|
|
109
|
+
* `contextPercent` (Pi `getContextUsage`, pull-only — no token event exists)
|
|
110
|
+
* refresh the workflow's queryable coarse state read by `getCoarseActivityQuery`.
|
|
111
|
+
* Omitted by senders that don't report coarse — the handler merges field-wise.
|
|
98
112
|
*/
|
|
99
113
|
exports.heartbeatSignal = (0, workflow_1.defineSignal)('heartbeat');
|
|
100
114
|
/**
|
|
@@ -210,6 +224,15 @@ exports.getActivityStateQuery = (0, workflow_1.defineQuery)('getActivityState');
|
|
|
210
224
|
* boundary.
|
|
211
225
|
*/
|
|
212
226
|
exports.getLeaseStateQuery = (0, workflow_1.defineQuery)('getLeaseState');
|
|
227
|
+
/**
|
|
228
|
+
* 3c Tier-1 coarse activity — the player's current tool + context-token usage,
|
|
229
|
+
* refreshed by the heartbeat piggyback (`heartbeatSignal`). Read by the snapshot
|
|
230
|
+
* fan-out (`getPlayerWireMeta`) → projected onto `PlayerSummaryV1` → the aggregate
|
|
231
|
+
* poll/diff emits `player.activity`. `currentTool` is `null` when idle/between
|
|
232
|
+
* tools; the context fields are absent when Pi can't report usage (e.g. right
|
|
233
|
+
* after compaction). A live read of volatile state — NOT durable metadata.
|
|
234
|
+
*/
|
|
235
|
+
exports.getCoarseActivityQuery = (0, workflow_1.defineQuery)('getCoarseActivity');
|
|
213
236
|
// ── Test-only Signals ──
|
|
214
237
|
//
|
|
215
238
|
// **Test-only.** Forces the session workflow's main loop to take the
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: tempo-composer
|
|
3
|
-
description: Software architect — designs system structure, defines interfaces, makes technology decisions. Focuses on the "what" and "why", not implementation.
|
|
4
|
-
model: opus
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
You are the **Composer** of the ensemble — the Software Architect. You design the structure of the system: modules, boundaries, interfaces, data flow, and technical direction. You define *what* gets built and *why*, then hand off the *how* to soloists.
|
|
8
|
-
|
|
9
|
-
## Responsibilities
|
|
10
|
-
|
|
11
|
-
- Design system architecture: module boundaries, service decomposition, data flow
|
|
12
|
-
- Define interfaces and contracts between components
|
|
13
|
-
- Make technology choices with clear rationale
|
|
14
|
-
- Analyze dependencies, coupling, and integration points
|
|
15
|
-
- Identify scalability, security, and maintainability risks before they become problems
|
|
16
|
-
- Review proposed designs and implementations for architectural consistency
|
|
17
|
-
- Select API paradigms (REST, GraphQL, RPC) based on use case requirements
|
|
18
|
-
|
|
19
|
-
## Working Style
|
|
20
|
-
|
|
21
|
-
- **Understand before proposing**: Read existing code and architecture before suggesting changes. Respect what's already there.
|
|
22
|
-
- **Think in systems**: Focus on boundaries, trade-offs, and data flow — not individual functions.
|
|
23
|
-
- **Document decisions**: Every architectural decision should come with rationale and trade-offs considered. Write ADRs when the decision is significant.
|
|
24
|
-
- **Be opinionated but open**: Have strong views on architecture, loosely held. Change your mind when presented with evidence.
|
|
25
|
-
- **Delegate implementation**: Define the shape of the solution, then hand off to soloists. Don't get pulled into writing production code.
|
|
26
|
-
- **Consider observability**: Design systems that are debuggable. Think about logging, tracing, and error reporting from the start.
|
|
27
|
-
- **Don't over-architect**: Design the simplest structure that meets the known requirements. If you're adding abstraction layers without a concrete, present-tense benefit, remove them. Apply `/simplify` thinking — if a design element can't be justified by a real requirement, it doesn't belong.
|
|
28
|
-
- **Avoid designing for imagined futures**: Add extensibility only where there's evidence you'll need it. Speculative abstractions become maintenance burdens. You can always refactor when the need is real.
|
|
29
|
-
|
|
30
|
-
## Ensemble Collaboration
|
|
31
|
-
|
|
32
|
-
- **`ensemble`**: Check who's active before proposing designs that affect multiple players' work. Understand the current state of implementation.
|
|
33
|
-
- **`cue`**: Use to share design decisions, interface definitions, and architectural guidance with soloists. When a soloist asks a design question, respond with structured reasoning: context, options, recommendation, trade-offs.
|
|
34
|
-
- **`report`**: Report to the conductor when:
|
|
35
|
-
- A design decision is made (so it can be communicated to affected players)
|
|
36
|
-
- You identify an architectural risk or concern
|
|
37
|
-
- You need input on requirements before you can finalize a design
|
|
38
|
-
- A design review is complete (with approve/reject/concerns)
|
|
39
|
-
- **`who_am_i`**: Check your assignment and any type-specific instructions at startup.
|
|
40
|
-
- **`agent_types`**: If you identify a need for a specialist (e.g., security review of your design), suggest the conductor recruit one.
|
|
41
|
-
|
|
42
|
-
### When other players cue you
|
|
43
|
-
|
|
44
|
-
- **Soloists asking design questions**: Respond promptly with clear, actionable guidance. Don't send them in circles.
|
|
45
|
-
- **Conductor asking for design review**: Provide structured feedback — approved, changes requested, or concerns flagged — with specific reasoning.
|
|
46
|
-
- **Tuners reporting architectural test gaps**: Acknowledge and adjust the design to improve testability if needed.
|
|
47
|
-
|
|
48
|
-
## Context Pressure
|
|
49
|
-
|
|
50
|
-
If you notice your context growing large, you're losing track of earlier instructions, or you find yourself repeating work, report to the conductor immediately with a structured summary:
|
|
51
|
-
|
|
52
|
-
1. **Current task**: What you're working on right now
|
|
53
|
-
2. **Key findings so far**: Important decisions, completed work, file paths changed
|
|
54
|
-
3. **Recommended next steps**: What remains to be done
|
|
55
|
-
|
|
56
|
-
This lets the conductor refresh your session with a clean context while preserving continuity.
|
|
1
|
+
---
|
|
2
|
+
name: tempo-composer
|
|
3
|
+
description: Software architect — designs system structure, defines interfaces, makes technology decisions. Focuses on the "what" and "why", not implementation.
|
|
4
|
+
model: opus
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are the **Composer** of the ensemble — the Software Architect. You design the structure of the system: modules, boundaries, interfaces, data flow, and technical direction. You define *what* gets built and *why*, then hand off the *how* to soloists.
|
|
8
|
+
|
|
9
|
+
## Responsibilities
|
|
10
|
+
|
|
11
|
+
- Design system architecture: module boundaries, service decomposition, data flow
|
|
12
|
+
- Define interfaces and contracts between components
|
|
13
|
+
- Make technology choices with clear rationale
|
|
14
|
+
- Analyze dependencies, coupling, and integration points
|
|
15
|
+
- Identify scalability, security, and maintainability risks before they become problems
|
|
16
|
+
- Review proposed designs and implementations for architectural consistency
|
|
17
|
+
- Select API paradigms (REST, GraphQL, RPC) based on use case requirements
|
|
18
|
+
|
|
19
|
+
## Working Style
|
|
20
|
+
|
|
21
|
+
- **Understand before proposing**: Read existing code and architecture before suggesting changes. Respect what's already there.
|
|
22
|
+
- **Think in systems**: Focus on boundaries, trade-offs, and data flow — not individual functions.
|
|
23
|
+
- **Document decisions**: Every architectural decision should come with rationale and trade-offs considered. Write ADRs when the decision is significant.
|
|
24
|
+
- **Be opinionated but open**: Have strong views on architecture, loosely held. Change your mind when presented with evidence.
|
|
25
|
+
- **Delegate implementation**: Define the shape of the solution, then hand off to soloists. Don't get pulled into writing production code.
|
|
26
|
+
- **Consider observability**: Design systems that are debuggable. Think about logging, tracing, and error reporting from the start.
|
|
27
|
+
- **Don't over-architect**: Design the simplest structure that meets the known requirements. If you're adding abstraction layers without a concrete, present-tense benefit, remove them. Apply `/simplify` thinking — if a design element can't be justified by a real requirement, it doesn't belong.
|
|
28
|
+
- **Avoid designing for imagined futures**: Add extensibility only where there's evidence you'll need it. Speculative abstractions become maintenance burdens. You can always refactor when the need is real.
|
|
29
|
+
|
|
30
|
+
## Ensemble Collaboration
|
|
31
|
+
|
|
32
|
+
- **`ensemble`**: Check who's active before proposing designs that affect multiple players' work. Understand the current state of implementation.
|
|
33
|
+
- **`cue`**: Use to share design decisions, interface definitions, and architectural guidance with soloists. When a soloist asks a design question, respond with structured reasoning: context, options, recommendation, trade-offs.
|
|
34
|
+
- **`report`**: Report to the conductor when:
|
|
35
|
+
- A design decision is made (so it can be communicated to affected players)
|
|
36
|
+
- You identify an architectural risk or concern
|
|
37
|
+
- You need input on requirements before you can finalize a design
|
|
38
|
+
- A design review is complete (with approve/reject/concerns)
|
|
39
|
+
- **`who_am_i`**: Check your assignment and any type-specific instructions at startup.
|
|
40
|
+
- **`agent_types`**: If you identify a need for a specialist (e.g., security review of your design), suggest the conductor recruit one.
|
|
41
|
+
|
|
42
|
+
### When other players cue you
|
|
43
|
+
|
|
44
|
+
- **Soloists asking design questions**: Respond promptly with clear, actionable guidance. Don't send them in circles.
|
|
45
|
+
- **Conductor asking for design review**: Provide structured feedback — approved, changes requested, or concerns flagged — with specific reasoning.
|
|
46
|
+
- **Tuners reporting architectural test gaps**: Acknowledge and adjust the design to improve testability if needed.
|
|
47
|
+
|
|
48
|
+
## Context Pressure
|
|
49
|
+
|
|
50
|
+
If you notice your context growing large, you're losing track of earlier instructions, or you find yourself repeating work, report to the conductor immediately with a structured summary:
|
|
51
|
+
|
|
52
|
+
1. **Current task**: What you're working on right now
|
|
53
|
+
2. **Key findings so far**: Important decisions, completed work, file paths changed
|
|
54
|
+
3. **Recommended next steps**: What remains to be done
|
|
55
|
+
|
|
56
|
+
This lets the conductor refresh your session with a clean context while preserving continuity.
|