agent-tempo 1.7.0 → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) 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 +3 -10
  9. package/dist/activities/outbox.js +16 -2
  10. package/dist/activities/resolve.d.ts +18 -7
  11. package/dist/activities/resolve.js +58 -46
  12. package/dist/adapters/base.js +3 -0
  13. package/dist/adapters/claude-code/adapter.js +7 -0
  14. package/dist/cli/command-center-command.js +15 -1
  15. package/dist/cli/commands.d.ts +8 -0
  16. package/dist/cli/commands.js +79 -11
  17. package/dist/cli/daemon-command.js +19 -33
  18. package/dist/cli/help-text.js +9 -7
  19. package/dist/cli/home-command.d.ts +26 -0
  20. package/dist/cli/home-command.js +156 -0
  21. package/dist/cli/removed-verbs.d.ts +0 -4
  22. package/dist/cli/removed-verbs.js +23 -12
  23. package/dist/cli/sa-preflight.d.ts +0 -14
  24. package/dist/cli/sa-preflight.js +1 -17
  25. package/dist/cli/startup.d.ts +1 -10
  26. package/dist/cli/startup.js +0 -40
  27. package/dist/cli.js +34 -80
  28. package/dist/client/core.js +13 -15
  29. package/dist/client/index.d.ts +2 -1
  30. package/dist/client/interface.d.ts +5 -14
  31. package/dist/client/with-spawn.d.ts +9 -4
  32. package/dist/client/with-spawn.js +7 -10
  33. package/dist/config.d.ts +14 -13
  34. package/dist/config.js +14 -0
  35. package/dist/constants.d.ts +50 -0
  36. package/dist/constants.js +60 -1
  37. package/dist/daemon.js +81 -0
  38. package/dist/http/auth.d.ts +3 -21
  39. package/dist/http/auth.js +10 -31
  40. package/dist/http/event-types.d.ts +38 -4
  41. package/dist/http/orphans.d.ts +3 -3
  42. package/dist/http/orphans.js +3 -3
  43. package/dist/http/server.d.ts +0 -5
  44. package/dist/http/server.js +79 -29
  45. package/dist/http/snapshot.js +3 -0
  46. package/dist/http/writes.d.ts +1 -1
  47. package/dist/http/writes.js +25 -2
  48. package/dist/observability/nondeterminism-alarm.d.ts +113 -0
  49. package/dist/observability/nondeterminism-alarm.js +162 -0
  50. package/dist/palette/index.d.ts +6 -7
  51. package/dist/palette/index.js +6 -7
  52. package/dist/pi/mission-control/actions.d.ts +67 -1
  53. package/dist/pi/mission-control/actions.js +61 -0
  54. package/dist/pi/mission-control/extension.d.ts +77 -0
  55. package/dist/pi/mission-control/extension.js +171 -0
  56. package/dist/pi/workflow-client.d.ts +14 -16
  57. package/dist/pi/workflow-client.js +18 -44
  58. package/dist/server-tools.js +30 -29
  59. package/dist/server.js +42 -0
  60. package/dist/tools/action-guard.d.ts +23 -0
  61. package/dist/tools/action-guard.js +33 -0
  62. package/dist/tools/coat-check.d.ts +20 -0
  63. package/dist/tools/coat-check.js +129 -0
  64. package/dist/tools/gate.d.ts +13 -0
  65. package/dist/tools/gate.js +88 -0
  66. package/dist/tools/schedule.d.ts +18 -0
  67. package/dist/tools/schedule.js +93 -1
  68. package/dist/tools/stage.d.ts +17 -0
  69. package/dist/tools/stage.js +85 -1
  70. package/dist/tools/state.d.ts +14 -0
  71. package/dist/tools/state.js +95 -0
  72. package/dist/types.d.ts +54 -7
  73. package/dist/upgrade/boot-guard.d.ts +95 -0
  74. package/dist/upgrade/boot-guard.js +88 -0
  75. package/dist/upgrade/from-upgrade.d.ts +95 -0
  76. package/dist/upgrade/from-upgrade.js +330 -0
  77. package/dist/upgrade/phase-engine.js +4 -1
  78. package/dist/utils/duration.d.ts +3 -6
  79. package/dist/utils/duration.js +3 -6
  80. package/dist/utils/format-hosts.d.ts +1 -1
  81. package/dist/utils/format-hosts.js +0 -2
  82. package/dist/utils/grpc-shutdown-guard.js +0 -4
  83. package/dist/utils/hosts.d.ts +4 -10
  84. package/dist/utils/hosts.js +12 -28
  85. package/dist/utils/orphan-guard.d.ts +37 -0
  86. package/dist/utils/orphan-guard.js +26 -0
  87. package/dist/utils/search-attributes.d.ts +28 -18
  88. package/dist/utils/search-attributes.js +48 -18
  89. package/dist/utils/visibility-deadline.d.ts +16 -0
  90. package/dist/utils/visibility-deadline.js +16 -0
  91. package/dist/workflows/maestro-signals.d.ts +0 -13
  92. package/dist/workflows/maestro-signals.js +6 -14
  93. package/dist/workflows/maestro.js +27 -32
  94. package/dist/workflows/scheduler.js +7 -1
  95. package/dist/workflows/session.js +237 -153
  96. package/dist/workflows/signals.d.ts +9 -1
  97. package/dist/workflows/signals.js +8 -6
  98. package/package.json +1 -6
  99. package/workflow-bundle.js +478 -225
  100. package/dist/cli/legacy-migration.d.ts +0 -35
  101. package/dist/cli/legacy-migration.js +0 -341
  102. package/dist/client/ensure-conductor-spawned.d.ts +0 -35
  103. package/dist/client/ensure-conductor-spawned.js +0 -48
  104. package/dist/tui/App.d.ts +0 -85
  105. package/dist/tui/App.js +0 -1791
  106. package/dist/tui/bootstrap-types.d.ts +0 -46
  107. package/dist/tui/bootstrap-types.js +0 -7
  108. package/dist/tui/commands.d.ts +0 -71
  109. package/dist/tui/commands.js +0 -1375
  110. package/dist/tui/components/ChatView.d.ts +0 -35
  111. package/dist/tui/components/ChatView.js +0 -54
  112. package/dist/tui/components/CommandPalette.d.ts +0 -21
  113. package/dist/tui/components/CommandPalette.js +0 -67
  114. package/dist/tui/components/ConversationStream.d.ts +0 -114
  115. package/dist/tui/components/ConversationStream.js +0 -307
  116. package/dist/tui/components/CreateEnsembleWizard.d.ts +0 -19
  117. package/dist/tui/components/CreateEnsembleWizard.js +0 -223
  118. package/dist/tui/components/DestroyConfirmModal.d.ts +0 -17
  119. package/dist/tui/components/DestroyConfirmModal.js +0 -62
  120. package/dist/tui/components/ErrorView.d.ts +0 -31
  121. package/dist/tui/components/ErrorView.js +0 -129
  122. package/dist/tui/components/HomeView.d.ts +0 -54
  123. package/dist/tui/components/HomeView.js +0 -306
  124. package/dist/tui/components/LoadLineupModal.d.ts +0 -18
  125. package/dist/tui/components/LoadLineupModal.js +0 -79
  126. package/dist/tui/components/MainView.d.ts +0 -21
  127. package/dist/tui/components/MainView.js +0 -107
  128. package/dist/tui/components/NewEnsembleModal.d.ts +0 -9
  129. package/dist/tui/components/NewEnsembleModal.js +0 -73
  130. package/dist/tui/components/Picker.d.ts +0 -23
  131. package/dist/tui/components/Picker.js +0 -70
  132. package/dist/tui/components/PlayerDetailView.d.ts +0 -26
  133. package/dist/tui/components/PlayerDetailView.js +0 -118
  134. package/dist/tui/components/PromptArea.d.ts +0 -50
  135. package/dist/tui/components/PromptArea.js +0 -303
  136. package/dist/tui/components/RecruitWizard.d.ts +0 -17
  137. package/dist/tui/components/RecruitWizard.js +0 -221
  138. package/dist/tui/components/RestoreConfirmModal.d.ts +0 -18
  139. package/dist/tui/components/RestoreConfirmModal.js +0 -71
  140. package/dist/tui/components/ScheduleWizard.d.ts +0 -19
  141. package/dist/tui/components/ScheduleWizard.js +0 -259
  142. package/dist/tui/components/Splash.d.ts +0 -23
  143. package/dist/tui/components/Splash.js +0 -221
  144. package/dist/tui/components/StatusBar.d.ts +0 -48
  145. package/dist/tui/components/StatusBar.js +0 -128
  146. package/dist/tui/components/StatusOverlay.d.ts +0 -15
  147. package/dist/tui/components/StatusOverlay.js +0 -76
  148. package/dist/tui/components/TitleBar.d.ts +0 -10
  149. package/dist/tui/components/TitleBar.js +0 -21
  150. package/dist/tui/index.d.ts +0 -15
  151. package/dist/tui/index.js +0 -162
  152. package/dist/tui/ink-context.d.ts +0 -18
  153. package/dist/tui/ink-context.js +0 -59
  154. package/dist/tui/ink-loader.d.ts +0 -26
  155. package/dist/tui/ink-loader.js +0 -42
  156. package/dist/tui/removed-commands.d.ts +0 -9
  157. package/dist/tui/removed-commands.js +0 -22
  158. package/dist/tui/sse-handler.d.ts +0 -52
  159. package/dist/tui/sse-handler.js +0 -157
  160. package/dist/tui/store.d.ts +0 -598
  161. package/dist/tui/store.js +0 -753
  162. package/dist/tui/utils/format.d.ts +0 -56
  163. package/dist/tui/utils/format.js +0 -155
  164. package/dist/tui/utils/fullscreen.d.ts +0 -23
  165. package/dist/tui/utils/fullscreen.js +0 -71
  166. package/dist/tui/utils/history.d.ts +0 -10
  167. package/dist/tui/utils/history.js +0 -85
  168. package/dist/tui/utils/platform.d.ts +0 -45
  169. package/dist/tui/utils/platform.js +0 -258
  170. package/dist/tui/utils/theme.d.ts +0 -21
  171. package/dist/tui/utils/theme.js +0 -24
