Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
0df9676
feat(chat-swarm): define deterministic continuation contract
James3014 Sep 13, 2026
cb167d0
feat(chat-swarm): add continuation epoch domain guards
James3014 Sep 13, 2026
2916471
test(chat-swarm): cover continuation epoch guards
James3014 Sep 13, 2026
c5215a8
test(chat-swarm): run continuation domain regressions
James3014 Sep 13, 2026
b57a8f9
fix(chat-swarm): bind continuation target to authenticated carrier
James3014 Sep 13, 2026
ad9fec4
fix(chat-swarm): separate authenticated target from caller input
James3014 Sep 13, 2026
8d15f2c
test(chat-swarm): prove target identity is externally bound
James3014 Sep 13, 2026
2f7a453
feat(chat-swarm): persist continuation in existing durable operation …
James3014 Sep 13, 2026
edfcf3b
fix(chat-swarm): make continuation request revisions monotonic
James3014 Sep 13, 2026
74aa846
test(chat-swarm): exercise durable continuation transfer
James3014 Sep 13, 2026
411c327
test(chat-swarm): include durable continuation regressions
James3014 Sep 13, 2026
43d2603
feat(chat-swarm): bind continuation actions to authenticated identities
James3014 Sep 13, 2026
1d9d7bd
test(chat-swarm): bind continuation control to authenticated caller i…
James3014 Sep 13, 2026
18b5c38
fix(chat-swarm): enforce owner before continuation approval readback
James3014 Sep 13, 2026
ec9c132
test(chat-swarm): exercise coordinator owner boundary
James3014 Sep 13, 2026
94a61dd
test(chat-swarm): include continuation coordinator authority tests
James3014 Sep 13, 2026
a911639
fix(chat-swarm): persist expiry and arbitrate continuation at commit
James3014 Sep 13, 2026
1252dd0
test(chat-swarm): cover bounded parallel continuation arbitration
James3014 Sep 13, 2026
61c71c3
feat(chat-swarm): host continuation inside existing lifecycle
James3014 Sep 13, 2026
1c5d6a5
test(chat-swarm): verify lifecycle continuation recovery and drain ad…
James3014 Sep 13, 2026
2517825
feat(chat-swarm): represent superseded continuation targets
James3014 Sep 13, 2026
75f2a9a
fix(chat-swarm): supersede losing continuation targets atomically
James3014 Sep 13, 2026
91a9863
test(chat-swarm): prove losing continuation targets are terminally su…
James3014 Sep 13, 2026
82e8db5
fix(chat-swarm): normalize generic restart fencing exactly once
James3014 Sep 13, 2026
b86c667
test(chat-swarm): mirror formal server restart ordering
James3014 Sep 13, 2026
3e70b26
fix(chat-swarm): exact-bind continuation reconcile swarm
James3014 Sep 13, 2026
1e1010b
fix(chat-swarm): keep expiry terminal across restart reconciliation
James3014 Sep 13, 2026
70bed16
test(chat-swarm): reject cross-swarm continuation reconcile
James3014 Sep 13, 2026
2bd93c2
fix(chat-swarm): expose bound continuation ttl
James3014 Sep 13, 2026
8c54cf2
fix(chat-swarm): bind continuation replay to ttl and attempt identity
James3014 Sep 13, 2026
7e1bcec
test(chat-swarm): reject ttl drift and persisted attempt tamper
James3014 Sep 13, 2026
d4ba7b8
test(chat-swarm): bind domain fixture ttl
James3014 Sep 13, 2026
510adc4
fix(chat-swarm): derive continuation expiry from one clock read
James3014 Sep 13, 2026
e52c713
fix(chat-swarm): fence new continuation while outcome unresolved
James3014 Sep 13, 2026
7495740
test(chat-swarm): block second replacement while continuation unresolved
James3014 Sep 13, 2026
1adbb53
test(chat-swarm): include unresolved continuation restart fence
James3014 Sep 13, 2026
09af450
refactor(chat-swarm): keep one authoritative continuation request hash
James3014 Sep 13, 2026
2807a97
test(chat-swarm): keep request hash authority in durable store
James3014 Sep 13, 2026
f8e756d
test(chat-swarm): keep retired carrier fenced from continuation target
James3014 Sep 13, 2026
4594e1e
test(chat-swarm): include retired carrier continuation fence
James3014 Sep 13, 2026
f09da74
fix(chat-swarm): fence retired continuation carriers
James3014 Sep 13, 2026
cbd4743
fix(chat-swarm): make terminal continuation replay state-independent
James3014 Sep 13, 2026
077017c
test(chat-swarm): preserve terminal continuation replay identity
James3014 Sep 13, 2026
a98f031
test(chat-swarm): include terminal continuation replay witnesses
James3014 Sep 13, 2026
492a3aa
test(chat-swarm): fail closed on corrupt pending continuation state
James3014 Sep 13, 2026
b26bb98
test(chat-swarm): include corrupt continuation fail-closed witness
James3014 Sep 13, 2026
bd37051
fix(chat-swarm): validate pending continuation durable identity
James3014 Sep 13, 2026
7e21a2c
ci(chat-swarm): isolate continuation regressions on macOS
James3014 Sep 13, 2026
7550f74
ci(chat-swarm): split continuation focused tests by job
James3014 Sep 13, 2026
503c900
test(chat-swarm): assert corrupt continuation reads fail closed
James3014 Sep 13, 2026
2d1aff0
fix(chat-swarm): fail closed on corrupt continuation terminal state
James3014 Sep 13, 2026
6f7ccd1
test(chat-swarm): fence corrupt continuation terminal metadata
James3014 Sep 13, 2026
9dc6114
fix(chat-swarm): bind absolute continuation expiry
James3014 Sep 13, 2026
0a35df8
test(chat-swarm): bind absolute continuation expiry
James3014 Sep 13, 2026
a168bb3
ci(chat-swarm): verify pull request diff
James3014 Sep 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ concurrency:
cancel-in-progress: true

