claude-tempo 0.26.0 → 0.28.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.
- package/CLAUDE.md +10 -2
- package/README.md +21 -21
- package/dist/activities/outbox.js +20 -12
- package/dist/adapters/base.d.ts +84 -0
- package/dist/adapters/base.js +345 -20
- package/dist/cli/commands.d.ts +21 -73
- package/dist/cli/commands.js +200 -646
- package/dist/cli/daemon-command.d.ts +4 -0
- package/dist/cli/daemon-command.js +102 -1
- package/dist/cli/daemon.d.ts +31 -3
- package/dist/cli/daemon.js +58 -16
- package/dist/cli/help-text.js +47 -47
- package/dist/cli/removed-verbs.d.ts +9 -0
- package/dist/cli/removed-verbs.js +75 -0
- package/dist/cli/startup.d.ts +103 -0
- package/dist/cli/startup.js +615 -0
- package/dist/cli.js +54 -170
- package/dist/client/core.d.ts +24 -0
- package/dist/client/core.js +1033 -0
- package/dist/client/ensure-conductor-spawned.d.ts +35 -0
- package/dist/client/ensure-conductor-spawned.js +48 -0
- package/dist/client/index.d.ts +29 -7
- package/dist/client/index.js +15 -583
- package/dist/client/interface.d.ts +258 -4
- package/dist/client/subscribe.d.ts +108 -0
- package/dist/client/subscribe.js +590 -0
- package/dist/client/with-spawn.d.ts +27 -0
- package/dist/client/with-spawn.js +87 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +57 -7
- package/dist/daemon.d.ts +19 -0
- package/dist/daemon.js +133 -77
- package/dist/ensemble/loader.js +9 -0
- package/dist/ensemble/saver.js +3 -1
- package/dist/ensemble/schema.d.ts +7 -1
- package/dist/http/aggregate.d.ts +161 -0
- package/dist/http/aggregate.js +466 -0
- package/dist/http/auth.d.ts +67 -0
- package/dist/http/auth.js +177 -0
- package/dist/http/cors.d.ts +42 -0
- package/dist/http/cors.js +111 -0
- package/dist/http/event-bus.d.ts +217 -0
- package/dist/http/event-bus.js +365 -0
- package/dist/http/event-id.d.ts +77 -0
- package/dist/http/event-id.js +117 -0
- package/dist/http/event-types.d.ts +280 -0
- package/dist/http/event-types.js +36 -0
- package/dist/http/index.d.ts +21 -0
- package/dist/http/index.js +61 -0
- package/dist/http/port-file.d.ts +22 -0
- package/dist/http/port-file.js +132 -0
- package/dist/http/responses.d.ts +27 -0
- package/dist/http/responses.js +40 -0
- package/dist/http/ring-buffer.d.ts +41 -0
- package/dist/http/ring-buffer.js +80 -0
- package/dist/http/server.d.ts +101 -0
- package/dist/http/server.js +368 -0
- package/dist/http/snapshot.d.ts +51 -0
- package/dist/http/snapshot.js +109 -0
- package/dist/http/sse-handler.d.ts +75 -0
- package/dist/http/sse-handler.js +276 -0
- package/dist/reconcile/orphans.d.ts +131 -2
- package/dist/reconcile/orphans.js +207 -10
- package/dist/server.js +32 -11
- package/dist/tools/destroy.d.ts +1 -1
- package/dist/tools/destroy.js +167 -24
- package/dist/tools/{pause-ensemble.d.ts → pause.d.ts} +1 -1
- package/dist/tools/pause.js +36 -0
- package/dist/tools/{resume-ensemble.d.ts → play.d.ts} +1 -1
- package/dist/tools/play.js +57 -0
- package/dist/tools/restore.d.ts +4 -0
- package/dist/tools/restore.js +107 -0
- package/dist/tools/shutdown.d.ts +4 -0
- package/dist/tools/shutdown.js +54 -0
- package/dist/tui/App.d.ts +63 -0
- package/dist/tui/App.js +619 -221
- package/dist/tui/bootstrap-types.d.ts +46 -0
- package/dist/tui/bootstrap-types.js +7 -0
- package/dist/tui/commands.d.ts +48 -17
- package/dist/tui/commands.js +383 -197
- package/dist/tui/components/DestroyConfirmModal.d.ts +17 -0
- package/dist/tui/components/DestroyConfirmModal.js +62 -0
- package/dist/tui/components/HomeView.d.ts +54 -0
- package/dist/tui/components/HomeView.js +306 -0
- package/dist/tui/components/LoadLineupModal.d.ts +18 -0
- package/dist/tui/components/LoadLineupModal.js +79 -0
- package/dist/tui/components/NewEnsembleModal.d.ts +9 -0
- package/dist/tui/components/NewEnsembleModal.js +73 -0
- package/dist/tui/components/PromptArea.js +9 -12
- package/dist/tui/components/RestoreConfirmModal.d.ts +18 -0
- package/dist/tui/components/RestoreConfirmModal.js +71 -0
- package/dist/tui/components/StatusBar.d.ts +34 -1
- package/dist/tui/components/StatusBar.js +59 -14
- package/dist/tui/index.d.ts +8 -0
- package/dist/tui/index.js +21 -0
- package/dist/tui/removed-commands.d.ts +9 -0
- package/dist/tui/removed-commands.js +22 -0
- package/dist/tui/sse-handler.d.ts +52 -0
- package/dist/tui/sse-handler.js +159 -0
- package/dist/tui/store.d.ts +168 -2
- package/dist/tui/store.js +213 -3
- package/dist/tui/utils/format.d.ts +19 -0
- package/dist/tui/utils/format.js +17 -0
- package/dist/tui/utils/history.d.ts +0 -5
- package/dist/tui/utils/history.js +0 -17
- package/dist/tui/utils/platform.d.ts +0 -11
- package/dist/tui/utils/platform.js +0 -37
- package/dist/utils/ensemble-ops.d.ts +61 -0
- package/dist/utils/ensemble-ops.js +77 -0
- package/dist/utils/hosts.d.ts +32 -8
- package/dist/utils/hosts.js +52 -21
- package/dist/workflows/maestro-signals.d.ts +21 -0
- package/dist/workflows/maestro-signals.js +19 -1
- package/dist/workflows/maestro.js +7 -0
- package/dist/workflows/session.js +41 -4
- package/package.json +6 -2
- package/workflow-bundle.js +68 -6
- package/dist/tools/detach.d.ts +0 -4
- package/dist/tools/detach.js +0 -45
- package/dist/tools/pause-ensemble.js +0 -58
- package/dist/tools/resume-ensemble.js +0 -79
- package/dist/tui/components/CommandOverlay.d.ts +0 -15
- package/dist/tui/components/CommandOverlay.js +0 -34
- package/dist/tui/components/ScheduleOverlay.d.ts +0 -13
- package/dist/tui/components/ScheduleOverlay.js +0 -113
package/CLAUDE.md
CHANGED
|
@@ -28,6 +28,8 @@ src/
|
|
|
28
28
|
│ ├── mcp.ts # MCP server registration helpers (init, global vs project)
|
|
29
29
|
│ ├── output.ts # Shared CLI output formatting helpers
|
|
30
30
|
│ ├── preflight.ts # Environment preflight checks
|
|
31
|
+
│ ├── removed-verbs.ts # lookup table for the 10 CLI verbs removed in #288 — dispatches migration hints before loading Temporal surface
|
|
32
|
+
│ ├── startup.ts # auto-provisioning bootstrap state machine (#289) — six-step idempotent sequence used by bare `claude-tempo` invocation
|
|
31
33
|
│ └── upgrade-command.ts # upgrade subcommand — crash-proof; dynamic-imports Temporal only for active-session warning
|
|
32
34
|
├── adapters/
|
|
33
35
|
│ ├── README.md # Adapter contract documentation
|
|
@@ -69,12 +71,11 @@ src/
|
|
|
69
71
|
│ ├── worktree.ts / stage.ts / stages.ts / cancel-stage.ts
|
|
70
72
|
│ ├── load-lineup.ts / save-lineup.ts / agent-types.ts / resolve.ts
|
|
71
73
|
│ ├── set-name.ts / set-part.ts / who-am-i.ts / release.ts
|
|
72
|
-
│ ├── pause
|
|
74
|
+
│ ├── pause.ts / play.ts / shutdown.ts / restore.ts
|
|
73
75
|
│ ├── hosts.ts
|
|
74
76
|
│ └── helpers.ts # Zod/MCP tool registration wrapper
|
|
75
77
|
├── tui/
|
|
76
78
|
│ ├── App.tsx / store.ts / commands.ts # TUI root, state, slash commands
|
|
77
|
-
│ ├── client.ts # Backward-compat shim → src/client/
|
|
78
79
|
│ ├── components/ # Ink components — see docs/tui.md for inventory
|
|
79
80
|
│ └── utils/ # format, platform, theme, fullscreen, history
|
|
80
81
|
├── utils/
|
|
@@ -106,6 +107,13 @@ npm test
|
|
|
106
107
|
> surveys or migrations, always grep **both** `test/` and `tests/` or you will miss
|
|
107
108
|
> mocks and assertions that only live in one directory.
|
|
108
109
|
|
|
110
|
+
> **Test-only hooks live with the module they reset and follow the
|
|
111
|
+
> `__<verb><Noun>ForTests` naming convention** — see
|
|
112
|
+
> [docs/adr/0006-test-hooks-naming.md](docs/adr/0006-test-hooks-naming.md). The
|
|
113
|
+
> double-underscore prefix telegraphs "test escape hatch, do not call from
|
|
114
|
+
> production code"; the hook's doc-comment should restate that explicitly. Hooks
|
|
115
|
+
> are never surfaced through barrels or `TempoClient`.
|
|
116
|
+
|
|
109
117
|
See [docs/development.md](docs/development.md) for full setup (Temporal dev server command,
|
|
110
118
|
daemon worker notes, `npx ts-node` dev runner).
|
|
111
119
|
|
package/README.md
CHANGED
|
@@ -59,11 +59,10 @@ claude-tempo up
|
|
|
59
59
|
This starts Temporal, registers the MCP server, launches the daemon, and opens a conductor session. Then add players:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
claude-tempo start # open a player session
|
|
63
62
|
claude-tempo status # see who's active
|
|
64
63
|
```
|
|
65
64
|
|
|
66
|
-
Or ask the conductor to `recruit`
|
|
65
|
+
Or use the TUI to recruit players, or ask the conductor to `recruit` from inside Claude Code.
|
|
67
66
|
|
|
68
67
|
### Manual setup
|
|
69
68
|
|
|
@@ -71,8 +70,7 @@ Or ask the conductor to `recruit` players from inside Claude Code.
|
|
|
71
70
|
claude-tempo server # start Temporal dev server
|
|
72
71
|
claude-tempo init # register MCP server globally
|
|
73
72
|
claude-tempo preflight # verify environment
|
|
74
|
-
claude-tempo
|
|
75
|
-
claude-tempo start # start a player
|
|
73
|
+
claude-tempo up # launch conductor via auto-provisioning
|
|
76
74
|
```
|
|
77
75
|
|
|
78
76
|
## Upgrading
|
|
@@ -90,12 +88,15 @@ claude-tempo upgrade 0.22.0
|
|
|
90
88
|
## Stopping & Tear Down
|
|
91
89
|
|
|
92
90
|
```bash
|
|
93
|
-
#
|
|
94
|
-
claude-tempo
|
|
91
|
+
# Terminate all sessions in an ensemble
|
|
92
|
+
claude-tempo destroy my-ensemble
|
|
95
93
|
|
|
96
94
|
# Tear down everything (all sessions, schedulers, and Maestro workflows)
|
|
97
95
|
claude-tempo down --all
|
|
98
96
|
|
|
97
|
+
# Tear down and terminate all workflows in one step
|
|
98
|
+
claude-tempo down --destroy -y
|
|
99
|
+
|
|
99
100
|
# Stop the background daemon
|
|
100
101
|
claude-tempo daemon stop
|
|
101
102
|
```
|
|
@@ -107,17 +108,17 @@ claude-tempo daemon stop
|
|
|
107
108
|
## Core Concepts
|
|
108
109
|
|
|
109
110
|
- **Player** — A Claude Code session registered as a Temporal workflow
|
|
110
|
-
- **Conductor** —
|
|
111
|
+
- **Conductor** — Required orchestration hub (one per ensemble); receives `report` calls and connects to external interfaces. Lineup schema enforces its presence.
|
|
111
112
|
- **Ensemble** — A named group of players isolated from other ensembles; defaults to `default`
|
|
112
113
|
- **Cue** — A message sent to a player by name via Temporal signal
|
|
113
114
|
- **Lineup** — A YAML file that defines a full team and recruits them in one step
|
|
114
115
|
- **Player Type** — A reusable agent definition (`.md` with YAML frontmatter) that gives a player a named role
|
|
115
116
|
|
|
116
|
-
Players in one ensemble cannot see or message players in another:
|
|
117
|
+
Players in one ensemble cannot see or message players in another. Launch `claude-tempo` to open the TUI and switch between ensembles, or target a specific ensemble directly:
|
|
117
118
|
|
|
118
119
|
```bash
|
|
119
|
-
claude-tempo
|
|
120
|
-
claude-tempo
|
|
120
|
+
claude-tempo up frontend # provision and launch conductor in "frontend"
|
|
121
|
+
claude-tempo up backend # provision and launch conductor in "backend"
|
|
121
122
|
```
|
|
122
123
|
|
|
123
124
|
## MCP Tools
|
|
@@ -139,17 +140,16 @@ Tools available inside Claude Code sessions connected to claude-tempo:
|
|
|
139
140
|
## CLI
|
|
140
141
|
|
|
141
142
|
```bash
|
|
142
|
-
claude-tempo
|
|
143
|
-
claude-tempo
|
|
144
|
-
claude-tempo
|
|
143
|
+
claude-tempo # launch TUI (auto-provisions on first run)
|
|
144
|
+
claude-tempo up [ensemble] # provision infrastructure and launch conductor
|
|
145
|
+
claude-tempo down [--destroy] # tear down infrastructure (--destroy also terminates workflows)
|
|
145
146
|
claude-tempo status [ensemble] # list active sessions
|
|
147
|
+
claude-tempo destroy <ensemble> # terminate all sessions in an ensemble
|
|
148
|
+
claude-tempo restore <ensemble> # restore orphaned sessions on this host
|
|
146
149
|
claude-tempo hosts # list daemons polling this Temporal namespace (--all/--json)
|
|
147
|
-
claude-tempo recall <name> # read a player's message history (--limit/--offset/--preview/--
|
|
150
|
+
claude-tempo recall <name> # read a player's message history (--limit/--offset/--preview/--json)
|
|
148
151
|
claude-tempo attachment-info <name> # inspect a session's phase, holder, lease, and heartbeat age
|
|
149
152
|
claude-tempo release [ensemble] # release held players (unlock + deliver tasks)
|
|
150
|
-
claude-tempo pause [ensemble] # pause all sessions and the scheduler
|
|
151
|
-
claude-tempo resume [ensemble] # resume a paused ensemble (--release also releases held players)
|
|
152
|
-
claude-tempo tui # open the terminal UI
|
|
153
153
|
claude-tempo daemon <sub> # manage the worker daemon
|
|
154
154
|
claude-tempo upgrade # update to latest
|
|
155
155
|
```
|
|
@@ -240,7 +240,7 @@ claude-tempo tui --ensemble my-ensemble # direct ensemble mode
|
|
|
240
240
|
The TUI provides a chat-focused shell for managing your ensemble:
|
|
241
241
|
|
|
242
242
|
- **Ensemble chat feed** — live aggregated view of conductor + player traffic; type bare text to message the conductor, `@player message` to message directly
|
|
243
|
-
- **Slash commands** — `/recruit`, `/status`, `/schedule`, `/gates`, `/stages`, `/worktree`, `/go` (release held), `/pause`, `/
|
|
243
|
+
- **Slash commands** — `/recruit`, `/status`, `/schedule`, `/gates`, `/stages`, `/worktree`, `/go` (release held), `/pause`, `/play`, `/shutdown`, `/restore`, `/home`, and more; type `/help` for the full list
|
|
244
244
|
- **Interactive overlays and wizards** — step-by-step flows for recruiting players, creating schedules, and managing ensembles
|
|
245
245
|
|
|
246
246
|
📖 [TUI reference → docs/tui.md](docs/tui.md)
|
|
@@ -249,10 +249,10 @@ The TUI provides a chat-focused shell for managing your ensemble:
|
|
|
249
249
|
|
|
250
250
|
> **Experimental** — subject to breaking changes.
|
|
251
251
|
|
|
252
|
-
GitHub Copilot CLI sessions can join an ensemble using `--agent copilot
|
|
252
|
+
GitHub Copilot CLI sessions can join an ensemble using `--agent copilot`. Recruit one from the TUI:
|
|
253
253
|
|
|
254
|
-
```
|
|
255
|
-
|
|
254
|
+
```
|
|
255
|
+
/recruit copilot-1 --agent copilot
|
|
256
256
|
```
|
|
257
257
|
|
|
258
258
|
📖 [Copilot bridge setup and limitations → docs/copilot.md](docs/copilot.md)
|
|
@@ -575,15 +575,22 @@ function createOutboxActivities(client, config) {
|
|
|
575
575
|
// already exists at `~/.claude/projects/<encoded-path>/<uuid>.jsonl`
|
|
576
576
|
// ("Session ID already in use"). A prior failed spawn can leave that
|
|
577
577
|
// file behind, wedging every subsequent `fresh` restart that reuses the
|
|
578
|
-
// stored sessionId.
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
578
|
+
// stored sessionId.
|
|
579
|
+
//
|
|
580
|
+
// #306: `/restart` ALWAYS spawns a fresh Claude Code process — never
|
|
581
|
+
// `--resume <id>`. The transcript `.jsonl` for the prior `spawnSessionId`
|
|
582
|
+
// is NOT guaranteed to have been flushed to disk before the prior
|
|
583
|
+
// adapter was hard-terminated (Windows `taskkill /T /F` is synchronous
|
|
584
|
+
// and unconditional). Claude Code then errors out with "No conversation
|
|
585
|
+
// found with session ID" and the new terminal drops to shell.
|
|
586
|
+
//
|
|
587
|
+
// Context preservation is already handled by the Step 5 replay above —
|
|
588
|
+
// we re-send recent messages to the fresh session. That's authoritative;
|
|
589
|
+
// the session-id `--resume` path was only ever a bonus on top. So we
|
|
590
|
+
// mint a new UUID on every restart, persist it to metadata, and never
|
|
591
|
+
// pass `resume: true` to the spawn.
|
|
592
|
+
const spawnSessionId = crypto.randomUUID();
|
|
593
|
+
await handle.signal(signals_1.updateMetadataSignal, { sessionId: spawnSessionId });
|
|
587
594
|
// Issue #184: re-resolve on the invoker host against the session's
|
|
588
595
|
// workDir (NOT the daemon's process.cwd — daemon runs elsewhere than
|
|
589
596
|
// the session's project, so the project-tier lookup needs the session's
|
|
@@ -598,8 +605,9 @@ function createOutboxActivities(client, config) {
|
|
|
598
605
|
host: targetHost,
|
|
599
606
|
attachmentId: token.attachmentId,
|
|
600
607
|
runId: token.runId,
|
|
601
|
-
|
|
602
|
-
|
|
608
|
+
// #306: `/restart` is always a fresh spawn (see comment above).
|
|
609
|
+
resume: false,
|
|
610
|
+
sessionId: spawnSessionId,
|
|
603
611
|
adapterId,
|
|
604
612
|
...(resolved ? {
|
|
605
613
|
agentDefinition: resolved.name,
|
|
@@ -608,7 +616,7 @@ function createOutboxActivities(client, config) {
|
|
|
608
616
|
} : {}),
|
|
609
617
|
}],
|
|
610
618
|
});
|
|
611
|
-
log(`Restart prepared for "${targetPlayerId}" — attachmentId=${token.attachmentId}, spawnEntryId=${spawnEntryId}, host=${targetHost}${fresh ?
|
|
619
|
+
log(`Restart prepared for "${targetPlayerId}" — attachmentId=${token.attachmentId}, spawnEntryId=${spawnEntryId}, host=${targetHost}, fresh sessionId=${spawnSessionId}${fresh ? ' (context replay skipped)' : ''}`);
|
|
612
620
|
return { success: true };
|
|
613
621
|
}
|
|
614
622
|
catch (err) {
|
package/dist/adapters/base.d.ts
CHANGED
|
@@ -14,6 +14,35 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { Client, WorkflowHandle } from '@temporalio/client';
|
|
16
16
|
import type { AdapterClass, AdapterDescriptor, AttachmentToken, AttachmentPhase, DetachReason } from '../types';
|
|
17
|
+
/** Snapshot of adapter state included in every telemetry frame. */
|
|
18
|
+
interface AdapterTelemetrySnapshot {
|
|
19
|
+
attachmentId: string | null;
|
|
20
|
+
workflowId: string | null;
|
|
21
|
+
runId: string | null;
|
|
22
|
+
heartbeatsSent: number;
|
|
23
|
+
phaseTicksDone: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the structured frame emitted by every lifecycle handler. Pure
|
|
27
|
+
* function — exposed for unit tests that don't want to spawn a child
|
|
28
|
+
* process.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildProcessTerminatingFrame(signal: string, errorMessage?: string, snapshot?: AdapterTelemetrySnapshot[]): string;
|
|
31
|
+
/**
|
|
32
|
+
* Install the process-lifecycle telemetry handlers. Idempotent. Skipped
|
|
33
|
+
* by default in test environments (see {@link shouldInstallLifecycleTelemetry}).
|
|
34
|
+
*
|
|
35
|
+
* Production callers (and the first `startV2Lifecycle()` call on any
|
|
36
|
+
* adapter) invoke without arguments. Unit tests pass `{ force: true }`
|
|
37
|
+
* to bypass the env gate.
|
|
38
|
+
*/
|
|
39
|
+
export declare function installProcessLifecycleTelemetry(opts?: {
|
|
40
|
+
force?: boolean;
|
|
41
|
+
}): void;
|
|
42
|
+
/** Test-only — uninstall handlers + reset state. */
|
|
43
|
+
export declare function _resetProcessLifecycleTelemetryForTest(): void;
|
|
44
|
+
/** Test-only — direct access to the live-adapter set. */
|
|
45
|
+
export declare function _liveAdaptersForTest(): ReadonlySet<BaseAttachment>;
|
|
17
46
|
/**
|
|
18
47
|
* Override bundle for the reconnect loop timing (#201). Production defaults are
|
|
19
48
|
* tuned for laptop-sleep cycles (15-min elapsed budget, 10s base, 60s cap). Tests
|
|
@@ -124,6 +153,13 @@ export declare abstract class BaseAttachment {
|
|
|
124
153
|
onPhaseChange(listener: (phase: AttachmentPhase) => void): () => void;
|
|
125
154
|
/** Subscribe to lease-revocation events (§9.3 split-brain resolution). */
|
|
126
155
|
onLeaseRevoked(listener: (reason: DetachReason) => void): () => void;
|
|
156
|
+
/**
|
|
157
|
+
* Hypothesis A telemetry — capture the adapter state included in
|
|
158
|
+
* process-lifecycle log frames. Public so the module-level
|
|
159
|
+
* `snapshotLiveAdapters()` helper can read private fields without an
|
|
160
|
+
* `any` cast; consumers other than the telemetry path should not call it.
|
|
161
|
+
*/
|
|
162
|
+
_captureTelemetrySnapshot(): AdapterTelemetrySnapshot;
|
|
127
163
|
/**
|
|
128
164
|
* Subscribe to terminal events — `WorkflowNotFound` (§9.4) and phase `gone`.
|
|
129
165
|
* Terminal fires at most once per instance. Subclasses stop delivery + exit.
|
|
@@ -216,7 +252,55 @@ export declare abstract class BaseAttachment {
|
|
|
216
252
|
* once per terminal — not on every tick.
|
|
217
253
|
*/
|
|
218
254
|
private findCanSuccessorRunId;
|
|
255
|
+
/**
|
|
256
|
+
* Fire the terminal hook — the adapter is going dark and won't recover.
|
|
257
|
+
*
|
|
258
|
+
* #258: emits a structured log line on every fire so the next post-CAN
|
|
259
|
+
* silence incident is unambiguous in logs. Pre-#258, a `fireTerminal`
|
|
260
|
+
* from an unexpected source (the root cause was a silent destroy from
|
|
261
|
+
* the reconnect-loop pre-check on a transient terminal-class error) was
|
|
262
|
+
* indistinguishable from process death in workflow history — both produced
|
|
263
|
+
* "no further heartbeats." The structured log includes:
|
|
264
|
+
*
|
|
265
|
+
* - `reason` — the existing DetachReason
|
|
266
|
+
* - `callsite` — the calling function or rationale (passed by every
|
|
267
|
+
* callsite so the source is grep-able without parsing stack traces)
|
|
268
|
+
* - `attachmentId` / `workflowId` / `runId` — for cross-referencing
|
|
269
|
+
* against workflow history when bisecting an incident
|
|
270
|
+
* - `heartbeatsSent` / `phaseTicksDone` — the existing #249 counters
|
|
271
|
+
* so an operator can correlate "loop alive at N heartbeats, then
|
|
272
|
+
* terminal fired at this callsite" without external context
|
|
273
|
+
*
|
|
274
|
+
* Idempotent — repeat calls (e.g. reconnect-exhausted re-fires after
|
|
275
|
+
* destroy) early-return without re-logging. The first fire wins.
|
|
276
|
+
*/
|
|
219
277
|
private fireTerminal;
|
|
278
|
+
/**
|
|
279
|
+
* #258 tiebreaker: confirm whether a workflow is genuinely terminal after
|
|
280
|
+
* the reconnect-loop pre-check threw a terminal-class error. Used to
|
|
281
|
+
* distinguish a real workflow-gone state from a transient gRPC /
|
|
282
|
+
* visibility-API blip that classified as terminal.
|
|
283
|
+
*
|
|
284
|
+
* Returns:
|
|
285
|
+
* - `{ kind: 'running', statusName }` — workflow is alive (any
|
|
286
|
+
* non-terminal status). Caller should treat the original error as
|
|
287
|
+
* transient and continue the reconnect loop.
|
|
288
|
+
* - `{ kind: 'terminal', statusName }` — workflow is in a terminal
|
|
289
|
+
* status (`COMPLETED` / `FAILED` / `CANCELLED` / `TERMINATED` /
|
|
290
|
+
* `CONTINUED_AS_NEW` / `TIMED_OUT`). Caller should fire destroy.
|
|
291
|
+
* - `{ kind: 'describe-threw' }` — `describe()` itself failed. Treat
|
|
292
|
+
* as terminal (fire destroy) — consistent with pre-#258 semantics
|
|
293
|
+
* when classification is ambiguous, and avoids spinning forever on
|
|
294
|
+
* a workflow we can't reach.
|
|
295
|
+
* - `{ kind: 'timed-out' }` — `describe()` exceeded
|
|
296
|
+
* {@link DESCRIBE_TIMEOUT_MS}. Treat as terminal (fire destroy) —
|
|
297
|
+
* same rationale: prefer clean shutdown to a hung loop.
|
|
298
|
+
*
|
|
299
|
+
* The unpinned handle follows any CAN chain to the latest run, so
|
|
300
|
+
* `desc.status.name === 'CONTINUED_AS_NEW'` here means the workflow
|
|
301
|
+
* id itself is closed (no successor) — genuinely terminal.
|
|
302
|
+
*/
|
|
303
|
+
private confirmWorkflowTerminal;
|
|
220
304
|
/**
|
|
221
305
|
* Opt-in reconnect policy. Default: return `false` — the base class behaves
|
|
222
306
|
* exactly as it did before #201 (fire terminal, tear down). Subclasses that
|