agent-tempo 1.7.0 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CLAUDE.md +10 -13
  2. package/README.md +21 -15
  3. package/dashboard/dist/assets/index-BbR7zIdK.js.map +1 -1
  4. package/dashboard/package.json +1 -1
  5. package/dist/activities/hard-terminate.d.ts +2 -2
  6. package/dist/activities/hard-terminate.js +3 -9
  7. package/dist/activities/maestro.d.ts +8 -13
  8. package/dist/activities/maestro.js +0 -10
  9. package/dist/activities/outbox.js +4 -2
  10. package/dist/adapters/base.js +3 -0
  11. package/dist/cli/commands.d.ts +8 -0
  12. package/dist/cli/commands.js +79 -11
  13. package/dist/cli/daemon-command.js +19 -33
  14. package/dist/cli/help-text.js +7 -7
  15. package/dist/cli/home-command.d.ts +26 -0
  16. package/dist/cli/home-command.js +156 -0
  17. package/dist/cli/removed-verbs.d.ts +0 -4
  18. package/dist/cli/removed-verbs.js +23 -12
  19. package/dist/cli/sa-preflight.d.ts +0 -14
  20. package/dist/cli/sa-preflight.js +1 -17
  21. package/dist/cli/startup.d.ts +1 -10
  22. package/dist/cli/startup.js +0 -40
  23. package/dist/cli.js +34 -80
  24. package/dist/client/core.js +13 -15
  25. package/dist/client/index.d.ts +2 -1
  26. package/dist/client/interface.d.ts +5 -14
  27. package/dist/client/with-spawn.d.ts +9 -4
  28. package/dist/client/with-spawn.js +7 -10
  29. package/dist/config.d.ts +0 -13
  30. package/dist/constants.d.ts +22 -0
  31. package/dist/constants.js +23 -1
  32. package/dist/daemon.js +52 -0
  33. package/dist/http/auth.d.ts +3 -21
  34. package/dist/http/auth.js +10 -31
  35. package/dist/http/event-types.d.ts +5 -4
  36. package/dist/http/orphans.d.ts +3 -3
  37. package/dist/http/orphans.js +3 -3
  38. package/dist/http/server.d.ts +0 -5
  39. package/dist/http/server.js +69 -29
  40. package/dist/http/writes.d.ts +1 -1
  41. package/dist/http/writes.js +25 -2
  42. package/dist/palette/index.d.ts +6 -7
  43. package/dist/palette/index.js +6 -7
  44. package/dist/pi/mission-control/actions.d.ts +67 -1
  45. package/dist/pi/mission-control/actions.js +61 -0
  46. package/dist/pi/mission-control/extension.d.ts +77 -0
  47. package/dist/pi/mission-control/extension.js +171 -0
  48. package/dist/pi/workflow-client.d.ts +14 -16
  49. package/dist/pi/workflow-client.js +18 -44
  50. package/dist/types.d.ts +15 -6
  51. package/dist/upgrade/boot-guard.d.ts +95 -0
  52. package/dist/upgrade/boot-guard.js +88 -0
  53. package/dist/upgrade/from-upgrade.d.ts +95 -0
  54. package/dist/upgrade/from-upgrade.js +330 -0
  55. package/dist/upgrade/phase-engine.js +4 -1
  56. package/dist/utils/duration.d.ts +3 -6
  57. package/dist/utils/duration.js +3 -6
  58. package/dist/utils/format-hosts.d.ts +1 -1
  59. package/dist/utils/format-hosts.js +0 -2
  60. package/dist/utils/grpc-shutdown-guard.js +0 -4
  61. package/dist/utils/hosts.d.ts +4 -10
  62. package/dist/utils/hosts.js +12 -28
  63. package/dist/utils/search-attributes.d.ts +27 -18
  64. package/dist/utils/search-attributes.js +39 -18
  65. package/dist/utils/visibility-deadline.d.ts +16 -0
  66. package/dist/utils/visibility-deadline.js +16 -0
  67. package/dist/workflows/maestro-signals.d.ts +0 -13
  68. package/dist/workflows/maestro-signals.js +6 -14
  69. package/dist/workflows/maestro.js +27 -32
  70. package/dist/workflows/scheduler.js +7 -1
  71. package/dist/workflows/session.js +44 -110
  72. package/dist/workflows/signals.d.ts +9 -1
  73. package/dist/workflows/signals.js +8 -6
  74. package/package.json +1 -6
  75. package/workflow-bundle.js +239 -182
  76. package/dist/cli/legacy-migration.d.ts +0 -35
  77. package/dist/cli/legacy-migration.js +0 -341
  78. package/dist/client/ensure-conductor-spawned.d.ts +0 -35
  79. package/dist/client/ensure-conductor-spawned.js +0 -48
  80. package/dist/tui/App.d.ts +0 -85
  81. package/dist/tui/App.js +0 -1791
  82. package/dist/tui/bootstrap-types.d.ts +0 -46
  83. package/dist/tui/bootstrap-types.js +0 -7
  84. package/dist/tui/commands.d.ts +0 -71
  85. package/dist/tui/commands.js +0 -1375
  86. package/dist/tui/components/ChatView.d.ts +0 -35
  87. package/dist/tui/components/ChatView.js +0 -54
  88. package/dist/tui/components/CommandPalette.d.ts +0 -21
  89. package/dist/tui/components/CommandPalette.js +0 -67
  90. package/dist/tui/components/ConversationStream.d.ts +0 -114
  91. package/dist/tui/components/ConversationStream.js +0 -307
  92. package/dist/tui/components/CreateEnsembleWizard.d.ts +0 -19
  93. package/dist/tui/components/CreateEnsembleWizard.js +0 -223
  94. package/dist/tui/components/DestroyConfirmModal.d.ts +0 -17
  95. package/dist/tui/components/DestroyConfirmModal.js +0 -62
  96. package/dist/tui/components/ErrorView.d.ts +0 -31
  97. package/dist/tui/components/ErrorView.js +0 -129
  98. package/dist/tui/components/HomeView.d.ts +0 -54
  99. package/dist/tui/components/HomeView.js +0 -306
  100. package/dist/tui/components/LoadLineupModal.d.ts +0 -18
  101. package/dist/tui/components/LoadLineupModal.js +0 -79
  102. package/dist/tui/components/MainView.d.ts +0 -21
  103. package/dist/tui/components/MainView.js +0 -107
  104. package/dist/tui/components/NewEnsembleModal.d.ts +0 -9
  105. package/dist/tui/components/NewEnsembleModal.js +0 -73
  106. package/dist/tui/components/Picker.d.ts +0 -23
  107. package/dist/tui/components/Picker.js +0 -70
  108. package/dist/tui/components/PlayerDetailView.d.ts +0 -26
  109. package/dist/tui/components/PlayerDetailView.js +0 -118
  110. package/dist/tui/components/PromptArea.d.ts +0 -50
  111. package/dist/tui/components/PromptArea.js +0 -303
  112. package/dist/tui/components/RecruitWizard.d.ts +0 -17
  113. package/dist/tui/components/RecruitWizard.js +0 -221
  114. package/dist/tui/components/RestoreConfirmModal.d.ts +0 -18
  115. package/dist/tui/components/RestoreConfirmModal.js +0 -71
  116. package/dist/tui/components/ScheduleWizard.d.ts +0 -19
  117. package/dist/tui/components/ScheduleWizard.js +0 -259
  118. package/dist/tui/components/Splash.d.ts +0 -23
  119. package/dist/tui/components/Splash.js +0 -221
  120. package/dist/tui/components/StatusBar.d.ts +0 -48
  121. package/dist/tui/components/StatusBar.js +0 -128
  122. package/dist/tui/components/StatusOverlay.d.ts +0 -15
  123. package/dist/tui/components/StatusOverlay.js +0 -76
  124. package/dist/tui/components/TitleBar.d.ts +0 -10
  125. package/dist/tui/components/TitleBar.js +0 -21
  126. package/dist/tui/index.d.ts +0 -15
  127. package/dist/tui/index.js +0 -162
  128. package/dist/tui/ink-context.d.ts +0 -18
  129. package/dist/tui/ink-context.js +0 -59
  130. package/dist/tui/ink-loader.d.ts +0 -26
  131. package/dist/tui/ink-loader.js +0 -42
  132. package/dist/tui/removed-commands.d.ts +0 -9
  133. package/dist/tui/removed-commands.js +0 -22
  134. package/dist/tui/sse-handler.d.ts +0 -52
  135. package/dist/tui/sse-handler.js +0 -157
  136. package/dist/tui/store.d.ts +0 -598
  137. package/dist/tui/store.js +0 -753
  138. package/dist/tui/utils/format.d.ts +0 -56
  139. package/dist/tui/utils/format.js +0 -155
  140. package/dist/tui/utils/fullscreen.d.ts +0 -23
  141. package/dist/tui/utils/fullscreen.js +0 -71
  142. package/dist/tui/utils/history.d.ts +0 -10
  143. package/dist/tui/utils/history.js +0 -85
  144. package/dist/tui/utils/platform.d.ts +0 -45
  145. package/dist/tui/utils/platform.js +0 -258
  146. package/dist/tui/utils/theme.d.ts +0 -21
  147. package/dist/tui/utils/theme.js +0 -24
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-tempo-dashboard",
3
3
  "private": true,
