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
@@ -15,20 +15,6 @@ export declare const REQUIRED_SEARCH_ATTRIBUTES: ReadonlyArray<{
15
15
  name: string;
16
16
  type: 'Keyword' | 'Bool';
17
17
  }>;
18
- /**
19
- * T0.5 (#747) — legacy attributes that pre-v1.8 deployments registered and
20
- * pre-v1.8 workflow runs still carry. NEVER auto-unregistered (dropping a
21
- * search attribute is a privileged, operator-only action — see
22
- * `docs/ops/sa-diet-migration.md`); fresh namespaces simply don't register
23
- * them. Kept here so tooling can name them in diagnostics and so the
24
- * dual-read fallback window (memo preferred, SA fallback in
25
- * `utils/search-attributes.ts`) is documented next to the list it falls
26
- * back to. Dual-read fallback may be removed at the next major.
27
- */
28
- export declare const LEGACY_SEARCH_ATTRIBUTES: ReadonlyArray<{
29
- name: string;
30
- type: 'Keyword' | 'Bool';
31
- }>;
32
18
  export interface SearchAttributePreflightOpts {
33
19
  temporalAddress: string;
34
20
  temporalNamespace: string;
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.LEGACY_SEARCH_ATTRIBUTES = exports.REQUIRED_SEARCH_ATTRIBUTES = void 0;
36
+ exports.REQUIRED_SEARCH_ATTRIBUTES = void 0;
37
37
  exports.defaultProbeRegisteredAttributes = defaultProbeRegisteredAttributes;
38
38
  exports.isTemporalCloud = isTemporalCloud;
39
39
  exports.sdkProbeRegisteredAttributes = sdkProbeRegisteredAttributes;
@@ -97,22 +97,6 @@ exports.REQUIRED_SEARCH_ATTRIBUTES = Object.freeze([
97
97
  { name: 'AgentTempoAttachedHost', type: 'Keyword' },
98
98
  { name: 'AgentTempoAttachmentState', type: 'Keyword' },
99
99
  ]);
100
- /**
101
- * T0.5 (#747) — legacy attributes that pre-v1.8 deployments registered and
102
- * pre-v1.8 workflow runs still carry. NEVER auto-unregistered (dropping a
103
- * search attribute is a privileged, operator-only action — see
104
- * `docs/ops/sa-diet-migration.md`); fresh namespaces simply don't register
105
- * them. Kept here so tooling can name them in diagnostics and so the
106
- * dual-read fallback window (memo preferred, SA fallback in
107
- * `utils/search-attributes.ts`) is documented next to the list it falls
108
- * back to. Dual-read fallback may be removed at the next major.
109
- */
110
- exports.LEGACY_SEARCH_ATTRIBUTES = Object.freeze([
111
- { name: 'AgentTempoGitRoot', type: 'Keyword' },
112
- { name: 'AgentTempoPlayerType', type: 'Keyword' },
113
- { name: 'AgentTempoIsConductor', type: 'Bool' },
114
- { name: 'AgentTempoAttachmentId', type: 'Keyword' },
115
- ]);
116
100
  /**
117
101
  * Default probe — shells out to `temporal operator search-attribute list`.
118
102
  * Returns an empty set on any error so callers fall through to the
@@ -1,6 +1,6 @@
1
1
  import { Config } from '../config';
2
2
  import type { EnsembleSummary } from '../client/interface';
3
- export type StepName = 'legacyHomeMigration' | 'preflight' | 'mcpConfig' | 'temporalReach' | 'searchAttrs' | 'daemonBoot' | 'badgeCollection';
3
+ export type StepName = 'preflight' | 'mcpConfig' | 'temporalReach' | 'searchAttrs' | 'daemonBoot' | 'badgeCollection';
4
4
  /** Structured per-step result. `ok` = ran successfully; `skipped` = cache hit;
5
5
  * `action-taken` = needed repair (e.g. started daemon, registered MCP);
6
6
  * `failed` = non-fatal failure recorded for the TUI to surface. */
@@ -55,15 +55,6 @@ export interface BootstrapArgs {
55
55
  * simulates a successful start; `'failed'` + `detail` simulates failure.
56
56
  */
57
57
  daemonBoot?: (config: Config) => Promise<StepOutcome>;
58
- /**
59
- * PR-2 of the v1.0 rebrand — one-shot copy of `~/.agent-tempo/` →
60
- * `~/.agent-tempo/`. Runs once on first boot of the new binary; idempotent
61
- * thereafter. Tests should override to avoid touching real $HOME.
62
- *
63
- * Failure here MUST NOT block the boot path — record `'failed'` with a
64
- * detail string and let the rest of bootstrap proceed.
65
- */
66
- legacyHomeMigration?: () => Promise<StepOutcome>;
67
58
  /**
68
59
  * Inject the Temporal reachability probe (steps 3 + 6). Tests override
69
60
  * to avoid real network I/O — default performs `createTemporalConnection`
@@ -73,7 +73,6 @@ const orphans_1 = require("../reconcile/orphans");
73
73
  const client_2 = require("../client");
74
74
  const os_1 = require("os");
75
75
  const git_info_1 = require("../git-info");
76
- const legacy_migration_1 = require("./legacy-migration");
77
76
  // ─────────────────────────────────────────────────────────────────────────
78
77
  // Cache (schema v1)
79
78
  // ─────────────────────────────────────────────────────────────────────────
@@ -241,38 +240,6 @@ function daemonLogErrorTail(logPath) {
241
240
  // ─────────────────────────────────────────────────────────────────────────
242
241
  // Individual steps
243
242
  // ─────────────────────────────────────────────────────────────────────────
244
- /**
245
- * Default step-0 implementation — invokes the one-shot legacy home migration
246
- * (`~/.agent-tempo/` → `~/.agent-tempo/`). Idempotent: re-runs are
247
- * `'skipped'` once the marker is in place. Failure is recorded but never
248
- * blocks bootstrap (PR-2 brief).
249
- */
250
- async function defaultLegacyHomeMigrationStep() {
251
- const t0 = perf_hooks_1.performance.now();
252
- try {
253
- const result = await (0, legacy_migration_1.migrateLegacyHome)();
254
- const durationMs = perf_hooks_1.performance.now() - t0;
255
- const detail = (0, legacy_migration_1.formatMigrationResult)(result);
256
- switch (result.status) {
257
- case 'no-legacy':
258
- case 'already-migrated':
259
- return { status: 'skipped', durationMs, detail };
260
- case 'migrated':
261
- return { status: 'action-taken', durationMs, detail };
262
- case 'skipped':
263
- return { status: 'skipped', durationMs, detail };
264
- case 'failed':
265
- return { status: 'failed', durationMs, detail };
266
- }
267
- }
268
- catch (err) {
269
- return {
270
- status: 'failed',
271
- durationMs: perf_hooks_1.performance.now() - t0,
272
- detail: `legacy-migration crashed: ${err.message}`,
273
- };
274
- }
275
- }
276
243
  async function stepPreflight(cache, now) {
277
244
  if (isCacheFresh(cache.steps.preflight, TTL_24H, now)) {
278
245
  return { status: 'skipped', durationMs: 0 };
@@ -622,12 +589,6 @@ async function bootstrap(args) {
622
589
  const fetchLatest = args.fetchLatestVersion ?? defaultFetchLatestVersion;
623
590
  const cache = readCache(cacheDir, binaryVersion);
624
591
  const reachableProbe = args.isTemporalReachable ?? ((c) => isTemporalReachable(c, 3000));
625
- // Step 0: legacy home migration (`~/.agent-tempo/` → `~/.agent-tempo/`)
626
- // — PR-2 of the v1.0 rebrand. Runs before preflight so subsequent steps
627
- // (cache read, MCP config write, daemon boot) all see the new home.
628
- // MUST NOT block bootstrap — failure becomes a `'failed'` step with
629
- // `detail`, and we keep going.
630
- const legacyHomeMigration = await (args.legacyHomeMigration ?? defaultLegacyHomeMigrationStep)();
631
592
  // Steps 1–5: fail-fast on step 1 so we don't try to register MCP on an
632
593
  // incompatible Node. Each step mutates the cache in-place.
633
594
  const preflight = await stepPreflight(cache, now);
@@ -668,7 +629,6 @@ async function bootstrap(args) {
668
629
  return {
669
630
  durationMs: perf_hooks_1.performance.now() - start,
670
631
  steps: {
671
- legacyHomeMigration,
672
632
  preflight,
673
633
  mcpConfig,
674
634
  temporalReach,
package/dist/cli.js CHANGED
@@ -60,7 +60,6 @@ const out = __importStar(require("./cli/output"));
60
60
  const dev_banner_1 = require("./cli/dev-banner");
61
61
  const types_1 = require("./types");
62
62
  const config_1 = require("./config");
63
- const legacy_migration_1 = require("./cli/legacy-migration");
64
63
  const global_wrapper_1 = require("./cli/global-wrapper");
65
64
  const resolve_ensemble_1 = require("./cli/resolve-ensemble");
66
65
  const grpc_shutdown_guard_1 = require("./utils/grpc-shutdown-guard");
@@ -68,7 +67,9 @@ const grpc_shutdown_guard_1 = require("./utils/grpc-shutdown-guard");
68
67
  const PACKAGE_ROOT = (0, path_1.resolve)(__dirname, '..');
69
68
  function parseArgs(argv) {
70
69
  const result = {
71
- command: 'tui',
70
+ // #789 — the TUI is deleted; a bare `agent-tempo` now resolves to the
71
+ // `home` command (bootstrap + status + hints), NOT a terminal UI launch.
72
+ command: 'home',
72
73
  positional: [],
73
74
  dir: process.cwd(),
74
75
  skipPreflight: false,
@@ -145,6 +146,11 @@ function parseArgs(argv) {
145
146
  else if (arg === '--no-hold') {
146
147
  result.noHold = true;
147
148
  }
149
+ else if (arg === '--from-upgrade') {
150
+ // `up --from-upgrade` (#786) — recreate protocol-2 workflows from the
151
+ // upgrade snapshot left by `upgrade-to-2`.
152
+ result.fromUpgrade = true;
153
+ }
148
154
  else if (arg === '--destroy') {
149
155
  result.destroy = true;
150
156
  }
@@ -168,12 +174,10 @@ function parseArgs(argv) {
168
174
  result.host = argv[++i];
169
175
  }
170
176
  else if (arg === '--force') {
171
- // Consumed by `daemon start --force` (bypass stale-PID guard) and by
172
- // `migrate-from-claude-tempo --force` (bypass conflict + volatile-state guards).
177
+ // Consumed by `daemon start --force` (bypass stale-PID guard).
173
178
  result.force = true;
174
179
  }
175
180
  else if (arg === '--dry-run') {
176
- // `migrate-from-claude-tempo --dry-run` — print the plan, write nothing.
177
181
  // `upgrade-to-2 --dry-run` — print snapshot + destroy list, change nothing.
178
182
  result.dryRun = true;
179
183
  }
@@ -302,39 +306,6 @@ function cliOverrides(args) {
302
306
  temporalTlsKeyPath: args.temporalTlsKeyPath,
303
307
  };
304
308
  }
305
- /**
306
- * Format + print a {@link LegacyMigrationResult} from
307
- * `migrate-from-claude-tempo`. Extracted to module scope (an if/else cascade
308
- * rather than a nested switch) so the body doesn't trip the CLI surface-drift
309
- * detector, which scans top-level `case '...':` labels in this file.
310
- */
311
- function reportMigrationResult(result) {
312
- const msg = (0, legacy_migration_1.formatMigrationResult)(result);
313
- if (result.status === 'no-legacy' || result.status === 'already-migrated') {
314
- out.success(msg);
315
- return;
316
- }
317
- if (result.status === 'migrated') {
318
- out.success(msg);
319
- if (result.copiedFiles?.length) {
320
- for (const f of result.copiedFiles)
321
- out.log(` ${out.dim('+')} ${f}`);
322
- }
323
- if (result.errors?.length) {
324
- for (const e of result.errors)
325
- out.warn(e);
326
- }
327
- return;
328
- }
329
- if (result.status === 'skipped') {
330
- out.warn(msg);
331
- process.exitCode = 1;
332
- return;
333
- }
334
- // 'failed'
335
- out.error(msg);
336
- process.exitCode = 1;
337
- }
338
309
  async function main() {
339
310
  // Neutralize the Temporal/grpc-js "Channel has been shut down" retry-after-
340
311
  // close race before any Temporal-touching command runs. See
@@ -519,6 +490,7 @@ async function main() {
519
490
  noHold: args.noHold,
520
491
  agent: resolvedAgent(),
521
492
  ...(args.scenario ? { scenario: args.scenario } : {}),
493
+ ...(args.fromUpgrade ? { fromUpgrade: true } : {}),
522
494
  ...overrides,
523
495
  });
524
496
  break;
@@ -687,20 +659,6 @@ async function main() {
687
659
  out.log(out.dim(' A bare `pi` stays a plain coding session (neither extension activates).'));
688
660
  break;
689
661
  }
690
- case 'migrate-from-claude-tempo': {
691
- // PR-2 of the v1.0 rebrand — one-shot copy of `~/.agent-tempo/` →
692
- // `~/.agent-tempo/`. Crash-proof (no Temporal deps). The `--dev`
693
- // top-level flag selects the dev profile (`~/.agent-tempo-dev/` →
694
- // `~/.agent-tempo-dev/`) via {@link isDevMode}.
695
- const { migrateLegacyHome } = await Promise.resolve().then(() => __importStar(require('./cli/legacy-migration')));
696
- const result = await migrateLegacyHome({
697
- dryRun: !!args.dryRun,
698
- force: !!args.force,
699
- profile: (0, config_1.isDevMode)() ? 'dev' : 'prod',
700
- });
701
- reportMigrationResult(result);
702
- break;
703
- }
704
662
  case 'preflight': {
705
663
  // Preflight legitimately requires Temporal (that's what it tests), so
706
664
  // it's NOT in the crash-proof module set. Dynamic-imported here so its
@@ -721,35 +679,31 @@ async function main() {
721
679
  out.success('All checks passed');
722
680
  break;
723
681
  }
724
- case 'tui': {
682
+ case 'home': {
683
+ // E.8 / D2 (#789) — bare `agent-tempo`: the #289 six-step auto-provisioning
684
+ // bootstrap (MCP registration, Temporal reachability, daemon boot,
685
+ // badges + ensembles prefetch), then a status snapshot + next-step hints.
686
+ // One-shot; the live operator surfaces are mission-control
687
+ // (`command-center`, when the Pi seat is installed) and the web dashboard.
688
+ // `--skip-preflight` renders the degraded no-result home. The rich,
689
+ // unit-tested render lives in the crash-proof `home-command` module.
725
690
  const config = (0, config_1.getConfig)(overrides);
726
- // If --ensemble or positional arg given, start in single-ensemble view.
727
- // Otherwise, start in multi-ensemble home view.
728
- const tuiEnsemble = args.ensemble || args.positional[1] || undefined;
729
- // #289 / S7: run the auto-provisioning bootstrap before handing off
730
- // to the TUI. Steps 1–6 register MCP, ensure Temporal reachability,
731
- // boot the daemon, and prefetch badges + ensembles so the TUI home
732
- // view renders instantly. `skipPreflight` bypass: power users can
733
- // opt out if the cache / disk-probe cost ever regresses (no current
734
- // way to trigger, but the flag is already on the parser).
735
- let bootstrapResult;
736
- if (!args.skipPreflight) {
737
- const { bootstrap } = await Promise.resolve().then(() => __importStar(require('./cli/startup')));
738
- try {
739
- bootstrapResult = await bootstrap({ config });
740
- }
741
- catch (err) {
742
- // Bootstrap is best-effort — per-step failures degrade into
743
- // `StepOutcome.status: 'failed'` and a usable result. A thrown
744
- // error means something outside the step boundaries broke
745
- // (e.g. cache-dir creation). Log + continue with an undefined
746
- // bootstrap payload so the TUI still launches.
747
- out.warn(`Bootstrap hit an unexpected error: ${err instanceof Error ? err.message : String(err)}`);
748
- }
749
- }
750
- // Dynamic import — TUI module uses ESM ink
751
- const { run: runTui } = await Promise.resolve().then(() => __importStar(require('./tui/index')));
752
- await runTui({ config, ensemble: tuiEnsemble, ...(bootstrapResult ? { bootstrap: bootstrapResult } : {}) });
691
+ const { runHome } = await Promise.resolve().then(() => __importStar(require('./cli/home-command')));
692
+ const { probePi, checkPiNodeFloor } = await Promise.resolve().then(() => __importStar(require('./pi/probe')));
693
+ const pkgVersion = require('../package.json').version;
694
+ await runHome({
695
+ bootstrap: args.skipPreflight
696
+ ? null
697
+ : async () => {
698
+ const { bootstrap } = await Promise.resolve().then(() => __importStar(require('./cli/startup')));
699
+ return bootstrap({ config });
700
+ },
701
+ // The command-center seat needs BOTH the Pi package and the Node floor
702
+ // (the same pair command-center-command itself enforces) suggesting
703
+ // it on an old Node would dead-end the user.
704
+ piAvailable: probePi().available && checkPiNodeFloor().ok,
705
+ version: pkgVersion,
706
+ });
753
707
  break;
754
708
  }
755
709
  // `version`, `help`, `upgrade`, `config`, `daemon`, and removed-verb
@@ -42,9 +42,9 @@ exports.createTempoClientCore = createTempoClientCore;
42
42
  * future SSE event source, and any external SDK consumer that wants a
43
43
  * headless surface (no `child_process` dependency).
44
44
  *
45
- * The two spawn methods (`createEnsemble`, `spawnConductor`) and the
46
- * `runTempoCli` helper live in `./with-spawn.ts`, which composes this
47
- * factory and adds the TTY-bound surface.
45
+ * The spawn method (`createEnsemble`) and the `runTempoCli` helper live in
46
+ * `./with-spawn.ts`, which composes this factory and adds the TTY-bound surface.
47
+ * (`spawnConductor` was removed in #789 with the Ink TUI.)
48
48
  *
49
49
  * See `docs/adr/0007-tempoclient-core-withspawn-split.md` and
50
50
  * `docs/design/tempoclient-core-spawn-split.md`.
@@ -207,10 +207,10 @@ function createTempoClientCore(client, opts = {}) {
207
207
  continue;
208
208
  const entry = ensembleMap.get(name) || { count: 0, hasConductor: false };
209
209
  entry.count++;
210
- // Preferred: memo `AgentTempoIsConductor` (v1.8 SA diet) with
211
- // legacy-SA fallback via the dual-read helper. Final fallback:
212
- // workflow ID convention — covers the brief window after a
213
- // conductor spawn before visibility propagates.
210
+ // Preferred: memo `AgentTempoIsConductor` (memo-only after the #788
211
+ // legacy-SA prune). Final fallback: workflow ID convention covers
212
+ // the brief window after a conductor spawn before visibility
213
+ // propagates.
214
214
  const isConductorFromSA = (0, search_attributes_1.getIsConductor)(wf) === true;
215
215
  const isConductorFromId = wf.workflowId?.endsWith('-conductor') ?? false;
216
216
  if (isConductorFromSA || isConductorFromId) {
@@ -257,10 +257,9 @@ function createTempoClientCore(client, opts = {}) {
257
257
  // agent — counting it produced confusing "(2 players)" rows on
258
258
  // a fresh ensemble with one real player. Mirrors the
259
259
  // `filterRealPlayers` rule used in StatusBar (cf6becd). Detect
260
- // via the `AgentTempoPlayerType` memo (v1.8 SA diet; legacy-SA
261
- // fallback via the dual-read helper), with a workflow-id-suffix
262
- // fallback for the brief post-start window before visibility
263
- // propagates.
260
+ // via the `AgentTempoPlayerType` memo (memo-only after the #788
261
+ // legacy-SA prune), with a workflow-id-suffix fallback for the
262
+ // brief post-start window before visibility propagates.
264
263
  const playerType = (0, search_attributes_1.getPlayerType)(wf);
265
264
  const isMaestroSession = playerType === 'maestro'
266
265
  || (wf.workflowId?.endsWith('-maestro') ?? false);
@@ -379,10 +378,9 @@ function createTempoClientCore(client, opts = {}) {
379
378
  for await (const wf of listWorkflows({ query })) {
380
379
  const sa = wf.searchAttributes || {};
381
380
  const playerId = Array.isArray(sa.AgentTempoPlayerId) ? String(sa.AgentTempoPlayerId[0]) : wf.workflowId;
382
- // Preferred: memo (v1.8 SA diet) with legacy-SA fallback via the
383
- // dual-read helper. Final fallback: workflow ID convention —
384
- // covers the brief window after a conductor spawn before
385
- // visibility propagates.
381
+ // Preferred: memo (memo-only after the #788 legacy-SA prune). Final
382
+ // fallback: workflow ID convention — covers the brief window after a
383
+ // conductor spawn before visibility propagates.
386
384
  const isConductorFromSA = (0, search_attributes_1.getIsConductor)(wf) === true;
387
385
  const isConductorFromId = wf.workflowId?.endsWith('-conductor') ?? false;
388
386
  players.push({
@@ -7,7 +7,8 @@
7
7
  * - {@link createTempoClientCore} — pure Temporal RPC, headless-safe
8
8
  * (daemon, MCP server, SSE event source, future SDK consumers).
9
9
  * - {@link createTempoClientWithSpawn} — the TTY-bound superset adding
10
- * `createEnsemble` and `spawnConductor`. Use from the TUI/CLI only.
10
+ * `createEnsemble` (the CLI `up` path + the command-center board's
11
+ * `/ensemble-up`). `spawnConductor` was removed in #789 with the Ink TUI.
11
12
  *
12
13
  * `createTempoClient` is preserved as a permanent alias for
13
14
  * `createTempoClientWithSpawn` so every existing call site keeps the same
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * TempoClient — public interface and related types.
3
3
  *
4
- * Extracted from `src/tui/client.ts` so that non-TUI consumers (CLI, tests,
5
- * external integrations) can depend on the interface without pulling in Ink/React.
4
+ * Originally extracted from the former `src/tui/client.ts` so that headless
5
+ * consumers (CLI, tests, external integrations) could depend on the interface
6
+ * without pulling in Ink/React. (The Ink TUI was deleted in #789; the interface
7
+ * remains the shared contract for the CLI, daemon, command-center board, and
8
+ * external integrations.)
6
9
  */
7
10
  import type { AgentType, MaestroPlayerInfo, MaestroRelayMessage, HistoryEntry, Message, SentMessage, SessionMetadata, ScheduleEntry, QualityGate, StageEntry, WorktreeEntry, EnsembleChatResult, AttachmentInfo, HostInfo, AnswerEntry, CoatCheckEntry } from '../types';
8
11
  import type { CoatCheckPutInput, CoatCheckPutResult, CoatCheckGetInput } from '../workflows/maestro-signals';
@@ -515,18 +518,6 @@ export interface TempoClientWithSpawn extends TempoClientCore {
515
518
  * daemon, or other headless processes.
516
519
  */
517
520
  createEnsemble(opts: CreateEnsembleOpts): Promise<void>;
518
- /**
519
- * Spawn a conductor terminal for an existing ensemble — the restore-
520
- * after-shutdown path. Shells out to `agent-tempo up <name>` which is
521
- * idempotent at the workflow layer. Semantically distinct from
522
- * {@link TempoClientWithSpawn.createEnsemble}: this fires on an ensemble
523
- * that already exists; a "create" contradiction would mislead future
524
- * readers. **Requires a TTY context.**
525
- */
526
- spawnConductor(opts: {
527
- ensemble: string;
528
- workDir?: string;
529
- }): Promise<void>;
530
521
  }
531
522
  /**
532
523
  * Backwards-compatible alias preserved indefinitely (per ADR 0007's
@@ -7,8 +7,12 @@
7
7
  * no TTY and a stray spawn would launch a terminal nothing can render.
8
8
  *
9
9
  * The boundary is enforced by the type system: headless callers depend on
10
- * `TempoClientCore`; only the TUI (and its `ensure-conductor-spawned`
11
- * helper) opts into the spawn surface.
10
+ * `TempoClientCore`; the spawn surface is opt-in.
11
+ *
12
+ * NOTE (#789): `spawnConductor` was removed with the Ink TUI (its only caller
13
+ * was the TUI's `ensure-conductor-spawned` helper). WithSpawn's only distinct
14
+ * method is now `createEnsemble` — still live (CLI `up`, command-center board).
15
+ * The Core-vs-WithSpawn split STAYS (WithSpawn is the universal client alias).
12
16
  *
13
17
  * See `docs/adr/0007-tempoclient-core-withspawn-split.md`.
14
18
  */
@@ -18,8 +22,9 @@ import type { TempoClientWithSpawn } from './interface';
18
22
  export type { CreateTempoClientOpts } from './core';
19
23
  /**
20
24
  * Build a `TempoClientWithSpawn` over a configured Temporal `Client`.
21
- * Composes {@link createTempoClientCore} and adds the two TTY-bound
22
- * spawn methods. Use this for TUI-side consumers; headless callers
25
+ * Composes {@link createTempoClientCore} and adds the TTY-bound
26
+ * `createEnsemble` spawn method. Use this for spawn-capable consumers (the CLI
27
+ * `up` path, the command-center board's `/ensemble-up`); pure-headless callers
23
28
  * should use {@link createTempoClientCore} directly.
24
29
  *
25
30
  * `opts` (e.g. `subscribeDeps`) is forwarded to the Core factory.
@@ -59,8 +59,9 @@ async function runTempoCli(args, workDir) {
59
59
  }
60
60
  /**
61
61
  * Build a `TempoClientWithSpawn` over a configured Temporal `Client`.
62
- * Composes {@link createTempoClientCore} and adds the two TTY-bound
63
- * spawn methods. Use this for TUI-side consumers; headless callers
62
+ * Composes {@link createTempoClientCore} and adds the TTY-bound
63
+ * `createEnsemble` spawn method. Use this for spawn-capable consumers (the CLI
64
+ * `up` path, the command-center board's `/ensemble-up`); pure-headless callers
64
65
  * should use {@link createTempoClientCore} directly.
65
66
  *
66
67
  * `opts` (e.g. `subscribeDeps`) is forwarded to the Core factory.
@@ -75,13 +76,9 @@ function createTempoClientWithSpawn(client, opts = {}) {
75
76
  : ['up', opts.ensemble];
76
77
  await runTempoCli(args, opts.workDir);
77
78
  },
78
- async spawnConductor(opts) {
79
- // `agent-tempo up <ensemble>` is idempotent at the workflow layer —
80
- // re-invoking it on a live ensemble reuses the existing conductor
81
- // workflow (deterministic workflow ID). Distinct from
82
- // `createEnsemble` so call sites read as "make sure the conductor
83
- // terminal is running", not "create a new ensemble".
84
- await runTempoCli(['up', opts.ensemble], opts.workDir);
85
- },
79
+ // #789: `spawnConductor` removed with the Ink TUI — its only caller was the
80
+ // TUI's `ensure-conductor-spawned` helper (also deleted). WithSpawn's only
81
+ // distinct method is now `createEnsemble` (still used by the command-center
82
+ // board's `/ensemble-up` + the CLI `up` path).
86
83
  };
87
84
  }
package/dist/config.d.ts CHANGED
@@ -257,19 +257,6 @@ export interface PersistedConfig {
257
257
  temporalTlsKeyPath?: string;
258
258
  defaultAgent?: AgentType;
259
259
  claudeBin?: string;
260
- /**
261
- * Bearer token for the daemon's HTTP/SSE event source (#94, #95,
262
- * SSE-PROTOCOL.md §3.1). Auto-generated on first daemon boot when
263
- * bearer mode is required (`AGENT_TEMPO_HTTP_BIND` non-loopback OR
264
- * a request with a non-loopback `Origin`) and no token is set:
265
- * `crypto.randomBytes(32).toString('base64url')`, 0600 on POSIX.
266
- * Rotation = delete this field; next daemon boot regenerates.
267
- *
268
- * 3e: this LEGACY single token is migrated to the READ tier (T1) — a daemon
269
- * with only `httpToken` set keeps read access and emits a one-time startup
270
- * warning to set an admin token for writes/gate/inner. Prefer `readToken`.
271
- */
272
- httpToken?: string;
273
260
  /**
274
261
  * 3e RBAC — the READ-tier (T1) bearer token. Env `AGENT_TEMPO_HTTP_READ_TOKEN`
275
262
  * takes precedence over this; auto-generated here on first bearer-mode boot if
@@ -16,6 +16,28 @@
16
16
  * for the kill regex to match.
17
17
  */
18
18
  export declare const ENSEMBLE_SENTINEL_FLAG = "--remote-control-session-name-prefix";
19
+ /**
20
+ * 2.0 wire-protocol version (#786 — the cutover keystone). Single source of truth
21
+ * for the protocol stamp, the boot guard, and the `claimAttachment` adapter
22
+ * handshake.
23
+ *
24
+ * - **Stamp:** every 2.0-created workflow records `protocol: PROTOCOL_VERSION` on
25
+ * its START input (so it's in history from run 1 and survives `continueAsNew`
26
+ * via the input spread) and upserts the memo key {@link
27
+ * MEMO_KEYS.protocol | `AgentTempoProtocol`} = PROTOCOL_VERSION.
28
+ * - **Boot guard:** a 2.0 daemon refuses to register workers if visibility shows
29
+ * any Running agent-tempo workflow lacking the stamp — a 2.0 worker can never
30
+ * deterministically replay a 1.x-recorded history, so we fail LOUD at boot
31
+ * (printing `agent-tempo upgrade-to-2`) instead of non-determinism-faulting
32
+ * deep in a workflow task later.
33
+ * - **claimAttachment:** the adapter sends `protocolVersion`; a 2.0 workflow
34
+ * rejects any value `!== PROTOCOL_VERSION` (incl. a v1 adapter that omits it).
35
+ *
36
+ * This module is runtime-free, so the constant is safe to import from BOTH the
37
+ * sandboxed workflow bundle (the stamp + claim validator) AND the daemon guard /
38
+ * CLI (the check). Bumping it is a hard cutover — see `docs/design/v2-scoping.md`.
39
+ */
40
+ export declare const PROTOCOL_VERSION: 2;
19
41
  /**
20
42
  * Escape the two regex metacharacters that can appear in validated player/ensemble
21
43
  * names (which are constrained to `[A-Za-z0-9._-]+`). Safe to interpolate the
package/dist/constants.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * code without dragging Node-only modules into the sandboxed bundle.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ENSEMBLE_SENTINEL_FLAG = void 0;
9
+ exports.PROTOCOL_VERSION = exports.ENSEMBLE_SENTINEL_FLAG = void 0;
10
10
  exports.escapeNameForRegex = escapeNameForRegex;
11
11
  exports.ensembleReadyBanner = ensembleReadyBanner;
12
12
  exports.ensembleReadyDirective = ensembleReadyDirective;
@@ -22,6 +22,28 @@ exports.ensembleReadyDirective = ensembleReadyDirective;
22
22
  * for the kill regex to match.
23
23
  */
24
24
  exports.ENSEMBLE_SENTINEL_FLAG = '--remote-control-session-name-prefix';
25
+ /**
26
+ * 2.0 wire-protocol version (#786 — the cutover keystone). Single source of truth
27
+ * for the protocol stamp, the boot guard, and the `claimAttachment` adapter
28
+ * handshake.
29
+ *
30
+ * - **Stamp:** every 2.0-created workflow records `protocol: PROTOCOL_VERSION` on
31
+ * its START input (so it's in history from run 1 and survives `continueAsNew`
32
+ * via the input spread) and upserts the memo key {@link
33
+ * MEMO_KEYS.protocol | `AgentTempoProtocol`} = PROTOCOL_VERSION.
34
+ * - **Boot guard:** a 2.0 daemon refuses to register workers if visibility shows
35
+ * any Running agent-tempo workflow lacking the stamp — a 2.0 worker can never
36
+ * deterministically replay a 1.x-recorded history, so we fail LOUD at boot
37
+ * (printing `agent-tempo upgrade-to-2`) instead of non-determinism-faulting
38
+ * deep in a workflow task later.
39
+ * - **claimAttachment:** the adapter sends `protocolVersion`; a 2.0 workflow
40
+ * rejects any value `!== PROTOCOL_VERSION` (incl. a v1 adapter that omits it).
41
+ *
42
+ * This module is runtime-free, so the constant is safe to import from BOTH the
43
+ * sandboxed workflow bundle (the stamp + claim validator) AND the daemon guard /
44
+ * CLI (the check). Bumping it is a hard cutover — see `docs/design/v2-scoping.md`.
45
+ */
46
+ exports.PROTOCOL_VERSION = 2;
25
47
  /**
26
48
  * Escape the two regex metacharacters that can appear in validated player/ensemble
27
49
  * names (which are constrained to `[A-Za-z0-9._-]+`). Safe to interpolate the
package/dist/daemon.js CHANGED
@@ -952,6 +952,58 @@ async function main() {
952
952
  log('search-attribute preflight probe failed (non-fatal — createWorkers will surface the real error):', result.probeError);
953
953
  }
954
954
  }
955
+ // #786 — 2.0 cutover BOOT GUARD. Runs AFTER the search-attribute preflight
956
+ // and BEFORE createWorkers(): a 2.0 worker cannot deterministically replay a
957
+ // 1.x-recorded history, so refuse to register workers if visibility still
958
+ // shows ANY Running agent-tempo workflow lacking the protocol-2 stamp (a
959
+ // pre-cutover 1.x run). FAIL-CLOSED — a timed-out/errored scan, or a failure
960
+ // to even run the scan, refuses too (we couldn't PROVE the cutover is clean;
961
+ // see the #845 partial-scan lesson). Namespace-scoped via the client below.
962
+ // See src/upgrade/boot-guard.ts.
963
+ {
964
+ const { checkProtocolGuard } = await Promise.resolve().then(() => __importStar(require('./upgrade/boot-guard')));
965
+ let result;
966
+ let guardConn = null;
967
+ try {
968
+ guardConn = await (0, connection_1.createTemporalConnection)(config);
969
+ const guardClient = new client_1.Client({
970
+ connection: guardConn,
971
+ namespace: config.temporalNamespace,
972
+ interceptors: (0, action_counters_1.actionCountingInterceptors)(),
973
+ });
974
+ result = await checkProtocolGuard(guardClient, { log });
975
+ }
976
+ catch (err) {
977
+ // FAIL-CLOSED — couldn't even run the scan (e.g. connection failure).
978
+ // Without a completed scan we can't prove a clean cutover, so refuse
979
+ // rather than risk a 2.0 worker replaying a 1.x history.
980
+ result = {
981
+ ok: false,
982
+ reason: 'scan-incomplete',
983
+ message: `agent-tempo 2.0 boot guard could not run the protocol scan ` +
984
+ `(${err instanceof Error ? err.message : String(err)}). Refusing to boot ` +
985
+ `(fail-closed). Verify Temporal at ${config.temporalAddress} ` +
986
+ `(namespace ${config.temporalNamespace}) is reachable, then retry.`,
987
+ };
988
+ }
989
+ finally {
990
+ if (guardConn)
991
+ await guardConn.close().catch(() => { });
992
+ }
993
+ if (!result.ok) {
994
+ process.stderr.write('ERROR: ' + result.message + '\n');
995
+ log(`Daemon refused to boot — ${result.reason} (#786 protocol guard)`);
996
+ try {
997
+ fs.unlinkSync(daemon_1.DAEMON_PID_PATH);
998
+ }
999
+ catch { /* ignore */ }
1000
+ try {
1001
+ fs.unlinkSync(daemon_1.DAEMON_HEARTBEAT_PATH);
1002
+ }
1003
+ catch { /* ignore */ }
1004
+ process.exit(1);
1005
+ }
1006
+ }
955
1007
  // Use mutable refs so signal handlers can be registered before workers
956
1008
  // are created — closes the narrow window where a SIGTERM during
957
1009
  // createWorkers() would be missed.