jobs:
diff-check:
name: Diff check (PR)
if: github.event_name == 'pull_request'
runs-on: macos-latest
timeout-minutes: 5

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Git diff check
run: git diff --check "${{ github.event.pull_request.base.sha }}...HEAD"

smoke:
name: Smoke (${{ matrix.os }})
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -64,6 +79,45 @@ jobs:
- name: Doctor
run: node dist/cli.js doctor

chat-swarm-continuation:
name: Chat Swarm continuation ${{ matrix.name }} (macOS)
runs-on: macos-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
include:
- name: coordinator
file: src/chat-swarm-continuation-coordinator.test.ts
- name: durable-store
file: src/chat-swarm-continuation-store.test.ts
- name: restart-fence
file: src/chat-swarm-continuation-restart-fence.test.ts
- name: retired-carrier
file: src/chat-swarm-continuation-stale-carrier.test.ts
- name: terminal-replay
file: src/chat-swarm-continuation-terminal-replay.test.ts
- name: corruption-fence
file: src/chat-swarm-continuation-corruption-fence.test.ts
- name: aggregate-domain
file: src/chat-swarm-continuation-domain.test.ts

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Run focused continuation test
run: npx tsx ${{ matrix.file }}

local-agent-sessions:
name: Local agent sessions (macOS)
runs-on: macos-latest
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"postinstall": "node scripts/fix-node-pty-permissions.mjs",
"test:node-pty-permissions": "node --import tsx --test src/node-pty-postinstall.test.ts",
"start": "node dist/cli.js serve",
"test": "npm run test:node-pty-permissions && npm run test:carrier-binding && tsx src/coordination-reader-loader.test.ts && tsx src/control-plane-ownership.test.ts && tsx src/control-plane-handoff.test.ts && tsx src/control-plane-continuation.test.ts && tsx src/deployment-convergence.test.ts && tsx src/current-completion-matrix.test.ts && tsx src/control-plane-consumer.test.ts && tsx src/local-agent-cline-catalog.test.ts && tsx src/chat-swarm-contract.test.ts && tsx src/chat-swarm-store.test.ts && tsx src/chat-swarm-runtime-owner.test.ts && tsx src/local-agent-opencode-mcp-catalog.test.ts && tsx src/chat-swarm-coordinator.test.ts && tsx src/chat-swarm-peer-runtime.test.ts && tsx src/chat-swarm-tools.test.ts && tsx src/chat-swarm-lifecycle.test.ts && tsx src/chat-swarm-carrier.test.ts && tsx src/chat-swarm-peer-admission.test.ts && tsx src/chat-swarm-task-ledger.test.ts && tsx src/git-worktrees.test.ts && tsx src/execution-protocol.test.ts && tsx src/durable-operations-ci.test.ts && tsx src/git-candidate.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions-ci.test.ts && tsx src/codex-goal-sessions-ci.test.ts && tsx src/mcp-sessions.test.ts && tsx src/cutover-state.test.ts && tsx src/cutover-state-recovery-guard.test.ts && tsx src/cutover-build-ready.test.ts && tsx src/cutover-orchestration.test.ts && tsx src/mcp-cutover.test.ts && tsx src/cutover-restart.test.ts && tsx src/cutover-http.test.ts && tsx src/cutover-recovery.test.ts && tsx src/cutover-binding-repair.test.ts && tsx src/capability-manifest.test.ts && tsx src/server-shutdown.test.ts && tsx src/codex-runtime.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-opencode-catalog.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-profile-source.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-toolchains.test.ts && tsx src/local-agent-idle-policy.test.ts && tsx src/local-agent-capacity.test.ts && tsx src/local-agent-execution-contract.test.ts && tsx src/local-agent-continuation.test.ts && tsx src/provider-scratch.test.ts && tsx src/git-integration-ci.test.ts && tsx src/repository-intelligence.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/conversation-isolation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server-ci.test.ts && tsx src/oauth-store.test.ts && tsx src/cli-ci.test.ts && tsx src/oauth-json-and-child-reap.test.ts && tsx src/local-agent-errors.test.ts && tsx src/provider-output-redaction.test.ts && tsx src/host-operation-policy.test.ts && tsx src/host-operations.test.ts && tsx src/host-operations-http.test.ts",
"test": "npm run test:node-pty-permissions && npm run test:carrier-binding && tsx src/coordination-reader-loader.test.ts && tsx src/control-plane-ownership.test.ts && tsx src/control-plane-handoff.test.ts && tsx src/control-plane-continuation.test.ts && tsx src/deployment-convergence.test.ts && tsx src/current-completion-matrix.test.ts && tsx src/control-plane-consumer.test.ts && tsx src/local-agent-cline-catalog.test.ts && tsx src/chat-swarm-contract.test.ts && tsx src/chat-swarm-store.test.ts && tsx src/chat-swarm-runtime-owner.test.ts && tsx src/local-agent-opencode-mcp-catalog.test.ts && tsx src/chat-swarm-coordinator.test.ts && tsx src/chat-swarm-peer-runtime.test.ts && tsx src/chat-swarm-tools.test.ts && tsx src/chat-swarm-lifecycle.test.ts && tsx src/chat-swarm-carrier.test.ts && tsx src/chat-swarm-continuation-domain.test.ts && tsx src/chat-swarm-continuation-store.test.ts && tsx src/chat-swarm-peer-admission.test.ts && tsx src/chat-swarm-task-ledger.test.ts && tsx src/git-worktrees.test.ts && tsx src/execution-protocol.test.ts && tsx src/durable-operations-ci.test.ts && tsx src/git-candidate.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions-ci.test.ts && tsx src/codex-goal-sessions-ci.test.ts && tsx src/mcp-sessions.test.ts && tsx src/cutover-state.test.ts && tsx src/cutover-state-recovery-guard.test.ts && tsx src/cutover-build-ready.test.ts && tsx src/cutover-orchestration.test.ts && tsx src/mcp-cutover.test.ts && tsx src/cutover-restart.test.ts && tsx src/cutover-http.test.ts && tsx src/cutover-recovery.test.ts && tsx src/cutover-binding-repair.test.ts && tsx src/capability-manifest.test.ts && tsx src/server-shutdown.test.ts && tsx src/codex-runtime.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-opencode-catalog.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-profile-source.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-toolchains.test.ts && tsx src/local-agent-idle-policy.test.ts && tsx src/local-agent-capacity.test.ts && tsx src/local-agent-execution-contract.test.ts && tsx src/local-agent-continuation.test.ts && tsx src/provider-scratch.test.ts && tsx src/git-integration-ci.test.ts && tsx src/repository-intelligence.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/conversation-isolation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server-ci.test.ts && tsx src/oauth-store.test.ts && tsx src/cli-ci.test.ts && tsx src/oauth-json-and-child-reap.test.ts && tsx src/local-agent-errors.test.ts && tsx src/provider-output-redaction.test.ts && tsx src/host-operation-policy.test.ts && tsx src/host-operations.test.ts && tsx src/host-operations-http.test.ts",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test:carrier-binding": "tsx src/carrier-binding.test.ts && tsx src/carrier-binding-http.test.ts",
"test:local-agent-sessions": "node --import tsx --test src/local-agent-sessions.test.ts"
Expand Down Expand Up @@ -85,4 +85,4 @@
"optionalDependencies": {
"node-pty": "1.1.0"
}
}
}
43 changes: 43 additions & 0 deletions src/chat-swarm-continuation-contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export const CHAT_SWARM_CONTINUATION_STATES = [
"PENDING",
"APPROVED",
"EXPIRED",
"SUPERSEDED",
"RECONCILE_REQUIRED",
] as const;

