@quantiya/codevibe-codex-plugin 2.0.63 → 2.0.65

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.
Files changed (59) hide show
  1. package/dist/server.js +38 -36
  2. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +302 -8
  3. package/node_modules/@quantiya/codevibe-core/dist/appsync/index.d.ts +25 -1
  4. package/node_modules/@quantiya/codevibe-core/dist/appsync/queries.d.ts +10 -0
  5. package/node_modules/@quantiya/codevibe-core/dist/appsync/standalone-companion-status.d.ts +6 -0
  6. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-deferred-inputs.d.ts +104 -0
  7. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-desktop-registration.d.ts +133 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-event-client.d.ts +110 -0
  9. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-inbound-journal.d.ts +142 -0
  10. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-input-delivery.d.ts +13 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-live-connection.d.ts +87 -0
  12. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-mobile-echo.d.ts +289 -0
  13. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-operation-journal.d.ts +236 -0
  14. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-prompt-publication.d.ts +153 -0
  15. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-prompt-resolution.d.ts +38 -0
  16. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-prompt-retention.d.ts +99 -0
  17. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-read-client.d.ts +131 -0
  18. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-resolution-recovery.d.ts +29 -0
  19. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-security-posture.d.ts +32 -0
  20. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-session-retirement.d.ts +30 -0
  21. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-shell-source.d.ts +151 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-source-cursor.d.ts +529 -0
  23. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-source-file.d.ts +33 -0
  24. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-source-pair.d.ts +33 -0
  25. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-state.d.ts +51 -0
  26. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-usage-warning.d.ts +33 -0
  27. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-workspace-outcome.d.ts +23 -0
  28. package/node_modules/@quantiya/codevibe-core/dist/config/config.d.ts +3 -0
  29. package/node_modules/@quantiya/codevibe-core/dist/credential-broker/oauth-account-lock.d.ts +4 -0
  30. package/node_modules/@quantiya/codevibe-core/dist/index.d.ts +26 -0
  31. package/node_modules/@quantiya/codevibe-core/dist/index.js +584 -537
  32. package/node_modules/@quantiya/codevibe-core/dist/local-executor/local-executor-impl.d.ts +11 -11
  33. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +9 -4
  34. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +50680 -44643
  35. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/OrchestrationApp.d.ts +6 -0
  36. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/StatusBar.d.ts +2 -0
  37. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/decision-attempt-recovery.d.ts +2 -0
  38. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/emit-shell-event.d.ts +31 -8
  39. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/gate-prompts.d.ts +4 -0
  40. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +8 -7
  41. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/planner-offer-e1.d.ts +5 -1
  42. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +9 -2
  43. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/shell-sync-recovery.d.ts +23 -0
  44. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/slash-routes/reviewers.d.ts +3 -1
  45. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +8 -8
  46. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/workspace-terminal-coordinator.d.ts +1 -1
  47. package/node_modules/@quantiya/codevibe-core/dist/planner/types.d.ts +2 -2
  48. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/atomic-file.d.ts +13 -3
  49. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/consolidator.d.ts +10 -2
  50. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/index.d.ts +1 -0
  51. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/ledger.d.ts +17 -9
  52. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/outbox.d.ts +20 -2
  53. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/replay-cursor.d.ts +2 -2
  54. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/sync-transport.d.ts +53 -0
  55. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/types.d.ts +28 -24
  56. package/node_modules/@quantiya/codevibe-core/dist/types/events.d.ts +3 -0
  57. package/node_modules/@quantiya/codevibe-core/dist/types/reviewer.d.ts +14 -4
  58. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  59. package/package.json +2 -2
@@ -4,6 +4,8 @@ import { ConversationEntry, PostDecisionAction, Tier } from '../types';
4
4
  import type { PlannerRuntimeKind } from '../status-format';
5
5
  import type { UpdateReviewerPolicyInput, UserReviewerPolicySnapshot } from '../../types/reviewer';
6
6
  import { type ApplyUserDecisionClient as SharedApplyUserDecisionClient, type GroupDecisionSubmitDeps } from '../gate-decision-submit';
7
+ import { type CompanionSyncStateSource } from '../../appsync/sync-state';
8
+ import { type CompanionSyncWarningStore } from '../../appsync/sync-usage-warning';
7
9
  /** P46b §3.7 — at most this many per-task progress lines render (`+n more` after). */
8
10
  export declare const MAX_TASK_PROGRESS_LINES = 3;
9
11
  /**
@@ -70,6 +72,10 @@ export interface OrchestrationAppProps {
70
72
  * runtime.
71
73
  */
72
74
  appsyncClient: ApplyUserDecisionClient;
75
+ /** Display-only cloud status feed; production shell always wires its client. */
76
+ companionSyncStateSource?: CompanionSyncStateSource;
77
+ /** Local-only once-per-period advisory; never inserted into conversation sync. */
78
+ companionSyncWarningStore?: CompanionSyncWarningStore;
73
79
  /**
74
80
  * Session-key resolver invoked just-in-time when a gate-prompt submit
75
81
  * includes notes. Returns the base64 session key for the caller's
@@ -14,6 +14,8 @@ export interface StatusBarProps {
14
14
  plannerRuntimeKind: PlannerRuntimeKind;
15
15
  /** Display label for the local model (e.g. `gemma4`). */
16
16
  plannerLabel: string;
17
+ /** Closed display label from validated cloud status, never a local gate. */
18
+ companionSyncBadge?: string | null;
17
19
  /** Test seam — overrides the terminal width used for cwd truncation. */