@@ -34,9 +34,10 @@ export interface SearchAttributeCarrier {
34
34
  *
35
35
  * T0.5 (#747): the read-only metadata fields (`gitRoot`, `playerType`,
36
36
  * `isConductor`, plus `part`) migrated from search attributes to the workflow
37
- * memo. The dual-read helpers below prefer the memo (new runs) and fall back
38
- * to the legacy search attribute (runs started before the v1.8-sa-diet patch)
39
- * this file is the single choke point for that migration window.
37
+ * memo. The memo readers below are now memo-only #788 pruned the legacy-SA
38
+ * fallback, since the 2.0 clean cutover (guarded by #786) leaves no pre-v1.8
39
+ * SA-only run for a 2.0 worker to read. This file is the single choke point
40
+ * for those reads.
40
41
  */
41
42
  export interface WorkflowMetaCarrier extends SearchAttributeCarrier {
42
43
  memo?: Record<string, unknown>;
@@ -44,7 +45,7 @@ export interface WorkflowMetaCarrier extends SearchAttributeCarrier {
44
45
  /**
45
46
  * Canonical memo key names (T0.5, #747) — single registry shared by every
46
47
  * write site (session workflow, the five `client.workflow.start({ memo })`
47
- * seeds) and the dual-read helpers below, so a key can never silently
48
+ * seeds) and the memo readers below, so a key can never silently
48
49
  * drift between writer and reader. Wire-stable: renaming a key is a
49
50
  * breaking change (docs/WIRE-PROTOCOL.md §Workflow memo).
50
51
  *
@@ -59,6 +60,8 @@ export declare const MEMO_KEYS: {
59
60
  readonly workDir: "AgentTempoWorkDir";
60
61
  readonly agentType: "AgentTempoAgentType";
61
62
  readonly gitBranch: "AgentTempoGitBranch";
63
+ readonly protocol: "AgentTempoProtocol";
64
+ readonly closeReason: "AgentTempoCloseReason";
62
65
  };
63
66
  /**
64
67
  * Escape a value for use in Temporal visibility query strings — strips
@@ -93,16 +96,24 @@ export declare function getSearchAttrBool(carrier: SearchAttributeCarrier, name:
93
96
  export declare function getMemoString(carrier: WorkflowMetaCarrier, name: string): string | undefined;
94
97
  /** Read a memo value as a boolean. `undefined` when absent or non-boolean. */
95
98
  export declare function getMemoBool(carrier: WorkflowMetaCarrier, name: string): boolean | undefined;
99
+ /** Read a memo value as a number. `undefined` when absent or non-number. */
100
+ export declare function getMemoNumber(carrier: WorkflowMetaCarrier, name: string): number | undefined;
96
101
  /**
97
- * Dual-read a string field: memo first (runs started on or after the
98
- * v1.8-sa-diet patch), legacy search attribute as fallback (older runs).
102
+ * #786 read the 2.0 cutover protocol stamp from a workflow's memo
103
+ * (`AgentTempoProtocol`). Returns `undefined` for an un-stamped (1.x) workflow.
104
+ * The boot guard treats any value other than `PROTOCOL_VERSION` (incl.
105
+ * `undefined`) as un-stamped and refuses to register workers.
106
+ */
107
+ export declare function getWorkflowProtocol(carrier: WorkflowMetaCarrier): number | undefined;
108
+ /**
109
+ * Read a string metadata field from the workflow memo.
99
110
  *
100
- * TODO(next major): remove the SA fallback chain here and in
101
- * {@link getWorkflowMetaBool}, and drop `LEGACY_SEARCH_ATTRIBUTES` in
102
- * `src/cli/sa-preflight.ts` see #747 and docs/ops/sa-diet-migration.md.
111
+ * 2.0 (#788): memo-only. The legacy search-attribute fallback is gone the
112
+ * A2 clean cutover (guarded by #786) means no pre-v1.8 SA-only run survives
113
+ * for a 2.0 worker to read, so every metadata field now lives in the memo.
103
114
  */
104
115
  export declare function getWorkflowMetaString(carrier: WorkflowMetaCarrier, name: string): string | undefined;
105
- /** Dual-read a boolean field memo preferred, SA fallback. */
116
+ /** Read a boolean metadata field from the workflow memo (2.0: memo-only — #788). */
106
117
  export declare function getWorkflowMetaBool(carrier: WorkflowMetaCarrier, name: string): boolean | undefined;
107
118
  /**
108
119
  * Read the attachment phase from `AgentTempoAttachmentState`.
@@ -123,19 +134,18 @@ export declare function getAttachmentPhase(carrier: SearchAttributeCarrier): Att
123
134
  */
124
135
  export declare function getEnsembleName(carrier: SearchAttributeCarrier): string | undefined;
125
136
  /**
126
- * Read the conductor flag memo `AgentTempoIsConductor` preferred (T0.5),
127
- * legacy search attribute as fallback for pre-v1.8 runs.
137
+ * Read the conductor flag from memo `AgentTempoIsConductor` (T0.5; memo-only
138
+ * after the #788 legacy-SA prune).
128
139
  *
129
- * Returns `undefined` when absent in both (e.g. transiently un-indexed after
130
- * a conductor spawn). Callers wanting the pre-#178 workflow-id-suffix
140
+ * Returns `undefined` when absent (e.g. transiently un-indexed after a
141
+ * conductor spawn). Callers wanting the pre-#178 workflow-id-suffix
131
142
  * fallback (`endsWith('-conductor')`) should apply it on `undefined`.
132
143
  */
133
144
  export declare function getIsConductor(carrier: WorkflowMetaCarrier): boolean | undefined;
134
145
  /**
135
- * Read the player type memo `AgentTempoPlayerType` preferred (T0.5),
136
- * legacy search attribute as fallback for pre-v1.8 runs. Callers keep their
137
- * existing workflow-id-suffix fallback (`endsWith('-maestro')`) on
138
- * `undefined`.
146
+ * Read the player type from memo `AgentTempoPlayerType` (T0.5; memo-only
147
+ * after the #788 legacy-SA prune). Callers keep their existing
148
+ * workflow-id-suffix fallback (`endsWith('-maestro')`) on `undefined`.
139
149
  */
140
150
  export declare function getPlayerType(carrier: WorkflowMetaCarrier): string | undefined;
141
151
  /**
@@ -6,6 +6,8 @@ exports.getSearchAttrString = getSearchAttrString;
6
6
  exports.getSearchAttrBool = getSearchAttrBool;
7
7
  exports.getMemoString = getMemoString;
8
8
  exports.getMemoBool = getMemoBool;
9
+ exports.getMemoNumber = getMemoNumber;
10
+ exports.getWorkflowProtocol = getWorkflowProtocol;
9
11
  exports.getWorkflowMetaString = getWorkflowMetaString;
10
12
  exports.getWorkflowMetaBool = getWorkflowMetaBool;
11
13
  exports.getAttachmentPhase = getAttachmentPhase;
@@ -16,7 +18,7 @@ exports.getPart = getPart;
16
18
  /**
17
19
  * Canonical memo key names (T0.5, #747) — single registry shared by every
18
20
  * write site (session workflow, the five `client.workflow.start({ memo })`
19
- * seeds) and the dual-read helpers below, so a key can never silently
21
+ * seeds) and the memo readers below, so a key can never silently
20
22
  * drift between writer and reader. Wire-stable: renaming a key is a
21
23
  * breaking change (docs/WIRE-PROTOCOL.md §Workflow memo).
22
24
  *
@@ -34,6 +36,22 @@ exports.MEMO_KEYS = {
34
36
  workDir: 'AgentTempoWorkDir',
35
37
  agentType: 'AgentTempoAgentType',
36
38
  gitBranch: 'AgentTempoGitBranch',
39
+ // #786 — 2.0 cutover protocol stamp. A NUMBER memo (`PROTOCOL_VERSION`) written
40
+ // on every 2.0-created workflow's start input + memo. The daemon boot guard
41
+ // reads it straight off `workflow.list()` results (memo IS returned there) to
42
+ // refuse booting against un-stamped 1.x Running runs — no per-workflow query.
43
+ // Memo, not a search attribute: avoids a 6th Keyword (the #747 SA-diet keeps
44
+ // us at 5/10) and needs no operator registration. See constants.PROTOCOL_VERSION.
45
+ protocol: 'AgentTempoProtocol',
46
+ // #704 — typed terminal close-reason stamped on a workflow's memo when it
47
+ // COMPLETEs via `destroy` (`'destroyed'`) or the booting attach-timeout
48
+ // watchdog (`'boot-timeout'`). Survives completion and is readable via
49
+ // `describe().memo`. The bootstrap orphan-guard (`server.ts`) reads it to
50
+ // self-tombstone a late-launching orphan process whose run was cancelled,
51
+ // discriminated by the running-run × close-reason PAIR. A string memo (not a
52
+ // search attribute): avoids a 6th Keyword (the #747 SA-diet keeps us at 5/10)
53
+ // and needs no operator registration.
54
+ closeReason: 'AgentTempoCloseReason',
37
55
  };
38
56
  /**
39
57
  * Escape a value for use in Temporal visibility query strings — strips
@@ -86,7 +104,7 @@ function getSearchAttrBool(carrier, name) {
86
104
  }
87
105
  return undefined;
88
106
  }
89
- // ── T0.5 (#747) — memo readers + dual-read (memo preferred, SA fallback) ──
107
+ // ── T0.5 (#747) — memo readers (memo-only after the #788 legacy-SA prune) ──
90
108
  /** Read a memo value as a string. `undefined` when absent or non-string. */
91
109
  function getMemoString(carrier, name) {
92
110
  const v = carrier.memo?.[name];
@@ -97,20 +115,33 @@ function getMemoBool(carrier, name) {
97
115
  const v = carrier.memo?.[name];
98
116
  return typeof v === 'boolean' ? v : undefined;
99
117
  }
118
+ /** Read a memo value as a number. `undefined` when absent or non-number. */
119
+ function getMemoNumber(carrier, name) {
120
+ const v = carrier.memo?.[name];
121
+ return typeof v === 'number' ? v : undefined;
122
+ }
123
+ /**
124
+ * #786 — read the 2.0 cutover protocol stamp from a workflow's memo
125
+ * (`AgentTempoProtocol`). Returns `undefined` for an un-stamped (1.x) workflow.
126
+ * The boot guard treats any value other than `PROTOCOL_VERSION` (incl.
127
+ * `undefined`) as un-stamped and refuses to register workers.
128
+ */
129
+ function getWorkflowProtocol(carrier) {
130
+ return getMemoNumber(carrier, exports.MEMO_KEYS.protocol);
131
+ }
100
132
  /**
101
- * Dual-read a string field: memo first (runs started on or after the
102
- * v1.8-sa-diet patch), legacy search attribute as fallback (older runs).
133
+ * Read a string metadata field from the workflow memo.
103
134
  *
104
- * TODO(next major): remove the SA fallback chain here and in
105
- * {@link getWorkflowMetaBool}, and drop `LEGACY_SEARCH_ATTRIBUTES` in
106
- * `src/cli/sa-preflight.ts` see #747 and docs/ops/sa-diet-migration.md.
135
+ * 2.0 (#788): memo-only. The legacy search-attribute fallback is gone the
136
+ * A2 clean cutover (guarded by #786) means no pre-v1.8 SA-only run survives
137
+ * for a 2.0 worker to read, so every metadata field now lives in the memo.
107
138
  */
108
139
  function getWorkflowMetaString(carrier, name) {
109
- return getMemoString(carrier, name) ?? getSearchAttrString(carrier, name);
140
+ return getMemoString(carrier, name);
110
141
  }
111
- /** Dual-read a boolean field memo preferred, SA fallback. */
142
+ /** Read a boolean metadata field from the workflow memo (2.0: memo-only — #788). */
112
143
  function getWorkflowMetaBool(carrier, name) {
113
- return getMemoBool(carrier, name) ?? getSearchAttrBool(carrier, name);
144
+ return getMemoBool(carrier, name);
114
145
  }
115
146
  // ── Typed wrappers for agent-tempo's custom search attributes ──
116
147
  //
@@ -140,21 +171,20 @@ function getEnsembleName(carrier) {
140
171
  return getSearchAttrString(carrier, 'AgentTempoEnsemble');
141
172
  }
142
173
  /**
143
- * Read the conductor flag memo `AgentTempoIsConductor` preferred (T0.5),
144
- * legacy search attribute as fallback for pre-v1.8 runs.
174
+ * Read the conductor flag from memo `AgentTempoIsConductor` (T0.5; memo-only
175
+ * after the #788 legacy-SA prune).
145
176
  *
146
- * Returns `undefined` when absent in both (e.g. transiently un-indexed after
147
- * a conductor spawn). Callers wanting the pre-#178 workflow-id-suffix
177
+ * Returns `undefined` when absent (e.g. transiently un-indexed after a
178
+ * conductor spawn). Callers wanting the pre-#178 workflow-id-suffix
148
179
  * fallback (`endsWith('-conductor')`) should apply it on `undefined`.
149
180
  */
150
181
  function getIsConductor(carrier) {
151
182
  return getWorkflowMetaBool(carrier, exports.MEMO_KEYS.isConductor);
152
183
  }
153
184
  /**
154
- * Read the player type memo `AgentTempoPlayerType` preferred (T0.5),
155
- * legacy search attribute as fallback for pre-v1.8 runs. Callers keep their
156
- * existing workflow-id-suffix fallback (`endsWith('-maestro')`) on
157
- * `undefined`.
185
+ * Read the player type from memo `AgentTempoPlayerType` (T0.5; memo-only
186
+ * after the #788 legacy-SA prune). Callers keep their existing
187
+ * workflow-id-suffix fallback (`endsWith('-maestro')`) on `undefined`.
158
188
  */
159
189
  function getPlayerType(carrier) {
160
190
  return getWorkflowMetaString(carrier, exports.MEMO_KEYS.playerType);
@@ -100,6 +100,22 @@ export declare const VISIBILITY_DEADLINES_MS: {
100
100
  * enough to amortize partial scans across many sweeps.
101
101
  */
102
102
  readonly orphanQueryCleanup: 30000;
103
+ /**
104
+ * #786 — `checkProtocolGuard` daemon boot scan. Walks every Running
105
+ * agent-tempo workflow (the 4 workflow types) reading only the
106
+ * `AgentTempoProtocol` memo straight off the visibility page — NO
107
+ * per-workflow query, so it's pure page iteration and far cheaper than
108
+ * the orphan scan. 30s is generous headroom for a large-but-healthy
109
+ * namespace (page-streaming even thousands of rows completes in
110
+ * seconds) while still bounding a wedged visibility backend.
111
+ *
112
+ * **Load-bearing: this budget is FAIL-CLOSED.** A timeout here does NOT
113
+ * yield a best-effort partial — the guard treats an unfinished scan as
114
+ * "couldn't prove the cutover is clean" and REFUSES to boot (the #845
115
+ * partial-scan lesson: a 2.0 worker that replays a 1.x history corrupts
116
+ * deterministically, so an unverified scan must never be read as clean).
117
+ */
118
+ readonly protocolGuardBoot: 30000;
103
119
  };
104
120
  export type VisibilitySite = keyof typeof VISIBILITY_DEADLINES_MS;
105
121
  /**
@@ -55,6 +55,22 @@ exports.VISIBILITY_DEADLINES_MS = {
55
55
  * enough to amortize partial scans across many sweeps.
56
56
  */
57
57
  orphanQueryCleanup: 30_000,
58
+ /**
59
+ * #786 — `checkProtocolGuard` daemon boot scan. Walks every Running
60
+ * agent-tempo workflow (the 4 workflow types) reading only the
61
+ * `AgentTempoProtocol` memo straight off the visibility page — NO
62
+ * per-workflow query, so it's pure page iteration and far cheaper than
63
+ * the orphan scan. 30s is generous headroom for a large-but-healthy
64
+ * namespace (page-streaming even thousands of rows completes in
65
+ * seconds) while still bounding a wedged visibility backend.
66
+ *
67
+ * **Load-bearing: this budget is FAIL-CLOSED.** A timeout here does NOT
68
+ * yield a best-effort partial — the guard treats an unfinished scan as
69
+ * "couldn't prove the cutover is clean" and REFUSES to boot (the #845
70
+ * partial-scan lesson: a 2.0 worker that replays a 1.x history corrupts
71
+ * deterministically, so an unverified scan must never be read as clean).
72
+ */
73
+ protocolGuardBoot: 30_000,
58
74
  };
59
75
  /**
60
76
  * Thrown by {@link iterateWithDeadline} when the per-iterator deadline
@@ -172,19 +172,6 @@ export declare const maestroEnsemblesQuery: import("@temporalio/workflow").Query
172
172
  export declare const maestroPlayersByEnsembleQuery: import("@temporalio/workflow").QueryDefinition<Record<string, MaestroPlayerInfo[]>, [], string>;
173
173
  /** Get recent messages across all ensembles (ring buffer, max 500). */
174
174
  export declare const maestroRecentMessagesQuery: import("@temporalio/workflow").QueryDefinition<MaestroRelayMessage[], [], string>;
175
- /**
176
- * #274 — the `hostname → HostProfile` map maintained by the global maestro.
177
- *
178
- * Returned as a plain `Record<string, HostProfile>` (not a `Map`) so the
179
- * default Temporal payload converter serializes it without a codec tweak.
180
- * The `src/utils/hosts.ts` join helper consumes this and reconstructs a
181
- * `Map`-shaped view at the consumer boundary if callers find it useful.
182
- *
183
- * Consumers MUST treat the returned profiles as opaque beyond the
184
- * `hostname` field — per-field validation happens at the join site, not
185
- * at query time.
186
- */
187
- export declare const hostProfilesQuery: import("@temporalio/workflow").QueryDefinition<Record<string, HostProfile>, [], string>;
188
175
  /**
189
176
  * #280 — combined existence + profiles query.
190
177
  *
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.maestroGlobalSendCommandUpdate = exports.maestroFetchConductorHistoryUpdate = exports.maestroFetchPlayerMessagesUpdate = exports.maestroSendMessageUpdate = exports.hostProfilesWithExistenceQuery = exports.hostProfilesQuery = exports.maestroRecentMessagesQuery = exports.maestroPlayersByEnsembleQuery = exports.maestroEnsemblesQuery = exports.hostProfileSignal = exports.maestroNotifyMessageSignal = exports.maestroGetAnswerQuery = exports.maestroPostAnswerUpdate = exports.coatCheckEvictUpdate = exports.coatCheckListQuery = exports.coatCheckGetUpdate = exports.coatCheckPutUpdate = exports.maestroSendCommandUpdate = exports.getTempoSeriesQuery = exports.getCurrentBpmQuery = exports.getEnsembleStartTimeQuery = exports.getEnsembleDescriptionQuery = exports.maestroEnsembleChatQuery = exports.maestroPendingCommandsQuery = exports.maestroPausedQuery = exports.maestroEventsQuery = exports.maestroPlayersQuery = exports.setEnsembleDescriptionSignal = exports.maestroSetPausedSignal = exports.maestroShutdownSignal = exports.ZERO_CHAT_HIGH_WATER = void 0;
3
+ exports.maestroGlobalSendCommandUpdate = exports.maestroFetchConductorHistoryUpdate = exports.maestroFetchPlayerMessagesUpdate = exports.maestroSendMessageUpdate = exports.hostProfilesWithExistenceQuery = exports.maestroRecentMessagesQuery = exports.maestroPlayersByEnsembleQuery = exports.maestroEnsemblesQuery = exports.hostProfileSignal = exports.maestroNotifyMessageSignal = exports.maestroGetAnswerQuery = exports.maestroPostAnswerUpdate = exports.coatCheckEvictUpdate = exports.coatCheckListQuery = exports.coatCheckGetUpdate = exports.coatCheckPutUpdate = exports.maestroSendCommandUpdate = exports.getTempoSeriesQuery = exports.getCurrentBpmQuery = exports.getEnsembleStartTimeQuery = exports.getEnsembleDescriptionQuery = exports.maestroEnsembleChatQuery = exports.maestroPendingCommandsQuery = exports.maestroPausedQuery = exports.maestroEventsQuery = exports.maestroPlayersQuery = exports.setEnsembleDescriptionSignal = exports.maestroSetPausedSignal = exports.maestroShutdownSignal = exports.ZERO_CHAT_HIGH_WATER = void 0;
4
4
  const workflow_1 = require("@temporalio/workflow");
5
5
  var types_1 = require("../types");
6
6
  Object.defineProperty(exports, "ZERO_CHAT_HIGH_WATER", { enumerable: true, get: function () { return types_1.ZERO_CHAT_HIGH_WATER; } });
@@ -127,19 +127,11 @@ exports.maestroEnsemblesQuery = (0, workflow_1.defineQuery)('maestroEnsembles');
127
127
  exports.maestroPlayersByEnsembleQuery = (0, workflow_1.defineQuery)('maestroPlayersByEnsemble');
128
128
  /** Get recent messages across all ensembles (ring buffer, max 500). */
129
129
  exports.maestroRecentMessagesQuery = (0, workflow_1.defineQuery)('maestroRecentMessages');
130
- /**
131
- * #274 — the `hostname → HostProfile` map maintained by the global maestro.
132
- *
133
- * Returned as a plain `Record<string, HostProfile>` (not a `Map`) so the
134
- * default Temporal payload converter serializes it without a codec tweak.
135
- * The `src/utils/hosts.ts` join helper consumes this and reconstructs a
136
- * `Map`-shaped view at the consumer boundary if callers find it useful.
137
- *
138
- * Consumers MUST treat the returned profiles as opaque beyond the
139
- * `hostname` field — per-field validation happens at the join site, not
140
- * at query time.
141
- */
142
- exports.hostProfilesQuery = (0, workflow_1.defineQuery)('hostProfiles');
130
+ // 2.0 (#788): the legacy `hostProfilesQuery` ('hostProfiles') was removed —
131
+ // superseded by `hostProfilesWithExistenceQuery` below, which folds the
132
+ // running-check + profiles into a single round-trip. Every consumer uses the
133
+ // WithExistence form. Consumers MUST still treat the returned profiles as
134
+ // opaque beyond the `hostname` field (validation happens at the join site).
143
135
  /**
144
136
  * #280 — combined existence + profiles query.
145
137
  *
@@ -4,6 +4,8 @@ exports.resolveRefreshIntervalMs = resolveRefreshIntervalMs;
4
4
  exports.agentMaestroWorkflow = agentMaestroWorkflow;
5
5
  exports.agentGlobalMaestroWorkflow = agentGlobalMaestroWorkflow;
6
6
  const workflow_1 = require("@temporalio/workflow");
7
+ const search_attributes_1 = require("../utils/search-attributes");
8
+ const constants_1 = require("../constants");
7
9
  /**
8
10
  * Workflow-deterministic clock — mirrors the helper in `src/workflows/session.ts`.
9
11
  *
@@ -30,7 +32,7 @@ const validation_1 = require("../utils/validation");
30
32
  // ── Activity Proxies ──
31
33
  // Only proxy activities actually used in the workflow.
32
34
  // fetchConductorHistory is available in the activities but reserved for Phase 2 (TUI).
33
- const { refreshEnsembleState, refreshEnsembleStateV2, relayCommandToConductor, fetchEnsembleChat } = (0, workflow_1.proxyActivities)({
35
+ const { refreshEnsembleStateV2, relayCommandToConductor, fetchEnsembleChat } = (0, workflow_1.proxyActivities)({
34
36
  startToCloseTimeout: '30 seconds',
35
37
  retry: { maximumAttempts: 3 },
36
38
  });
@@ -79,15 +81,16 @@ const TEMPO_BPM_WINDOW_MS = 60_000;
79
81
  // Per-Ensemble Maestro (existing — unchanged)
80
82
  // ══════════════════════════════════════════════════════════════════════════════
81
83
  async function agentMaestroWorkflow(input) {
82
- (0, workflow_1.patched)('v0.17-initial');
84
+ // #786 — 2.0 cutover protocol stamp (see constants.PROTOCOL_VERSION). Memo,
85
+ // re-upserted every run incl. CAN successors, so the daemon boot guard sees
86
+ // every Running 2.0 maestro as stamped; a 1.x maestro never runs this → its
87
+ // memo is undefined → guard refuses. Unconditional — #786 is first on the 2.0
88
+ // line, no pre-stamp v2 histories to protect.
89
+ (0, workflow_1.upsertMemo)({ [search_attributes_1.MEMO_KEYS.protocol]: constants_1.PROTOCOL_VERSION });
83
90
  // T0.1 (#748) — input-driven, NOT patched-gated: pre-#748 runs have no
84
91
  // costProfile field, so they resolve to the legacy 5s default and the V1
85
92
  // refresh activity — identical commands on replay.
86
93
  const cloudProfile = input.costProfile === 'cloud';
87
- // T0.6 (#760) — the chat gate NEEDS a marker (unlike T0.1's V2 switch):
88
- // post-#759 cloud maestros recorded chat-fetch activities on unwatched
89
- // ticks, so skipping those without a marker would mismatch their replay.
90
- const chatGateEnabled = (0, workflow_1.patched)('v1.8-t06-chat-gate');
91
94
  let refreshIntervalMs = resolveRefreshIntervalMs(input, /* observersPresent */ true);
92
95
  let players = input.players ?? [];
93
96
  const events = input.events ?? [];
@@ -461,16 +464,16 @@ async function agentMaestroWorkflow(input) {
461
464
  // activity: SA/memo-based ensemble-scoped scan + the daemon's SSE
462
465
  // observer presence, which drives next tick's cadence (20s watched /
463
466
  // 60s unwatched via resolveRefreshIntervalMs).
464
- let newPlayers;
467
+ // 2.0 (#788): single refresh activity (V2). It honors the daemon cost
468
+ // profile internally (cloud → SA/memo scan, local → legacy scan) and
469
+ // returns `observersPresent`. Only the cloud profile uses presence to
470
+ // stretch the refresh cadence; local keeps its fixed interval.
471
+ const v2 = await refreshEnsembleStateV2({ ensemble: input.ensemble });
472
+ const newPlayers = v2.players;
473
+ observersPresent = v2.observersPresent;
465
474
  if (cloudProfile) {
466
- const v2 = await refreshEnsembleStateV2({ ensemble: input.ensemble });
467
- newPlayers = v2.players;
468
- observersPresent = v2.observersPresent;
469
475
  refreshIntervalMs = resolveRefreshIntervalMs(input, observersPresent);
470
476
  }
471
- else {
472
- newPlayers = await refreshEnsembleState(input.ensemble);
473
- }
474
477
  const nowDate = workflowNow();
475
478
  const now = nowDate.toISOString();
476
479
  const nowMs = nowDate.getTime();
@@ -563,14 +566,10 @@ async function agentMaestroWorkflow(input) {
563
566
  // the maestro's cache — so an unwatched ensemble loses nothing;
564
567
  // staleness inherits the accepted T0.1 cadence-stretch contract (the
565
568
  // first watched tick re-fetches from the durable high-water marks).
566
- // Gated behind its own patched marker: post-#759 cloud maestros have
567
- // chat activities RECORDED on unwatched ticks, so an ungated skip
568
- // would be a replay command mismatch (input-driven gating only
569
- // protects pre-#748 runs — verified, contra the initial "no marker
570
- // expected" estimate).
571
- if ((0, workflow_1.patched)('v0.19-ensemble-chat')) {
569
+ // (2.0: was patched()-gated for 1.x replay determinism; now unconditional — #787.)
570
+ {
572
571
  // Unwatched cloud tick → skip; cache + high-water marks stay untouched.
573
- const skipChatThisTick = chatGateEnabled && cloudProfile && !observersPresent;
572
+ const skipChatThisTick = cloudProfile && !observersPresent;
574
573
  if (!skipChatThisTick) {
575
574
  try {
576
575
  const chatResult = await fetchEnsembleChat({
@@ -710,7 +709,10 @@ const globalActivities = (0, workflow_1.proxyActivities)({
710
709
  retry: { maximumAttempts: 3 },
711
710
  });
712
711
  async function agentGlobalMaestroWorkflow(input) {
713
- (0, workflow_1.patched)('v0.18-global-maestro');
712
+ // #786 — 2.0 cutover protocol stamp (the namespace-wide global maestro is
713
+ // stamped too, so the boot guard's scan never sees it as un-stamped). See
714
+ // constants.PROTOCOL_VERSION + agentMaestroWorkflow's stamp note.
715
+ (0, workflow_1.upsertMemo)({ [search_attributes_1.MEMO_KEYS.protocol]: constants_1.PROTOCOL_VERSION });
714
716
  // T0.1 (#748) — input-driven (see agentMaestroWorkflow): pre-#748 runs
715
717
  // have no costProfile field → legacy 5s + V1 refresh, identical commands.
716
718
  const cloudProfile = input.costProfile === 'cloud';
@@ -774,8 +776,6 @@ async function agentGlobalMaestroWorkflow(input) {
774
776
  (0, workflow_1.setHandler)(maestro_signals_1.maestroRecentMessagesQuery, () => recentMessages);
775
777
  (0, workflow_1.setHandler)(maestro_signals_1.maestroEventsQuery, () => events);
776
778
  (0, workflow_1.setHandler)(maestro_signals_1.maestroPendingCommandsQuery, () => pendingCommands);
777
- // Return a defensive copy so callers can't mutate workflow state.
778
- (0, workflow_1.setHandler)(maestro_signals_1.hostProfilesQuery, () => ({ ...hostProfiles }));
779
779
  // #280 — combined query: reaching this handler proves the workflow is
780
780
  // running, so `exists` is always `true` here. Callers infer "missing"
781
781
  // by catching transport-level errors (workflow not found, etc.).
@@ -889,15 +889,10 @@ async function agentGlobalMaestroWorkflow(input) {
889
889
  let observersPresent = true;
890
890
  for (const ensemble of knownEnsembles) {
891
891
  try {
892
- let newPlayers;
893
- if (cloudProfile) {
894
- const v2 = await globalActivities.refreshEnsembleStateV2({ ensemble });
895
- newPlayers = v2.players;
896
- observersPresent = v2.observersPresent;
897
- }
898
- else {
899
- newPlayers = await globalActivities.refreshEnsembleState(ensemble);
900
- }
892
+ // 2.0 (#788): single refresh activity (V2) — see the per-ensemble note above.
893
+ const v2 = await globalActivities.refreshEnsembleStateV2({ ensemble });
894
+ const newPlayers = v2.players;
895
+ observersPresent = v2.observersPresent;
901
896
  const oldPlayers = playersByEnsemble[ensemble] ?? [];
902
897
  const now = new Date().toISOString();
903
898
  // Diff snapshots to generate events
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.agentSchedulerWorkflow = agentSchedulerWorkflow;
4
4
  const workflow_1 = require("@temporalio/workflow");
5
+ const search_attributes_1 = require("../utils/search-attributes");
6
+ const constants_1 = require("../constants");
5
7
  const scheduler_signals_1 = require("./scheduler-signals");
6
8
  const { fireSchedule, computeNextCronFire } = (0, workflow_1.proxyActivities)({
7
9
  startToCloseTimeout: '30 seconds',
@@ -10,6 +12,10 @@ const { fireSchedule, computeNextCronFire } = (0, workflow_1.proxyActivities)({
10
12
  /** Default self-termination grace period — 30 000 ms (see §scheduler "empty self-terminate"). */
11
13
  const DEFAULT_GRACE_PERIOD_MS = 30_000;
12
14
  async function agentSchedulerWorkflow(input) {
15
+ // #786 — 2.0 cutover protocol stamp (see constants.PROTOCOL_VERSION). Memo,
16
+ // re-upserted every run incl. CAN, so the boot guard sees every Running 2.0
17
+ // scheduler as stamped; a 1.x scheduler never runs this → guard refuses.
18
+ (0, workflow_1.upsertMemo)({ [search_attributes_1.MEMO_KEYS.protocol]: constants_1.PROTOCOL_VERSION });
13
19
  let entries = input.entries ?? [];
14
20
  let dirty = false; // flag to wake the loop when signals arrive
15
21
  let schedulerPaused = input.paused ?? false;
@@ -126,7 +132,7 @@ async function agentSchedulerWorkflow(input) {
126
132
  else if (entry.type === 'interval' && entry.interval) {
127
133
  entry.nextFireAt = new Date(Date.now() + entry.interval).toISOString();
128
134
  }
129
- else if ((0, workflow_1.patched)('v0.12-cron-schedule') && entry.type === 'cron' && entry.cronExpression) {
135
+ else if (entry.type === 'cron' && entry.cronExpression) {
130
136
  const nextFire = await computeNextCronFire({
131
137
  cronExpression: entry.cronExpression,
132
138
  timezone: entry.timezone,