export type ChatSwarmContinuationState = (typeof CHAT_SWARM_CONTINUATION_STATES)[number];

export interface ChatSwarmContinuationRequest {
id: string;
swarmId: string;
workerId: string;
attemptKey: string;
requestHash: string;
sourceEpoch: number;
targetEpoch: number;
sourceCarrierFingerprint: string;
targetCarrierFingerprint: string;
checkpointHash: string;
ttlSeconds: number;
version: number;
status: ChatSwarmContinuationState;
requestedAt: string;
expiresAt: string;
approvedAt?: string;
}

export interface CreateContinuationRequestInput {
swarmId: string;
workerId: string;
attemptKey: string;
sourceEpoch: number;
ttlSeconds?: number;
}

export interface ApproveContinuationRequestInput {
swarmId: string;
requestId: string;
expectedRequestVersion: number;
expectedSwarmVersion: number;
}
147 changes: 147 additions & 0 deletions src/chat-swarm-continuation-coordinator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import assert from "node:assert/strict";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { ChatSwarmError } from "./chat-swarm-contract.js";
import { ChatSwarmContinuationCoordinator } from "./chat-swarm-continuation-coordinator.js";
import { ChatSwarmContinuationStore } from "./chat-swarm-continuation-store.js";
import { ChatSwarmCoordinator } from "./chat-swarm-coordinator.js";
import { ChatSwarmStore } from "./chat-swarm-store.js";

