agent-tempo 1.1.0 → 1.3.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 (103) hide show
  1. package/CLAUDE.md +219 -219
  2. package/LICENSE +21 -21
  3. package/README.md +289 -289
  4. package/assets/icon-dark.svg +9 -9
  5. package/assets/icon.svg +9 -9
  6. package/assets/logo-dark.svg +11 -11
  7. package/assets/logo-light.svg +11 -11
  8. package/dashboard/README.md +91 -91
  9. package/dashboard/dist/assets/index-D6Xyje_n.js.map +1 -1
  10. package/dashboard/dist/index.html +19 -19
  11. package/dashboard/package.json +47 -47
  12. package/dist/adapters/copilot/adapter.js +12 -1
  13. package/dist/cli/commands.d.ts +39 -0
  14. package/dist/cli/commands.js +83 -2
  15. package/dist/cli/global-wrapper.d.ts +19 -0
  16. package/dist/cli/global-wrapper.js +169 -0
  17. package/dist/cli/help-text.js +97 -97
  18. package/dist/cli/sa-preflight.d.ts +27 -3
  19. package/dist/cli/sa-preflight.js +169 -9
  20. package/dist/cli/startup.js +45 -8
  21. package/dist/cli/upgrade-command.js +81 -81
  22. package/dist/cli.js +12 -0
  23. package/dist/daemon.js +6 -0
  24. package/dist/http/catalog.js +17 -3
  25. package/dist/scripts/verify-daemon-isolation-guard.js +24 -24
  26. package/dist/server.js +4 -0
  27. package/dist/spawn.js +12 -12
  28. package/dist/tools/coat-check-evict.js +2 -2
  29. package/dist/tools/coat-check-get.js +2 -2
  30. package/dist/tools/coat-check-put.js +4 -4
  31. package/dist/tools/fetch-state.js +2 -2
  32. package/dist/tools/save-state.js +13 -13
  33. package/dist/utils/grpc-shutdown-guard.d.ts +52 -0
  34. package/dist/utils/grpc-shutdown-guard.js +88 -0
  35. package/examples/agents/tempo-composer.md +56 -56
  36. package/examples/agents/tempo-conductor.md +117 -117
  37. package/examples/agents/tempo-critic.md +73 -73
  38. package/examples/agents/tempo-improv.md +74 -74
  39. package/examples/agents/tempo-liner.md +75 -75
  40. package/examples/agents/tempo-roadie.md +61 -61
  41. package/examples/agents/tempo-soloist.md +71 -71
  42. package/examples/agents/tempo-tuner.md +94 -94
  43. package/examples/ensembles/tempo-big-band.yaml +146 -146
  44. package/examples/ensembles/tempo-dev-team.yaml +58 -58
  45. package/examples/ensembles/tempo-headless-jam.yaml +77 -77
  46. package/examples/ensembles/tempo-jam-session.yaml +41 -41
  47. package/examples/ensembles/tempo-mock-jam.yaml +79 -79
  48. package/examples/ensembles/tempo-review-squad.yaml +32 -32
  49. package/package.json +173 -172
  50. package/packaging/launchd/com.agent.tempo.plist +46 -46
  51. package/packaging/systemd/agent-tempo.service +32 -32
  52. package/packaging/windows/install-task.ps1 +71 -71
  53. package/scenarios/conductor-recruit-mock.yaml +33 -33
  54. package/scenarios/echo-roundtrip.yaml +15 -15
  55. package/scenarios/multi-player-handoff.yaml +38 -38
  56. package/scenarios/recruit-cascade.yaml +38 -38
  57. package/scenarios/two-player-conversation.yaml +33 -33
  58. package/workflow-bundle.js +1 -1
  59. package/dist/activities/claude-stop.d.ts +0 -21
  60. package/dist/activities/claude-stop.js +0 -94
  61. package/dist/channel.d.ts +0 -3
  62. package/dist/channel.js +0 -48
  63. package/dist/copilot-bridge.d.ts +0 -22
  64. package/dist/copilot-bridge.js +0 -565
  65. package/dist/scripts/258-spotcheck.js +0 -303
  66. package/dist/tools/detach.d.ts +0 -4
  67. package/dist/tools/detach.js +0 -45
  68. package/dist/tools/encore.d.ts +0 -4
  69. package/dist/tools/encore.js +0 -31
  70. package/dist/tools/pause-ensemble.d.ts +0 -4
  71. package/dist/tools/pause-ensemble.js +0 -58
  72. package/dist/tools/resume-ensemble.d.ts +0 -4
  73. package/dist/tools/resume-ensemble.js +0 -79
  74. package/dist/tools/stop.d.ts +0 -4
  75. package/dist/tools/stop.js +0 -29
  76. package/dist/tui/client.d.ts +0 -6
  77. package/dist/tui/client.js +0 -9
  78. package/dist/tui/components/ActivityLog.d.ts +0 -16
  79. package/dist/tui/components/ActivityLog.js +0 -36
  80. package/dist/tui/components/CommandOverlay.d.ts +0 -15
  81. package/dist/tui/components/CommandOverlay.js +0 -34
  82. package/dist/tui/components/ConductorChat.d.ts +0 -16
  83. package/dist/tui/components/ConductorChat.js +0 -32
  84. package/dist/tui/components/EnsembleListView.d.ts +0 -14
  85. package/dist/tui/components/EnsembleListView.js +0 -32
  86. package/dist/tui/components/EnsemblePanel.d.ts +0 -12
  87. package/dist/tui/components/EnsemblePanel.js +0 -40
  88. package/dist/tui/components/InputBar.d.ts +0 -13
  89. package/dist/tui/components/InputBar.js +0 -58
  90. package/dist/tui/components/ScheduleOverlay.d.ts +0 -13
  91. package/dist/tui/components/ScheduleOverlay.js +0 -113
  92. package/dist/tui/components/TopBar.d.ts +0 -12
  93. package/dist/tui/components/TopBar.js +0 -15
  94. package/dist/tui/core-api.d.ts +0 -26
  95. package/dist/tui/core-api.js +0 -67
  96. package/dist/tui/hooks/useEnsembleDiscovery.d.ts +0 -3
  97. package/dist/tui/hooks/useEnsembleDiscovery.js +0 -30
  98. package/dist/tui/hooks/useMaestroPoller.d.ts +0 -3
  99. package/dist/tui/hooks/useMaestroPoller.js +0 -36
  100. package/dist/tui/hooks/useSendCommand.d.ts +0 -7
  101. package/dist/tui/hooks/useSendCommand.js +0 -29
  102. package/dist/utils/bg-preflight.d.ts +0 -25
  103. package/dist/utils/bg-preflight.js +0 -154