18
20
  columns?: number;
19
21
  }
@@ -10,6 +10,7 @@ export interface DecisionAttemptRecoveryClient {
10
10
  getGateResolution(input: {
11
11
  sessionId: string;
12
12
  promptId: string;
13
+ sessionGenerationId?: string;
13
14
  }): Promise<unknown>;
14
15
  submitGateDecisionV2?(request: Extract<DecisionAttemptReceipt['request'], {
15
16
  promptKind: 'GATE';
@@ -63,6 +64,7 @@ export declare function createDurableResolutionAttemptHandoff(args: {
63
64
  */
64
65
  export declare function recoverDecisionAttemptReceipts(args: {
65
66
  sessionId: string;
67
+ sessionGenerationId?: string;
66
68
  client: DecisionAttemptRecoveryClient;
67
69
  store?: DecisionAttemptReceiptStore;
68
70
  onResolutionEvent: (event: Event) => Promise<DurableGateResolution | null> | DurableGateResolution | null;
@@ -1,6 +1,9 @@
1
1
  import { AppSyncClient } from '../appsync';
2
2
  import { CreateEventInput, type Session } from '../types';
3
3
  import type { E1ProducerKind } from '../e1';
4
+ import type { PromptPublicationJournal } from '../appsync/sync-prompt-publication';
5
+ import type { ShellSyncSource } from '../appsync/sync-shell-source';
6
+ import type { ToolActivitySyncEncryption } from '../tool-activity/sync-transport';
4
7
  /**
5
8
  * §3 LOCK lines 173-181. Names + per-type metadata shapes are part of
6
9
  * the Stage A contract. CP-1.a ships the union; CP-1.b/c/d add call
@@ -53,6 +56,8 @@ export interface ShellEventEmit {
53
56
  promptId?: string;
54
57
  producerKind?: E1ProducerKind;
55
58
  mobileActionable?: boolean;
59
+ /** Local-only identity of one durably recorded canonical prompt absence. */
60
+ syncReRaiseToken?: string;
56
61
  resolvedPromptId?: string;
57
62
  resolutionAt?: string;
58
63
  }
@@ -65,6 +70,13 @@ export interface EmitShellEventDeps {
65
70
  appsyncClient: Pick<AppSyncClient, 'createEvent'>;
66
71
  getSessionKey: (sessionId: string) => Promise<string | null>;
67
72
  session?: Session;
73
+ /** Cloud-selected source path. Prompt raises use their own durable episode,
74
+ * not ordinary append semantics; resolution/outcome writers remain separate. */
75
+ sync?: {
76
+ source: ShellSyncSource;
77
+ context: ToolActivitySyncEncryption;
78
+ promptPublication?: PromptPublicationJournal;
79
+ };
68
80
  /**
69
81
  * Persist an exact encrypted/signed input without sending it. Reserved for
70
82
  * crash-safe workspace-outcome drafts that must remain undiscoverable until
@@ -73,6 +85,16 @@ export interface EmitShellEventDeps {
73
85
  prepareOnly?: (input: CreateEventInput) => Promise<void>;
74
86
  }
75
87
  export type ShellEventEmitResult = {
88
+ emitted: true;
89
+ controlClass: 'PROMPT_OWNERSHIP';
90
+ promptId: string;
91
+ state: 'RESOLVED';
92
+ sessionId: string;
93
+ type: 'NOTIFICATION';
94
+ source: 'DESKTOP';
95
+ eventId?: never;
96
+ writer?: never;
97
+ } | {
76
98
  emitted: true;
77
99
  eventId: string;
78
100
  sessionId: string;
@@ -86,7 +108,7 @@ export type ShellEventEmitResult = {
86
108
  };
87
109
  } | {
88
110
  emitted: false;
89
- reason: 'missing_session_key' | 'auth_required' | 'auth_unavailable' | 'create_failed' | 'definitive_rejection' | 'invalid_response' | 'prepared_only' | 'writer_ineligible';
111
+ reason: 'missing_session_key' | 'auth_required' | 'auth_unavailable' | 'create_failed' | 'definitive_rejection' | 'invalid_response' | 'prepared_only' | 'sync_quota_exhausted' | 'sync_already_settled' | 'sync_pending' | 'sync_unavailable' | 'writer_ineligible';
90
112
  error?: unknown;
91
113
  };
92
114
  /**
@@ -103,10 +125,11 @@ export type ShellEventEmitResult = {
103
125
  * codevibe-core (each plugin instantiates its own `AppSyncClient`).
104
126
  */
105
127
  export declare function emitShellEvent(args: ShellEventEmit, deps: EmitShellEventDeps): Promise<ShellEventEmitResult>;
106
- /**
107
- * Convenience helper for production callers — binds default
108
- * dependencies (`keychainManager` for session-key lookup) and accepts
109
- * the AppSync client separately because there's no global default in
110
- * `codevibe-core`.
111
- */
112
- export declare function createShellEventEmitter(appsyncClient: Pick<AppSyncClient, 'createEvent'>, session?: Session): (args: ShellEventEmit) => Promise<ShellEventEmitResult>;
128
+ /** Sync quota/delivery is not authority to disable the local desktop. These
129
+ * results are explicit, never masquerading as a successful cloud Event. */
130
+ export declare function isNonBlockingShellSyncResult(result: ShellEventEmitResult): boolean;
131
+ /** Join every shell/CLI/team emitter sharing this exact client + session before
132
+ * terminal source cleanup. Old closures retain their closed owner; other
133
+ * sessions and clients are untouched. No timer or process is created here. */
134
+ export declare function closeShellEventEmitters(client: object, sessionId: string): Promise<void>;
135
+ export declare function createShellEventEmitter(appsyncClient: Pick<AppSyncClient, 'createEvent' | 'selectCompanionSourceTransport' | 'recoverShellSyncSources' | 'resolveCoreOfferPrompt'>, session?: Session): (args: ShellEventEmit) => Promise<ShellEventEmitResult>;
@@ -452,6 +452,8 @@ export interface ReviewSummaryTerminalModel {
452
452
  }>;
453
453
  readonly roundCount: number;
454
454
  readonly revisionCount: number;
455
+ readonly approvalRule?: string;
456
+ readonly autoRevisionLimit?: number;
455
457
  };
456
458
  readonly mergeGate: {
457
459
  readonly applicable: boolean;
@@ -530,6 +532,8 @@ export interface DecryptedReviewSummaryInput {
530
532
  }>;
531
533
  readonly roundCount?: number;
532
534
  readonly revisionCount?: number;
535
+ readonly approvalRule?: string;
536
+ readonly autoRevisionLimit?: number;
533
537
  };
534
538
  readonly mergeGate?: {
535
539
  readonly applicable?: boolean;
@@ -1,5 +1,6 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import { AppSyncClient } from '../appsync';
3
+ import type { MobileInputStatusUpdater } from '../appsync';
3
4
  import { Session } from '../types';
4
5
  import { OrchestrationStore } from './store';
5
6
  import { type RetainedBrowsePage } from './route-browse';
@@ -33,7 +34,7 @@ import { type RunFlagCommandResult } from './flag-command';
33
34
  import { Tier } from './types';
34
35
  import type { ConversationEntry, DurableGateResolution, PendingApplyConflict, PendingClarification } from './types';
35
36
  import { type GatePromptEnvelope, type DecryptedReviewSummaryInput } from './gate-prompts';
36
- import type { Event, UpdateEventStatusInput } from '../types/events';
37
+ import type { Event } from '../types/events';
37
38
  import { type StructuralSummaryGenerator } from '../structural-summary';
38
39
  import type { PlannerAdapter } from '../planner/adapter';
39
40
  import type { PlannerCacheLayer } from '../planner/cache';
@@ -491,10 +492,10 @@ export declare function handleSubscribedEvent(event: Event, store: Orchestration
491
492
  * The mobile `USER_PROMPT` content is E2E-encrypted (`isEncrypted:true`); decrypt it
492
493
  * with the session key (the SAME resolver the non-gate prompt path uses) before
493
494
  * submitting. Fail-soft: a missing resolver/key, a decrypt failure, or empty text logs
494
- * and DROPS (never crashes the shell, never submits ciphertext). Async — the sync
495
- * subscription callback fires-and-forgets via `void`; every throw is caught here.
495
+ * and drops (never submits ciphertext). Legacy routing is fire-and-forget.
496
+ * R2 routing is awaited by the durable inbox; pre-handoff failure stays visible.
496
497
  */
497
- export declare function routeMobileUserPrompt(event: Event, sessionKeyResolver: ((sessionId: string) => Promise<string | null>) | undefined, submit: (text: string, images?: string[], options?: ShellSubmitOptions) => Promise<void>, updateEventStatus?: (input: UpdateEventStatusInput) => Promise<unknown>): Promise<boolean>;
498
+ export declare function routeMobileUserPrompt(event: Event, sessionKeyResolver: ((sessionId: string) => Promise<string | null>) | undefined, submit: (text: string, images?: string[], options?: ShellSubmitOptions) => Promise<void>, updateEventStatus?: MobileInputStatusUpdater): Promise<boolean>;
498
499
  /**
499
500
  * Route a mobile composer reply through the desktop's canonical gate state
500
501
  * machine. Mobile always transports the text as an encrypted `USER_PROMPT`;
@@ -507,7 +508,7 @@ export declare function routeMobileUserPrompt(event: Event, sessionKeyResolver:
507
508
  */
508
509
  export declare function routeMobileGatePromptInput(deps: GateDecisionSubmitDeps, text: string): Promise<boolean>;
509
510
  /**
510
- * Build the single `(event) => void` callback the AppSync events subscription
511
+ * Build the single callback the AppSync events subscription
511
512
  * invokes. Routes each delivered event to exactly ONE handler:
512
513
  *
513
514
  * - MOBILE-source `USER_PROMPT` → `routeMobileUserPrompt` (the mobile→desktop RETURN
@@ -534,8 +535,8 @@ export declare function createSubscribedEventDispatcher(deps: {
534
535
  reviewSummaryFetcher?: ReviewSummaryFetcher;
535
536
  maybeAutoContinue?: (envelope: GatePromptEnvelope) => boolean;
536
537
  onDurableGateResolution?: (resolution: DurableGateResolution) => Promise<void> | void;
537
- updateEventStatus?: (input: UpdateEventStatusInput) => Promise<unknown>;
538
- }): (event: Event) => void;
538
+ updateEventStatus?: MobileInputStatusUpdater;
539
+ }): (event: Event) => void | Promise<void>;
539
540
  export declare function registerTeamTrackStarts(taskGroupId: string, initialRunning: number): void;
540
541
  export declare function routeTeamShellEventToStore(store: OrchestrationStore, evt: TeamTapEvent, postLine?: (text: string) => void): void;
541
542
  /**
@@ -1,8 +1,10 @@
1
+ import type { PromptReRaiseDisposition } from '../appsync/sync-prompt-retention';
1
2
  import type { ShellEventEmit, ShellEventEmitResult } from './emit-shell-event';
2
3
  /** The run-scoped shell emitter (`createShellEventEmitter(...)`). */
3
4
  export type ShellEmitter = (args: ShellEventEmit) => Promise<ShellEventEmitResult>;
4
5
  /** The run-scoped `openPrompts` TTL co-refresh transport (§3.1). */
5
6
  export type OpenPromptTtlRefresher = (promptIds: string[]) => Promise<string[]>;
7
+ export type SessionPromptTtlRefresher = (sessionId: string, promptIds: string[], reRaise: (promptId: string, reRaiseToken?: string) => Promise<PromptReRaiseDisposition>) => Promise<void>;
6
8
  /** Everything the ledger needs to (re-)emit an offer raise. */
7
9
  export interface PlannerOfferRaiseContext {
8
10
  sessionId: string;
@@ -26,9 +28,11 @@ export declare class PlannerOfferE1Ledger {
26
28
  private readonly needsReRaise;
27
29
  /** promptIds whose resolution emit is un-acked → retry-until-ack (F-C4). */
28
30
  private readonly pendingResolutions;
31
+ private publications;
29
32
  private timer;
30
33
  private emit;
31
34
  private refreshTtl;
35
+ private refreshSessionTtl;
32
36
  private readonly refreshMs;
33
37
  private draining;
34
38
  constructor(opts?: {
@@ -38,7 +42,7 @@ export declare class PlannerOfferE1Ledger {
38
42
  * Install the run-scoped TTL-refresh transport and RESET all per-run state. Call
39
43
  * once at shell start; a re-run (tests) gets a clean ledger + no stale timer.
40
44
  */
41
- beginRun(refreshTtl: OpenPromptTtlRefresher): void;
45
+ beginRun(refreshTtl: OpenPromptTtlRefresher, refreshSessionTtl?: SessionPromptTtlRefresher): void;
42
46
  /**
43
47
  * F-C1 — raise (or re-present) a planner offer and INSPECT the emit result. On a
44
48
  * transient failure (`{emitted:false}` or a throw) PARK the promptId for
@@ -1,5 +1,6 @@
1
1
  import * as fsSync from 'node:fs';
2
2
  import type { AppSyncClient, TeamTrackRecoveryRow } from '../appsync/appsync-client';
3
+ import type { SyncSecurityPosture } from '../appsync/sync-security-posture';
3
4
  import type { Session } from '../types';
4
5
  import type { LocalExecutor } from '../local-executor';
5
6
  import type { ClassBInbound, QueuedWorkEndedPayload } from '../local-executor/types';
@@ -376,6 +377,7 @@ export type ReviewerSubstrateEngager = (input: {
376
377
  /** Canonical host workdir the sandbox mounts (the loop's working dir). */
377
378
  workdir: string;
378
379
  }) => Promise<ReviewerSubstrateEngageResult>;
380
+ export type ReviewerSafetyNoticeSink = (message: string, posture: SyncSecurityPosture) => Promise<void>;
379
381
  export interface QuorumLoopDeps {
380
382
  appsyncClient: AppSyncClient;
381
383
  session: Session;
@@ -527,6 +529,10 @@ export interface QuorumLoopDeps {
527
529
  * `emitShellEvent` NOTIFICATION. ABSENT (tests) → logged only.
528
530
  */
529
531
  surfaceHalt?: (message: string) => void;
532
+ /** Closed posture from the engage result, never inferred from warning text.
533
+ * Production provides local echo plus canonical V2/legacy routing. Advisory
534
+ * failure cannot prevent reviewer execution or sandbox teardown. */
535
+ surfaceReviewerSafetyNotice?: ReviewerSafetyNoticeSink;
530
536
  /**
531
537
  * PHASE-589/469 W1 (LOCK #589-E) — flip the desktop's OWN team track to
532
538
  * `Failed` + render the group-terminal from LOCAL state. Wired by the TEAM
@@ -2356,8 +2362,9 @@ export declare class QuorumLoop {
2356
2362
  * CP-7 §8 (Stage-1-resolved) — surface the PROMINENT reduced-trust badge for a
2357
2363
  * reviewer seat (mirrors the implementor's badge, scoped to the loop's
2358
2364
  * available NOTIFICATION channel). The moat being OFF/coarse for a reviewer is
2359
- * VISIBLE + auditable, never silent: a user-visible NOTIFICATION (via
2360
- * `surfaceHalt`, the loop's NOTIFICATION surface) + a WARN log. Best-effort —
2365
+ * locally visible + auditable: a desktop notice and WARN log. Production
2366
+ * additionally reports a canonical V2 notice, or the legacy NOTIFICATION.
2367
+ * Persistence/transport acceptance is not a phone acknowledgment. Best-effort —
2361
2368
  * unlike the implementor's `tier:'none'` path this is NOT delivery-gated,
2362
2369
  * because a reviewer reduced-trust badge does not change whether the seat
2363
2370
  * RUNS (it always runs; the badge only informs the user).
@@ -0,0 +1,23 @@
1
+ /** One retry owner per mounted orchestration shell, not per emitter closure.
2
+ * Recovery reads only the bounded local source inventory when it is empty;
3
+ * cloud I/O is for previously prepared operations, never new encryption.
4
+ */
5
+ export declare class ShellSyncRecovery {
6
+ private readonly deps;
7
+ private state;
8
+ private timer?;
9
+ private active?;
10
+ private retryDelayMs;
11
+ private stopPromise?;
12
+ constructor(deps: {
13
+ recover(signal: AbortSignal): Promise<number>;
14
+ onFailure(error: unknown): void;
15
+ });
16
+ start(): void;
17
+ /** Synchronous cancellation fence; joins that exact attempt, never launches
18
+ * a final uncancellable cloud sweep or deletes a pending source on exit.
19
+ */
20
+ stop(): Promise<void>;
21
+ private schedule;
22
+ private run;
23
+ }
@@ -1,5 +1,5 @@
1
1
  import type { AppSyncClient } from '../../appsync';
2
- import type { ReviewerAgentSpec, UserReviewerPolicySnapshot } from '../../types/reviewer';
2
+ import type { ReviewerAgentSpec, ReviewerQuorumSettings, UserReviewerPolicySnapshot } from '../../types/reviewer';
3
3
  import { detectInstalledAgents } from '../../orchestration';
4
4
  /**
5
5
  * Human-readable, ANSI-free rendering of a reviewer-policy snapshot. Renders ONLY
@@ -10,6 +10,7 @@ import { detectInstalledAgents } from '../../orchestration';
10
10
  * (it would always render a misleading default).
11
11
  */
12
12
  export declare function formatReviewerPolicy(snapshot: UserReviewerPolicySnapshot): string;
13
+ export declare function formatPassRule(settings: ReviewerQuorumSettings): string;
13
14
  /**
14
15
  * Fetches + formats the user's reviewer panel. Re-pushes the locally detected
15
16
  * agents (idempotent) and renders the returned snapshot. Never throws — returns
@@ -24,6 +25,7 @@ export declare function runReviewersCli(client: AppSyncClient): Promise<string>;
24
25
  export interface ReviewerWizardData {
25
26
  installedAgents: ReturnType<typeof detectInstalledAgents>;
26
27
  currentSeats: ReviewerAgentSpec[] | null;
28
+ currentQuorum?: ReviewerQuorumSettings | null;
27
29
  }
28
30
  /**
29
31
  * Gathers everything the interactive wizard needs to render synchronously:
@@ -2,7 +2,7 @@ import { Session } from '../types/session';
2
2
  import type { StructuralSummary } from '../structural-summary/types';
3
3
  import type { GatePromptEnvelope, ReviewSummaryPanelModel } from './gate-prompts';
4
4
  import type { TaskProgressEvent } from './task-progress';
5
- import type { ReviewerAgentSpec } from '../types/reviewer';
5
+ import type { ReviewerAgentSpec, ReviewerQuorumSettings } from '../types/reviewer';
6
6
  import type { ImplementorPreference } from './implementor-preference';
7
7
  import type { AgentKind as LocalExecutorAgentKind } from '../local-executor/types';
8
8
  import type { WorkItemInput } from '../local-executor/task-group-types';
@@ -208,10 +208,9 @@ export interface ReviewerSeatState {
208
208
  verdictDigest?: string;
209
209
  }
210
210
  /**
211
- * §5.1 LOCK (lines 558-569). Per-gate aggregate over reviewer-quorum +
212
- * auto-revise round count. `autoReviseCap: 2 | 6` literal-union prevents
213
- * accidental drift in implementor code per CP-0 §6 line 155 (Pro = 2,
214
- * Max = 6).
211
+ * Per-gate aggregate over reviewer-quorum and the cloud-admitted auto-revise
212
+ * limit. R2 uses Pro=0, Max=2. Six is retained only for historical display;
213
+ * this presentation type never grants a revision or derives a local cap.
215
214
  */
216
215
  export interface GateState {
217
216
  gateId: string;
@@ -220,7 +219,7 @@ export interface GateState {
220
219
  quorumRequired: number;
221
220
  quorumAchieved: number;
222
221
  autoReviseRound: number;
223
- autoReviseCap: 2 | 6;
222
+ autoReviseCap: 0 | 2 | 6;
224
223
  status: 'pending' | 'in_review' | 'PASS' | 'REVISE' | 'BLOCK' | 'merge_gate_pending';
225
224
  finalizedAt?: string;
226
225
  }
@@ -926,7 +925,7 @@ export interface AuditFlagIndex {
926
925
  * opens. The entrypoint (`handleShellUserInput`) gathers these BEFORE
927
926
  * dispatching `REVIEWER_WIZARD_OPEN` so the wizard renders synchronously from
928
927
  * props with no in-component network I/O:
929
- * - `tier` decides the seat budget (Pro = 2 seats, Max = 3).
928
+ * - `tier` bounds the panel (Pro = 1 seat, Max = 1–2; default 2).
930
929
  * - `installedAgents` is the host-detected agent set — the wizard offers ONLY
931
930
  * these as reviewer choices (mirrors the `/reviewer-setup`
932
931
  * CLI), so a user can never pick an uninstalled agent.
@@ -936,12 +935,13 @@ export interface AuditFlagIndex {
936
935
  *
937
936
  * NB: there is no enable/disable state — the legacy `orchestrationEnabledDefault`
938
937
  * opt-in was removed in CP-1 (2.0 orchestration is shell-driven, not a stored
939
- * flag), so the wizard configures the reviewer PANEL only.
938
+ * flag). The wizard configures the panel, approval rule and revision depth.
940
939
  */
941
940
  export interface ReviewerWizardState {
942
941
  tier: Tier;
943
942
  installedAgents: Array<'CLAUDE' | 'GEMINI' | 'CODEX' | 'ANTIGRAVITY'>;
944
943
  currentSeats: ReviewerAgentSpec[] | null;
944
+ currentQuorum?: ReviewerQuorumSettings | null;
945
945
  }
946
946
  /**
947
947
  * `/implementor-setup` interactive wizard state.
@@ -46,7 +46,7 @@ export interface WorkspaceOutcomeSinkTap {
46
46
  }
47
47
  export interface WorkspaceTerminalCoordinatorOptions {
48
48
  session: Session;
49
- appsyncClient: Pick<AppSyncClient, 'createEvent'>;
49
+ appsyncClient: Pick<AppSyncClient, 'createEvent' | 'submitWorkspaceOutcome'>;
50
50
  getSessionKey: EmitShellEventDeps['getSessionKey'];
51
51
  rootDir?: string;
52
52
  now?: () => Date;
@@ -17,15 +17,15 @@ export declare const SessionContextSchema: z.ZodObject<{
17
17
  recentEventCount: z.ZodNumber;
18
18
  }, "strip", z.ZodTypeAny, {
19
19
  sessionId: string;
20
- userId: string;
21
20
  tier: "FREE" | "PRO" | "MAX";
21
+ userId: string;
22
22
  currentTaskState: "none" | "in_progress" | "awaiting_user" | "awaiting_review" | "merge_gate_pending";
23
23
  structuralSummaryDigest: string;
24
24
  recentEventCount: number;
25
25
  }, {
26
26
  sessionId: string;
27
- userId: string;
28
27
  tier: "FREE" | "PRO" | "MAX";
28
+ userId: string;
29
29
  currentTaskState: "none" | "in_progress" | "awaiting_user" | "awaiting_review" | "merge_gate_pending";
30
30
  structuralSummaryDigest: string;
31
31
  recentEventCount: number;
@@ -1,6 +1,12 @@
1
1
  import type { z } from 'zod';
2
2
  /** Marker infix used by every temp file so restart sweeps can find them. */
3
3
  export declare const TEMP_INFIX = ".tmp.";
4
+ /** Strict bounded snapshot for terminal cleanup; never follows a file symlink. */
5
+ export declare function readBoundedToolFile(filePath: string, maximumBytes: number): Promise<{
6
+ bytes: Buffer;
7
+ dev: number;
8
+ ino: number;
9
+ }>;
4
10
  /**
5
11
  * fsync a directory so a preceding `rename` into it is durable across a crash.
6
12
  * Best-effort (skipped on Windows / filesystems that reject a dir fsync).
@@ -14,9 +20,13 @@ export declare function fsyncDir(dir: string): Promise<void>;
14
20
  * the rename is intra-filesystem, and carries the `.tmp.` infix + pid + random
15
21
  * suffix so a restart sweep recognizes an interrupted write.
16
22
  */
17
- export declare function atomicWriteFileSync(finalPath: string, bytes: string): Promise<void>;
23
+ export declare function atomicWriteFileSync(finalPath: string, bytes: string, options?: {
24
+ requireDirectorySync?: boolean;
25
+ }): Promise<void>;
18
26
  /** Crash-atomic write of a JSON value (schema-validated before writing). */
19
- export declare function atomicWriteJson<T>(finalPath: string, schema: z.ZodType<T>, value: T): Promise<void>;
27
+ export declare function atomicWriteJson<T>(finalPath: string, schema: z.ZodType<T>, value: T, options?: {
28
+ requireDirectorySync?: boolean;
29
+ }): Promise<void>;
20
30
  /**
21
31
  * Read + schema-validate a crash-atomic JSON state file. Returns `null` when the
22
32
  * file is ABSENT, unreadable, non-JSON, or fails the schema — the caller then
@@ -24,4 +34,4 @@ export declare function atomicWriteJson<T>(finalPath: string, schema: z.ZodType<
24
34
  * uniqueness authority; §4.5). `onInvalid` fires (for a CRITICAL log) only when a
25
35
  * PRESENT file failed to parse/validate, distinguishing corruption from absence.
26
36
  */
27
- export declare function readValidatedJson<T>(filePath: string, schema: z.ZodType<T>, onInvalid?: (reason: string) => void): Promise<T | null>;
37
+ export declare function readValidatedJson<T>(filePath: string, schema: z.ZodType<T>, onInvalid?: (reason: string) => void, maximumBytes?: number): Promise<T | null>;
@@ -2,6 +2,7 @@ import type { Logger } from '../logger';
2
2
  import type { ToolActivityOutbox } from './outbox';
3
3
  import type { ToolActivityLedger } from './ledger';
4
4
  import type { ToolActivityOutboxEnvelope } from './types';
5
+ import type { DurableSyncResult } from '../appsync/sync-operation-journal';
5
6
  /** An observed tool occurrence handed to {@link ToolActivityConsolidator.observe}. */
6
7
  /**
7
8
  * The ATOMIC (under-lock) outcome of {@link ToolActivityConsolidator.observe}, so the
@@ -32,9 +33,15 @@ export interface ObservedOccurrence {
32
33
  /** `digestOf(toolInput)` (§0) — becomes the group's `detail[].digest`. Omit if no input. */
33
34
  digest?: string;
34
35
  }
35
- /** Encrypt-at-send transport — throws on a retryable failure; returns on ack. */
36
+ /** Legacy return-on-ack or R2's durable receipt/omission + post-ledger acknowledgment. */
36
37
  export interface ToolActivityTransport {
37
- send(sessionId: string, envelope: ToolActivityOutboxEnvelope): Promise<void>;
38
+ protocol?: 'LEGACY' | 'SYNC_V2';
39
+ /** Required for SYNC_V2; captured in each source window before any send. */
40
+ sessionGenerationId?: string;
41
+ /** SYNC_V2 must persist the exact request/terminal outcome before returning. */
42
+ send(sessionId: string, envelope: ToolActivityOutboxEnvelope): Promise<void | DurableSyncResult>;
43
+ /** Required for SYNC_V2: idempotent cleanup after durable source settlement. */
44
+ settled?(sessionId: string, envelope: ToolActivityOutboxEnvelope): Promise<void>;
38
45
  }
39
46
  /**
40
47
  * True when a failed TOOL_ACTIVITY `createEvent` is the backend's TYPED
@@ -254,6 +261,7 @@ export declare class ToolActivityConsolidator {
254
261
  private enqueue;
255
262
  /** Start as many sends as back-pressure allows. */
256
263
  private pump;
264
+ private requireSourceProtocol;
257
265
  private deliver;
258
266
  /** Test/diagnostic: windows still awaiting delivery (queued + in flight + backoff-limbo). */
259
267
  pendingCount(): number;
@@ -7,3 +7,4 @@ export * from './session-map';
7
7
  export * from './socket-rollout-lock';
8
8
  export * from './flock-rollout-lock';
9
9
  export * from './consolidator';
10
+ export * from './sync-transport';
@@ -14,12 +14,12 @@ export declare const LedgerEntrySchema: z.ZodObject<{
14
14
  byteOffset: z.ZodNumber;
15
15
  state: z.ZodEnum<["durably-outboxed", "acked", "terminal"]>;
16
16
  }, "strip", z.ZodTypeAny, {
17
- state: "terminal" | "durably-outboxed" | "acked";
18
17
  byteOffset: number;
18
+ state: "terminal" | "durably-outboxed" | "acked";
19
19
  clientEventId: string;
20
20
  }, {
21
- state: "terminal" | "durably-outboxed" | "acked";
22
21
  byteOffset: number;
22
+ state: "terminal" | "durably-outboxed" | "acked";
23
23
  clientEventId: string;
24
24
  }>;
25
25
  export type LedgerEntry = z.infer<typeof LedgerEntrySchema>;
@@ -38,12 +38,12 @@ export declare const LedgerFileSchema: z.ZodObject<{
38
38
  byteOffset: z.ZodNumber;
39
39
  state: z.ZodEnum<["durably-outboxed", "acked", "terminal"]>;
40
40
  }, "strip", z.ZodTypeAny, {
41
- state: "terminal" | "durably-outboxed" | "acked";
42
41
  byteOffset: number;
42
+ state: "terminal" | "durably-outboxed" | "acked";
43
43
  clientEventId: string;
44
44
  }, {
45
- state: "terminal" | "durably-outboxed" | "acked";
46
45
  byteOffset: number;
46
+ state: "terminal" | "durably-outboxed" | "acked";
47
47
  clientEventId: string;
48
48
  }>>;
49
49
  /**
@@ -53,16 +53,16 @@ export declare const LedgerFileSchema: z.ZodObject<{
53
53
  terminalClientEventIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
54
54
  }, "strip", z.ZodTypeAny, {
55
55
  entries: Record<string, {
56
- state: "terminal" | "durably-outboxed" | "acked";
57
56
  byteOffset: number;
57
+ state: "terminal" | "durably-outboxed" | "acked";
58
58
  clientEventId: string;
59
59
  }>;
60
60
  version: 1;
61
61
  terminalClientEventIds: string[];
62
62
  }, {
63
63
  entries: Record<string, {
64
- state: "terminal" | "durably-outboxed" | "acked";
65
64
  byteOffset: number;
65
+ state: "terminal" | "durably-outboxed" | "acked";
66
66
  clientEventId: string;
67
67
  }>;
68
68
  version: 1;
@@ -136,7 +136,9 @@ export declare class ToolActivityLedger {
136
136
  * EMPTY ledger (the caller then rebuilds from the outbox via
137
137
  * {@link rebuildFromOutbox}); losing it never skips an occurrence.
138
138
  */
139
- load(): Promise<void>;
139
+ load(options?: {
140
+ failClosed?: boolean;
141
+ }): Promise<void>;
140
142
  /**
141
143
  * Seed `durably-outboxed` mappings from the surviving outbox entries (§4.5
142
144
  * rebuildability) — used after a ledger loss so a still-pending window is not
@@ -178,7 +180,7 @@ export declare class ToolActivityLedger {
178
180
  */
179
181
  isLegacyInFlight(id: string): boolean;
180
182
  getState(id: string): 'durably-outboxed' | 'acked' | 'terminal' | undefined;
181
- /** True when a window has been durably settled as permanently undeliverable. */
183
+ /** True when a window is durably terminal: rejected, quota-omitted, or R2-accepted. */
182
184
  isTerminalWindow(clientEventId: string): boolean;
183
185
  /**
184
186
  * Unique `clientEventId`s still in the `durably-outboxed` state (sealed, not yet
@@ -208,6 +210,10 @@ export declare class ToolActivityLedger {
208
210
  * persisted before its outbox file is pruned so restart cannot resend it.
209
211
  */
210
212
  markTerminal(clientEventId: string, invocationIds?: string[]): Promise<void>;
213
+ /** Retirement is permanent replay suppression, not delivery/quota settlement. */
214
+ markRetired(clientEventId: string, invocationIds: string[]): Promise<void>;
215
+ /** R2 keeps a window settlement independently of occurrence remapping/pruning. */
216
+ markSettled(clientEventId: string, invocationIds: string[], disposition: 'acked' | 'terminal', requireDirectorySync?: boolean): Promise<void>;
211
217
  /**
212
218
  * Crash-ordered repair step (2) (§4.5 V3/W2): DEMOTE/remove every mapping
213
219
  * referencing a quarantined or missing `clientEventId` so its occurrences are
@@ -275,7 +281,9 @@ export declare class ToolActivityLedger {
275
281
  * seen before the backstop re-derives it (§4.8). An absent/invalid file is a
276
282
  * no-op (nothing to suppress yet).
277
283
  */
278
- reloadLegacyOwned(): Promise<void>;
284
+ reloadLegacyOwned(options?: {
285
+ failClosed?: boolean;
286
+ }): Promise<void>;
279
287
  /** Test/diagnostic: a snapshot of the in-memory durable legacy-owned id set. */
280
288
  legacyOwnedSnapshot(): string[];
281
289
  /** Test/diagnostic: a snapshot of the in-memory legacy IN-FLIGHT id set. */
@@ -1,4 +1,6 @@
1
1
  import type { Logger } from '../logger';
2
+ import type { DesktopSyncSessionScope } from '../appsync/sync-operation-journal';
3
+ import type { ToolSyncRetirementLocation } from '../appsync/sync-session-retirement';
2
4
  import { ToolActivityOutboxEnvelope } from './types';
3
5
  export interface ToolActivityOutboxOptions {
4
6
  /** Plugin agent slug — `claude` | `codex` (the on-disk namespace). */
@@ -27,6 +29,8 @@ export declare class ToolActivityOutbox {
27
29
  constructor(opts: ToolActivityOutboxOptions);
28
30
  /** The per-session directory holding one file per sealed window. */
29
31
  sessionDir(sessionId: string): string;
32
+ /** Trusted local namespace persisted inside the encrypted cleanup intent. */
33
+ retirementLocation(): ToolSyncRetirementLocation;
30
34
  private entryPath;
31
35
  /**
32
36
  * Durably persist a sealed window `pending`, BEFORE any send (§4.5 lifecycle
@@ -40,11 +44,25 @@ export declare class ToolActivityOutbox {
40
44
  * `<id>.json`, and QUARANTINES (renames aside, logged) any file that is
41
45
  * unreadable, schema-invalid, or whose filename does not match its own
42
46
  * `clientEventId`. Quarantined occurrences are recovered by the caller's
43
- * transcript rescan — NEVER dead-lettered.
47
+ * transcript rescan — NEVER dead-lettered on the legacy path. R2 callers
48
+ * set failClosed: invalid or previously quarantined sources remain untouched
49
+ * and stop recovery rather than creating a new operation from a rescan.
44
50
  */
45
- listPending(sessionId: string): Promise<ListPendingResult>;
51
+ listPending(sessionId: string, options?: {
52
+ failClosed?: boolean;
53
+ }): Promise<ListPendingResult>;
46
54
  /** Prune a delivered entry on ack — a single idempotent `unlink` (§4.5). */
47
55
  prune(sessionId: string, clientEventId: string): Promise<void>;
56
+ /** Serialize R2 recovery/retirement, not legacy admission. The provider must
57
+ * still stop and join its normal consolidator before terminal retirement. */
58
+ withSyncRecovery<T>(sessionId: string, action: (fence: () => Promise<void>) => Promise<T>): Promise<T | null>;
59
+ /** Exact generation retirement plan, bounded independently of legacy scans.
60
+ * No temp sweep, quarantine, generation rewrite or legacy window deletion. */
61
+ withRetirementSources<T>(scope: DesktopSyncSessionScope, action: (sources: {
62
+ windows: ToolActivityOutboxEnvelope[];
63
+ markRetired(): Promise<void>;
64
+ prune(): Promise<void>;
65
+ }) => Promise<T>): Promise<T | null>;
48
66
  /**
49
67
  * Enumerate the session ids with a durable outbox directory (§4.5 startup
50
68
  * drain — resume EACH session's send-drain independent of which session's
@@ -36,9 +36,9 @@ export declare const ReplayCursorStateSchema: z.ZodObject<{
36
36
  /** Partial trailing line as of the last checkpoint (diagnostic; re-derived on read). */
37
37
  carryover: z.ZodString;
38
38
  }, "strip", z.ZodTypeAny, {
39
+ byteOffset: number;
39
40
  version: 1;
40
41
  canonicalPath: string;
41
- byteOffset: number;
42
42
  fileIdentity: {
43
43
  dev: number;
44
44
  ino: number;
@@ -46,9 +46,9 @@ export declare const ReplayCursorStateSchema: z.ZodObject<{
46
46
  lastKnownSize: number;
47
47
  carryover: string;
48
48
  }, {
49
+ byteOffset: number;
49
50
  version: 1;
50
51
  canonicalPath: string;
51
- byteOffset: number;
52
52
  fileIdentity: {
53
53
  dev: number;
54
54
  ino: number;