function fixture() {
const root = mkdtempSync(join(tmpdir(), "swarm-continuation-coordinator-"));
const swarmStore = new ChatSwarmStore(root);
const swarmCoordinator = new ChatSwarmCoordinator(swarmStore);
const continuationStore = new ChatSwarmContinuationStore(root);
const continuation = new ChatSwarmContinuationCoordinator(continuationStore, swarmCoordinator);
const ownerMeta = { "openai/session": "continuation-owner" };
const attackerMeta = { "openai/session": "continuation-attacker" };
const sourceMeta = { "openai/conversation_id": "continuation-source" };
const targetMeta = { "openai/conversation_id": "continuation-target" };
const otherTargetMeta = { "openai/conversation_id": "continuation-other-target" };
const swarm = swarmCoordinator.createSwarm(ownerMeta, { workerLimit: 3, metadata: { test: true } });
const worker = swarmCoordinator.joinWorker(sourceMeta, swarm.id, {
label: "Worker-01",
runtimeKind: "mcp_peer",
});
swarmCoordinator.checkpoint(
sourceMeta,
worker.id,
0,
new Date(Date.now() + 60 * 60 * 1000).toISOString(),
{ summary: "safe checkpoint" },
);
return {
root,
swarmStore,
continuationStore,
continuation,
ownerMeta,
attackerMeta,
sourceMeta,
targetMeta,
otherTargetMeta,
swarm,
worker,
};
}

function cleanup(f: ReturnType<typeof fixture>) {
f.continuationStore.close();
f.swarmStore.close();
rmSync(f.root, { recursive: true, force: true });
}

test("target request identity comes from authenticated metadata and exact target can read it", () => {
const f = fixture();
try {
const created = f.continuation.request(f.targetMeta, {
swarmId: f.swarm.id,
workerId: f.worker.id,
attemptKey: "target-bound-1",
sourceEpoch: 0,
});
assert.equal(created.created, true);
assert.equal(f.continuation.targetStatus(f.targetMeta, f.swarm.id)?.id, created.request.id);
assert.equal(f.continuation.targetStatus(f.otherTargetMeta, f.swarm.id), undefined);

assert.throws(
() => f.continuation.request(f.sourceMeta, {
swarmId: f.swarm.id,
workerId: f.worker.id,
attemptKey: "source-cannot-replace-itself",
sourceEpoch: 0,
}),
(error: unknown) => error instanceof ChatSwarmError && error.code === "INVALID_INPUT",
);
} finally {
cleanup(f);
}
});

