merge: integrate origin/main (406 commits) into live branch

Merge upstream's paired-room stabilization, dedicated message-runtime-loop,
outbound delivery refactor (ipc-outbound-delivery / deliverFormattedCanonicalMessage),
discord module split, dashboard rework, and migrations 015-018 into our branch.

Conflict policy: prefer upstream for the heavily-refactored paired-room /
message-runtime / db cluster (it supersedes our earlier loop band-aids), keep
only orthogonal unique fixes:
- codex bundled-JS launcher fix (codex-warmup.ts)
- slot-aligned Claude+Codex usage primer (usage-primer.ts) re-wired into index.ts
- MemoryHigh=3G cgroup bound, discord perms diagnostic, prompt docs
- progress null-race + silent-failure publish guards (message-turn-controller.ts)

Dropped (superseded by upstream or unwired): reviewer STEP_DONE/TASK_DONE loop
band-aids, router markdown-escape override, register tribunal-default+git-init,
restart-context rewindToSeq (unwired).

Verified: typecheck clean, build clean, vitest shows zero new regressions vs the
origin/main baseline (only pre-existing env-config failures remain).
This commit is contained in:
Codex
2026-06-08 21:15:39 +09:00
409 changed files with 53523 additions and 10332 deletions

View File

@@ -829,3 +829,53 @@ describe('MessageTurnController outbound audit logging', () => {
});
});
});
describe('MessageTurnController human interruptions', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('does not replay owner progress as final when a human message interrupted the turn', async () => {
const channel = { ...makeChannel(), name: 'discord' } satisfies Channel;
const deliverFinalText = vi.fn().mockResolvedValue(true);
const controller = new MessageTurnController({
chatJid: 'dc:test-room',
group: makeGroup(),
runId: 'run-owner-human-interrupted',
channel,
idleTimeout: 1_000,
failureFinalText: '실패',
isClaudeCodeAgent: true,
clearSession: vi.fn(),
requestClose: vi.fn(),
deliverFinalText,
getCloseReason: () => 'human-message-detected',
deliveryRole: 'owner',
pairedTurnIdentity: {
turnId: 'task-1:2026-04-10T14:22:00.000Z:owner-turn',
taskId: 'task-1',
taskUpdatedAt: '2026-04-10T14:22:00.000Z',
intentKind: 'owner-turn',
role: 'owner',
},
});
await controller.start();
await controller.handleOutput({
status: 'success',
phase: 'progress',
result: '아비터 판단을 내리겠습니다.',
} as any);
await controller.handleOutput({
status: 'success',
phase: 'progress',
result: '추가 확인 중입니다.',
} as any);
await flushAsync();
await controller.finish('success');
expect(channel.sendAndTrack).toHaveBeenCalledTimes(1);
expect(deliverFinalText).not.toHaveBeenCalled();
});
});