@linzumi/cli 1.0.265 → 1.0.267

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "1.0.265",
3
+ "version": "1.0.267",
4
4
  "description": "Linzumi CLI \u2014 point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -86,6 +86,9 @@ import {
86
86
  recoverRetainedTerminalDebt,
87
87
  } from './src/channelSession';
88
88
  import {adoptAdtStreamBinding, installAdtPublicationBinding} from './src/pipeline/contracts';
89
+ import {createThreadOutbox} from './src/pipeline/outbox';
90
+ import {enqueueTerminalDebtRepair} from './src/pipeline/integration';
91
+ import {createHash} from 'node:crypto';
89
92
 
90
93
  // --- Placeholder + snapshot plumbing --------------------------------------------
91
94
 
@@ -534,9 +537,59 @@ async function handleRespawn(testCase) {
534
537
 
535
538
  // Spec: spec/commander-recovery-rules.md rule01 and rule05; retained terminal
536
539
  // debt is not an unknown provider-start ACK and cannot reset unrelated jobs.
540
+ async function terminalDebtJournal(testCase) {
541
+ const control = testCase.control;
542
+ const logs = [];
543
+ const body = 'Synthetic completed answer.';
544
+ const outbox = createThreadOutbox({threadKey:'synthetic-terminal-debt', persistDir:scratch(),
545
+ push:async()=>{throw new Error('pending repair must not publish or resend provider work');},
546
+ builders:{'adt.terminal_attempt':{build:()=>{throw new Error('synthetic retained failure');}}},
547
+ retryDelaysMs:[], onPermanentFailure:()=>{}, log:(event,fields)=>logs.push({event,fields}),
548
+ });
549
+ try {
550
+ outbox.enqueue({kind:'adt.terminal_attempt',resolution:'must_ack',data:{seq:44,status:'processed',
551
+ turn_id:'synthetic-completed-turn',terminal_frame:{session_id:3600,lease_epoch:2,frame_index:40,
552
+ frame_kind:'terminal',assistant_phase:'final_answer',source_stream_key:'synthetic-rejected-key',body}}});
553
+ await outbox.flush(1000);
554
+ outbox.pause();
555
+ await outbox.journalSettled();
556
+ const gate = Promise.withResolvers();
557
+ const entered = Promise.withResolvers();
558
+ let settled = false;
559
+ let authority = true;
560
+ const barrierOutbox = {...outbox,
561
+ journalSettled:async()=>{entered.resolve();await gate.promise;await outbox.journalSettled();settled=true;},
562
+ terminalPublicationDebt:()=>!settled || control==='write-failure'
563
+ ? {kind:'unavailable',reason:'controlled journal not durable'} : outbox.terminalPublicationDebt(),
564
+ };
565
+ const repair = enqueueTerminalDebtRepair(barrierOutbox,{threadId:'synthetic-terminal-debt',sourceSeq:44,
566
+ sessionId:3600,originalLeaseEpoch:2,authoritativeTerminal:false,authorize:()=>authority,
567
+ binding:{session_id:3600,lease_epoch:4,next_frame_index:39,
568
+ checkpoint_config:{version:3,thresholds:{max_bytes:512,max_deltas:1,max_interval_ms:2000}},
569
+ final_checkpoint:{frame_index:38,source_stream_key:'synthetic-canonical-key',
570
+ body_sha256:createHash('sha256').update(body).digest('hex'),byte_len:Buffer.byteLength(body)}}},
571
+ (event,fields)=>logs.push({event,fields}));
572
+ const first = await Promise.race([entered.promise.then(()=>'waiting'),repair.then(()=>'finished')]);
573
+ const before = outbox.pendingTerminalEntries().length;
574
+ authority = control !== 'superseded';
575
+ gate.resolve();
576
+ const result = await repair;
577
+ return {first,before,count:outbox.pendingTerminalEntries().length,ready:result.kind==='ready',result,logs};
578
+ } finally {await outbox.close(0);}
579
+ }
580
+
537
581
  async function terminalDebtStorm(testCase) {
538
- const binding = {session_id: 3600, lease_epoch: 1, next_frame_index: 39,
582
+ const ackLease = testCase.control === 'newer-ack-lease' ? 2 : testCase.control === 'future-ack' ? 4 : 1;
583
+ const binding = {session_id: 3600, lease_epoch: ackLease, next_frame_index: 39,
539
584
  checkpoint_config: {version: 3, thresholds: {max_bytes: 1, max_deltas: 1, max_interval_ms: 2000}}};
585
+ // Spec: commander-recovery-rules authenticated-terminal-evidence. The
586
+ // retained frame and original ACK belong to one attempt across lease churn.
587
+ const retainedLease = testCase.control === 'newer-retained-lease' ? 2 : 1;
588
+ const body = 'Synthetic completed answer.';
589
+ const reconciledBinding = {...binding, lease_epoch: 3, final_checkpoint: {
590
+ frame_index: 38, source_stream_key: 'synthetic-canonical-key',
591
+ body_sha256: createHash('sha256').update(body).digest('hex'), byte_len: Buffer.byteLength(body),
592
+ }};
540
593
  const adopted = adoptAdtStreamBinding(binding, 44, 0);
541
594
  if (adopted.kind !== 'accepted') throw new Error('terminal debt fixture binding refused');
542
595
  const session = adopted.session;
@@ -544,6 +597,26 @@ async function terminalDebtStorm(testCase) {
544
597
  let resets = 0;
545
598
  let restarts = 0;
546
599
  let reconciliations = 0;
600
+ let repairs = 0;
601
+ const outbox = createThreadOutbox({threadKey:'synthetic-terminal-debt',
602
+ persistDir:scratch(),
603
+ push:async()=>{throw new Error('failed builder must not publish');},
604
+ builders:{'adt.terminal_attempt':{build:()=>{throw new Error('synthetic final rejection');}}},
605
+ retryDelaysMs:[], onPermanentFailure:()=>{}, log:(event,fields)=>logs.push({event,fields}),
606
+ });
607
+ try {
608
+ outbox.enqueue({kind:'adt.terminal_attempt',resolution:'must_ack',data:{seq:44,status:'processed',
609
+ turn_id:'synthetic-completed-turn',terminal_frame:{session_id:3600,lease_epoch:retainedLease,
610
+ frame_index:40,frame_kind:'terminal',assistant_phase:'final_answer',source_stream_key:'synthetic-rejected-key',body}}});
611
+ await outbox.flush(1000);
612
+ outbox.pause();
613
+ // Queue drain completion is distinct from durable journal settlement.
614
+ await outbox.journalSettled();
615
+ const durableDebt = outbox.terminalPublicationDebt();
616
+ if (typeof durableDebt !== 'object' || durableDebt.kind !== 'repair_needed' || durableDebt.count !== 1) {
617
+ throw new Error('warm repair fixture must have one durable failed terminal: ' + JSON.stringify(durableDebt));
618
+ }
619
+ const original = JSON.stringify(outbox.failedTerminalEntries());
547
620
  const transport = {
548
621
  push: async (_topic, event) => {
549
622
  if (event === 'adt_binding_ack') return {status: 'ok', response: {ok: true}};
@@ -551,7 +624,7 @@ async function terminalDebtStorm(testCase) {
551
624
  reconciliations += 1;
552
625
  if(testCase.control === 'repair-unavailable' && reconciliations > 1) return {status:'ok',response:{ok:true,adt:{verdict:'still_unavailable'}}};
553
626
  return {status: 'ok', response: {
554
- ok: true, adt: {verdict: 'already_applied', binding: testCase.control === 'malformed-binding' ? {session_id: 3600} : binding},
627
+ ok: true, adt: {verdict: 'already_applied', binding: testCase.control === 'malformed-binding' ? {session_id: 3600} : reconciledBinding},
555
628
  }};
556
629
  }
557
630
  throw new Error('unexpected terminal-debt fixture event: ' + event);
@@ -560,7 +633,7 @@ async function terminalDebtStorm(testCase) {
560
633
  };
561
634
  const candidate = registerRequiredAdtCandidate({transport,
562
635
  identity: {thread_id: 'synthetic-terminal-debt', source_seq: 44, session_id: 3600,
563
- lease_epoch: 1, checkpoint_config_version: 3},
636
+ lease_epoch: ackLease, checkpoint_config_version: 3},
564
637
  installFreshBinding: raw => {
565
638
  const fresh = adoptAdtStreamBinding(raw, 44, 0);
566
639
  if (fresh.kind !== 'accepted') return false;
@@ -569,17 +642,23 @@ async function terminalDebtStorm(testCase) {
569
642
  },
570
643
  publicationSession: () => testCase.localState === 'idle' ? undefined : session,
571
644
  failedTerminalEntries: () => {
572
- const entries = [{entrySeq: 61, kind: 'adt.terminal_attempt',
573
- data: {seq: 44, status: 'processed', turn_id: 'synthetic-completed-turn',
574
- terminal_frame: {session_id: testCase.control === 'foreign-debt' ? 3601 : 3600, lease_epoch: 1, frame_index: 40,
575
- frame_kind: 'terminal', assistant_phase: 'final_answer',
576
- source_stream_key: 'synthetic-rejected-key', body: 'Synthetic completed answer.'}}}];
645
+ const entries = outbox.failedTerminalEntries().map(entry => ({...entry,data:{...entry.data,
646
+ ...(testCase.control === 'malformed-repair-presence' ? {terminal_repair:undefined} : {}),
647
+ terminal_frame:{...entry.data.terminal_frame,session_id:testCase.control === 'foreign-debt' ? 3601 : 3600,
648
+ lease_epoch:testCase.control === 'future-debt' ? 4 : testCase.control === 'fractional-debt' ? 1.5 : retainedLease}}}));
577
649
  if (testCase.control === 'missing-debt') return [];
578
650
  if (testCase.control === 'ambiguous-debt') return [...entries, {...entries[0], entrySeq: 62}];
579
651
  return entries;
580
652
  },
653
+ repairTerminalDebt:async (fresh,authoritativeTerminal,authorize,evidenceEpoch)=>{
654
+ if(evidenceEpoch !== retainedLease) throw new Error('repair callback received ACK lease instead of retained evidence');
655
+ const result=await enqueueTerminalDebtRepair(outbox,{threadId:'synthetic-terminal-debt',sourceSeq:44,
656
+ sessionId:3600,originalLeaseEpoch:evidenceEpoch,binding:fresh,authoritativeTerminal,authorize},()=>{});
657
+ if(result.kind!=='ready') throw new Error('actual repair selector refused: '+result.message);
658
+ repairs += 1;
659
+ },
581
660
  onUnrecoverable: () => {restarts += 1;},
582
- log: event => logs.push(event),
661
+ log: (event,fields) => logs.push({event,fields}),
583
662
  });
584
663
  try {
585
664
  await acknowledgeRequiredAdtCandidate({topic: 'local_runner:fixture', payload: adopted.ackPayload, ticket: candidate});
@@ -590,12 +669,16 @@ async function terminalDebtStorm(testCase) {
590
669
  closeRequiredAdtAdmissionForDisconnect(transport);
591
670
  await reconcileRequiredAdtCandidates({transport, topic: 'local_runner:fixture'});
592
671
  }
593
- return {resets, restarts, retained: findRequiredAdtCandidate(transport,
672
+ return {resets, restarts, repairs, originalUnchanged:JSON.stringify(outbox.failedTerminalEntries())===original,
673
+ corrections:outbox.pendingTerminalEntries().map(entry=>({originalLease:entry.data.terminal_repair?.original_lease_epoch,
674
+ lease:entry.data.terminal_frame?.lease_epoch,body:entry.data.terminal_frame?.body})),
675
+ retained: findRequiredAdtCandidate(transport,
594
676
  identity => identity.thread_id === 'synthetic-terminal-debt' && identity.source_seq === 44) === candidate,
595
677
  providerEffect: candidate.providerEffect, verdict: candidate.verdict,
596
678
  nextProviderAdmission: consumeRequiredAdtAdmission(candidate).kind,
597
679
  admission: requiredTransportFinalAdmission(transport).kind, logs};
598
680
  } finally {releaseRequiredAdtCandidate(candidate);}
681
+ } finally {await outbox.close(0);}
599
682
  }
600
683
 
601
684
  async function handle(testCase) {
@@ -603,24 +686,33 @@ async function handle(testCase) {
603
686
  case 'terminalDebtCold': {
604
687
  const events = [];
605
688
  let repairs = 0;
606
- const identity = {thread_id:'synthetic-terminal-debt',source_seq:44,session_id:3600,lease_epoch:1,checkpoint_config_version:7};
607
- const binding = {session_id:3600,lease_epoch:2,next_frame_index:39,checkpoint_config:{version:8,thresholds:{max_bytes:512,max_deltas:1,max_interval_ms:2000}}};
689
+ const retainedEpoch = ['ack-before-frame','binding-before-frame'].includes(testCase.control) ? 2 : 1;
690
+ const ackEpoch = ['ack-after-frame','binding-before-ack'].includes(testCase.control) ? 2 : 1;
691
+ const freshEpoch = testCase.control.startsWith('binding-before-') ? 1 : testCase.control.startsWith('ack-') ? 4 : 2;
692
+ const hasAnchor = testCase.control !== 'missing-anchor';
693
+ const ackVersion = ['ready','authoritative-terminal','missing-anchor'].includes(testCase.control) ? 7 : 9;
694
+ const identity = {thread_id:'synthetic-terminal-debt',source_seq:44,session_id:3600,lease_epoch:retainedEpoch,checkpoint_config_version:7};
695
+ const anchor = testCase.control === 'malformed-anchor' ? null : {...identity,
696
+ thread_id:testCase.control === 'foreign-anchor' ? 'foreign-thread' : identity.thread_id,
697
+ lease_epoch:testCase.control === 'fractional-anchor' ? 1.5 : ackEpoch,checkpoint_config_version:ackVersion};
698
+ const binding = {session_id:3600,lease_epoch:freshEpoch,next_frame_index:39,checkpoint_config:{version:8,thresholds:{max_bytes:512,max_deltas:1,max_interval_ms:2000}}};
608
699
  const transport = {push:async (_topic,event,payload) => {
609
700
  events.push(event);
610
701
  if(event === 'terminal_debt_identity') {
611
702
  if(testCase.control === 'disconnect') closeRequiredAdtAdmissionForDisconnect(transport);
612
- return {status:'ok',response:{ok:true,identity:{...identity,...(testCase.control === 'foreign' ? {thread_id:'foreign-thread'} : {})}}};
703
+ return {status:'ok',response:{ok:true,identity:{...identity,...(hasAnchor ? {ack_identity:anchor} : {}),...(testCase.control === 'foreign' ? {thread_id:'foreign-thread'} : {})}}};
613
704
  }
614
705
  if(event === 'adt_binding_reconcile') {
615
- if(payload.checkpoint_config_version !== 7 || payload.lease_epoch !== 1) throw new Error('cold lookup changed original generation');
616
- return {status:'ok',response:{ok:true,adt:{verdict:'already_applied',binding}}};
706
+ if(payload.checkpoint_config_version !== ackVersion || payload.lease_epoch !== ackEpoch) throw new Error('cold lookup changed durable ACK identity');
707
+ return {status:'ok',response:{ok:true,adt:{verdict:testCase.control === 'authoritative-terminal' ? 'authoritative_terminal' : testCase.control === 'missing-anchor' ? 'applied' : 'already_applied',binding}}};
617
708
  }
618
709
  throw new Error('cold recovery attempted unexpected event: '+event);
619
710
  }};
620
711
  const pipeline = {
621
- failedTerminalEntries:()=>[{entrySeq:61,kind:'adt.terminal_attempt',data:{seq:44,status:'processed',turn_id:'synthetic-completed-turn',terminal_frame:{session_id:3600,lease_epoch:1,frame_index:40,frame_kind:'terminal',assistant_phase:'final_answer',source_stream_key:'wrong-key',body:'Synthetic completed answer.'}}}],
712
+ failedTerminalEntries:()=>[{entrySeq:61,kind:'adt.terminal_attempt',data:{seq:44,status:'processed',turn_id:'synthetic-completed-turn',terminal_frame:{session_id:3600,lease_epoch:retainedEpoch,frame_index:40,frame_kind:'terminal',assistant_phase:'final_answer',source_stream_key:'wrong-key',body:'Synthetic completed answer.'}}}],
622
713
  repairTerminalDebt:async request => {
623
- if(request.sessionId !== 3600 || request.originalLeaseEpoch !== 1 || request.sourceSeq !== 44 || request.binding !== binding || request.authoritativeTerminal) throw new Error('cold repair identity mismatch');
714
+ const terminal = testCase.control === 'authoritative-terminal';
715
+ if(request.sessionId !== 3600 || request.originalLeaseEpoch !== retainedEpoch || request.sourceSeq !== 44 || request.binding !== (terminal ? null : binding) || request.authoritativeTerminal !== terminal) throw new Error('cold repair identity mismatch');
624
716
  if(!request.authorize({seq:44})) throw new Error('cold repair unexpectedly unauthorized');
625
717
  repairs += 1;
626
718
  return {kind:'ready',id:'fixture',serializedData:'fixture'};
@@ -636,6 +728,8 @@ async function handle(testCase) {
636
728
  }
637
729
  case 'terminalDebtStorm':
638
730
  return await terminalDebtStorm(testCase);
731
+ case 'terminalDebtJournal':
732
+ return await terminalDebtJournal(testCase);
639
733
  case 'controlDispatch':
640
734
  return await handleControlDispatch(testCase);
641
735
  case 'staleness':