test("only exact owner may approve, including idempotent approved readback", () => {
const f = fixture();
try {
const created = f.continuation.request(f.targetMeta, {
swarmId: f.swarm.id,
workerId: f.worker.id,
attemptKey: "owner-bound-1",
sourceEpoch: 0,
});

assert.throws(
() => f.continuation.approve(f.attackerMeta, {
swarmId: f.swarm.id,
requestId: created.request.id,
expectedRequestVersion: 1,
expectedSwarmVersion: 1,
}),
(error: unknown) => error instanceof ChatSwarmError && error.code === "OWNERSHIP_CONFLICT",
);

const approved = f.continuation.approve(f.ownerMeta, {
swarmId: f.swarm.id,
requestId: created.request.id,
expectedRequestVersion: 1,
expectedSwarmVersion: 1,
});
assert.equal(approved.status, "APPROVED");

assert.throws(
() => f.continuation.approve(f.attackerMeta, {
swarmId: f.swarm.id,
requestId: created.request.id,
expectedRequestVersion: 1,
expectedSwarmVersion: 1,
}),
(error: unknown) => error instanceof ChatSwarmError && error.code === "OWNERSHIP_CONFLICT",
);
} finally {
cleanup(f);
}
});

test("same owner cannot reconcile a continuation through a different swarm id", () => {
const f = fixture();
try {
const created = f.continuation.request(f.targetMeta, {
swarmId: f.swarm.id,
workerId: f.worker.id,
attemptKey: "cross-swarm-reconcile",
sourceEpoch: 0,
});
const otherSwarm = f.continuation.swarmCoordinator.createSwarm(f.ownerMeta, {
workerLimit: 1,
metadata: { test: "other-swarm" },
});

assert.throws(
() => f.continuation.reconcileNoEffect(f.ownerMeta, otherSwarm.id, created.request.id),
(error: unknown) => error instanceof ChatSwarmError && error.code === "OWNERSHIP_CONFLICT",
);
assert.equal(f.continuationStore.getRequest(created.request.id)?.status, "PENDING");
} finally {
cleanup(f);
}
});
56 changes: 56 additions & 0 deletions src/chat-swarm-continuation-coordinator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ChatSwarmError } from "./chat-swarm-contract.js";
import type {
ApproveContinuationRequestInput,
ChatSwarmContinuationRequest,
CreateContinuationRequestInput,
} from "./chat-swarm-continuation-contract.js";
import { ChatSwarmContinuationStore } from "./chat-swarm-continuation-store.js";
import { ChatSwarmCoordinator } from "./chat-swarm-coordinator.js";
import { resolveChatSwarmIdentity } from "./request-meta.js";

export class ChatSwarmContinuationCoordinator {
constructor(
readonly store: ChatSwarmContinuationStore,
readonly swarmCoordinator: ChatSwarmCoordinator,
) {}

request(
meta: unknown,
input: CreateContinuationRequestInput,
): { request: ChatSwarmContinuationRequest; created: boolean } {
const identity = resolveChatSwarmIdentity(meta);
return this.store.createRequest(identity.fingerprint, input);
}

targetStatus(meta: unknown, swarmId: string): ChatSwarmContinuationRequest | undefined {
const identity = resolveChatSwarmIdentity(meta);
return this.store.getLatestForTarget(swarmId, identity.fingerprint);
}

approve(meta: unknown, input: ApproveContinuationRequestInput): ChatSwarmContinuationRequest {
this.swarmCoordinator.assertOwnerForLifecycle(meta, input.swarmId);
const identity = resolveChatSwarmIdentity(meta);
return this.store.approveRequest(identity.fingerprint, input);
}

reconcileNoEffect(
meta: unknown,
swarmId: string,
requestId: string,
): ChatSwarmContinuationRequest {
this.swarmCoordinator.assertOwnerForLifecycle(meta, swarmId);
const request = this.store.getRequest(requestId);
if (!request || request.swarmId !== swarmId) {
throw new ChatSwarmError(
"OWNERSHIP_CONFLICT",
"continuation request does not belong to asserted swarm",
);
}
const identity = resolveChatSwarmIdentity(meta);
return this.store.reconcileUnknownNoEffect(identity.fingerprint, requestId);
}

recoverAfterRestart(): number {
return this.store.recoverAfterRestart();
}
}
Loading
Loading