@cotal-ai/runtime 0.47.1 → 0.48.0
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/dist/mesh-handler.d.ts +12 -15
- package/dist/mesh-handler.d.ts.map +1 -1
- package/dist/mesh-handler.js +108 -39
- package/dist/mesh-handler.js.map +1 -1
- package/dist/run-command.d.ts.map +1 -1
- package/dist/run-command.js +47 -27
- package/dist/run-command.js.map +1 -1
- package/dist/run-driver.d.ts +1 -1
- package/dist/run-driver.d.ts.map +1 -1
- package/dist/run-driver.js +16 -40
- package/dist/run-driver.js.map +1 -1
- package/dist/run-effect-host.d.ts +13 -0
- package/dist/run-effect-host.d.ts.map +1 -0
- package/dist/run-effect-host.js +57 -0
- package/dist/run-effect-host.js.map +1 -0
- package/dist/run-host.d.ts.map +1 -1
- package/dist/run-host.js +14 -13
- package/dist/run-host.js.map +1 -1
- package/dist/run-pause-host.d.ts +23 -0
- package/dist/run-pause-host.d.ts.map +1 -0
- package/dist/run-pause-host.js +99 -0
- package/dist/run-pause-host.js.map +1 -0
- package/dist/run-record-host.d.ts +20 -0
- package/dist/run-record-host.d.ts.map +1 -0
- package/dist/run-record-host.js +76 -0
- package/dist/run-record-host.js.map +1 -0
- package/dist/run-scope-authority.d.ts +51 -0
- package/dist/run-scope-authority.d.ts.map +1 -0
- package/dist/run-scope-authority.js +193 -0
- package/dist/run-scope-authority.js.map +1 -0
- package/dist/run-wait-host.d.ts +22 -0
- package/dist/run-wait-host.d.ts.map +1 -0
- package/dist/run-wait-host.js +70 -0
- package/dist/run-wait-host.js.map +1 -0
- package/package.json +4 -4
package/dist/mesh-handler.d.ts
CHANGED
|
@@ -3,6 +3,14 @@ import type { NatsConnection } from "@nats-io/transport-node";
|
|
|
3
3
|
import type { JetStreamClient, JetStreamManager } from "@nats-io/jetstream";
|
|
4
4
|
import { type KV } from "@nats-io/kv";
|
|
5
5
|
import { type AskRequest, type AgentHandleValue, type ChannelHandleValue, type ConclaveRequest, type WaitRequest, type CheckpointRaw, type CheckpointRequest, type EffectContext, type JournalEntry, type MonitorRequest, type NotifyRequest, type SleepRequest, type SpawnRequest, type TurnRequest, type TurnResultValue } from "@cotal-ai/lang";
|
|
6
|
+
import type { RunPauseHost } from "./run-pause-host.js";
|
|
7
|
+
import type { RunWaitHost } from "./run-wait-host.js";
|
|
8
|
+
import type { RunScopeAuthority } from "./run-scope-authority.js";
|
|
9
|
+
export interface RunMeshServices {
|
|
10
|
+
readonly pauses: RunPauseHost;
|
|
11
|
+
readonly waits: RunWaitHost;
|
|
12
|
+
readonly authority: RunScopeAuthority;
|
|
13
|
+
}
|
|
6
14
|
/** Who this handler acts as, and where. All of it is the DRIVER's identity, not the program's. */
|
|
7
15
|
export interface MeshHandlerBinding {
|
|
8
16
|
readonly space: string;
|
|
@@ -85,7 +93,8 @@ export declare class MeshHandler {
|
|
|
85
93
|
private readonly binding;
|
|
86
94
|
private readonly watcher;
|
|
87
95
|
private readonly clock;
|
|
88
|
-
|
|
96
|
+
private readonly services?;
|
|
97
|
+
constructor(nc: NatsConnection, kv: KV, js: JetStreamClient, jsm: JetStreamManager, binding: MeshHandlerBinding, watcher: SettleWatcher, clock?: () => number, services?: RunMeshServices | undefined);
|
|
89
98
|
/**
|
|
90
99
|
* The resolved manager service, memoized as a PROMISE so concurrent branches share one describe
|
|
91
100
|
* round-trip — and dropped on failure, so a resolve that lost to a manager restart is retried by
|
|
@@ -152,6 +161,7 @@ export declare class MeshHandler {
|
|
|
152
161
|
* its identity, its worktree and its turn history across the edit. Called by the driver on the
|
|
153
162
|
* migrated run before the program runs; a map with no entry for a persona changes nothing.
|
|
154
163
|
*/
|
|
164
|
+
restoreMigratedSeats(entries: readonly JournalEntry[]): Promise<void>;
|
|
155
165
|
adoptMigratedSeats(seats: ReadonlyMap<string, readonly JournalEntry[]>): void;
|
|
156
166
|
/**
|
|
157
167
|
* Rebuild the in-memory run memos an adopted run needs to keep performing `turn`: the roster
|
|
@@ -632,19 +642,7 @@ export declare class MeshHandler {
|
|
|
632
642
|
/** Take this deadline's fire for as long as somebody is waiting on it. */
|
|
633
643
|
private pumpFires;
|
|
634
644
|
}
|
|
635
|
-
|
|
636
|
-
* which is why a resumed run finds the consumer its earlier attempt created rather than starting
|
|
637
|
-
* again from "now" — and why nothing about the wait has to be remembered across a crash. */
|
|
638
|
-
export declare function waitConsumerName(requestId: string): string;
|
|
639
|
-
/**
|
|
640
|
-
* The one definition of a wait's consumer, shared by the handler and by anything that has to
|
|
641
|
-
* recreate it — so the name a resume looks for and the name a wait creates cannot drift apart.
|
|
642
|
-
*
|
|
643
|
-
* `deliver_policy: "new"` applies only to the FIRST create: an existing durable keeps its own
|
|
644
|
-
* position, which is exactly what a resume needs, and events from before the program asked are not
|
|
645
|
-
* this wait's to see.
|
|
646
|
-
*/
|
|
647
|
-
export declare function waitConsumerConfig(space: string, requestId: string, channel: string): Record<string, unknown>;
|
|
645
|
+
export { waitConsumerName, waitConsumerConfig } from "@cotal-ai/core";
|
|
648
646
|
/**
|
|
649
647
|
* Read a spawn's `permits` as the budgets this host can enforce: `turns`, a positive integer of
|
|
650
648
|
* turns the run may dispatch to the agent, and `wallClock`, a duration from the spawn after which
|
|
@@ -736,5 +734,4 @@ export declare class EpfSettleWatcher implements SettleWatcher {
|
|
|
736
734
|
constructor(jsm: JetStreamManager, space: string, pollMs?: number);
|
|
737
735
|
awaitSettle(ref: CheckpointRef): Promise<CheckpointSettleFact>;
|
|
738
736
|
}
|
|
739
|
-
export {};
|
|
740
737
|
//# sourceMappingURL=mesh-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mesh-handler.d.ts","sourceRoot":"","sources":["../src/mesh-handler.ts"],"names":[],"mappings":"AAmBA,OAAO,
|
|
1
|
+
{"version":3,"file":"mesh-handler.d.ts","sourceRoot":"","sources":["../src/mesh-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/mesh-handler.js
CHANGED
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* again.
|
|
18
18
|
*/
|
|
19
19
|
import { createHash } from "node:crypto";
|
|
20
|
-
import { mintCheckpoint, heartbeatCheckpoint, resumeCheckpoint, readCheckpointSettle, readCheckpointAnswer, readCheckpointStatus, readCheckpointSpec, reconcileCheckpointSchedule, handleCheckpointFire, eptStreamName, eptSubject, chatStream,
|
|
20
|
+
import { mintCheckpoint, heartbeatCheckpoint, resumeCheckpoint, readCheckpointSettle, readCheckpointAnswer, readCheckpointStatus, readCheckpointSpec, reconcileCheckpointSchedule, handleCheckpointFire, eptStreamName, eptSubject, chatStream, waitConsumerName, waitConsumerConfig, isConcreteChannel, assertSafePattern, assertValidChannel, presenceBucket, principalKey, liveKvEntries, IncompleteKvScan, openMembersRegistry, openChannelRegistry, writeChannelConfig, readMember, commitMember, tombstoneMember, StaleMembershipWrite, runNoticeId, writeRunNotice, actionContext, invokeCommand, readGoalResult, readGoalStatus, resolveService, listRunNotices, listRunMigrations, markRunNoticeConsumed, listRunNoticesForRun, readRunRecord, writeRunStatus, EpEnvelopeError, } from "@cotal-ai/core";
|
|
21
21
|
import { renderRunContext } from "./run-context.js";
|
|
22
|
+
import { migrationSeats } from "./migrate.js";
|
|
22
23
|
import { Kvm } from "@nats-io/kv";
|
|
23
24
|
import { parseDuration, Cancelled, EffectError, Journal, EffectRefused, askSchemaShape, conformsToAskSchema, journalEntryKeyString, stepKeyString, } from "@cotal-ai/lang";
|
|
24
25
|
/**
|
|
@@ -56,7 +57,8 @@ export class MeshHandler {
|
|
|
56
57
|
binding;
|
|
57
58
|
watcher;
|
|
58
59
|
clock;
|
|
59
|
-
|
|
60
|
+
services;
|
|
61
|
+
constructor(nc, kv, js, jsm, binding, watcher, clock = () => Date.now(), services) {
|
|
60
62
|
this.nc = nc;
|
|
61
63
|
this.kv = kv;
|
|
62
64
|
this.js = js;
|
|
@@ -64,6 +66,7 @@ export class MeshHandler {
|
|
|
64
66
|
this.binding = binding;
|
|
65
67
|
this.watcher = watcher;
|
|
66
68
|
this.clock = clock;
|
|
69
|
+
this.services = services;
|
|
67
70
|
}
|
|
68
71
|
/**
|
|
69
72
|
* The resolved manager service, memoized as a PROMISE so concurrent branches share one describe
|
|
@@ -163,8 +166,10 @@ export class MeshHandler {
|
|
|
163
166
|
* cannot advance it.
|
|
164
167
|
*/
|
|
165
168
|
async adopted(entries) {
|
|
166
|
-
const folded = foldEntries(entries);
|
|
169
|
+
const folded = foldEntries(this.services ? await this.services.authority.journal() : entries);
|
|
167
170
|
this.seedRunMemos(folded);
|
|
171
|
+
if (this.services)
|
|
172
|
+
return [...await this.services.pauses.rearm()];
|
|
168
173
|
return await rearmOutstandingPauses({ kv: this.kv, js: this.js, jsm: this.jsm }, this.binding, folded);
|
|
169
174
|
}
|
|
170
175
|
/**
|
|
@@ -174,6 +179,18 @@ export class MeshHandler {
|
|
|
174
179
|
* its identity, its worktree and its turn history across the edit. Called by the driver on the
|
|
175
180
|
* migrated run before the program runs; a map with no entry for a persona changes nothing.
|
|
176
181
|
*/
|
|
182
|
+
async restoreMigratedSeats(entries) {
|
|
183
|
+
const source = this.services ? await this.services.authority.journal() : entries;
|
|
184
|
+
const kept = new Map();
|
|
185
|
+
for (const migration of await listRunMigrations(this.kv, this.binding.endpoint, this.binding.runId)) {
|
|
186
|
+
if (migration.applied === undefined)
|
|
187
|
+
continue;
|
|
188
|
+
for (const [persona, seats] of migrationSeats(source, migration.spec).adopt)
|
|
189
|
+
kept.set(persona, [...(kept.get(persona) ?? []), ...seats]);
|
|
190
|
+
}
|
|
191
|
+
if (kept.size > 0)
|
|
192
|
+
this.adoptMigratedSeats(kept);
|
|
193
|
+
}
|
|
177
194
|
adoptMigratedSeats(seats) {
|
|
178
195
|
for (const [persona, entries] of seats)
|
|
179
196
|
this.adoptable.set(persona, [...(this.adoptable.get(persona) ?? []), ...entries]);
|
|
@@ -306,7 +323,8 @@ export class MeshHandler {
|
|
|
306
323
|
* landed, and the flip to `issued: true` happens only after it returns.
|
|
307
324
|
*/
|
|
308
325
|
async discharge(entries) {
|
|
309
|
-
|
|
326
|
+
const owed = this.services ? await this.services.authority.cleanupEntries() : entries;
|
|
327
|
+
for (const e of owed) {
|
|
310
328
|
if (e.requestId === undefined)
|
|
311
329
|
continue;
|
|
312
330
|
if (e.kind === "spawn") {
|
|
@@ -337,6 +355,10 @@ export class MeshHandler {
|
|
|
337
355
|
await this.cancelTimer({ endpoint: this.binding.endpoint, token: current });
|
|
338
356
|
if (e.kind === "wait") {
|
|
339
357
|
await this.cancelTimer({ endpoint: this.binding.endpoint, token: derivedToken(e.requestId, "wait-timeout") });
|
|
358
|
+
if (this.services) {
|
|
359
|
+
await this.services.waits.close(e.requestId);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
340
362
|
try {
|
|
341
363
|
await this.jsm.consumers.delete(chatStream(this.binding.space), waitConsumerName(e.requestId));
|
|
342
364
|
}
|
|
@@ -456,6 +478,7 @@ export class MeshHandler {
|
|
|
456
478
|
const plan = e.external;
|
|
457
479
|
if (!isConclavePlan(plan))
|
|
458
480
|
return;
|
|
481
|
+
assertConclaveOwnership(plan, e.requestId);
|
|
459
482
|
await this.releaseConclave(plan);
|
|
460
483
|
this.conclaves.delete(e.requestId);
|
|
461
484
|
}
|
|
@@ -538,7 +561,7 @@ export class MeshHandler {
|
|
|
538
561
|
// was accepted, so an answer found by token alone could be the loser's.
|
|
539
562
|
const answer = settled.answerId === undefined
|
|
540
563
|
? undefined
|
|
541
|
-
: await readCheckpointAnswer(this.kv, this.binding.endpoint, ref.token, settled.answerId);
|
|
564
|
+
: this.services ? await this.services.pauses.readAnswer(ref.token) : await readCheckpointAnswer(this.kv, this.binding.endpoint, ref.token, settled.answerId);
|
|
542
565
|
if (answer === undefined)
|
|
543
566
|
throw new CheckpointAnswerMissing(ref.token, settled.answerId);
|
|
544
567
|
return {
|
|
@@ -576,6 +599,19 @@ export class MeshHandler {
|
|
|
576
599
|
if (!isConcreteChannel(ev.channel)) {
|
|
577
600
|
throw new Error(`wait() cannot await a wildcard channel ("${ev.channel}"); an await names one channel`);
|
|
578
601
|
}
|
|
602
|
+
const waitBinding = {
|
|
603
|
+
...ctx.resume,
|
|
604
|
+
waitChannel: ev.channel,
|
|
605
|
+
waitTimers: [
|
|
606
|
+
...(ev.event === "idle" || req.timeout !== undefined ? [ctx.requestId] : []),
|
|
607
|
+
...(ev.event === "idle" && req.timeout !== undefined ? [derivedToken(ctx.requestId, "wait-timeout")] : []),
|
|
608
|
+
],
|
|
609
|
+
};
|
|
610
|
+
if (this.services) {
|
|
611
|
+
if (ctx.resume?.waitChannel !== undefined && ctx.resume.waitChannel !== ev.channel)
|
|
612
|
+
throw new Error(`wait ${ctx.requestId} recorded a different channel`);
|
|
613
|
+
await ctx.bind(waitBinding);
|
|
614
|
+
}
|
|
579
615
|
// A recorded seq is a previous attempt's MATCH, taken before the crash. Return that message
|
|
580
616
|
// rather than looking again: the consumer has already acked it, so looking again would wait for
|
|
581
617
|
// a second event the program never asked for.
|
|
@@ -586,7 +622,7 @@ export class MeshHandler {
|
|
|
586
622
|
// same reason the live match path does: this is the ending, it just happened last time.
|
|
587
623
|
await this.cancelTimer({ endpoint: this.binding.endpoint, token: ctx.requestId });
|
|
588
624
|
await this.cancelTimer({ endpoint: this.binding.endpoint, token: derivedToken(ctx.requestId, "wait-timeout") });
|
|
589
|
-
return await this.messageAt(bound);
|
|
625
|
+
return await this.messageAt(bound, ctx.requestId);
|
|
590
626
|
}
|
|
591
627
|
const timeoutAt = req.timeout === undefined ? undefined : this.now() + parseDuration(req.timeout);
|
|
592
628
|
const idleFor = ev.event === "idle" ? parseDuration(ev.duration) : undefined;
|
|
@@ -608,8 +644,11 @@ export class MeshHandler {
|
|
|
608
644
|
await this.arm(outer, timeoutAt);
|
|
609
645
|
const durable = waitConsumerName(ctx.requestId);
|
|
610
646
|
const stream = chatStream(this.binding.space);
|
|
611
|
-
|
|
612
|
-
|
|
647
|
+
if (this.services)
|
|
648
|
+
await this.services.waits.open(ctx.requestId, ev.channel);
|
|
649
|
+
else
|
|
650
|
+
await this.jsm.consumers.add(stream, waitConsumerConfig(this.binding.space, ctx.requestId, ev.channel));
|
|
651
|
+
const consumer = this.services ? undefined : await this.js.consumers.get(stream, durable);
|
|
613
652
|
// Set on each of the three paths that END the wait, and read by the cleanup below. A THROW is
|
|
614
653
|
// not one of them — see the note there.
|
|
615
654
|
let over = false;
|
|
@@ -649,24 +688,30 @@ export class MeshHandler {
|
|
|
649
688
|
over = true;
|
|
650
689
|
return { channel: ev.channel, at: this.now() };
|
|
651
690
|
}
|
|
652
|
-
|
|
691
|
+
const batch = this.services
|
|
692
|
+
? (await this.services.waits.fetch(ctx.requestId)).map((message) => ({
|
|
693
|
+
seq: message.sequence, data: message.data,
|
|
694
|
+
ack: () => this.services.waits.ack(ctx.requestId, message.receipt),
|
|
695
|
+
}))
|
|
696
|
+
: await consumer.fetch({ max_messages: 16, expires: WAIT_POLL_MS });
|
|
697
|
+
for await (const m of batch) {
|
|
653
698
|
const msg = decodeMessage(m.data);
|
|
654
699
|
if (idleFor !== undefined) {
|
|
655
700
|
// ANY traffic resets an idle window, matched or not: "idle" is a fact about the
|
|
656
701
|
// channel, not about the messages this program finds interesting.
|
|
657
|
-
m.ack();
|
|
702
|
+
await m.ack();
|
|
658
703
|
await this.push(primary, this.now() + idleFor);
|
|
659
704
|
continue;
|
|
660
705
|
}
|
|
661
706
|
if (msg === undefined || !matchesEvent(msg, from, matcher)) {
|
|
662
|
-
m.ack();
|
|
707
|
+
await m.ack();
|
|
663
708
|
continue;
|
|
664
709
|
}
|
|
665
710
|
// BIND BEFORE ACK. The bind is durable; the ack is what makes the message unrecoverable.
|
|
666
711
|
// In this order a crash in between redelivers it, and a crash after it is answered from
|
|
667
712
|
// the recorded sequence — in the other order the match is simply lost.
|
|
668
|
-
await ctx.bind({ chatSeq: m.seq });
|
|
669
|
-
m.ack();
|
|
713
|
+
await ctx.bind(this.services ? { ...waitBinding, chatSeq: m.seq } : { chatSeq: m.seq });
|
|
714
|
+
await m.ack();
|
|
670
715
|
if (primary !== undefined)
|
|
671
716
|
await this.cancelTimer(primary);
|
|
672
717
|
if (outer !== undefined)
|
|
@@ -687,7 +732,9 @@ export class MeshHandler {
|
|
|
687
732
|
// Keeping the consumer costs one durable on an abandoned run, which is what a host crash
|
|
688
733
|
// already costs; reaping on inactivity instead could delete a live wait's position while its
|
|
689
734
|
// host was down.
|
|
690
|
-
if (over)
|
|
735
|
+
if (over && this.services)
|
|
736
|
+
await this.services.waits.close(ctx.requestId);
|
|
737
|
+
else if (over) {
|
|
691
738
|
try {
|
|
692
739
|
await this.jsm.consumers.delete(stream, durable);
|
|
693
740
|
}
|
|
@@ -1356,7 +1403,7 @@ export class MeshHandler {
|
|
|
1356
1403
|
}
|
|
1357
1404
|
const answer = settled.answerId === undefined
|
|
1358
1405
|
? undefined
|
|
1359
|
-
: await readCheckpointAnswer(this.kv, this.binding.endpoint, token, settled.answerId);
|
|
1406
|
+
: this.services ? await this.services.pauses.readAnswer(token) : await readCheckpointAnswer(this.kv, this.binding.endpoint, token, settled.answerId);
|
|
1360
1407
|
if (answer === undefined)
|
|
1361
1408
|
throw new CheckpointAnswerMissing(token, settled.answerId);
|
|
1362
1409
|
if (conformsToAskSchema(answer.value, shape))
|
|
@@ -1552,6 +1599,7 @@ export class MeshHandler {
|
|
|
1552
1599
|
plan = await this.planConclave(req, ctx);
|
|
1553
1600
|
await ctx.bind({ ...plan });
|
|
1554
1601
|
}
|
|
1602
|
+
assertConclaveOwnership(plan, ctx.requestId, req);
|
|
1555
1603
|
await this.executeConclavePlan(plan);
|
|
1556
1604
|
this.conclaves.set(ctx.requestId, plan);
|
|
1557
1605
|
return { channel: plan.channel };
|
|
@@ -1571,6 +1619,7 @@ export class MeshHandler {
|
|
|
1571
1619
|
const plan = this.conclaves.get(ctx.requestId) ?? (isConclavePlan(ctx.resume) ? ctx.resume : undefined);
|
|
1572
1620
|
if (plan === undefined)
|
|
1573
1621
|
throw new Error(`conclave close for "${ctx.requestId}" has no recorded plan: the open that owes this close bound none`);
|
|
1622
|
+
assertConclaveOwnership(plan, ctx.requestId);
|
|
1574
1623
|
await this.releaseConclave(plan);
|
|
1575
1624
|
this.conclaves.delete(ctx.requestId);
|
|
1576
1625
|
return null;
|
|
@@ -1715,6 +1764,8 @@ export class MeshHandler {
|
|
|
1715
1764
|
* raised rather than waited on.
|
|
1716
1765
|
*/
|
|
1717
1766
|
async arm(ref, deadline) {
|
|
1767
|
+
if (this.services)
|
|
1768
|
+
return this.services.pauses.arm(ref.token, deadline);
|
|
1718
1769
|
// Over already: an expiry or an answer landed while this host was away. Nothing to arm, and the
|
|
1719
1770
|
// caller reads the fact next.
|
|
1720
1771
|
if ((await readCheckpointSettle(this.jsm, this.binding.space, ref)) !== undefined)
|
|
@@ -1768,6 +1819,8 @@ export class MeshHandler {
|
|
|
1768
1819
|
/** Push a live deadline out — the idle window restarting. The heartbeat CAS-advances the
|
|
1769
1820
|
* generation before replacing the timer, so a fire from the old one no-ops rather than racing. */
|
|
1770
1821
|
async push(ref, deadline) {
|
|
1822
|
+
if (this.services)
|
|
1823
|
+
return this.services.pauses.heartbeat(ref.token, deadline);
|
|
1771
1824
|
await heartbeatCheckpoint(this.kv, this.js, this.jsm, this.binding.space, {
|
|
1772
1825
|
ref,
|
|
1773
1826
|
instanceId: this.binding.instanceId,
|
|
@@ -1792,6 +1845,8 @@ export class MeshHandler {
|
|
|
1792
1845
|
* owner-behind clock skew, and over-emission is idempotent at the timer writer.
|
|
1793
1846
|
*/
|
|
1794
1847
|
async takeFire(ref) {
|
|
1848
|
+
if (this.services)
|
|
1849
|
+
return this.services.pauses.takeFire(ref.token);
|
|
1795
1850
|
const subject = eptSubject(this.binding.space, ref.endpoint, this.binding.instanceId, this.binding.epoch, ref.token, "fire");
|
|
1796
1851
|
const fired = await this.jsm.streams
|
|
1797
1852
|
.getMessage(eptStreamName(this.binding.space), { last_by_subj: subject })
|
|
@@ -1823,6 +1878,14 @@ export class MeshHandler {
|
|
|
1823
1878
|
async expired(ref) {
|
|
1824
1879
|
if (ref === undefined)
|
|
1825
1880
|
return undefined;
|
|
1881
|
+
if (this.services) {
|
|
1882
|
+
const settled = await this.services.pauses.readSettle(ref.token);
|
|
1883
|
+
if (settled !== undefined)
|
|
1884
|
+
return settled;
|
|
1885
|
+
if (!(await this.services.pauses.takeFire(ref.token)))
|
|
1886
|
+
return undefined;
|
|
1887
|
+
return this.services.pauses.readSettle(ref.token);
|
|
1888
|
+
}
|
|
1826
1889
|
const settled = await readCheckpointSettle(this.jsm, this.binding.space, ref);
|
|
1827
1890
|
if (settled !== undefined)
|
|
1828
1891
|
return settled;
|
|
@@ -1834,6 +1897,8 @@ export class MeshHandler {
|
|
|
1834
1897
|
* A timer left armed would fire into a run that has moved on; claiming it is how the plane says
|
|
1835
1898
|
* "this pause is over" without a second mechanism for cancellation. */
|
|
1836
1899
|
async cancelTimer(ref) {
|
|
1900
|
+
if (this.services)
|
|
1901
|
+
return this.services.pauses.claim(ref.token);
|
|
1837
1902
|
const st = await readCheckpointStatus(this.kv, ref);
|
|
1838
1903
|
if (st?.value.state !== "waiting")
|
|
1839
1904
|
return;
|
|
@@ -1866,7 +1931,13 @@ export class MeshHandler {
|
|
|
1866
1931
|
}
|
|
1867
1932
|
}
|
|
1868
1933
|
/** The message at a recorded stream sequence — the re-bind path after a crash. */
|
|
1869
|
-
async messageAt(seq) {
|
|
1934
|
+
async messageAt(seq, requestId) {
|
|
1935
|
+
if (this.services) {
|
|
1936
|
+
const message = decodeMessage(await this.services.waits.messageAt(requestId, seq));
|
|
1937
|
+
if (message === undefined)
|
|
1938
|
+
throw new Error(`wait ${requestId}'s recorded message does not decode`);
|
|
1939
|
+
return message;
|
|
1940
|
+
}
|
|
1870
1941
|
const m = await this.jsm.streams.getMessage(chatStream(this.binding.space), { seq });
|
|
1871
1942
|
if (m === null || m === undefined) {
|
|
1872
1943
|
throw new Error(`the message this wait matched (sequence ${seq}) is no longer on the channel's stream; a recorded match cannot be re-read`);
|
|
@@ -1885,7 +1956,7 @@ export class MeshHandler {
|
|
|
1885
1956
|
* already in the past.
|
|
1886
1957
|
*/
|
|
1887
1958
|
async settle(ref, signal) {
|
|
1888
|
-
const already = await readCheckpointSettle(this.jsm, this.binding.space, ref);
|
|
1959
|
+
const already = this.services ? await this.services.pauses.readSettle(ref.token) : await readCheckpointSettle(this.jsm, this.binding.space, ref);
|
|
1889
1960
|
if (already !== undefined)
|
|
1890
1961
|
return already;
|
|
1891
1962
|
// The watcher waits for the FACT. For a pause with an answer the fact arrives because somebody
|
|
@@ -1942,28 +2013,7 @@ export class MeshHandler {
|
|
|
1942
2013
|
}
|
|
1943
2014
|
}
|
|
1944
2015
|
}
|
|
1945
|
-
|
|
1946
|
-
* which is why a resumed run finds the consumer its earlier attempt created rather than starting
|
|
1947
|
-
* again from "now" — and why nothing about the wait has to be remembered across a crash. */
|
|
1948
|
-
export function waitConsumerName(requestId) {
|
|
1949
|
-
return `wfw_${requestId}`;
|
|
1950
|
-
}
|
|
1951
|
-
/**
|
|
1952
|
-
* The one definition of a wait's consumer, shared by the handler and by anything that has to
|
|
1953
|
-
* recreate it — so the name a resume looks for and the name a wait creates cannot drift apart.
|
|
1954
|
-
*
|
|
1955
|
-
* `deliver_policy: "new"` applies only to the FIRST create: an existing durable keeps its own
|
|
1956
|
-
* position, which is exactly what a resume needs, and events from before the program asked are not
|
|
1957
|
-
* this wait's to see.
|
|
1958
|
-
*/
|
|
1959
|
-
export function waitConsumerConfig(space, requestId, channel) {
|
|
1960
|
-
return {
|
|
1961
|
-
durable_name: waitConsumerName(requestId),
|
|
1962
|
-
filter_subject: chatSubject(space, "*", "*", channel),
|
|
1963
|
-
ack_policy: "explicit",
|
|
1964
|
-
deliver_policy: "new",
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
2016
|
+
export { waitConsumerName, waitConsumerConfig } from "@cotal-ai/core";
|
|
1967
2017
|
/** How long one poll of a wait's consumer blocks. The deadline itself is durable; this is only how
|
|
1968
2018
|
* late its observation can be, and a shorter poll buys latency at the cost of fetch traffic. */
|
|
1969
2019
|
const WAIT_POLL_MS = 2_000;
|
|
@@ -2126,6 +2176,25 @@ function spawnHandleOf(req, ext, fact, endpoint) {
|
|
|
2126
2176
|
...(typeof d.role === "string" ? { role: d.role } : {}),
|
|
2127
2177
|
};
|
|
2128
2178
|
}
|
|
2179
|
+
/** A recorded flag cannot turn a borrowed channel into a channel this step owns. */
|
|
2180
|
+
function assertConclaveOwnership(plan, requestId, req) {
|
|
2181
|
+
const derived = `conclave-${createHash("sha256").update(requestId).digest("hex").slice(0, 12)}`;
|
|
2182
|
+
if (plan.registered && plan.channel !== derived)
|
|
2183
|
+
throw new Error(`recorded conclave channel ${plan.channel} is not owned by step ${requestId}`);
|
|
2184
|
+
if (req !== undefined) {
|
|
2185
|
+
if (plan.channel !== (req.channel ?? derived) || plan.registered !== (req.channel === undefined))
|
|
2186
|
+
throw new Error(`recorded conclave channel does not match the requested channel for ${requestId}`);
|
|
2187
|
+
const members = new Set(req.members.map((member) => member.agent));
|
|
2188
|
+
if (plan.members.length !== members.size || plan.members.some((member) => !members.delete(member.agent)))
|
|
2189
|
+
throw new Error(`recorded conclave members do not match the request for ${requestId}`);
|
|
2190
|
+
}
|
|
2191
|
+
for (const member of plan.members) {
|
|
2192
|
+
const [owner, actor, ...extra] = member.principal.split(".");
|
|
2193
|
+
if (owner === undefined || actor === undefined || extra.length !== 0 || principalKey(owner, actor).key !== member.principal
|
|
2194
|
+
|| parseAgentHandle(member.agent).uid !== member.uid || !Number.isSafeInteger(member.generation) || member.generation < 1)
|
|
2195
|
+
throw new Error(`recorded conclave member identity is malformed for ${requestId}`);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2129
2198
|
/** Whether a recorded external is a conclave plan. A journal is bytes from an earlier process, so
|
|
2130
2199
|
* the shape is checked rather than trusted — a malformed external reads as "nothing was bound". */
|
|
2131
2200
|
function isConclavePlan(v) {
|