agent-tempo 1.7.0 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +10 -13
- package/README.md +21 -15
- package/dashboard/dist/assets/index-BbR7zIdK.js.map +1 -1
- package/dashboard/package.json +1 -1
- package/dist/activities/hard-terminate.d.ts +2 -2
- package/dist/activities/hard-terminate.js +3 -9
- package/dist/activities/maestro.d.ts +8 -13
- package/dist/activities/maestro.js +0 -10
- package/dist/activities/outbox.js +4 -2
- package/dist/adapters/base.js +3 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.js +79 -11
- package/dist/cli/daemon-command.js +19 -33
- package/dist/cli/help-text.js +7 -7
- package/dist/cli/home-command.d.ts +26 -0
- package/dist/cli/home-command.js +156 -0
- package/dist/cli/removed-verbs.d.ts +0 -4
- package/dist/cli/removed-verbs.js +23 -12
- package/dist/cli/sa-preflight.d.ts +0 -14
- package/dist/cli/sa-preflight.js +1 -17
- package/dist/cli/startup.d.ts +1 -10
- package/dist/cli/startup.js +0 -40
- package/dist/cli.js +34 -80
- package/dist/client/core.js +13 -15
- package/dist/client/index.d.ts +2 -1
- package/dist/client/interface.d.ts +5 -14
- package/dist/client/with-spawn.d.ts +9 -4
- package/dist/client/with-spawn.js +7 -10
- package/dist/config.d.ts +0 -13
- package/dist/constants.d.ts +22 -0
- package/dist/constants.js +23 -1
- package/dist/daemon.js +52 -0
- package/dist/http/auth.d.ts +3 -21
- package/dist/http/auth.js +10 -31
- package/dist/http/event-types.d.ts +5 -4
- package/dist/http/orphans.d.ts +3 -3
- package/dist/http/orphans.js +3 -3
- package/dist/http/server.d.ts +0 -5
- package/dist/http/server.js +69 -29
- package/dist/http/writes.d.ts +1 -1
- package/dist/http/writes.js +25 -2
- package/dist/palette/index.d.ts +6 -7
- package/dist/palette/index.js +6 -7
- package/dist/pi/mission-control/actions.d.ts +67 -1
- package/dist/pi/mission-control/actions.js +61 -0
- package/dist/pi/mission-control/extension.d.ts +77 -0
- package/dist/pi/mission-control/extension.js +171 -0
- package/dist/pi/workflow-client.d.ts +14 -16
- package/dist/pi/workflow-client.js +18 -44
- package/dist/types.d.ts +15 -6
- package/dist/upgrade/boot-guard.d.ts +95 -0
- package/dist/upgrade/boot-guard.js +88 -0
- package/dist/upgrade/from-upgrade.d.ts +95 -0
- package/dist/upgrade/from-upgrade.js +330 -0
- package/dist/upgrade/phase-engine.js +4 -1
- package/dist/utils/duration.d.ts +3 -6
- package/dist/utils/duration.js +3 -6
- package/dist/utils/format-hosts.d.ts +1 -1
- package/dist/utils/format-hosts.js +0 -2
- package/dist/utils/grpc-shutdown-guard.js +0 -4
- package/dist/utils/hosts.d.ts +4 -10
- package/dist/utils/hosts.js +12 -28
- package/dist/utils/search-attributes.d.ts +27 -18
- package/dist/utils/search-attributes.js +39 -18
- package/dist/utils/visibility-deadline.d.ts +16 -0
- package/dist/utils/visibility-deadline.js +16 -0
- package/dist/workflows/maestro-signals.d.ts +0 -13
- package/dist/workflows/maestro-signals.js +6 -14
- package/dist/workflows/maestro.js +27 -32
- package/dist/workflows/scheduler.js +7 -1
- package/dist/workflows/session.js +44 -110
- package/dist/workflows/signals.d.ts +9 -1
- package/dist/workflows/signals.js +8 -6
- package/package.json +1 -6
- package/workflow-bundle.js +239 -182
- package/dist/cli/legacy-migration.d.ts +0 -35
- package/dist/cli/legacy-migration.js +0 -341
- package/dist/client/ensure-conductor-spawned.d.ts +0 -35
- package/dist/client/ensure-conductor-spawned.js +0 -48
- package/dist/tui/App.d.ts +0 -85
- package/dist/tui/App.js +0 -1791
- package/dist/tui/bootstrap-types.d.ts +0 -46
- package/dist/tui/bootstrap-types.js +0 -7
- package/dist/tui/commands.d.ts +0 -71
- package/dist/tui/commands.js +0 -1375
- package/dist/tui/components/ChatView.d.ts +0 -35
- package/dist/tui/components/ChatView.js +0 -54
- package/dist/tui/components/CommandPalette.d.ts +0 -21
- package/dist/tui/components/CommandPalette.js +0 -67
- package/dist/tui/components/ConversationStream.d.ts +0 -114
- package/dist/tui/components/ConversationStream.js +0 -307
- package/dist/tui/components/CreateEnsembleWizard.d.ts +0 -19
- package/dist/tui/components/CreateEnsembleWizard.js +0 -223
- package/dist/tui/components/DestroyConfirmModal.d.ts +0 -17
- package/dist/tui/components/DestroyConfirmModal.js +0 -62
- package/dist/tui/components/ErrorView.d.ts +0 -31
- package/dist/tui/components/ErrorView.js +0 -129
- package/dist/tui/components/HomeView.d.ts +0 -54
- package/dist/tui/components/HomeView.js +0 -306
- package/dist/tui/components/LoadLineupModal.d.ts +0 -18
- package/dist/tui/components/LoadLineupModal.js +0 -79
- package/dist/tui/components/MainView.d.ts +0 -21
- package/dist/tui/components/MainView.js +0 -107
- package/dist/tui/components/NewEnsembleModal.d.ts +0 -9
- package/dist/tui/components/NewEnsembleModal.js +0 -73
- package/dist/tui/components/Picker.d.ts +0 -23
- package/dist/tui/components/Picker.js +0 -70
- package/dist/tui/components/PlayerDetailView.d.ts +0 -26
- package/dist/tui/components/PlayerDetailView.js +0 -118
- package/dist/tui/components/PromptArea.d.ts +0 -50
- package/dist/tui/components/PromptArea.js +0 -303
- package/dist/tui/components/RecruitWizard.d.ts +0 -17
- package/dist/tui/components/RecruitWizard.js +0 -221
- package/dist/tui/components/RestoreConfirmModal.d.ts +0 -18
- package/dist/tui/components/RestoreConfirmModal.js +0 -71
- package/dist/tui/components/ScheduleWizard.d.ts +0 -19
- package/dist/tui/components/ScheduleWizard.js +0 -259
- package/dist/tui/components/Splash.d.ts +0 -23
- package/dist/tui/components/Splash.js +0 -221
- package/dist/tui/components/StatusBar.d.ts +0 -48
- package/dist/tui/components/StatusBar.js +0 -128
- package/dist/tui/components/StatusOverlay.d.ts +0 -15
- package/dist/tui/components/StatusOverlay.js +0 -76
- package/dist/tui/components/TitleBar.d.ts +0 -10
- package/dist/tui/components/TitleBar.js +0 -21
- package/dist/tui/index.d.ts +0 -15
- package/dist/tui/index.js +0 -162
- package/dist/tui/ink-context.d.ts +0 -18
- package/dist/tui/ink-context.js +0 -59
- package/dist/tui/ink-loader.d.ts +0 -26
- package/dist/tui/ink-loader.js +0 -42
- package/dist/tui/removed-commands.d.ts +0 -9
- package/dist/tui/removed-commands.js +0 -22
- package/dist/tui/sse-handler.d.ts +0 -52
- package/dist/tui/sse-handler.js +0 -157
- package/dist/tui/store.d.ts +0 -598
- package/dist/tui/store.js +0 -753
- package/dist/tui/utils/format.d.ts +0 -56
- package/dist/tui/utils/format.js +0 -155
- package/dist/tui/utils/fullscreen.d.ts +0 -23
- package/dist/tui/utils/fullscreen.js +0 -71
- package/dist/tui/utils/history.d.ts +0 -10
- package/dist/tui/utils/history.js +0 -85
- package/dist/tui/utils/platform.d.ts +0 -45
- package/dist/tui/utils/platform.js +0 -258
- package/dist/tui/utils/theme.d.ts +0 -21
- package/dist/tui/utils/theme.js +0 -24
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.agentSessionWorkflow = agentSessionWorkflow;
|
|
4
4
|
const workflow_1 = require("@temporalio/workflow");
|
|
5
5
|
const search_attributes_1 = require("../utils/search-attributes");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
6
7
|
const common_1 = require("@temporalio/common");
|
|
7
8
|
/**
|
|
8
9
|
* Workflow-deterministic clock. The Temporal TS SDK intercepts `new Date()` at the
|
|
@@ -61,15 +62,6 @@ async function agentSessionWorkflow(input) {
|
|
|
61
62
|
// PR-C commit 6 (#119a): each attachment carries its own `leaseMs` (negotiated at
|
|
62
63
|
// claim time). No workflow-side default constant — heartbeats extend `expiresAt`
|
|
63
64
|
// by `currentAttachment.leaseMs`.
|
|
64
|
-
/**
|
|
65
|
-
* Legacy CAN-extension constant. Retained solely so sessions that ran on a
|
|
66
|
-
* pre-#249 workflow bundle replay deterministically: the `patched()` branch at
|
|
67
|
-
* the CAN-boundary extension site selects this constant on the non-patched
|
|
68
|
-
* side, matching the exact arg sequence those histories recorded. New runs
|
|
69
|
-
* (and all post-#249 CAN transitions) use `currentAttachment.leaseMs` instead
|
|
70
|
-
* — see the call site below.
|
|
71
|
-
*/
|
|
72
|
-
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
73
65
|
/**
|
|
74
66
|
* Default grace period for `draining → detached` transition after requestDetach. Used when a
|
|
75
67
|
* `requestDetach` signal omits `deadlineMs`. Per-signal overrides are honored via the
|
|
@@ -78,65 +70,26 @@ async function agentSessionWorkflow(input) {
|
|
|
78
70
|
const DEFAULT_DRAINING_DEADLINE_MS = 5_000;
|
|
79
71
|
/** Max duration a messageId can stay in-flight before the safety timer ejects it. */
|
|
80
72
|
const PROCESSING_DEADLINE_MS = 15 * 60 * 1000;
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
(0, workflow_1.patched)('v0.13-quality-gates');
|
|
88
|
-
(0, workflow_1.patched)('v0.14-worktrees');
|
|
89
|
-
(0, workflow_1.patched)('v0.15-blocked-detection');
|
|
90
|
-
(0, workflow_1.patched)('v0.18-stages');
|
|
91
|
-
(0, workflow_1.patched)('v0.23-hold-release');
|
|
92
|
-
(0, workflow_1.patched)('v0.25-attachment-lifecycle');
|
|
93
|
-
// Issue #172: kept as a replay marker for workflows that predate the
|
|
94
|
-
// simpler hold-on-startup design (v0.26). The state field + interceptor
|
|
95
|
-
// were removed in favor of baking the banner/directive into
|
|
96
|
-
// `SessionInput.messages` at workflow creation, but leaving the patched
|
|
97
|
-
// marker ensures existing replay histories that recorded this command
|
|
98
|
-
// still deserialize cleanly. Safe no-op today.
|
|
99
|
-
(0, workflow_1.patched)('v0.26-pending-startup-context');
|
|
100
|
-
// ── T0.5 SA diet (#747) — ONE marker gates the whole diet ──
|
|
101
|
-
//
|
|
102
|
-
// New runs (incl. post-deploy CAN successors — fresh executions re-record
|
|
103
|
-
// the marker) stop writing the read-only/write-only search attributes:
|
|
104
|
-
// - AgentTempoGitRoot / AgentTempoPlayerType / AgentTempoIsConductor
|
|
105
|
-
// migrate to the workflow MEMO (low-churn fields only — NEVER memo
|
|
106
|
-
// counters like activityCount/lastActivityAt: memo upserts are
|
|
107
|
-
// billable actions, that would trade query actions for upsert actions).
|
|
108
|
-
// - AgentTempoAttachmentId is dropped outright (zero readers anywhere;
|
|
109
|
-
// adapters correlate via the claimAttachment token).
|
|
110
|
-
// Old histories replay the recorded SA-write branch unchanged — safe,
|
|
111
|
-
// because any namespace with such histories has the legacy SAs registered.
|
|
112
|
-
// Readers go through the dual-read helpers in utils/search-attributes.ts
|
|
113
|
-
// (memo preferred, SA fallback). Filter SAs (Ensemble, PlayerId, Hostname,
|
|
114
|
-
// AttachedHost, AttachmentState) are untouched — they appear in visibility
|
|
115
|
-
// query expressions and MUST stay search attributes.
|
|
116
|
-
const saDiet = (0, workflow_1.patched)('v1.8-sa-diet');
|
|
117
|
-
// T0.1 (#748) — the observation-path memo extension: workDir / agentType /
|
|
118
|
-
// gitBranch join the memo so the cloud-profile maestro scan can read the
|
|
119
|
-
// full player row from visibility list results (zero per-player queries).
|
|
120
|
-
// Payload-only upsertMemo change, but gated behind its own marker per the
|
|
121
|
-
// repo's conservative determinism policy. All three pass the low-churn bar
|
|
122
|
-
// (workDir/agentType set-at-start; gitBranch via rare updateMetadata).
|
|
123
|
-
const memoObservationFields = (0, workflow_1.patched)('v1.8-memo-observation-fields');
|
|
73
|
+
// ── 2.0 clean-slate (#787) ──
|
|
74
|
+
// The replay-only `patched()` markers that protected in-flight 1.x sessions
|
|
75
|
+
// across rolling deploys are gone: 2.0 is a hard cutover (the #786 boot guard
|
|
76
|
+
// refuses to replay any 1.x history), so there are no pre-patch histories left
|
|
77
|
+
// to protect. The SA-diet (#747) and observation-field memo (#748) behavior is
|
|
78
|
+
// now unconditional — the new path IS the only path.
|
|
124
79
|
/**
|
|
125
80
|
* T0.5 — the memo mirror of the migrated read-only metadata fields.
|
|
126
81
|
* Shared by the run-start upsert and the updateMetadata handler so the
|
|
127
82
|
* two write sites can't drift. Key names come from the shared
|
|
128
83
|
* {@link MEMO_KEYS} registry (also used by the client-side
|
|
129
|
-
* `workflow.start({ memo })` seeds and the
|
|
84
|
+
* `workflow.start({ memo })` seeds and the memo readers).
|
|
130
85
|
*/
|
|
131
86
|
const metaMemo = () => ({
|
|
132
87
|
...(input.metadata.gitRoot ? { [search_attributes_1.MEMO_KEYS.gitRoot]: input.metadata.gitRoot } : {}),
|
|
133
88
|
...(input.metadata.playerType ? { [search_attributes_1.MEMO_KEYS.playerType]: input.metadata.playerType } : {}),
|
|
134
89
|
[search_attributes_1.MEMO_KEYS.isConductor]: input.metadata.isConductor === true,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
...(input.metadata.gitBranch ? { [search_attributes_1.MEMO_KEYS.gitBranch]: input.metadata.gitBranch } : {}),
|
|
139
|
-
} : {}),
|
|
90
|
+
[search_attributes_1.MEMO_KEYS.workDir]: input.metadata.workDir,
|
|
91
|
+
[search_attributes_1.MEMO_KEYS.agentType]: input.metadata.agentType || 'claude',
|
|
92
|
+
...(input.metadata.gitBranch ? { [search_attributes_1.MEMO_KEYS.gitBranch]: input.metadata.gitBranch } : {}),
|
|
140
93
|
});
|
|
141
94
|
// Ensure search attributes are always current — critical when reconnecting
|
|
142
95
|
// via WorkflowIdConflictPolicy.USE_EXISTING, which skips the attributes
|
|
@@ -145,17 +98,20 @@ async function agentSessionWorkflow(input) {
|
|
|
145
98
|
AgentTempoEnsemble: [input.metadata.ensemble],
|
|
146
99
|
AgentTempoPlayerId: [input.metadata.playerId],
|
|
147
100
|
AgentTempoHostname: [input.metadata.hostname],
|
|
148
|
-
...(saDiet ? {} : {
|
|
149
|
-
...(input.metadata.gitRoot ? { AgentTempoGitRoot: [input.metadata.gitRoot] } : {}),
|
|
150
|
-
...(input.metadata.playerType ? { AgentTempoPlayerType: [input.metadata.playerType] } : {}),
|
|
151
|
-
AgentTempoIsConductor: [input.metadata.isConductor === true],
|
|
152
|
-
}),
|
|
153
101
|
// v0.25 attachment search attributes — initial values for a fresh/restored workflow.
|
|
154
102
|
// Updated on every phase transition below.
|
|
155
103
|
AgentTempoAttachedHost: [input.currentAttachment?.hostname ?? ''],
|
|
156
104
|
AgentTempoAttachmentState: [input.phase ?? 'booting'],
|
|
157
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [input.currentAttachment?.attachmentId ?? ''] }),
|
|
158
105
|
});
|
|
106
|
+
// #786 — 2.0 cutover protocol STAMP. Authoritative from the constant (this IS
|
|
107
|
+
// the 2.0 bundle → always PROTOCOL_VERSION, regardless of `input.protocol`).
|
|
108
|
+
// Re-upserted on EVERY run, incl. continueAsNew successors, so visibility
|
|
109
|
+
// always shows the stamp for the daemon boot guard — no need to thread a memo
|
|
110
|
+
// arg through the CAN payload. A 1.x run never executes this, so the guard
|
|
111
|
+
// sees its memo as undefined → un-stamped → refuse. Unconditional (no
|
|
112
|
+
// `patched()` marker): #786 is the first change on the 2.0 line, so there are
|
|
113
|
+
// no pre-stamp v2 histories to protect from this new command.
|
|
114
|
+
(0, workflow_1.upsertMemo)({ [search_attributes_1.MEMO_KEYS.protocol]: constants_1.PROTOCOL_VERSION });
|
|
159
115
|
// ── State (carried across continue-as-new) ──
|
|
160
116
|
let part = input.part ?? input.autoSummary ?? 'No description set';
|
|
161
117
|
// T0.5 (#747) — memo carrier for the migrated read-only fields + part.
|
|
@@ -164,9 +120,7 @@ async function agentSessionWorkflow(input) {
|
|
|
164
120
|
// `memo` option so the fields are visible before the first workflow task
|
|
165
121
|
// (and on standard-visibility servers whose list results may lag memo
|
|
166
122
|
// upserts — see PR #747 T0.5 notes). Low-churn fields ONLY.
|
|
167
|
-
|
|
168
|
-
(0, workflow_1.upsertMemo)({ ...metaMemo(), [search_attributes_1.MEMO_KEYS.part]: part });
|
|
169
|
-
}
|
|
123
|
+
(0, workflow_1.upsertMemo)({ ...metaMemo(), [search_attributes_1.MEMO_KEYS.part]: part });
|
|
170
124
|
const messages = input.messages ?? [];
|
|
171
125
|
const sentMessages = input.sentMessages ?? [];
|
|
172
126
|
const outbox = input.outbox ?? [];
|
|
@@ -408,7 +362,7 @@ async function agentSessionWorkflow(input) {
|
|
|
408
362
|
// #399 W2 — every inbound cue counts as received traffic.
|
|
409
363
|
receivedCount++;
|
|
410
364
|
// Track inbound messages that expect a response (default: true for backward compat)
|
|
411
|
-
if (
|
|
365
|
+
if (msg.responseRequested !== false) {
|
|
412
366
|
lastInboundRRTime = workflowNow().getTime();
|
|
413
367
|
}
|
|
414
368
|
});
|
|
@@ -418,8 +372,7 @@ async function agentSessionWorkflow(input) {
|
|
|
418
372
|
// daemon aggregate — T0.1) can read it from visibility list results
|
|
419
373
|
// instead of a per-player getPart query. Low-churn by nature: part
|
|
420
374
|
// changes when a player re-describes its work, not per message.
|
|
421
|
-
|
|
422
|
-
(0, workflow_1.upsertMemo)({ [search_attributes_1.MEMO_KEYS.part]: newPart });
|
|
375
|
+
(0, workflow_1.upsertMemo)({ [search_attributes_1.MEMO_KEYS.part]: newPart });
|
|
423
376
|
lastActivityTime = workflowNow().getTime();
|
|
424
377
|
activityCount++;
|
|
425
378
|
lastOutboundTime = workflowNow().getTime();
|
|
@@ -461,7 +414,6 @@ async function agentSessionWorkflow(input) {
|
|
|
461
414
|
lastActivityTime = workflowNow().getTime();
|
|
462
415
|
activityCount++;
|
|
463
416
|
});
|
|
464
|
-
(0, workflow_1.setHandler)(signals_1.pendingResetQuery, () => pendingReset);
|
|
465
417
|
(0, workflow_1.setHandler)(signals_1.ackResetSignal, (resetId) => {
|
|
466
418
|
// Race-safe: only clear if the ack matches the current pending reset, so a
|
|
467
419
|
// newer reset landing during the extension's wipe isn't silently dropped.
|
|
@@ -494,16 +446,10 @@ async function agentSessionWorkflow(input) {
|
|
|
494
446
|
AgentTempoEnsemble: [input.metadata.ensemble],
|
|
495
447
|
AgentTempoPlayerId: [input.metadata.playerId],
|
|
496
448
|
AgentTempoHostname: [input.metadata.hostname],
|
|
497
|
-
...(saDiet ? {} : {
|
|
498
|
-
...(input.metadata.gitRoot ? { AgentTempoGitRoot: [input.metadata.gitRoot] } : {}),
|
|
499
|
-
...(input.metadata.playerType ? { AgentTempoPlayerType: [input.metadata.playerType] } : {}),
|
|
500
|
-
AgentTempoIsConductor: [input.metadata.isConductor === true],
|
|
501
|
-
}),
|
|
502
449
|
});
|
|
503
450
|
// T0.5 — keep the memo mirror current (low-churn: metadata updates are
|
|
504
451
|
// rare lifecycle events, not per-message traffic).
|
|
505
|
-
|
|
506
|
-
(0, workflow_1.upsertMemo)(metaMemo());
|
|
452
|
+
(0, workflow_1.upsertMemo)(metaMemo());
|
|
507
453
|
lastActivityTime = workflowNow().getTime();
|
|
508
454
|
activityCount++;
|
|
509
455
|
});
|
|
@@ -711,7 +657,6 @@ async function agentSessionWorkflow(input) {
|
|
|
711
657
|
}
|
|
712
658
|
(0, workflow_1.upsertSearchAttributes)({
|
|
713
659
|
AgentTempoAttachedHost: [''],
|
|
714
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [''] }),
|
|
715
660
|
});
|
|
716
661
|
setPhase('gone');
|
|
717
662
|
// Inject a final audit message so the old adapter-completion path has something to show.
|
|
@@ -799,13 +744,23 @@ async function agentSessionWorkflow(input) {
|
|
|
799
744
|
setPhase('attached');
|
|
800
745
|
(0, workflow_1.upsertSearchAttributes)({
|
|
801
746
|
AgentTempoAttachedHost: [host],
|
|
802
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [newAttachment.attachmentId] }),
|
|
803
747
|
});
|
|
804
748
|
lastActivityTime = nowMs;
|
|
805
749
|
activityCount++;
|
|
806
750
|
return attachmentTokenFrom(newAttachment, leaseMs);
|
|
807
751
|
}, {
|
|
808
|
-
validator: ({ leaseMs }) => {
|
|
752
|
+
validator: ({ host, leaseMs, protocolVersion }) => {
|
|
753
|
+
// #786 — cross-host cutover safety, checked FIRST (pure, synchronous, no
|
|
754
|
+
// IO, no history event — rejects pre-admission). A v1 adapter omits
|
|
755
|
+
// `protocolVersion` (→ undefined); any value other than PROTOCOL_VERSION is
|
|
756
|
+
// a stale/foreign adapter that must not claim this 2.0 workflow. Actionable
|
|
757
|
+
// error names the host + the fix.
|
|
758
|
+
if (protocolVersion !== constants_1.PROTOCOL_VERSION) {
|
|
759
|
+
throw common_1.ApplicationFailure.nonRetryable(`claimAttachment rejected on ${(0, workflow_1.workflowInfo)().workflowId}: adapter on host ` +
|
|
760
|
+
`'${host}' speaks protocol ${protocolVersion ?? '(v1/unset)'}, but this is a ` +
|
|
761
|
+
`protocol-${constants_1.PROTOCOL_VERSION} (2.0) workflow. Upgrade the agent-tempo install on ` +
|
|
762
|
+
`that host to 2.0 (it cannot drive a 2.0 session).`, 'ProtocolMismatch');
|
|
763
|
+
}
|
|
809
764
|
if (!Number.isInteger(leaseMs) || leaseMs < 1_000 || leaseMs > 600_000) {
|
|
810
765
|
throw common_1.ApplicationFailure.nonRetryable(`leaseMs must be between 1000 and 600000, got ${leaseMs}`, 'InvalidLease');
|
|
811
766
|
}
|
|
@@ -881,7 +836,7 @@ async function agentSessionWorkflow(input) {
|
|
|
881
836
|
// mismatch any history that recorded the race — conservative tiebreak.
|
|
882
837
|
// Evaluated lazily (the v0.26-can-lease-from-attachment single-use
|
|
883
838
|
// precedent): only histories that hit forceDetach record the marker.
|
|
884
|
-
if (
|
|
839
|
+
if (currentAttachment?.attachmentId !== reaped.attachmentId) {
|
|
885
840
|
workflow_1.log.info(`forceDetach: attachment changed during hard-terminate await ` +
|
|
886
841
|
`(expected ${reaped.attachmentId}, now ${currentAttachment?.attachmentId ?? 'none'}) — ` +
|
|
887
842
|
`not clobbering the fresh claim`);
|
|
@@ -898,7 +853,6 @@ async function agentSessionWorkflow(input) {
|
|
|
898
853
|
setPhase('detached');
|
|
899
854
|
(0, workflow_1.upsertSearchAttributes)({
|
|
900
855
|
AgentTempoAttachedHost: [''],
|
|
901
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [''] }),
|
|
902
856
|
});
|
|
903
857
|
// #159 Gap 1b: wake the main loop — `phase === 'detached'` isn't in the predicate
|
|
904
858
|
// and the condition would otherwise sleep on the now-stale lease-expiry deadline.
|
|
@@ -1025,7 +979,6 @@ async function agentSessionWorkflow(input) {
|
|
|
1025
979
|
setPhase('detached');
|
|
1026
980
|
(0, workflow_1.upsertSearchAttributes)({
|
|
1027
981
|
AgentTempoAttachedHost: [''],
|
|
1028
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [''] }),
|
|
1029
982
|
});
|
|
1030
983
|
lastActivityTime = workflowNow().getTime();
|
|
1031
984
|
activityCount++;
|
|
@@ -1356,12 +1309,10 @@ async function agentSessionWorkflow(input) {
|
|
|
1356
1309
|
// live handler. Reconciled entries keep the ORIGINAL receipt time as
|
|
1357
1310
|
// `reportedAt` and carry `reconciled: true`.
|
|
1358
1311
|
//
|
|
1359
|
-
//
|
|
1360
|
-
//
|
|
1361
|
-
//
|
|
1362
|
-
|
|
1363
|
-
// dependency on replay. Conservative tiebreak per the ruling: marker.
|
|
1364
|
-
if ((0, workflow_1.patched)('v0.27-stage-reconcile-reports')) {
|
|
1312
|
+
// Stage reconciliation — fold each player's latest result/blocker report
|
|
1313
|
+
// into stage status, then apply the stage's completion/failure transitions.
|
|
1314
|
+
// (2.0: was patched()-gated for 1.x replay determinism; now unconditional — #787.)
|
|
1315
|
+
{
|
|
1365
1316
|
const nowMs = workflowNow().getTime();
|
|
1366
1317
|
for (const playerEntry of entry.players) {
|
|
1367
1318
|
// Most recent stage-relevant (result|blocker) report from this
|
|
@@ -1543,7 +1494,6 @@ async function agentSessionWorkflow(input) {
|
|
|
1543
1494
|
setPhase('detached');
|
|
1544
1495
|
(0, workflow_1.upsertSearchAttributes)({
|
|
1545
1496
|
AgentTempoAttachedHost: [''],
|
|
1546
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [''] }),
|
|
1547
1497
|
});
|
|
1548
1498
|
workflow_1.log.warn(`lease expired for attachment ${reaped.attachmentId} (host=${reaped.hostname})`);
|
|
1549
1499
|
}
|
|
@@ -1607,7 +1557,6 @@ async function agentSessionWorkflow(input) {
|
|
|
1607
1557
|
setPhase('detached');
|
|
1608
1558
|
(0, workflow_1.upsertSearchAttributes)({
|
|
1609
1559
|
AgentTempoAttachedHost: [''],
|
|
1610
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [''] }),
|
|
1611
1560
|
});
|
|
1612
1561
|
if (reaped) {
|
|
1613
1562
|
workflow_1.log.info(`drainingDeadline exceeded (${Math.round(window / 1000)}s); ` +
|
|
@@ -1830,7 +1779,6 @@ async function agentSessionWorkflow(input) {
|
|
|
1830
1779
|
setPhase('detached');
|
|
1831
1780
|
(0, workflow_1.upsertSearchAttributes)({
|
|
1832
1781
|
AgentTempoAttachedHost: [''],
|
|
1833
|
-
...(saDiet ? {} : { AgentTempoAttachmentId: [''] }),
|
|
1834
1782
|
});
|
|
1835
1783
|
workflow_1.log.warn(`spawn failed for "${entry.targetName}"; rolled back attachment ${entry.attachmentId} → detached`);
|
|
1836
1784
|
}
|
|
@@ -1873,25 +1821,11 @@ async function agentSessionWorkflow(input) {
|
|
|
1873
1821
|
// matches what the adapter signed up for and covers at least one full heartbeat
|
|
1874
1822
|
// interval for every adapter class.
|
|
1875
1823
|
//
|
|
1876
|
-
// The `patched()` gate keeps replay of pre-#249 workflow runs deterministic:
|
|
1877
|
-
// histories that CAN'd on the old bundle recorded `extendAttachmentForCAN(…, 30_000, …)`,
|
|
1878
|
-
// so replaying those runs must pick the legacy constant. New runs (and in-flight
|
|
1879
|
-
// runs that CAN *after* the deploy) take the patched branch.
|
|
1880
|
-
//
|
|
1881
1824
|
// Math lives in `./attachment-math.ts` for direct unit testability (#127).
|
|
1882
|
-
//
|
|
1883
|
-
//
|
|
1884
|
-
// position it was introduced in — relocating it inside the
|
|
1885
|
-
// `currentAttachment ?` branch would skip marker recording on histories
|
|
1886
|
-
// that hit the CAN site with a null attachment, risking replay
|
|
1887
|
-
// non-determinism against those recordings. The dead-code cleanup is
|
|
1888
|
-
// strictly the removal of the `?? HEARTBEAT_INTERVAL_MS` fallback that
|
|
1889
|
-
// used to sit inside the ternary: on the patched branch it never fires
|
|
1890
|
-
// (Attachment.leaseMs is required), and on the pre-patched branch the
|
|
1891
|
-
// fallback is replaced by the bare constant — same value either way.
|
|
1892
|
-
const usePatchedLease = (0, workflow_1.patched)('v0.26-can-lease-from-attachment');
|
|
1825
|
+
// (2.0: the v0.26-can-lease-from-attachment patched() gate is gone — #787 —
|
|
1826
|
+
// so CAN always extends by `currentAttachment.leaseMs` = 3 × heartbeatMs.)
|
|
1893
1827
|
const extendedAttachment = currentAttachment
|
|
1894
|
-
? (0, attachment_math_1.extendAttachmentForCAN)(currentAttachment,
|
|
1828
|
+
? (0, attachment_math_1.extendAttachmentForCAN)(currentAttachment, currentAttachment.leaseMs, workflowNow().getTime())
|
|
1895
1829
|
: undefined;
|
|
1896
1830
|
await (0, workflow_1.continueAsNew)({
|
|
1897
1831
|
...input,
|
|
@@ -40,7 +40,6 @@ export declare const setPendingResetSignal: import("@temporalio/workflow").Signa
|
|
|
40
40
|
reason?: string;
|
|
41
41
|
requestedBy?: string;
|
|
42
42
|
}], string>;
|
|
43
|
-
export declare const pendingResetQuery: import("@temporalio/workflow").QueryDefinition<PendingReset | null, [], string>;
|
|
44
43
|
export declare const ackResetSignal: import("@temporalio/workflow").SignalDefinition<[string], string>;
|
|
45
44
|
/** Result shape of {@link pendingIntakeQuery}. */
|
|
46
45
|
export interface PendingIntake {
|
|
@@ -110,6 +109,15 @@ export declare const claimAttachmentUpdate: import("@temporalio/common").UpdateD
|
|
|
110
109
|
leaseMs: number;
|
|
111
110
|
/** Present on renewal; absent on fresh claim. */
|
|
112
111
|
expectedAttachmentId?: string;
|
|
112
|
+
/**
|
|
113
|
+
* #786 — REQUIRED 2.0-wire field: the adapter's protocol version
|
|
114
|
+
* (`PROTOCOL_VERSION`). A 2.0 workflow rejects any value `!== PROTOCOL_VERSION`
|
|
115
|
+
* (incl. `undefined` = a v1 adapter) in the update VALIDATOR — pre-admission,
|
|
116
|
+
* no history event — with an actionable error naming the host + the upgrade
|
|
117
|
+
* command. Cross-host cutover safety: a stale 1.x adapter on another machine
|
|
118
|
+
* cannot silently claim a 2.0 workflow.
|
|
119
|
+
*/
|
|
120
|
+
protocolVersion: number;
|
|
113
121
|
}], string>;
|
|
114
122
|
/**
|
|
115
123
|
* Revoke the current attachment. Options for drain-grace behavior during `draining` phase.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.evaluateGateCriteriaSignal = exports.setQualityGateSignal = exports.testForceContinueAsNewSignal = exports.getCoarseActivityQuery = exports.getLeaseStateQuery = exports.getActivityStateQuery = exports.getMessagingStateQuery = exports.getRunIdQuery = exports.outboxQuery = exports.submitOutboxUpdate = exports.playerStateKeysQuery = exports.playerStateQuery = exports.clearPlayerStateUpdate = exports.savePlayerStateUpdate = exports.orphanSummaryQuery = exports.attachmentInfoQuery = exports.adapterExitedSignal = exports.requestDetachSignal = exports.heartbeatSignal = exports.setPreferredHostUpdate = exports.enqueueSpawnUpdate = exports.forceDetachUpdate = exports.claimAttachmentUpdate = exports.isDestroyedQuery = exports.destroyUpdate = exports.inFlightMessagesQuery = exports.processingEndUpdate = exports.processingStartUpdate = exports.historyQuery = exports.playerReportSignal = exports.commandSignal = exports.pausedQuery = exports.setPausedSignal = exports.outboxLockedQuery = exports.releaseHeldSignal = exports.pendingIntakeQuery = exports.ackResetSignal = exports.
|
|
4
|
-
exports.stagesQuery = exports.cancelStageSignal = exports.setStageSignal = exports.worktreesQuery = exports.removeWorktreeSignal = exports.setWorktreeSignal =
|
|
3
|
+
exports.qualityGatesQuery = exports.evaluateGateCriteriaSignal = exports.setQualityGateSignal = exports.testForceContinueAsNewSignal = exports.getCoarseActivityQuery = exports.getLeaseStateQuery = exports.getActivityStateQuery = exports.getMessagingStateQuery = exports.getRunIdQuery = exports.outboxQuery = exports.submitOutboxUpdate = exports.playerStateKeysQuery = exports.playerStateQuery = exports.clearPlayerStateUpdate = exports.savePlayerStateUpdate = exports.orphanSummaryQuery = exports.attachmentInfoQuery = exports.adapterExitedSignal = exports.requestDetachSignal = exports.heartbeatSignal = exports.setPreferredHostUpdate = exports.enqueueSpawnUpdate = exports.forceDetachUpdate = exports.claimAttachmentUpdate = exports.isDestroyedQuery = exports.destroyUpdate = exports.inFlightMessagesQuery = exports.processingEndUpdate = exports.processingStartUpdate = exports.historyQuery = exports.playerReportSignal = exports.commandSignal = exports.pausedQuery = exports.setPausedSignal = exports.outboxLockedQuery = exports.releaseHeldSignal = exports.pendingIntakeQuery = exports.ackResetSignal = exports.setPendingResetSignal = exports.allSentMessagesQuery = exports.allMessagesQuery = exports.pendingMessagesQuery = exports.getMetadataQuery = exports.getPartQuery = exports.updateMetadataSignal = exports.setNameSignal = exports.markDeliveredSignal = exports.setPartSignal = exports.recordSentMessageSignal = exports.receiveMessageSignal = void 0;
|
|
4
|
+
exports.stagesQuery = exports.cancelStageSignal = exports.setStageSignal = exports.worktreesQuery = exports.removeWorktreeSignal = exports.setWorktreeSignal = void 0;
|
|
5
5
|
const workflow_1 = require("@temporalio/workflow");
|
|
6
6
|
// ── Player Signals ──
|
|
7
7
|
// `isScheduled` + `scheduleName` are set by `src/activities/schedule-fire.ts`
|
|
@@ -29,11 +29,13 @@ exports.allMessagesQuery = (0, workflow_1.defineQuery)('allMessages');
|
|
|
29
29
|
exports.allSentMessagesQuery = (0, workflow_1.defineQuery)('allSentMessages');
|
|
30
30
|
// ── Reset (D14) — context clean-wipe poll-delivery ──
|
|
31
31
|
// `deliverReset` sets the pending flag via `setPendingResetSignal`; the Pi
|
|
32
|
-
// extension
|
|
33
|
-
//
|
|
34
|
-
//
|
|
32
|
+
// extension reads the pending reset off the combined `pendingIntakeQuery`
|
|
33
|
+
// (`.pendingReset`), performs the wipe (newSession), then clears it via
|
|
34
|
+
// `ackResetSignal(resetId)`. Single-slot, latest-wins. The workflow stamps
|
|
35
|
+
// `requestedAt` (deterministic). See WIRE-PROTOCOL.md.
|
|
36
|
+
// 2.0 (#788): the standalone `pendingResetQuery` ('pendingReset') was removed —
|
|
37
|
+
// reset now rides the combined `pendingIntake` query; these signals stay.
|
|
35
38
|
exports.setPendingResetSignal = (0, workflow_1.defineSignal)('setPendingReset');
|
|
36
|
-
exports.pendingResetQuery = (0, workflow_1.defineQuery)('pendingReset');
|
|
37
39
|
exports.ackResetSignal = (0, workflow_1.defineSignal)('ackReset');
|
|
38
40
|
exports.pendingIntakeQuery = (0, workflow_1.defineQuery)('pendingIntake');
|
|
39
41
|
// ── Hold / Release ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-tempo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "Many agents, one tempo. Durable coordination for multi-agent work via Temporal.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -110,12 +110,8 @@
|
|
|
110
110
|
"@temporalio/worker": "~1.15.0",
|
|
111
111
|
"@temporalio/workflow": "~1.15.0",
|
|
112
112
|
"croner": "^10.0.1",
|
|
113
|
-
"ink": "^6.8.0",
|
|
114
|
-
"ink-spinner": "^5.0.0",
|
|
115
|
-
"ink-text-input": "^6.0.0",
|
|
116
113
|
"p-limit": "^3.1.0",
|
|
117
114
|
"qrcode-terminal": "^0.12.0",
|
|
118
|
-
"react": "^19.2.4",
|
|
119
115
|
"semver": "^7.7.4",
|
|
120
116
|
"typebox": "^1.0.1",
|
|
121
117
|
"yaml": "^2.8.3",
|
|
@@ -127,7 +123,6 @@
|
|
|
127
123
|
"@types/chai": "^4.3.20",
|
|
128
124
|
"@types/mocha": "^10.0.10",
|
|
129
125
|
"@types/node": "^20.0.0",
|
|
130
|
-
"@types/react": "^19.2.14",
|
|
131
126
|
"@types/semver": "^7.7.1",
|
|
132
127
|
"chai": "^4.5.0",
|
|
133
128
|
"mocha": "^11.7.5",
|