agent-ecosystem/test/main/services/team/OpenCodePromptDeliveryLedger.test.ts
777genius 6e4f8ff8c4 fix: stabilize opencode mcp transport refresh
Keep OpenCode app MCP transport evidence durable, refresh stale sessions without consuming normal delivery attempts, and keep recoverable runtime diagnostics out of member card errors.

Cover stable MCP restart/fallback, forced session refresh, resolved_behavior_changed recovery, and renderer diagnostics with regression and safe e2e tests.
2026-05-18 13:08:34 +03:00

927 lines
31 KiB
TypeScript

import { promises as fs } from 'fs';
import * as os from 'os';
import * as path from 'path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
buildOpenCodePromptDeliveryAttemptId,
createOpenCodePromptDeliveryLedgerStore,
hashOpenCodePromptDeliveryPayload,
isOpenCodePromptDeliveryAttemptDue,
isOpenCodeSessionRefreshResponseState,
} from '@main/services/team/opencode/delivery/OpenCodePromptDeliveryLedger';
describe('OpenCodePromptDeliveryLedger', () => {
let tempDir = '';
const corruptionCases: Array<[string, (record: Record<string, unknown>) => void]> = [
[
'unknown delivery status',
(record) => {
record.status = 'quietly_broken';
},
],
[
'unknown response state',
(record) => {
record.responseState = 'assistant_maybe_replied';
},
],
[
'invalid task reference shape',
(record) => {
record.taskRefs = [{ taskId: 'task-1', displayId: '#1' }];
},
],
[
'invalid diagnostic array',
(record) => {
record.diagnostics = ['ok', 42];
},
],
[
'invalid visible reply correlation',
(record) => {
record.visibleReplyCorrelation = 'guessed_from_text';
},
],
];
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'opencode-prompt-ledger-'));
});
afterEach(async () => {
if (tempDir) {
await fs.rm(tempDir, { recursive: true, force: true });
}
});
function createStore() {
return createOpenCodePromptDeliveryLedgerStore({
filePath: path.join(tempDir, 'opencode-prompt-delivery-ledger.json'),
clock: () => new Date('2026-04-25T10:00:00.000Z'),
});
}
function ledgerPath() {
return path.join(tempDir, 'opencode-prompt-delivery-ledger.json');
}
async function writeCorruptedLedgerRecord(
mutate: (record: Record<string, unknown>) => void
): Promise<ReturnType<typeof createStore>> {
const store = createStore();
await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-corrupt',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
actionMode: 'ask',
taskRefs: [],
payloadHash: 'sha256:corrupt',
now: '2026-04-25T10:00:00.000Z',
});
const envelope = JSON.parse(await fs.readFile(ledgerPath(), 'utf8')) as {
data: Record<string, unknown>[];
};
mutate(envelope.data[0]);
await fs.writeFile(ledgerPath(), `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');
return store;
}
it('is idempotent for the same inbox message and payload hash', async () => {
const store = createStore();
const payloadHash = hashOpenCodePromptDeliveryPayload({
text: 'Please answer',
replyRecipient: 'user',
actionMode: 'ask',
source: 'watcher',
});
const first = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
actionMode: 'ask',
taskRefs: [],
payloadHash,
now: '2026-04-25T10:00:00.000Z',
});
const second = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
actionMode: 'ask',
taskRefs: [],
payloadHash,
now: '2026-04-25T10:00:30.000Z',
});
expect(second.id).toBe(first.id);
expect(second.attempts).toBe(0);
await expect(store.list()).resolves.toHaveLength(1);
});
it('upgrades legacy pending records with message kind without changing payload identity', async () => {
const store = createStore();
const payloadHash = hashOpenCodePromptDeliveryPayload({
text: 'Work sync check',
replyRecipient: 'team-lead',
actionMode: 'do',
source: 'watcher',
});
const legacy = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-work-sync',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'team-lead',
actionMode: 'do',
taskRefs: [],
payloadHash,
now: '2026-04-25T10:00:00.000Z',
});
const envelope = JSON.parse(await fs.readFile(ledgerPath(), 'utf8')) as {
data: Record<string, unknown>[];
};
delete envelope.data[0].messageKind;
await fs.writeFile(ledgerPath(), `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');
const upgraded = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-work-sync',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
messageKind: 'member_work_sync_nudge',
replyRecipient: 'team-lead',
actionMode: 'do',
taskRefs: [],
payloadHash,
now: '2026-04-25T10:00:30.000Z',
});
expect(upgraded.id).toBe(legacy.id);
expect(upgraded.messageKind).toBe('member_work_sync_nudge');
expect(upgraded.payloadHash).toBe(payloadHash);
expect(upgraded.attempts).toBe(0);
await expect(store.list()).resolves.toHaveLength(1);
});
it.each(corruptionCases)('rejects corrupted persisted records with %s', async (_name, mutate) => {
const store = await writeCorruptedLedgerRecord(mutate);
await expect(store.list()).rejects.toMatchObject({
reason: 'invalid_data',
});
await expect(fs.readdir(tempDir)).resolves.toContain('opencode-prompt-delivery-ledger.json');
expect((await fs.readdir(tempDir)).some((name) => name.includes('.invalid_data.'))).toBe(true);
});
it('marks same logical delivery with a different payload hash terminal', async () => {
const store = createStore();
const original = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:first',
now: '2026-04-25T10:00:00.000Z',
});
const mismatch = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:second',
now: '2026-04-25T10:00:30.000Z',
});
expect(mismatch.id).toBe(original.id);
expect(mismatch.status).toBe('failed_terminal');
expect(mismatch.lastReason).toBe('opencode_prompt_delivery_payload_mismatch');
expect(mismatch.diagnostics.join('\n')).toContain('payload hash does not match');
await expect(store.list()).resolves.toHaveLength(1);
});
it('keeps ack-only destination proof nonterminal and due retry checks deterministic', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:first',
now: '2026-04-25T10:00:00.000Z',
});
const ackOnly = await store.applyDestinationProof({
id: record.id,
visibleReplyInbox: 'user',
visibleReplyMessageId: 'reply-1',
visibleReplyCorrelation: 'relayOfMessageId',
semanticallySufficient: false,
observedAt: '2026-04-25T10:00:01.000Z',
});
expect(ackOnly.status).toBe('pending');
expect(ackOnly.responseState).toBe('responded_visible_message');
expect(ackOnly.lastReason).toBe('visible_reply_ack_only_still_requires_answer');
const scheduled = await store.markNextAttemptScheduled({
id: record.id,
status: 'retry_scheduled',
nextAttemptAt: '2026-04-25T10:00:30.000Z',
reason: 'visible_reply_ack_only_still_requires_answer',
scheduledAt: '2026-04-25T10:00:02.000Z',
});
expect(
isOpenCodePromptDeliveryAttemptDue(scheduled, Date.parse('2026-04-25T10:00:29.000Z'))
).toBe(false);
expect(
isOpenCodePromptDeliveryAttemptDue(scheduled, Date.parse('2026-04-25T10:00:30.000Z'))
).toBe(true);
});
it('preserves missing taskRefs as the pending reason for insufficient destination proof', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-taskrefs',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:taskrefs',
now: '2026-04-25T10:00:00.000Z',
});
const missingTaskRefs = await store.applyDestinationProof({
id: record.id,
visibleReplyInbox: 'user',
visibleReplyMessageId: 'reply-taskrefs',
visibleReplyCorrelation: 'relayOfMessageId',
semanticallySufficient: false,
diagnostics: ['visible_reply_missing_task_refs_after_merge'],
observedAt: '2026-04-25T10:00:01.000Z',
});
expect(missingTaskRefs.status).toBe('pending');
expect(missingTaskRefs.responseState).toBe('responded_visible_message');
expect(missingTaskRefs.lastReason).toBe('visible_reply_missing_task_refs');
expect(missingTaskRefs.diagnostics).toContain('visible_reply_missing_task_refs_after_merge');
});
it('records empty assistant delivery results as unanswered and stores plain text previews', async () => {
const store = createStore();
const unanswered = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-empty',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:empty',
now: '2026-04-25T10:00:00.000Z',
});
const emptyResult = await store.applyDeliveryResult({
id: unanswered.id,
accepted: true,
attempted: true,
sessionId: 'oc-session-1',
runtimePromptMessageId: 'msg_prompt_1',
responseObservation: {
state: 'empty_assistant_turn',
deliveredUserMessageId: 'oc-user-1',
assistantMessageId: 'oc-assistant-1',
toolCallNames: [],
visibleMessageToolCallId: null,
visibleReplyMessageId: null,
visibleReplyCorrelation: null,
latestAssistantPreview: null,
reason: 'empty_assistant_turn',
},
now: '2026-04-25T10:00:05.000Z',
});
expect(emptyResult.status).toBe('unanswered');
expect(emptyResult.responseState).toBe('empty_assistant_turn');
expect(emptyResult.attempts).toBe(1);
expect(emptyResult.runtimeSessionId).toBe('oc-session-1');
expect(emptyResult.runtimePromptMessageId).toBe('msg_prompt_1');
const noAssistant = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-no-assistant',
inboxTimestamp: '2026-04-25T09:59:05.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:no-assistant',
now: '2026-04-25T10:00:06.000Z',
});
const noAssistantResult = await store.applyDeliveryResult({
id: noAssistant.id,
accepted: true,
attempted: true,
responseObservation: {
state: 'prompt_delivered_no_assistant_message',
deliveredUserMessageId: 'oc-user-no-assistant',
assistantMessageId: null,
toolCallNames: [],
visibleMessageToolCallId: null,
visibleReplyMessageId: null,
visibleReplyCorrelation: null,
latestAssistantPreview: null,
reason: 'prompt_delivered_no_assistant_message',
},
now: '2026-04-25T10:00:07.000Z',
});
expect(noAssistantResult.status).toBe('unanswered');
expect(noAssistantResult.responseState).toBe('prompt_delivered_no_assistant_message');
const plain = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-plain',
inboxTimestamp: '2026-04-25T09:59:10.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:plain',
now: '2026-04-25T10:00:10.000Z',
});
const observed = await store.applyObservation({
id: plain.id,
responseObservation: {
state: 'responded_plain_text',
deliveredUserMessageId: 'oc-user-2',
assistantMessageId: 'oc-assistant-2',
toolCallNames: [],
visibleMessageToolCallId: null,
visibleReplyMessageId: null,
visibleReplyCorrelation: null,
latestAssistantPreview: 'Понял',
reason: null,
},
observedAt: '2026-04-25T10:00:15.000Z',
});
expect(observed.status).toBe('responded');
expect(observed.observedAssistantPreview).toBe('Понял');
});
it('tracks accepted runtime prompt ids without double-counting recovered command status', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-accepted',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:accepted',
now: '2026-04-25T10:00:00.000Z',
});
const firstAccepted = await store.applyDeliveryResult({
id: record.id,
accepted: true,
attempted: true,
sessionId: 'oc-session-1',
runtimePromptMessageId: 'msg_prompt_1',
deliveryAttemptId: 'attempt-1',
now: '2026-04-25T10:00:05.000Z',
});
expect(firstAccepted).toMatchObject({
status: 'accepted',
attempts: 1,
runtimePromptMessageId: 'msg_prompt_1',
lastRuntimePromptMessageId: 'msg_prompt_1',
lastDeliveryAttemptIdWithAcceptedPrompt: 'attempt-1',
});
expect(firstAccepted.runtimePromptMessageIds).toEqual(['msg_prompt_1']);
const recoveredSamePrompt = await store.applyDeliveryResult({
id: record.id,
accepted: true,
attempted: true,
sessionId: 'oc-session-1',
runtimePromptMessageId: 'msg_prompt_1',
deliveryAttemptId: 'attempt-1',
now: '2026-04-25T10:00:06.000Z',
});
expect(recoveredSamePrompt.attempts).toBe(1);
expect(recoveredSamePrompt.runtimePromptMessageIds).toEqual(['msg_prompt_1']);
const retryAccepted = await store.applyDeliveryResult({
id: record.id,
accepted: true,
attempted: true,
sessionId: 'oc-session-2',
runtimePromptMessageId: 'msg_prompt_2',
deliveryAttemptId: 'attempt-2',
now: '2026-04-25T10:01:00.000Z',
});
expect(retryAccepted.attempts).toBe(2);
expect(retryAccepted.runtimePromptMessageIds).toEqual(['msg_prompt_1', 'msg_prompt_2']);
expect(retryAccepted.lastRuntimePromptMessageId).toBe('msg_prompt_2');
});
it('tracks session refresh retries without consuming normal delivery attempts', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-session-stale',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:session-stale',
now: '2026-04-25T10:00:00.000Z',
});
expect(buildOpenCodePromptDeliveryAttemptId(record)).toBe(
`${record.id}:1:${record.payloadHash.slice(0, 12)}`
);
const stale = await store.applyDeliveryResult({
id: record.id,
accepted: false,
attempted: true,
responseObservation: {
state: 'session_stale',
deliveredUserMessageId: null,
assistantMessageId: null,
toolCallNames: [],
visibleMessageToolCallId: null,
visibleReplyMessageId: null,
visibleReplyCorrelation: null,
latestAssistantPreview: null,
reason: 'resolved_behavior_changed:old->new',
},
diagnostics: ['OpenCode session reconcile skipped because the stored session is stale'],
now: '2026-04-25T10:00:05.000Z',
});
expect(stale.attempts).toBe(0);
expect(stale.responseState).toBe('session_stale');
expect(stale.lastSessionRefreshReason).toBe('resolved_behavior_changed:old->new');
const scheduled = await store.markSessionRefreshScheduled({
id: record.id,
nextAttemptAt: '2026-04-25T10:00:10.000Z',
reason: 'resolved_behavior_changed:old->new',
scheduledAt: '2026-04-25T10:00:06.000Z',
});
expect(scheduled.status).toBe('retry_scheduled');
expect(scheduled.attempts).toBe(0);
expect(scheduled.sessionRefreshAttempts).toBe(1);
expect(buildOpenCodePromptDeliveryAttemptId(scheduled)).toBe(
`${record.id}:1:${record.payloadHash.slice(0, 12)}:refresh1`
);
expect(
isOpenCodeSessionRefreshResponseState({
reason: 'opencode_app_mcp_transport_changed:old->new',
})
).toBe(true);
});
it('keeps schema-1 legacy prompt-id fields compatible and normalizes when touched', async () => {
const store = createStore();
const legacy = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-legacy-runtime-prompt',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:legacy-runtime-prompt',
now: '2026-04-25T10:00:00.000Z',
});
const envelope = JSON.parse(await fs.readFile(ledgerPath(), 'utf8')) as {
data: Record<string, unknown>[];
};
delete envelope.data[0].runtimePromptMessageIds;
delete envelope.data[0].lastRuntimePromptMessageId;
delete envelope.data[0].lastDeliveryAttemptIdWithAcceptedPrompt;
await fs.writeFile(ledgerPath(), `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');
await expect(store.list()).resolves.toHaveLength(1);
const touched = await store.applyDeliveryResult({
id: legacy.id,
accepted: true,
attempted: true,
runtimePromptMessageId: 'msg_prompt_legacy_touch',
deliveryAttemptId: 'attempt-legacy-touch',
now: '2026-04-25T10:00:05.000Z',
});
expect(touched.runtimePromptMessageIds).toEqual(['msg_prompt_legacy_touch']);
expect(touched.lastRuntimePromptMessageId).toBe('msg_prompt_legacy_touch');
expect(touched.lastDeliveryAttemptIdWithAcceptedPrompt).toBe('attempt-legacy-touch');
});
it('accepts task stall remediation message kind across ledger validation', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'task-stall:team-a:jack:task-a',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watchdog',
messageKind: 'task_stall_remediation',
replyRecipient: 'team-lead',
actionMode: 'do',
payloadHash: 'sha256:task-stall',
now: '2026-04-25T10:00:00.000Z',
});
expect(record.messageKind).toBe('task_stall_remediation');
await expect(store.list()).resolves.toMatchObject([
{ messageKind: 'task_stall_remediation' },
]);
});
it('upgrades acceptance-unknown records when exact observation finds the prompt', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-observed-later',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:observed-later',
now: '2026-04-25T10:00:00.000Z',
});
const unknown = await store.markAcceptanceUnknown({
id: record.id,
reason: 'opencode_prompt_acceptance_unknown_after_bridge_timeout',
nextAttemptAt: '2026-04-25T10:01:00.000Z',
markedAt: '2026-04-25T10:00:45.000Z',
});
expect(unknown.acceptanceUnknown).toBe(true);
const observed = await store.applyObservation({
id: record.id,
sessionId: 'oc-session-recovered',
runtimePromptMessageId: 'msg_prompt_recovered',
responseObservation: {
state: 'pending',
deliveredUserMessageId: 'msg_prompt_recovered',
assistantMessageId: null,
toolCallNames: [],
visibleMessageToolCallId: null,
visibleReplyMessageId: null,
visibleReplyCorrelation: null,
latestAssistantPreview: null,
reason: 'assistant_response_pending',
},
observedAt: '2026-04-25T10:00:50.000Z',
});
expect(observed.status).toBe('accepted');
expect(observed.acceptanceUnknown).toBe(false);
expect(observed.acceptedAt).toBe('2026-04-25T10:00:50.000Z');
expect(observed.runtimeSessionId).toBe('oc-session-recovered');
expect(observed.runtimePromptMessageIds).toEqual(['msg_prompt_recovered']);
});
it('keeps plain-text responses active until their visible inbox reply is materialized', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-plain-visible',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
actionMode: 'ask',
taskRefs: [],
payloadHash: 'sha256:plain-visible',
now: '2026-04-25T10:00:00.000Z',
});
const responded = await store.applyDeliveryResult({
id: record.id,
accepted: true,
attempted: true,
responseObservation: {
state: 'responded_plain_text',
deliveredUserMessageId: 'oc-user-plain',
assistantMessageId: 'oc-assistant-plain',
toolCallNames: [],
visibleMessageToolCallId: null,
visibleReplyMessageId: null,
visibleReplyCorrelation: null,
latestAssistantPreview: 'Concrete visible answer.',
reason: null,
},
now: '2026-04-25T10:00:05.000Z',
});
expect(responded.status).toBe('responded');
await expect(
store.getActiveForMember({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
})
).resolves.toMatchObject({
id: record.id,
responseState: 'responded_plain_text',
});
const materialized = await store.applyDestinationProof({
id: record.id,
visibleReplyInbox: 'user',
visibleReplyMessageId: 'opencode-plain-reply-1',
visibleReplyCorrelation: 'plain_assistant_text',
semanticallySufficient: true,
observedAt: '2026-04-25T10:00:06.000Z',
});
expect(materialized).toMatchObject({
status: 'responded',
responseState: 'responded_plain_text',
visibleReplyCorrelation: 'plain_assistant_text',
});
await expect(
store.getActiveForMember({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
})
).resolves.toBeNull();
});
it('does not keep responded live deliveries active when no inbox commit is needed', async () => {
const store = createStore();
const direct = await store.ensurePending({
teamName: 'team-a',
memberName: 'bob',
laneId: 'secondary:opencode:bob',
inboxMessageId: 'direct-ui-send',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'ui-send',
replyRecipient: 'user',
actionMode: 'ask',
taskRefs: [],
payloadHash: 'sha256:direct',
now: '2026-04-25T10:00:00.000Z',
});
const responded = await store.applyDeliveryResult({
id: direct.id,
accepted: true,
attempted: true,
responseObservation: {
state: 'responded_visible_message',
deliveredUserMessageId: 'oc-user-direct',
assistantMessageId: 'oc-assistant-direct',
toolCallNames: ['agent-teams_message_send'],
visibleMessageToolCallId: 'tool-call-direct',
visibleReplyMessageId: 'reply-direct',
visibleReplyCorrelation: 'direct_child_message_send',
latestAssistantPreview: 'I will send the requested update.',
reason: null,
},
now: '2026-04-25T10:00:05.000Z',
});
expect(responded.status).toBe('responded');
expect(responded.inboxReadCommittedAt).toBeNull();
await expect(
store.getActiveForMember({
teamName: 'team-a',
memberName: 'bob',
laneId: 'secondary:opencode:bob',
})
).resolves.toBeNull();
const peer = await store.ensurePending({
teamName: 'team-a',
memberName: 'bob',
laneId: 'secondary:opencode:bob',
inboxMessageId: 'peer-relay',
inboxTimestamp: '2026-04-25T10:01:00.000Z',
source: 'manual',
replyRecipient: 'jack',
actionMode: 'delegate',
taskRefs: [],
payloadHash: 'sha256:peer',
now: '2026-04-25T10:01:00.000Z',
});
await expect(
store.getActiveForMember({
teamName: 'team-a',
memberName: 'bob',
laneId: 'secondary:opencode:bob',
})
).resolves.toMatchObject({
id: peer.id,
inboxMessageId: 'peer-relay',
});
});
it('lists due nonterminal records in deterministic due order', async () => {
const store = createStore();
const first = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:first',
now: '2026-04-25T10:00:00.000Z',
});
const second = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-2',
inboxTimestamp: '2026-04-25T09:59:10.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:second',
now: '2026-04-25T10:00:01.000Z',
});
await store.markNextAttemptScheduled({
id: first.id,
status: 'retry_scheduled',
nextAttemptAt: '2026-04-25T10:00:20.000Z',
reason: 'empty_assistant_turn',
scheduledAt: '2026-04-25T10:00:02.000Z',
});
await store.markNextAttemptScheduled({
id: second.id,
status: 'retry_scheduled',
nextAttemptAt: '2026-04-25T10:00:10.000Z',
reason: 'empty_assistant_turn',
scheduledAt: '2026-04-25T10:00:02.000Z',
});
const dueBefore = await store.listDue({
teamName: 'team-a',
now: new Date('2026-04-25T10:00:15.000Z'),
limit: 10,
});
expect(dueBefore.map((record) => record.inboxMessageId)).toEqual(['msg-2']);
const dueAfter = await store.listDue({
teamName: 'team-a',
now: new Date('2026-04-25T10:00:21.000Z'),
limit: 10,
});
expect(dueAfter.map((record) => record.inboxMessageId)).toEqual(['msg-2', 'msg-1']);
});
it('rebuilds missing ledger rows as acceptance-unknown retryable records', async () => {
const store = createStore();
const record = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'msg-1',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watchdog',
replyRecipient: 'user',
payloadHash: 'sha256:first',
now: '2026-04-25T10:00:00.000Z',
});
const rebuilt = await store.markAcceptanceUnknown({
id: record.id,
reason: 'opencode_prompt_delivery_ledger_rebuilt_from_unread_inbox',
nextAttemptAt: '2026-04-25T10:00:00.000Z',
markedAt: '2026-04-25T10:00:00.000Z',
});
expect(rebuilt.status).toBe('failed_retryable');
expect(rebuilt.acceptanceUnknown).toBe(true);
expect(rebuilt.responseState).toBe('not_observed');
expect(rebuilt.lastReason).toBe('opencode_prompt_delivery_ledger_rebuilt_from_unread_inbox');
});
it('prunes only terminal records after their retention windows', async () => {
const store = createStore();
const responded = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'responded',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:responded',
now: '2026-04-25T10:00:00.000Z',
});
await store.applyDestinationProof({
id: responded.id,
visibleReplyInbox: 'user',
visibleReplyMessageId: 'reply-1',
visibleReplyCorrelation: 'relayOfMessageId',
semanticallySufficient: true,
observedAt: '2026-04-25T10:00:01.000Z',
});
await store.markInboxReadCommitted({
id: responded.id,
committedAt: '2026-04-25T10:00:02.000Z',
});
const failed = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'failed',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:failed',
now: '2026-04-25T10:00:00.000Z',
});
await store.markFailedTerminal({
id: failed.id,
reason: 'opencode_runtime_not_active',
failedAt: '2026-04-25T10:00:03.000Z',
});
const active = await store.ensurePending({
teamName: 'team-a',
memberName: 'jack',
laneId: 'secondary:opencode:jack',
inboxMessageId: 'active',
inboxTimestamp: '2026-04-25T09:59:00.000Z',
source: 'watcher',
replyRecipient: 'user',
payloadHash: 'sha256:active',
now: '2026-04-25T10:00:00.000Z',
});
await expect(
store.pruneTerminalRecords({
now: new Date('2026-04-25T10:00:20.000Z'),
respondedRetentionMs: 10_000,
failedRetentionMs: 30_000,
})
).resolves.toEqual({ pruned: 1, remaining: 2 });
expect((await store.list()).map((record) => record.inboxMessageId).sort()).toEqual([
active.inboxMessageId,
failed.inboxMessageId,
]);
await expect(
store.pruneTerminalRecords({
now: new Date('2026-04-25T10:00:40.000Z'),
respondedRetentionMs: 10_000,
failedRetentionMs: 30_000,
})
).resolves.toEqual({ pruned: 1, remaining: 1 });
expect((await store.list()).map((record) => record.inboxMessageId)).toEqual([
active.inboxMessageId,
]);
});
});