4
- "version": "1.7.0",
4
+ "version": "2.0.0-beta.1",
5
5
  "type": "module",
6
6
  "description": "Web dashboard for agent-tempo. Bundled into the npm package; served by the daemon at /dashboard/*.",
7
7
  "scripts": {
@@ -12,9 +12,9 @@ export interface HardTerminateInput {
12
12
  playerName: string;
13
13
  /** Adapter type — controls PID-file lookup and expected process-name verification. */
14
14
  agent: AgentType;
15
- /** Session's workDir — used to locate the copilot bridge PID file (`<workDir>/logs/<playerName>.pid`). */
15
+ /** Session's workDir — carried on the activity input as a session attribute. */
16
16
  workDir: string;
17
- /** Optional explicit logDir override; falls back to `<workDir>/logs`. */
17
+ /** Optional explicit logDir override for the central bridge log path. */
18
18
  logDir?: string;
19
19
  }
20
20
  export interface HardTerminateResult {
@@ -29,7 +29,6 @@ exports.hardTerminateAttachment = hardTerminateAttachment;
29
29
  */
30
30
  const child_process_1 = require("child_process");
31
31
  const fs_1 = require("fs");
32
- const path_1 = require("path");
33
32
  const config_1 = require("../config");
34
33
  const constants_1 = require("../constants");
35
34
  const log = (...args) => console.error('[agent-tempo:hard-terminate]', ...args);
@@ -38,19 +37,14 @@ const log = (...args) => console.error('[agent-tempo:hard-terminate]', ...args);
38
37
  * what happened so callers can record it in workflow history without blocking state flips.
39
38
  */
40
39
  async function hardTerminateAttachment(input) {
41
- const { ensemble, playerName, agent, workDir, logDir } = input;
40
+ const { ensemble, playerName, agent, logDir } = input;
42
41
  const notes = [];
43
42
  const killedPids = [];
44
43
  log(`hardTerminate start — ensemble=${ensemble} player=${playerName} agent=${agent}`);
45
44
  // ── Copilot bridge: PID file is authoritative ──
46
45
  if (agent === 'copilot') {
47
- // #690 — pid lives at the CENTRAL ~/.agent-tempo/logs/<ensemble>/ path now.
48
- // Transitional (one version, v1.6.2) READ-ONLY fallback to the legacy per-cwd
49
- // <workDir>/logs so we can still find+kill an orphan from a pre-upgrade spawn.
50
- // TODO(v1.7): drop the legacy fallback.
51
- const centralPid = (0, config_1.bridgeLogPaths)(ensemble, playerName, logDir).pidPath;
52
- const legacyPid = (0, path_1.join)(logDir || (0, path_1.join)(workDir, 'logs'), `${playerName}.pid`);
53
- const pidPath = (0, fs_1.existsSync)(centralPid) ? centralPid : legacyPid;
46
+ // #690 — pid lives at the CENTRAL ~/.agent-tempo/logs/<ensemble>/ path.
47
+ const pidPath = (0, config_1.bridgeLogPaths)(ensemble, playerName, logDir).pidPath;
54
48
  if ((0, fs_1.existsSync)(pidPath)) {
55
49
  try {
56
50
  const pidStr = (0, fs_1.readFileSync)(pidPath, 'utf8').trim();
@@ -86,19 +86,14 @@ export interface MaestroActivityOptions {
86
86
  /** Activity interface — used by proxyActivities in the Maestro workflow. */
87
87
  export interface MaestroActivities {
88
88
  /**
89
- * Legacy V1 refresh the `costProfile: 'local'` path AND the replay
90
- * path for every maestro started before #748.
91
- * TODO(next major, #748): remove once the minimum deployment age
92
- * exceeds the longest-lived pre-#748 maestro history (they replay V1);
93
- * remove together with the V1 branch in workflows/maestro.ts.
94
- */
95
- refreshEnsembleState(ensemble: string): Promise<MaestroPlayerInfo[]>;
96
- /**
97
- * T0.1 (#748) — additive V2: called only by maestros started with
98
- * `costProfile: 'cloud'` in their input. SA/memo-based ensemble-scoped
99
- * scan + in-process observer presence for workflow-side cadence
100
- * stretching. V1 stays for in-flight pre-#748 maestros (replay safety)
101
- * and the local profile.
89
+ * Ensemble-scoped player scan. Returns the player rows plus `observersPresent`
90
+ * (SSE subscriber presence) for workflow-side cadence stretching on the cloud
91
+ * profile. Honors the daemon's cost profile internally: cloud → SA/memo
92
+ * ensemble-scoped scan, local the legacy visibility scan.
93
+ *
94
+ * 2.0 (#788): the only refresh activity — the former V1 `refreshEnsembleState`
95
+ * was removed; both profiles call this (the A2 cutover means no pre-#748
96
+ * maestro survives to need the V1 replay path).
102
97
  */
103
98
  refreshEnsembleStateV2(input: {
104
99
  ensemble: string;
@@ -55,16 +55,6 @@ function createMaestroActivities(client, opts = {}) {
55
55
  }
56
56
  };
57
57
  return (0, action_counters_1.tagActionSource)(maestroSource, {
58
- async refreshEnsembleState(ensemble) {
59
- try {
60
- const sessions = await (0, resolve_1.scanEnsembleSessions)(client, ensemble);
61
- return sessions.map(toPlayerInfo(ensemble));
62
- }
63
- catch (err) {
64
- log('refreshEnsembleState failed:', err);
65
- throw activity_1.ApplicationFailure.nonRetryable(`Failed to scan ensemble: ${err instanceof Error ? err.message : String(err)}`);
66
- }
67
- },
68
58
  async refreshEnsembleStateV2(input) {
69
59
  try {
70
60
  // Honor the DAEMON's configured profile for the scan strategy: a
@@ -764,8 +764,9 @@ function createOutboxActivities(client, config, ingestTokens, doorbells) {
764
764
  /**
765
765
  * D14 `deliverReset` — set a `pendingReset` flag on the target via
766
766
  * `setPendingResetSignal` (a signal, like deliverCue — NOT a direct
767
- * subprocess call). The Pi extension polls `pendingResetQuery`, performs the
768
- * clean-wipe (`newSession`), then clears it via `ackResetSignal(resetId)`.
767
+ * subprocess call). The Pi extension reads the pending reset off the
768
+ * combined `pendingIntake` query, performs the clean-wipe (`newSession`),
769
+ * then clears it via `ackResetSignal(resetId)`.
769
770
  */
770
771
  async deliverReset(input) {
771
772
  const { ensemble, targetPlayerId, resetId, fresh, reason, requestedBy } = input;
@@ -892,6 +893,7 @@ function createOutboxActivities(client, config, ingestTokens, doorbells) {
892
893
  adapterId,
893
894
  adapterClass,
894
895
  leaseMs: validation_1.DEFAULT_RESTART_LEASE_MS,
896
+ protocolVersion: constants_1.PROTOCOL_VERSION, // #786 — 2.0 wire handshake
895
897
  }],
896
898
  });
897
899
  // Step 5 — context seed (saved state and/or transcript replay).
@@ -6,6 +6,7 @@ exports.installProcessLifecycleTelemetry = installProcessLifecycleTelemetry;
6
6
  exports._resetProcessLifecycleTelemetryForTest = _resetProcessLifecycleTelemetryForTest;
7
7
  exports._liveAdaptersForTest = _liveAdaptersForTest;
8
8
  const signals_1 = require("../workflows/signals");
9
+ const constants_1 = require("../constants");
9
10
  const terminal_error_1 = require("./terminal-error");
10
11
  const action_counters_1 = require("../utils/action-counters");
11
12
  const log = (...args) => console.error('[agent-tempo:adapter]', ...args);
@@ -389,6 +390,7 @@ class BaseAttachment {
389
390
  adapterId: this.descriptor.adapterId,
390
391
  adapterClass: this.descriptor.adapterClass,
391
392
  leaseMs: 3 * this.descriptor.heartbeatMs,
393
+ protocolVersion: constants_1.PROTOCOL_VERSION, // #786 — 2.0 wire handshake
392
394
  ...(expectedAttachmentId ? { expectedAttachmentId } : {}),
393
395
  }],
394
396
  });
@@ -1147,6 +1149,7 @@ class BaseAttachment {
1147
1149
  adapterId: this.descriptor.adapterId,
1148
1150
  adapterClass: this.descriptor.adapterClass,
1149
1151
  leaseMs: 3 * this.descriptor.heartbeatMs,
1152
+ protocolVersion: constants_1.PROTOCOL_VERSION, // #786 — 2.0 wire handshake
1150
1153
  }],
1151
1154
  });
1152
1155
  // Success — rebuild pinned handle from the NEW runId and hand it to the subclass.
@@ -50,6 +50,14 @@ interface UpOpts extends CliOverrides {
50
50
  * can't exist there anyway (gate 3 in recruit pre-flight).
51
51
  */
52
52
  scenario?: string;
53
+ /**
54
+ * #786 — `up --from-upgrade`. Recreate protocol-2 workflows from the
55
+ * `upgrade-snapshot-v1.json` left by `upgrade-to-2`, instead of starting a
56
+ * fresh ensemble. Recreates the roster (structural essentials only),
57
+ * surfaces undelivered cues for review, and archives the snapshot on
58
+ * success. Ignores `--lineup` / conductor-spawn flags.
59
+ */
60
+ fromUpgrade?: boolean;
53
61
  }
54
62
  /**
55
63
  * #832 — the operator-facing "ensemble ready" lines for `up` / `conduct`.
@@ -1060,6 +1060,71 @@ function formatEnsembleReadyLines(ensemble, lineupName, playerCount) {
1060
1060
  `Connect: agent-tempo command-center ${ensemble}`,
1061
1061
  ];
1062
1062
  }
1063
+ /**
1064
+ * #786 — `up --from-upgrade` orchestration. Opens a Temporal client, recreates
1065
+ * protocol-2 workflows from `upgrade-snapshot-v1.json`, prints the outcome
1066
+ * (incl. undelivered cues surfaced for review — NEVER redelivered), and
1067
+ * archives the snapshot on success. Separated from `up()` so the I/O is in one
1068
+ * place; the recreation logic itself lives (testably) in
1069
+ * `src/upgrade/from-upgrade.ts`.
1070
+ */
1071
+ async function runFromUpgradeFlow(config) {
1072
+ const { runFromUpgrade } = await Promise.resolve().then(() => __importStar(require('../upgrade/from-upgrade')));
1073
+ const { SnapshotValidationError } = await Promise.resolve().then(() => __importStar(require('../upgrade/snapshot-v1')));
1074
+ out.heading('agent-tempo up --from-upgrade');
1075
+ const connection = await (0, connection_1.createTemporalConnection)(config);
1076
+ const client = new client_1.Client({ connection, namespace: config.temporalNamespace });
1077
+ try {
1078
+ const result = await runFromUpgrade(client, {
1079
+ home: config_1.AGENT_TEMPO_HOME,
1080
+ taskQueue: config.taskQueue,
1081
+ temporalAddress: config.temporalAddress,
1082
+ temporalNamespace: config.temporalNamespace,
1083
+ log: (...args) => out.dim(' ' + args.map(String).join(' ')),
1084
+ });
1085
+ if (!result.snapshotFound) {
1086
+ out.warn(`No upgrade snapshot found in ${config_1.AGENT_TEMPO_HOME}.`);
1087
+ out.log(` Run \`agent-tempo upgrade-to-2\` on the 1.x install first.`);
1088
+ return;
1089
+ }
1090
+ out.success(`Recreated ${result.workflowsCreated} protocol-2 workflow(s) across ` +
1091
+ `${result.ensemblesRecreated} ensemble(s).`);
1092
+ if (result.undeliveredCues.length > 0) {
1093
+ out.log('');
1094
+ out.warn(`${result.undeliveredCues.length} undelivered cue(s) — review (NOT redelivered):`);
1095
+ for (const c of result.undeliveredCues) {
1096
+ const preview = c.text.length > 120 ? c.text.slice(0, 117) + '…' : c.text;
1097
+ out.log(` [${c.ensemble}] ${c.from} → ${c.playerId} (${c.timestamp}): ${preview}`);
1098
+ }
1099
+ }
1100
+ if (result.failures.length > 0) {
1101
+ out.log('');
1102
+ out.error(`${result.failures.length} workflow(s) failed to recreate — snapshot left in place for retry:`);
1103
+ for (const f of result.failures)
1104
+ out.log(` ${f.workflowId}: ${f.error}`);
1105
+ process.exitCode = 1;
1106
+ return;
1107
+ }
1108
+ if (result.archived) {
1109
+ out.dim(` Snapshot archived → ${result.archivePath}`);
1110
+ }
1111
+ out.log('');
1112
+ out.log(' Continuity seeding (state slots / schedules / sessionId / model) is a');
1113
+ out.log(' tracked #786 fast-follow — recreated sessions are structural skeletons.');
1114
+ }
1115
+ catch (err) {
1116
+ // A malformed snapshot is a LOUD failure (never a silent skip).
1117
+ if (err instanceof SnapshotValidationError) {
1118
+ out.error(`Upgrade snapshot is corrupt — refusing to proceed: ${err.message}`);
1119
+ process.exitCode = 1;
1120
+ return;
1121
+ }
1122
+ throw err;
1123
+ }
1124
+ finally {
1125
+ await connection.close().catch(() => { });
1126
+ }
1127
+ }
1063
1128
  async function up(opts) {
1064
1129
  const config = (0, config_1.getConfig)(opts);
1065
1130
  // #689 — best-effort sweep of stale 0600 secret env files (residual from a shell
@@ -1111,6 +1176,16 @@ async function up(opts) {
1111
1176
  out.log(` ${out.dim('You can start it manually: agent-tempo daemon start')}`);
1112
1177
  process.exit(1);
1113
1178
  }
1179
+ // #786 — `up --from-upgrade`: recreate protocol-2 workflows from the snapshot
1180
+ // left by `upgrade-to-2`, then stop. Runs AFTER ensureInfra (Temporal + daemon
1181
+ // up, so the recreated workflows have live workers AND the daemon's boot guard
1182
+ // has already verified a clean cutover) but instead of the lineup/conductor
1183
+ // spawn flow. Continuity seeding (#334 state / schedules / sessionId / model)
1184
+ // is a tracked #786 fast-follow — this MVP recreates structural skeletons only.
1185
+ if (opts.fromUpgrade) {
1186
+ await runFromUpgradeFlow(config);
1187
+ return;
1188
+ }
1114
1189
  // Step 4: Register MCP server if needed
1115
1190
  if ((0, mcp_1.isMcpConfigured)(process.cwd())) {
1116
1191
  out.check('MCP configured', true);
@@ -1907,12 +1982,8 @@ async function down(opts) {
1907
1982
  * Returns a formatted string like " (pid 12345)" or "" if no PID file found.
1908
1983
  */
1909
1984
  function getBridgePidInfo(ensemble, name) {
1910
- // #690 — pid lives at the CENTRAL ~/.agent-tempo/logs/<ensemble>/ path; transitional
1911
- // READ-ONLY fallback to the legacy per-cwd ./logs for a pre-upgrade bridge.
1912
- // TODO(v1.7): drop the legacy fallback.
1913
- const centralPid = (0, config_1.bridgeLogPaths)(ensemble, name).pidPath;
1914
- const legacyPid = (0, path_1.join)(process.cwd(), 'logs', `${name}.pid`);
1915
- const pidPath = (0, fs_1.existsSync)(centralPid) ? centralPid : legacyPid;
1985
+ // #690 — pid lives at the CENTRAL ~/.agent-tempo/logs/<ensemble>/ path.
1986
+ const pidPath = (0, config_1.bridgeLogPaths)(ensemble, name).pidPath;
1916
1987
  if (!(0, fs_1.existsSync)(pidPath))
1917
1988
  return '';
1918
1989
  try {
@@ -1946,13 +2017,10 @@ function getBridgePidInfo(ensemble, name) {
1946
2017
  * — do NOT reuse this global scan-all from a scoped op: it would kill OTHER live
1947
2018
  * ensembles' bridges. The param is intentionally NOT added now (no caller needs it
1948
2019
  * = speculative; backlogged per the architect's deviation ruling).
1949
- *
1950
- * Plus a transitional READ of the legacy per-cwd `./logs` for a pre-upgrade
1951
- * bridge. TODO(v1.7): drop the legacy `./logs` dir.
1952
2020
  */
1953
2021
  function killBridgeProcesses() {
1954
2022
  const centralRoot = (0, config_1.bridgeLogsRoot)();
1955
- const dirs = [(0, path_1.join)(process.cwd(), 'logs')]; // legacy (transitional)
2023
+ const dirs = [];
1956
2024
  try {
1957
2025
  for (const ent of (0, fs_1.readdirSync)(centralRoot, { withFileTypes: true })) {
1958
2026
  if (ent.isDirectory())
@@ -1960,7 +2028,7 @@ function killBridgeProcesses() {
1960
2028
  }
1961
2029
  }
1962
2030
  catch {
1963
- // no central logs root yet — nothing central to scan
2031
+ // no central logs root yet — nothing to scan
1964
2032
  }
1965
2033
  for (const logsDir of dirs) {
1966
2034
  if (!(0, fs_1.existsSync)(logsDir))
@@ -371,28 +371,20 @@ async function daemonInstall() {
371
371
  async function daemonUninstall() {
372
372
  const platform = process.platform;
373
373
  if (platform === 'linux') {
374
- // Backward-compat: clean up either the new (`agent-tempo.service`) or
375
- // legacy (`agent-tempo.service`) unit so v0.x installs migrate cleanly.
376
374
  const dirSystemd = (0, path_1.join)((0, os_1.homedir)(), '.config', 'systemd', 'user');
377
- const dstNew = (0, path_1.join)(dirSystemd, 'agent-tempo.service');
378
- const dstLegacy = (0, path_1.join)(dirSystemd, 'agent-tempo.service');
379
- for (const svc of ['agent-tempo', 'agent-tempo']) {
380
- try {
381
- (0, child_process_1.execFileSync)('systemctl', ['--user', 'disable', '--now', svc], { stdio: 'ignore' });
382
- }
383
- catch { /* may not be running */ }
375
+ const dst = (0, path_1.join)(dirSystemd, 'agent-tempo.service');
376
+ try {
377
+ (0, child_process_1.execFileSync)('systemctl', ['--user', 'disable', '--now', 'agent-tempo'], { stdio: 'ignore' });
384
378
  }
385
- let removed = false;
379
+ catch { /* may not be running */ }
386
380
  try {
387
- for (const dst of [dstNew, dstLegacy]) {
388
- if ((0, fs_1.existsSync)(dst)) {
389
- (0, fs_1.unlinkSync)(dst);
390
- out.success(`Removed ${dst}`);
391
- removed = true;
392
- }
381
+ if ((0, fs_1.existsSync)(dst)) {
382
+ (0, fs_1.unlinkSync)(dst);
383
+ out.success(`Removed ${dst}`);
393
384
  }
394
- if (!removed)
385
+ else {
395
386
  out.log('No systemd unit file found.');
387
+ }
396
388
  }
397
389
  catch (err) {
398
390
  out.error(`Failed to remove systemd unit: ${err?.message ?? err}`);
@@ -401,24 +393,18 @@ async function daemonUninstall() {
401
393
  return;
402
394
  }
403
395
  if (platform === 'darwin') {
404
- // Backward-compat: clean up either the new (`com.agent.tempo.plist`) or
405
- // legacy (`com.claude.tempo.plist`) plist so v0.x installs migrate cleanly.
406
- const dstNew = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents', 'com.agent.tempo.plist');
407
- const dstLegacy = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents', 'com.claude.tempo.plist');
408
- let removed = false;
409
- for (const dst of [dstNew, dstLegacy]) {
410
- try {
411
- (0, child_process_1.execFileSync)('launchctl', ['unload', dst], { stdio: 'ignore' });
412
- }
413
- catch { /* may not be loaded */ }
414
- if ((0, fs_1.existsSync)(dst)) {
415
- (0, fs_1.unlinkSync)(dst);
416
- out.success(`Removed ${dst}`);
417
- removed = true;
418
- }
396
+ const dst = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents', 'com.agent.tempo.plist');
397
+ try {
398
+ (0, child_process_1.execFileSync)('launchctl', ['unload', dst], { stdio: 'ignore' });
419
399
  }
420
- if (!removed)
400
+ catch { /* may not be loaded */ }
401
+ if ((0, fs_1.existsSync)(dst)) {
402
+ (0, fs_1.unlinkSync)(dst);
403
+ out.success(`Removed ${dst}`);
404
+ }
405
+ else {
421
406
  out.log('No launchd plist found.');
407
+ }
422
408
  return;
423
409
  }
424
410
  if (platform === 'win32') {
@@ -61,11 +61,11 @@ function printHelp() {
61
61
  ${out.bold('agent-tempo')} — Multi-session Claude Code coordination via Temporal
62
62
 
63
63
  ${out.bold('Getting started:')}
64
- ${out.cyan('agent-tempo up')} Start infrastructure, then launch the TUI with ${out.dim('agent-tempo')}
64
+ ${out.cyan('agent-tempo up')} Start infrastructure, then run ${out.dim('agent-tempo')} for status + operator hints
65
65
 
66
66
  ${out.bold('Usage:')}
67
- agent-tempo Launch the TUI (auto-provisions + opens home view)
68
- agent-tempo <ensemble> Launch the TUI directly into an ensemble view
67
+ agent-tempo Auto-provision, show status, and print operator hints
68
+ agent-tempo <ensemble> Same, scoped to one ensemble's status
69
69
  agent-tempo <command> [options]
70
70
 
71
71
  ${out.bold('Commands:')}
@@ -95,10 +95,10 @@ ${out.bold('Commands:')}
95
95
  ${out.cyan('preflight')} Run preflight checks only
96
96
  ${out.cyan('help')} Show this help message
97
97
 
98
- ${out.bold('Removed — use the TUI:')}
99
- ${out.dim('stop / restart / detach / migrate')} → ${out.dim('/destroy · /restart · /shutdown')}
100
- ${out.dim('conduct / start / recruit / disband')} → ${out.dim('launch `agent-tempo` · /recruit · /destroy')}
101
- ${out.dim('resume')} → ${out.dim('/play')}
98
+ ${out.bold('Removed — use the command-center board')} ${out.dim('(agent-tempo command-center)')}:
99
+ ${out.dim('stop / restart / detach / migrate')} → ${out.dim('/destroy · /restart · /shutdown · /migrate')}
100
+ ${out.dim('conduct / start / recruit / disband')} → ${out.dim('agent-tempo up · /recruit · /destroy')}
101
+ ${out.dim('resume / tui')} → ${out.dim('/play · run `agent-tempo` (status) or `command-center` (board)')}
102
102
  See https://github.com/vinceblank/agent-tempo/issues/285 for the full migration table.
103
103
 
104
104
  ${out.bold('Connection options (all commands):')}
@@ -0,0 +1,26 @@
1
+ import type { BootstrapResult } from './startup';
2
+ export interface RenderHomeOpts {
3
+ /** Whether the Pi operator seat is installed (drives the command-center hint). */
4
+ piAvailable: boolean;
5
+ /** This binary's version (header line). */
6
+ version: string;
7
+ /** Hostname for the header (injectable for tests). */
8
+ hostname?: string;
9
+ }
10
+ /**
11
+ * Pure renderer: BootstrapResult → status lines + next-step hints. `result`
12
+ * may be undefined when bootstrap threw outside its step boundaries — the
13
+ * home still renders with a degraded note (mirrors the old TUI's tolerance).
14
+ */
15
+ export declare function renderHome(result: BootstrapResult | undefined, opts: RenderHomeOpts): string[];
16
+ /**
17
+ * The `home` (bare-command) handler: bootstrap → render → exit. Bootstrap is
18
+ * best-effort exactly as the TUI treated it — a throw outside step
19
+ * boundaries degrades to the no-result rendering, never a crash.
20
+ */
21
+ export declare function runHome(opts: {
22
+ /** `null` = bootstrap skipped (`--skip-preflight`) — render the degraded home. */
23
+ bootstrap: (() => Promise<BootstrapResult>) | null;
24
+ piAvailable: boolean;
25
+ version: string;
26
+ }): Promise<void>;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.renderHome = renderHome;
37
+ exports.runHome = runHome;
38
+ /**
39
+ * Bare-command home (E.8, #789) — what `agent-tempo` with no verb does now
40
+ * that the Ink TUI is gone: run the #289 six-step auto-provisioning
41
+ * bootstrap, then print a STATUS snapshot + contextual next-step hints and
42
+ * exit. No alternate screen, no event loop — the live operator surfaces are
43
+ * mission-control (`agent-tempo command-center`, when the Pi seat is
44
+ * available) and the zero-dependency web dashboard.
45
+ *
46
+ * `renderHome` is PURE (BootstrapResult + flags → lines) so the hint logic
47
+ * unit-tests without a daemon, npm, or Pi install.
48
+ */
49
+ const os = __importStar(require("os"));
50
+ const out = __importStar(require("./output"));
51
+ const probe_1 = require("../pi/probe");
52
+ /** Friendly labels for the bootstrap steps. */
53
+ const STEP_LABELS = {
54
+ preflight: 'preflight',
55
+ mcpConfig: 'MCP registration',
56
+ temporalReach: 'Temporal reachability',
57
+ searchAttrs: 'search attributes',
58
+ daemonBoot: 'daemon',
59
+ badgeCollection: 'badges',
60
+ };
61
+ const STEP_GLYPH = {
62
+ ok: '✓',
63
+ skipped: '·',
64
+ 'action-taken': '+',
65
+ failed: '✗',
66
+ };
67
+ /**
68
+ * Pure renderer: BootstrapResult → status lines + next-step hints. `result`
69
+ * may be undefined when bootstrap threw outside its step boundaries — the
70
+ * home still renders with a degraded note (mirrors the old TUI's tolerance).
71
+ */
72
+ function renderHome(result, opts) {
73
+ const lines = [];
74
+ const host = opts.hostname ?? os.hostname();
75
+ lines.push(`agent-tempo v${opts.version} — ${host}`);
76
+ lines.push('');
77
+ // ── Bootstrap status (one line per non-quiet step) ──
78
+ if (result) {
79
+ const parts = [];
80
+ for (const [step, outcome] of Object.entries(result.steps)) {
81
+ if (outcome.status === 'skipped' || outcome.status === 'ok')
82
+ continue; // quiet steady state
83
+ parts.push(`${STEP_GLYPH[outcome.status]} ${STEP_LABELS[step]}${outcome.detail ? ` (${outcome.detail})` : ''}`);
84
+ }
85
+ lines.push(parts.length === 0
86
+ ? `bootstrap ✓ all steps healthy (${result.durationMs}ms)`
87
+ : `bootstrap: ${parts.join(' · ')}`);
88
+ // ── Badges ──
89
+ const b = result.badges;
90
+ if (b.orphanCount > 0) {
91
+ lines.push(`⚠ ${b.orphanCount} orphaned session${b.orphanCount === 1 ? '' : 's'} on this host — \`agent-tempo restore\` to re-attach`);
92
+ }
93
+ if (b.daemonLogErrors) {
94
+ lines.push(`⚠ ${b.daemonLogErrors.count} recent daemon-log error${b.daemonLogErrors.count === 1 ? '' : 's'} — see ${b.daemonLogErrors.logPath}`);
95
+ }
96
+ if (b.outdatedVersion) {
97
+ lines.push(`↑ v${b.outdatedVersion.latest} available (${b.outdatedVersion.severity}) — \`agent-tempo upgrade\``);
98
+ }
99
+ // ── Ensembles ──
100
+ lines.push('');
101
+ if (result.ensembles.length === 0) {
102
+ lines.push('No ensembles running.');
103
+ }
104
+ else {
105
+ lines.push(`Ensembles (${result.ensembles.length}):`);
106
+ for (const e of result.ensembles) {
107
+ const state = e.state ? ` — ${e.state}` : '';
108
+ lines.push(` ${e.name}: ${e.playerCount} player${e.playerCount === 1 ? '' : 's'}${e.hasConductor ? '' : ', no conductor'}${state}`);
109
+ }
110
+ }
111
+ }
112
+ else {
113
+ lines.push('bootstrap unavailable — run `agent-tempo daemon status` to diagnose.');
114
+ lines.push('');
115
+ }
116
+ // ── Next steps (contextual) ──
117
+ lines.push('');
118
+ lines.push('Next steps:');
119
+ const hasEnsembles = (result?.ensembles.length ?? 0) > 0;
120
+ if (!hasEnsembles) {
121
+ lines.push(' agent-tempo up start an ensemble here (--lineup <name> for a preset)');
122
+ }
123
+ if (opts.piAvailable) {
124
+ const desc = hasEnsembles
125
+ ? 'live board + operator seat (Pi)'
126
+ : 'operator seat once an ensemble is up (Pi)';
127
+ lines.push(` agent-tempo command-center ${desc}`);
128
+ }
129
+ lines.push(' agent-tempo dashboard web dashboard (zero-dependency fallback)');
130
+ if (hasEnsembles) {
131
+ lines.push(' agent-tempo status <ensemble> one-shot ensemble detail');
132
+ }
133
+ if (!opts.piAvailable) {
134
+ lines.push(` npm install -g ${probe_1.PI_PACKAGE} unlock the command-center seat (Node ≥ ${probe_1.PI_NODE_FLOOR})`);
135
+ }
136
+ return lines;
137
+ }
138
+ /**
139
+ * The `home` (bare-command) handler: bootstrap → render → exit. Bootstrap is
140
+ * best-effort exactly as the TUI treated it — a throw outside step
141
+ * boundaries degrades to the no-result rendering, never a crash.
142
+ */
143
+ async function runHome(opts) {
144
+ let result;
145
+ if (opts.bootstrap) {
146
+ try {
147
+ result = await opts.bootstrap();
148
+ }
149
+ catch (err) {
150
+ out.warn(`Bootstrap hit an unexpected error: ${err instanceof Error ? err.message : String(err)}`);
151
+ }
152
+ }
153
+ for (const line of renderHome(result, { piAvailable: opts.piAvailable, version: opts.version })) {
154
+ out.log(line);
155
+ }
156
+ }
@@ -1,7 +1,3 @@
1
- /**
2
- * Short hints completing "Use the TUI: agent-tempo → …". Keys are the
3
- * removed CLI verbs; values name the TUI equivalent.
4
- */
5
1
  export declare const REMOVED_VERBS: Record<string, string>;
6
2
  /** Format the error message for a single removed verb. */
7
3
  export declare function removedVerbMessage(verb: string): string;
@@ -53,24 +53,35 @@ exports.printRemovedVerbMessage = printRemovedVerbMessage;
53
53
  */
54
54
  const out = __importStar(require("./output"));
55
55
  /**
56
- * Short hints completing "Use the TUI: agent-tempo …". Keys are the
57
- * removed CLI verbs; values name the TUI equivalent.
56
+ * Hints for removed CLI verbs. Keys are the removed verbs; values complete
57
+ * the "Use …" sentence in {@link removedVerbMessage}.
58
+ *
59
+ * #789 (E.8): the Ink TUI is gone, so the #288-era "Use the TUI: …" hints
60
+ * were rewritten against the surviving operator surfaces — mission-control
61
+ * (`agent-tempo command-center`, slash commands) with the web dashboard as
62
+ * the zero-dependency fallback. Per the v2 scoping plan (§C.3), this WHOLE
63
+ * file — every entry including the new `tui` one — ships for one 2.0
64
+ * release as migration hints and is then deleted as a unit.
58
65
  */
66
+ const CC = 'the command-center: agent-tempo command-center → ';
59
67
  exports.REMOVED_VERBS = {
60
- stop: '/destroy',
61
- conduct: 'launch directly (the TUI auto-provisions the conductor)',
62
- start: '/recruit <name>',
63
- disband: '/destroy',
64
- detach: '/shutdown (ensemble-wide) detach is no longer a user-facing verb',
65
- restart: '/restart <player>',
66
- recruit: '/recruit <name>',
67
- migrate: '/migrate <player> <host>',
68
- resume: '/play',
68
+ stop: `${CC}/destroy <player>`,
69
+ conduct: '`agent-tempo up` (auto-provisions the conductor), then `agent-tempo command-center`',
70
+ start: `${CC}/recruit <name>`,
71
+ disband: `${CC}/destroy <player> (or /ensemble-down)`,
72
+ detach: `${CC}/ensemble-down (detach is no longer a user-facing verb)`,
73
+ restart: `${CC}/restart <player>`,
74
+ recruit: `${CC}/recruit <name>`,
75
+ migrate: `${CC}/migrate <player> <host>`,
76
+ resume: `${CC}/play`,
77
+ // #789 — the TUI launch verb itself. Bare `agent-tempo` is now the status
78
+ // home; the live surfaces are the command-center and the web dashboard.
79
+ tui: 'bare `agent-tempo` for status, `agent-tempo command-center` for the live board (Pi seat), or `agent-tempo dashboard`',
69
80
  };
70
81
  /** Format the error message for a single removed verb. */
71
82
  function removedVerbMessage(verb) {
72
83
  const hint = exports.REMOVED_VERBS[verb];
73
- return `"${verb}" is no longer a CLI verb. Use the TUI: agent-tempo → ${hint}. See https://github.com/vinceblank/agent-tempo/issues/285 for details.`;
84
+ return `"${verb}" is no longer a CLI verb. Use ${hint}. See https://github.com/vinceblank/agent-tempo/issues/285 for details.`;
74
85
  }
75
86
  /** Print the removed-verb error to stderr via the shared output helpers. */
76
87
  function printRemovedVerbMessage(verb) {