@@ -57,102 +57,102 @@ const types_1 = require("../types");
57
57
  // adding a new adapter automatically updates this surface.
58
58
  const AGENT_OPTIONS = types_1.AGENT_TYPES.join('|');
59
59
  function printHelp() {
60
- console.log(`
61
- ${out.bold('agent-tempo')} — Multi-session Claude Code coordination via Temporal
62
-
63
- ${out.bold('Getting started:')}
64
- ${out.cyan('agent-tempo up')} Start infrastructure, then launch the TUI with ${out.dim('agent-tempo')}
65
-
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
69
- agent-tempo <command> [options]
70
-
71
- ${out.bold('Commands:')}
72
- ${out.cyan('up')} Start infrastructure only — Temporal, daemon, MCP registration
73
- ${out.cyan('down')} Stop infrastructure; workflows stay parked for the next ${out.dim('up')}
74
- ${out.cyan('down --destroy [-y]')} Terminate every workflow across every ensemble, then stop infrastructure
75
- ${out.cyan('server')} Start the Temporal dev server and register search attributes
76
- ${out.cyan('status')} [ensemble] Show active sessions and Temporal health
77
- ${out.cyan('ensemble')} <sub> Manage saved ensemble lineups (save/list/show)
78
- ${out.cyan('broadcast')} <message> Send a message to all active players
79
- ${out.cyan('destroy')} <ensemble> [-y] Terminate every workflow in one ensemble (typed confirmation)
80
- ${out.cyan('attachment-info')} <name> Inspect the V2 attachment phase + current holder
81
- ${out.cyan('recall')} <name> Read a player's message history (--limit/--offset/--preview/--from/--since/--include-sent/--json)
82
- ${out.cyan('hosts')} List daemons polling this Temporal namespace with advertised capabilities (--all/--json)
83
- ${out.cyan('refresh-host-profile')} Re-advertise this daemon's capability profile to the global Maestro
84
- ${out.cyan('restore')} <ensemble> Restore orphaned sessions in one ensemble on this host (--all-hosts for cluster-view listing)
85
- ${out.cyan('release')} [ensemble] Release all held players (unlock outbox, deliver messages)
86
- ${out.cyan('agent-types')} <sub> Manage player type definitions (list/show/init)
87
- ${out.cyan('daemon')} <sub> Manage the worker daemon (start/stop/status/logs)
88
- ${out.cyan('dashboard')} Open the web dashboard (--no-open / --pair / --json)
89
- ${out.cyan('upgrade')} [version] Upgrade agent-tempo to latest (or specific version)
90
- ${out.cyan('config')} Configure Temporal connection settings
91
- ${out.cyan('init')} Register MCP server globally (or --project for .mcp.json)
92
- ${out.cyan('preflight')} Run preflight checks only
93
- ${out.cyan('help')} Show this help message
94
-
95
- ${out.bold('Removed — use the TUI:')}
96
- ${out.dim('stop / restart / detach / migrate')} → ${out.dim('/destroy · /restart · /shutdown')}
97
- ${out.dim('conduct / start / recruit / disband')} → ${out.dim('launch `agent-tempo` · /recruit · /destroy')}
98
- ${out.dim('resume')} → ${out.dim('/play')}
99
- See https://github.com/vinceblank/agent-tempo/issues/285 for the full migration table.
100
-
101
- ${out.bold('Connection options (all commands):')}
102
- --temporal-address <addr> Temporal server address (default: localhost:7233)
103
- --temporal-namespace <ns> Temporal namespace (default: default)
104
- --temporal-api-key <key> Temporal API key (for Temporal Cloud)
105
- --temporal-tls-cert <path> Path to TLS client certificate
106
- --temporal-tls-key <path> Path to TLS client key
107
-
108
- ${out.bold('Other options:')}
109
- --name <name> Set session window name (up only)
110
- --agent <name> Agent type to spawn — ${AGENT_OPTIONS} (default: from config; up)
111
- --dev Use the dev profile (~/.agent-tempo-dev, port 8474, namespace agent-tempo-dev)
112
- --skip-preflight Skip preflight checks
113
- --background Run Temporal in background (server only)
114
- --project Use per-project .mcp.json instead of global (init only)
115
- --keep-mcp Don't remove MCP config (down only)
116
- --keep-daemon Don't stop the worker daemon (down only)
117
- --destroy Also terminate every workflow (down only)
118
- --kill-shared-temporal Tear down the Temporal dev server even if the other profile is active (down only, #423)
119
- -y, --yes Skip confirmation prompt (down --destroy, destroy)
120
- --lineup <name|file> Load ensemble lineup by name or file path (up)
121
- --scenario <name|path> Force every mock player in the lineup into mockMode:scripted with this scenario (dev-mode-only, up + --lineup)
122
- --no-hold Skip startup hold (requires --lineup on up)
123
- --ensemble <name> Target a specific ensemble (broadcast, destroy, restore)
124
- --all-hosts List cross-host orphans across the whole namespace (restore — read-only, #151)
125
- -d, --dir <path> Target directory (default: cwd)
126
-
127
- ${out.bold('Config command:')}
128
- ${out.dim('agent-tempo config')} Interactive connection setup
129
- ${out.dim('agent-tempo config show')} Show resolved config
130
- ${out.dim('agent-tempo config set <k> <v>')} Set a config value
131
-
132
- Settings are saved to ~/.agent-tempo/config.json.
133
- Also reads ~/.config/temporalio/temporal.yaml as a fallback.
134
-
135
- ${out.bold('Resolution order:')} CLI flag > env var > config file > temporal CLI config > default
136
-
137
- ${out.bold('First time? Run this:')}
138
- ${out.dim('cd your-project')}
139
- ${out.dim('agent-tempo up')}
140
- ${out.dim('agent-tempo')} # Launch the TUI
141
-
142
- ${out.bold('Typical workflow:')}
143
- ${out.dim('agent-tempo up')} Start infrastructure (once per host)
144
- ${out.dim('agent-tempo')} Launch the TUI
145
- ${out.dim('agent-tempo status myband')} Check who's active in an ensemble
146
-
147
- ${out.bold('Environment:')}
148
- AGENT_TEMPO_ENSEMBLE Default ensemble name (fallback: "default")
149
- TEMPORAL_ADDRESS Default Temporal address (fallback: localhost:7233)
150
- TEMPORAL_NAMESPACE Default Temporal namespace (fallback: "default")
151
- TEMPORAL_API_KEY Temporal API key
152
- TEMPORAL_TLS_CERT_PATH Path to TLS client certificate
153
- TEMPORAL_TLS_KEY_PATH Path to TLS client key
154
- AGENT_TEMPO_DEFAULT_AGENT Default agent type: claude or copilot (fallback: claude)
155
- AGENT_TEMPO_DEV_MODE Set to "1" or "true" to enable the dev profile (alternative to --dev)
156
- AGENT_TEMPO_HOME_OVERRIDE Override the home dir entirely (escape hatch for triple-isolated envs)
60
+ console.log(`
61
+ ${out.bold('agent-tempo')} — Multi-session Claude Code coordination via Temporal
62
+
63
+ ${out.bold('Getting started:')}
64
+ ${out.cyan('agent-tempo up')} Start infrastructure, then launch the TUI with ${out.dim('agent-tempo')}
65
+
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
69
+ agent-tempo <command> [options]
70
+
71
+ ${out.bold('Commands:')}
72
+ ${out.cyan('up')} Start infrastructure only — Temporal, daemon, MCP registration
73
+ ${out.cyan('down')} Stop infrastructure; workflows stay parked for the next ${out.dim('up')}
74
+ ${out.cyan('down --destroy [-y]')} Terminate every workflow across every ensemble, then stop infrastructure
75
+ ${out.cyan('server')} Start the Temporal dev server and register search attributes
76
+ ${out.cyan('status')} [ensemble] Show active sessions and Temporal health
77
+ ${out.cyan('ensemble')} <sub> Manage saved ensemble lineups (save/list/show)
78
+ ${out.cyan('broadcast')} <message> Send a message to all active players
79
+ ${out.cyan('destroy')} <ensemble> [-y] Terminate every workflow in one ensemble (typed confirmation)
80
+ ${out.cyan('attachment-info')} <name> Inspect the V2 attachment phase + current holder
81
+ ${out.cyan('recall')} <name> Read a player's message history (--limit/--offset/--preview/--from/--since/--include-sent/--json)
82
+ ${out.cyan('hosts')} List daemons polling this Temporal namespace with advertised capabilities (--all/--json)
83
+ ${out.cyan('refresh-host-profile')} Re-advertise this daemon's capability profile to the global Maestro
84
+ ${out.cyan('restore')} <ensemble> Restore orphaned sessions in one ensemble on this host (--all-hosts for cluster-view listing)
85
+ ${out.cyan('release')} [ensemble] Release all held players (unlock outbox, deliver messages)
86
+ ${out.cyan('agent-types')} <sub> Manage player type definitions (list/show/init)
87
+ ${out.cyan('daemon')} <sub> Manage the worker daemon (start/stop/status/logs)
88
+ ${out.cyan('dashboard')} Open the web dashboard (--no-open / --pair / --json)
89
+ ${out.cyan('upgrade')} [version] Upgrade agent-tempo to latest (or specific version)
90
+ ${out.cyan('config')} Configure Temporal connection settings
91
+ ${out.cyan('init')} Register MCP server globally (or --project for .mcp.json)
92
+ ${out.cyan('preflight')} Run preflight checks only
93
+ ${out.cyan('help')} Show this help message
94
+
95
+ ${out.bold('Removed — use the TUI:')}
96
+ ${out.dim('stop / restart / detach / migrate')} → ${out.dim('/destroy · /restart · /shutdown')}
97
+ ${out.dim('conduct / start / recruit / disband')} → ${out.dim('launch `agent-tempo` · /recruit · /destroy')}
98
+ ${out.dim('resume')} → ${out.dim('/play')}
99
+ See https://github.com/vinceblank/agent-tempo/issues/285 for the full migration table.
100
+
101
+ ${out.bold('Connection options (all commands):')}
102
+ --temporal-address <addr> Temporal server address (default: localhost:7233)
103
+ --temporal-namespace <ns> Temporal namespace (default: default)
104
+ --temporal-api-key <key> Temporal API key (for Temporal Cloud)
105
+ --temporal-tls-cert <path> Path to TLS client certificate
106
+ --temporal-tls-key <path> Path to TLS client key
107
+
108
+ ${out.bold('Other options:')}
109
+ --name <name> Set session window name (up only)
110
+ --agent <name> Agent type to spawn — ${AGENT_OPTIONS} (default: from config; up)
111
+ --dev Use the dev profile (~/.agent-tempo-dev, port 8474, namespace agent-tempo-dev)
112
+ --skip-preflight Skip preflight checks
113
+ --background Run Temporal in background (server only)
114
+ --project Use per-project .mcp.json instead of global (init only)
115
+ --keep-mcp Don't remove MCP config (down only)
116
+ --keep-daemon Don't stop the worker daemon (down only)
117
+ --destroy Also terminate every workflow (down only)
118
+ --kill-shared-temporal Tear down the Temporal dev server even if the other profile is active (down only, #423)
119
+ -y, --yes Skip confirmation prompt (down --destroy, destroy)
120
+ --lineup <name|file> Load ensemble lineup by name or file path (up)
121
+ --scenario <name|path> Force every mock player in the lineup into mockMode:scripted with this scenario (dev-mode-only, up + --lineup)
122
+ --no-hold Skip startup hold (requires --lineup on up)
123
+ --ensemble <name> Target a specific ensemble (broadcast, destroy, restore)
124
+ --all-hosts List cross-host orphans across the whole namespace (restore — read-only, #151)
125
+ -d, --dir <path> Target directory (default: cwd)
126
+
127
+ ${out.bold('Config command:')}
128
+ ${out.dim('agent-tempo config')} Interactive connection setup
129
+ ${out.dim('agent-tempo config show')} Show resolved config
130
+ ${out.dim('agent-tempo config set <k> <v>')} Set a config value
131
+
132
+ Settings are saved to ~/.agent-tempo/config.json.
133
+ Also reads ~/.config/temporalio/temporal.yaml as a fallback.
134
+
135
+ ${out.bold('Resolution order:')} CLI flag > env var > config file > temporal CLI config > default
136
+
137
+ ${out.bold('First time? Run this:')}
138
+ ${out.dim('cd your-project')}
139
+ ${out.dim('agent-tempo up')}
140
+ ${out.dim('agent-tempo')} # Launch the TUI
141
+
142
+ ${out.bold('Typical workflow:')}
143
+ ${out.dim('agent-tempo up')} Start infrastructure (once per host)
144
+ ${out.dim('agent-tempo')} Launch the TUI
145
+ ${out.dim('agent-tempo status myband')} Check who's active in an ensemble
146
+
147
+ ${out.bold('Environment:')}
148
+ AGENT_TEMPO_ENSEMBLE Default ensemble name (fallback: "default")
149
+ TEMPORAL_ADDRESS Default Temporal address (fallback: localhost:7233)
150
+ TEMPORAL_NAMESPACE Default Temporal namespace (fallback: "default")
151
+ TEMPORAL_API_KEY Temporal API key
152
+ TEMPORAL_TLS_CERT_PATH Path to TLS client certificate
153
+ TEMPORAL_TLS_KEY_PATH Path to TLS client key
154
+ AGENT_TEMPO_DEFAULT_AGENT Default agent type: claude or copilot (fallback: claude)
155
+ AGENT_TEMPO_DEV_MODE Set to "1" or "true" to enable the dev profile (alternative to --dev)
156
+ AGENT_TEMPO_HOME_OVERRIDE Override the home dir entirely (escape hatch for triple-isolated envs)
157
157
  `);
158
158
  }
@@ -6,10 +6,13 @@ export declare const REQUIRED_SEARCH_ATTRIBUTES: ReadonlyArray<{
6
6
  export interface SearchAttributePreflightOpts {
7
7
  temporalAddress: string;
8
8
  temporalNamespace: string;
9
+ /** API key for Temporal Cloud — triggers SDK-based probe when set. */
10
+ temporalApiKey?: string;
9
11
  /**
10
12
  * Optional test seam — given a namespace, return the set of search
11
13
  * attribute names that ARE currently registered. Defaults to
12
- * {@link defaultProbeRegisteredAttributes} which shells out to
14
+ * {@link sdkProbeRegisteredAttributes} when `temporalApiKey` is set,
15
+ * otherwise {@link defaultProbeRegisteredAttributes} which shells out to
13
16
  * `temporal operator search-attribute list`.
14
17
  */
15
18
  probe?: (opts: {
@@ -35,15 +38,36 @@ export declare function defaultProbeRegisteredAttributes(opts: {
35
38
  temporalAddress: string;
36
39
  temporalNamespace: string;
37
40
  }): Promise<Set<string>>;
41
+ /** Returns true if the address looks like a Temporal Cloud endpoint. */
42
+ export declare function isTemporalCloud(address: string): boolean;
43
+ /**
44
+ * SDK-based probe — uses the Temporal Client SDK to verify search attribute
45
+ * existence by issuing a visibility query. Works with Temporal Cloud API keys
46
+ * where the `temporal operator` CLI commands are unauthorized.
47
+ *
48
+ * Strategy: for each required attribute, issue `listWorkflowExecutions` with
49
+ * a query referencing that attribute. If the attribute is registered the query
50
+ * returns (possibly empty) results. If not registered, Temporal responds with
51
+ * INVALID_ARGUMENT containing "is not a valid search attribute".
52
+ */
53
+ export declare function sdkProbeRegisteredAttributes(opts: {
54
+ temporalAddress: string;
55
+ temporalNamespace: string;
56
+ temporalApiKey?: string;
57
+ }): Promise<Set<string>>;
38
58
  /**
39
59
  * Format the missing-SA error message. Paste-friendly: operators copy the
40
- * `temporal operator search-attribute create` block verbatim.
60
+ * registration commands verbatim. Cloud-aware: shows `tcld` commands for
61
+ * Temporal Cloud namespaces.
41
62
  */
42
- export declare function formatPreflightError(missing: ReadonlyArray<typeof REQUIRED_SEARCH_ATTRIBUTES[number]>, namespace: string, probeError?: string): string;
63
+ export declare function formatPreflightError(missing: ReadonlyArray<typeof REQUIRED_SEARCH_ATTRIBUTES[number]>, namespace: string, probeError?: string, cloud?: boolean): string;
43
64
  /**
44
65
  * Verify all {@link REQUIRED_SEARCH_ATTRIBUTES} are registered on the
45
66
  * given namespace. Returns a structured result — callers decide whether
46
67
  * to log+continue (boot bootstrap step) or exit non-zero (daemon start).
68
+ *
69
+ * When `temporalApiKey` is set (or address is a Cloud endpoint), uses the
70
+ * SDK-based probe instead of shelling out to `temporal operator`.
47
71
  */
48
72
  export declare function verifySearchAttributes(opts: SearchAttributePreflightOpts): Promise<SearchAttributePreflightResult>;
49
73
  /**
@@ -1,7 +1,42 @@
1
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.REQUIRED_SEARCH_ATTRIBUTES = void 0;
4
37
  exports.defaultProbeRegisteredAttributes = defaultProbeRegisteredAttributes;
38
+ exports.isTemporalCloud = isTemporalCloud;
39
+ exports.sdkProbeRegisteredAttributes = sdkProbeRegisteredAttributes;
5
40
  exports.formatPreflightError = formatPreflightError;
6
41
  exports.verifySearchAttributes = verifySearchAttributes;
7
42
  exports.classifyRegistrationOutput = classifyRegistrationOutput;
@@ -29,6 +64,16 @@ exports.assertSearchAttributesOrExit = assertSearchAttributesOrExit;
29
64
  * - `src/daemon.ts` boot path calls {@link verifySearchAttributes}
30
65
  * directly to fail fast on `agent-tempo daemon start` before the worker
31
66
  * tries to register workflows.
67
+ *
68
+ * Cloud support:
69
+ * Temporal Cloud's operator gRPC service is not accessible with namespace
70
+ * API keys, causing `temporal operator search-attribute list/create` to
71
+ * fail with "Request unauthorized". When a Cloud namespace is detected
72
+ * (address contains `.tmprl.cloud` or an API key is configured), the
73
+ * preflight uses an SDK-based probe: issue a visibility query referencing
74
+ * each required attribute — a registered attribute returns an empty result
75
+ * set; an unregistered one throws INVALID_ARGUMENT. Registration
76
+ * instructions surface `tcld` commands instead of `temporal operator`.
32
77
  */
33
78
  const child_process_1 = require("child_process");
34
79
  /** Single source of truth — must match `SEARCH_ATTRIBUTES` in `src/cli/startup.ts`. */
@@ -73,22 +118,123 @@ async function defaultProbeRegisteredAttributes(opts) {
73
118
  }
74
119
  return names;
75
120
  }
121
+ /** Returns true if the address looks like a Temporal Cloud endpoint. */
122
+ function isTemporalCloud(address) {
123
+ return address.includes('.tmprl.cloud');
124
+ }
125
+ /**
126
+ * Substrings Temporal servers use to signal "this search attribute is not
127
+ * registered". The exact wording varies across server versions and storage
128
+ * backends (e.g. 'is not a valid search attribute', 'is not defined',
129
+ * 'no mapping defined for the field'), so we match a set rather than a
130
+ * single phrase — a wording change must not cause the probe to misclassify
131
+ * an unregistered SA as an unexpected error and re-throw.
132
+ */
133
+ const UNREGISTERED_SA_MARKERS = [
134
+ 'is not a valid search attribute',
135
+ 'is not defined',
136
+ 'no mapping defined for the field',
137
+ 'unknown or unindexed search attribute',
138
+ ];
139
+ /** gRPC status code for INVALID_ARGUMENT. */
140
+ const GRPC_INVALID_ARGUMENT = 3;
141
+ /**
142
+ * Classify a visibility-query error as "search attribute not registered".
143
+ *
144
+ * A registered-but-empty attribute returns results; an unregistered one
145
+ * fails. Temporal reports the failure as gRPC INVALID_ARGUMENT — we key on
146
+ * that status code first (wording-independent) and fall back to known
147
+ * message substrings for transports/versions that don't surface a code.
148
+ */
149
+ function isUnregisteredAttributeError(err) {
150
+ const msg = (err?.message || '').toLowerCase();
151
+ if (UNREGISTERED_SA_MARKERS.some((m) => msg.includes(m)))
152
+ return true;
153
+ // gRPC ServiceError exposes a numeric `code`; INVALID_ARGUMENT means the
154
+ // query referenced an attribute the namespace doesn't know about.
155
+ if (err?.code === GRPC_INVALID_ARGUMENT)
156
+ return true;
157
+ return false;
158
+ }
159
+ /**
160
+ * SDK-based probe — uses the Temporal Client SDK to verify search attribute
161
+ * existence by issuing a visibility query. Works with Temporal Cloud API keys
162
+ * where the `temporal operator` CLI commands are unauthorized.
163
+ *
164
+ * Strategy: for each required attribute, issue `listWorkflowExecutions` with
165
+ * a query referencing that attribute. If the attribute is registered the query
166
+ * returns (possibly empty) results. If not registered, Temporal responds with
167
+ * INVALID_ARGUMENT containing "is not a valid search attribute".
168
+ */
169
+ async function sdkProbeRegisteredAttributes(opts) {
170
+ const { Connection } = await Promise.resolve().then(() => __importStar(require('@temporalio/client')));
171
+ const tls = opts.temporalApiKey ? true : undefined;
172
+ const conn = await Connection.connect({
173
+ address: opts.temporalAddress,
174
+ tls: tls,
175
+ apiKey: opts.temporalApiKey,
176
+ });
177
+ const registered = new Set();
178
+ try {
179
+ for (const attr of exports.REQUIRED_SEARCH_ATTRIBUTES) {
180
+ // The probe value only has to be syntactically valid for the
181
+ // attribute's type so the visibility query parses. We support
182
+ // Keyword (quoted string literal) and Bool (`true`) here — the only
183
+ // two types in REQUIRED_SEARCH_ATTRIBUTES. A new SA type would need
184
+ // its own literal form added below.
185
+ const testValue = attr.type === 'Bool' ? 'true' : '"__probe__"';
186
+ try {
187
+ await conn.workflowService.listWorkflowExecutions({
188
+ namespace: opts.temporalNamespace,
189
+ query: `${attr.name} = ${testValue}`,
190
+ pageSize: 1,
191
+ });
192
+ registered.add(attr.name);
193
+ }
194
+ catch (err) {
195
+ if (isUnregisteredAttributeError(err)) {
196
+ // Attribute not registered — don't add to set
197
+ }
198
+ else {
199
+ // Unexpected error — re-throw
200
+ throw err;
201
+ }
202
+ }
203
+ }
204
+ }
205
+ finally {
206
+ await conn.close();
207
+ }
208
+ return registered;
209
+ }
76
210
  /**
77
211
  * Format the missing-SA error message. Paste-friendly: operators copy the
78
- * `temporal operator search-attribute create` block verbatim.
212
+ * registration commands verbatim. Cloud-aware: shows `tcld` commands for
213
+ * Temporal Cloud namespaces.
79
214
  */
80
- function formatPreflightError(missing, namespace, probeError) {
215
+ function formatPreflightError(missing, namespace, probeError, cloud) {
81
216
  const lines = [];
82
217
  lines.push(`Required search attributes not registered on namespace '${namespace}'.`);
83
218
  if (probeError) {
84
219
  lines.push(`(Could not probe namespace state: ${probeError})`);
85
220
  }
86
221
  lines.push('');
87
- lines.push('Run these commands once per Temporal namespace, then restart the daemon:');
88
- lines.push('');
89
- for (const attr of missing) {
90
- lines.push(` temporal operator search-attribute create ` +
91
- `--name ${attr.name} --type ${attr.type} --namespace ${namespace}`);
222
+ if (cloud) {
223
+ lines.push('Register via tcld (Temporal Cloud CLI) or the Cloud UI, then restart the daemon:');
224
+ lines.push('');
225
+ const saFlags = missing.map((attr) => `--sa "${attr.name}=${attr.type}"`).join(' \\\n ');
226
+ lines.push(` tcld namespace search-attributes add --namespace ${namespace} \\\n ${saFlags}`);
227
+ lines.push('');
228
+ lines.push('Or add them manually in the Temporal Cloud UI:');
229
+ lines.push(` https://cloud.temporal.io → Namespaces → ${namespace} → Search Attributes`);
230
+ }
231
+ else {
232
+ lines.push('Run these commands once per Temporal namespace, then restart the daemon:');
233
+ lines.push('');
234
+ for (const attr of missing) {
235
+ lines.push(` temporal operator search-attribute create ` +
236
+ `--name ${attr.name} --type ${attr.type} --namespace ${namespace}`);
237
+ }
92
238
  }
93
239
  lines.push('');
94
240
  lines.push('(See docs/ops/v1.0-migration.md for the full upgrade walkthrough.)');
@@ -98,9 +244,23 @@ function formatPreflightError(missing, namespace, probeError) {
98
244
  * Verify all {@link REQUIRED_SEARCH_ATTRIBUTES} are registered on the
99
245
  * given namespace. Returns a structured result — callers decide whether
100
246
  * to log+continue (boot bootstrap step) or exit non-zero (daemon start).
247
+ *
248
+ * When `temporalApiKey` is set (or address is a Cloud endpoint), uses the
249
+ * SDK-based probe instead of shelling out to `temporal operator`.
101
250
  */
102
251
  async function verifySearchAttributes(opts) {
103
- const probe = opts.probe ?? defaultProbeRegisteredAttributes;
252
+ const cloud = isTemporalCloud(opts.temporalAddress) || !!opts.temporalApiKey;
253
+ const defaultProbe = cloud
254
+ ? () => sdkProbeRegisteredAttributes({
255
+ temporalAddress: opts.temporalAddress,
256
+ temporalNamespace: opts.temporalNamespace,
257
+ temporalApiKey: opts.temporalApiKey,
258
+ })
259
+ : () => defaultProbeRegisteredAttributes({
260
+ temporalAddress: opts.temporalAddress,
261
+ temporalNamespace: opts.temporalNamespace,
262
+ });
263
+ const probe = opts.probe ?? defaultProbe;
104
264
  let registered;
105
265
  let probeError;
106
266
  try {
@@ -121,7 +281,7 @@ async function verifySearchAttributes(opts) {
121
281
  ok: false,
122
282
  missing,
123
283
  probeError,
124
- message: formatPreflightError(missing, opts.temporalNamespace, probeError),
284
+ message: formatPreflightError(missing, opts.temporalNamespace, probeError, cloud),
125
285
  };
126
286
  }
127
287
  /**
@@ -134,6 +134,7 @@ const TTL_60S = 60 * 1000;
134
134
  // (#605 consolidated the two duplicated literals).
135
135
  // ─────────────────────────────────────────────────────────────────────────
136
136
  const sa_preflight_1 = require("./sa-preflight");
137
+ const global_wrapper_1 = require("./global-wrapper");
137
138
  // ─────────────────────────────────────────────────────────────────────────
138
139
  // Semver-aware outdated-version badge rendering (#289 pin item 4)
139
140
  // ─────────────────────────────────────────────────────────────────────────
@@ -376,14 +377,40 @@ async function stepSearchAttrs(cache, config, now) {
376
377
  if (isCacheFresh(cache.steps.searchAttrs, TTL_24H, now)) {
377
378
  return { status: 'skipped', durationMs: 0 };
378
379
  }
379
- const { result: outcome, durationMs } = await timed(() => {
380
- // Per-attr classification via `registerSearchAttribute` (#605)
381
- // distinguishes `already-exists` (idempotent expected case) from real
382
- // failures. Pre-#605 every non-zero exit was swallowed as "already
383
- // registered", masking the SQLite dev-server's 10-Keyword-per-namespace
384
- // cap and other genuine errors until a downstream workflow start failed
385
- // with the confusing `INVALID_ARGUMENT: search attribute ... is not
386
- // defined`.
380
+ const cloud = (0, sa_preflight_1.isTemporalCloud)(config.temporalAddress) || !!config.temporalApiKey;
381
+ const { result: outcome, durationMs } = await timed(async () => {
382
+ if (cloud) {
383
+ // For Temporal Cloud, use SDK probe to verify SAs are present.
384
+ // Registration must be done via tcld or the Cloud UI — we cannot
385
+ // use `temporal operator search-attribute create`.
386
+ try {
387
+ const registered = await (0, sa_preflight_1.sdkProbeRegisteredAttributes)({
388
+ temporalAddress: config.temporalAddress,
389
+ temporalNamespace: config.temporalNamespace,
390
+ temporalApiKey: config.temporalApiKey,
391
+ });
392
+ const missing = sa_preflight_1.REQUIRED_SEARCH_ATTRIBUTES.filter((a) => !registered.has(a.name));
393
+ if (missing.length > 0) {
394
+ const saFlags = missing.map((a) => `--sa "${a.name}=${a.type}"`).join(' ');
395
+ return {
396
+ status: 'failed',
397
+ durationMs: 0,
398
+ detail: `${missing.length} search attribute(s) not registered on Temporal Cloud.\n` +
399
+ ` Register via: tcld namespace search-attributes add --namespace ${config.temporalNamespace} ${saFlags}\n` +
400
+ ` Or add them in the Cloud UI: https://cloud.temporal.io`,
401
+ };
402
+ }
403
+ return { status: 'ok', durationMs: 0 };
404
+ }
405
+ catch (err) {
406
+ return {
407
+ status: 'failed',
408
+ durationMs: 0,
409
+ detail: `SDK probe failed: ${err?.message || err}`,
410
+ };
411
+ }
412
+ }
413
+ // Self-hosted path: per-attr classification via `registerSearchAttribute` (#605)
387
414
  const failures = [];
388
415
  for (const attr of sa_preflight_1.REQUIRED_SEARCH_ATTRIBUTES) {
389
416
  const r = (0, sa_preflight_1.registerSearchAttribute)(attr, config.temporalAddress, config.temporalNamespace);
@@ -598,6 +625,16 @@ async function bootstrap(args) {
598
625
  // Steps 1–5: fail-fast on step 1 so we don't try to register MCP on an
599
626
  // incompatible Node. Each step mutates the cache in-place.
600
627
  const preflight = await stepPreflight(cache, now);
628
+ // Provision the global wrapper scripts (`~/.agent-tempo/bin/agent-tempo`)
629
+ // so the command is accessible without `npx`. Runs once per binary version
630
+ // (guarded by cache wipe on version change). Best-effort, non-blocking.
631
+ if (preflight.status !== 'failed') {
632
+ const { needsPathHint } = (0, global_wrapper_1.provisionWrapperScripts)();
633
+ if (needsPathHint) {
634
+ // Emit a one-time hint to stderr (won't pollute --json output).
635
+ process.stderr.write(`\n hint: ${(0, global_wrapper_1.getPathHint)()}\n\n`);
636
+ }
637
+ }
601
638
  // Steps 2 + 3 are independent (MCP config is local fs/shell; Temporal
602
639
  // reachability is network), so run them in parallel — up to ~300ms
603
640
  // cold-path savings when the claude-mcp-list shell-out is slow.