@quantiya/codevibe-core 2.0.71 → 2.0.73

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 (58) hide show
  1. package/dist/appsync/appsync-client.d.ts +42 -10
  2. package/dist/appsync/queries.d.ts +4 -0
  3. package/dist/appsync/session-freshness.d.ts +12 -0
  4. package/dist/appsync/sync-operation-journal.d.ts +1 -0
  5. package/dist/appsync/sync-session-retirement.d.ts +1 -1
  6. package/dist/appsync/sync-source-pair.d.ts +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +650 -569
  9. package/dist/orchestration/detect-agents.d.ts +20 -0
  10. package/dist/orchestration/index.d.ts +1 -1
  11. package/dist/orchestration-shell/audit-summary.d.ts +2 -0
  12. package/dist/orchestration-shell/brainstorm-context.d.ts +11 -0
  13. package/dist/orchestration-shell/cli.d.ts +15 -1
  14. package/dist/orchestration-shell/cli.js +55791 -53028
  15. package/dist/orchestration-shell/conductor-reply.d.ts +37 -0
  16. package/dist/orchestration-shell/context-items.d.ts +64 -0
  17. package/dist/orchestration-shell/doctor-cli.d.ts +2 -0
  18. package/dist/orchestration-shell/index.d.ts +44 -2
  19. package/dist/orchestration-shell/quorum-loop.d.ts +17 -3
  20. package/dist/orchestration-shell/resume-modal.d.ts +19 -0
  21. package/dist/orchestration-shell/slash-router.d.ts +2 -0
  22. package/dist/orchestration-shell/types.d.ts +48 -2
  23. package/dist/session/pending-session-create.d.ts +31 -0
  24. package/dist/session-resume/ResumePicker.d.ts +21 -0
  25. package/dist/session-resume/carry-over.d.ts +81 -0
  26. package/dist/session-resume/clean-exit.d.ts +33 -0
  27. package/dist/session-resume/cli-resume.d.ts +41 -0
  28. package/dist/session-resume/completeness-copy.d.ts +52 -0
  29. package/dist/session-resume/create-outcome.d.ts +43 -0
  30. package/dist/session-resume/e2e-fault.d.ts +28 -0
  31. package/dist/session-resume/hosted-loader.d.ts +88 -0
  32. package/dist/session-resume/local-completeness.d.ts +10 -0
  33. package/dist/session-resume/local-records.d.ts +121 -0
  34. package/dist/session-resume/picker-rows.d.ts +17 -0
  35. package/dist/session-resume/publish-retire.d.ts +40 -0
  36. package/dist/session-resume/relaunch.d.ts +72 -0
  37. package/dist/session-resume/restore-conversation.d.ts +27 -0
  38. package/dist/session-resume/resume-candidates.d.ts +52 -0
  39. package/dist/session-resume/resume-chosen.d.ts +103 -0
  40. package/dist/session-resume/resume-create.d.ts +121 -0
  41. package/dist/session-resume/resume-entry.d.ts +87 -0
  42. package/dist/session-resume/resume-header.d.ts +25 -0
  43. package/dist/session-resume/resume-io.d.ts +34 -0
  44. package/dist/session-resume/resume-ledger.d.ts +107 -0
  45. package/dist/session-resume/resume-messages.d.ts +68 -0
  46. package/dist/session-resume/resume-rows.d.ts +24 -0
  47. package/dist/session-resume/resume-target.d.ts +65 -0
  48. package/dist/session-resume/shell-resume-io.d.ts +25 -0
  49. package/dist/session-resume/shell-resume.d.ts +34 -0
  50. package/dist/session-resume/source-choice.d.ts +37 -0
  51. package/dist/session-resume/successor-carry-over.d.ts +54 -0
  52. package/dist/session-resume/will-retire.d.ts +24 -0
  53. package/dist/tool-activity/ledger.d.ts +5 -0
  54. package/dist/tool-activity/outbox.d.ts +3 -2
  55. package/dist/tool-activity/replay-cursor.d.ts +17 -3
  56. package/dist/tool-activity/sync-transport.d.ts +3 -0
  57. package/dist/types/session.d.ts +67 -0
  58. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ /**
2
+ * P61 §12.9 (S2-C4, C2-F2) — the conductor turn's text and its advisory
3
+ * segment boundaries.
4
+ *
5
+ * The persisted conductor `turn` joins every non-`localOnly` advisory of the
6
+ * turn (trimmed, blank-line separated). The join loses each advisory's
7
+ * boundaries, `source` and `handoffExcluded` flag, which the hand-off
8
+ * collectors use. `advisorySegments` records them in the item's telemetry —
9
+ * inside the encrypted envelope and the chain hash, outside the content
10
+ * address — so a resumed session can rebuild exactly the entries P had.
11
+ * The text is byte-identical to the join it replaces.
12
+ */
13
+ export interface AdvisorySegment {
14
+ /** UTF-16 offsets into `body.text` (JavaScript string indices). */
15
+ start: number;
16
+ end: number;
17
+ source: 'planner' | 'shell';
18
+ handoffExcluded?: true;
19
+ }
20
+ interface AdvisoryLike {
21
+ kind: string;
22
+ text?: string;
23
+ source?: 'planner' | 'shell';
24
+ localOnly?: boolean;
25
+ handoffExcluded?: boolean;
26
+ }
27
+ export declare function buildConductorReply(delta: readonly AdvisoryLike[]): {
28
+ text: string;
29
+ segments: AdvisorySegment[];
30
+ };
31
+ /**
32
+ * Read `advisorySegments` back from a carried conductor turn. Returns null
33
+ * when the field is absent or does not exactly tile the text, so the caller
34
+ * applies the conservative per-item legacy rule (§12.4).
35
+ */
36
+ export declare function parseAdvisorySegments(text: string, telemetry: unknown): AdvisorySegment[] | null;
37
+ export {};
@@ -135,6 +135,8 @@ export declare const CONTEXT_ITEM_TTL_SECONDS: number;
135
135
  export declare function ensureContextItemsSessionDirectoryDurable(sessionId: string, beforeParentFsync?: (parent: string, child: string) => Promise<void>): Promise<void>;
136
136
  /** Exported for the A1f retention sweep (`gcContextItemLogs`). */
137
137
  export declare function contextItemsRootDir(): string;
138
+ /** Filesystem-safe per-session dir key (session ids are opaque routing ids). */
139
+ export declare function sessionDirKey(sessionId: string): string;
138
140
  /** Exported for tests + diagnostics. */
139
141
  export declare function contextItemsLogPath(sessionId: string): string;
140
142
  /**
@@ -173,6 +175,15 @@ export declare function getContextItemLog(sessionId: string): {
173
175
  readDurableSnapshot: () => Promise<string | null>;
174
176
  commitAndVerifyAgentTurnBatch: (humanOccurrence: ContextItemOccurrence, drafts: CanonicalAgentTurnDraft[]) => Promise<ContextDurabilityReceipt>;
175
177
  };
178
+ /**
179
+ * P61 clean-exit tail for the INACTIVE update (design §9.4, §12.7). Reads the
180
+ * session's log only if this process already holds it — it never creates a
181
+ * writer — and returns null when there is none or its tail is unknown.
182
+ */
183
+ export declare function getContextItemLogTail(sessionId: string): {
184
+ seq: string;
185
+ itemSha256: string;
186
+ } | null;
176
187
  /**
177
188
  * Append one typed item to the session's canonical log (the deterministic-
178
189
  * emitter write path). Convenience over `getContextItemLog`.
@@ -213,6 +224,59 @@ export interface VerifiedContextItemsResult extends ChainVerificationResult {
213
224
  /** The full parsed chain — EMPTY unless `ok === true` (fail-closed). */
214
225
  items: ContextItem[];
215
226
  }
227
+ /** P61 B-8 — the outcome of a read-only read of a session's local log. */
228
+ export type ReadOnlyContextLogResult = {
229
+ status: 'ok';
230
+ items: ContextItem[];
231
+ /** The chain's end, as the clean-exit `contextTail` would name it; null for an empty log. */
232
+ tail: {
233
+ seq: string;
234
+ itemSha256: string;
235
+ } | null;
236
+ /** An uncommitted AGENT_TURN batch was left out in memory (the writer would truncate it). */
237
+ virtualRollback: boolean;
238
+ } | {
239
+ status: 'absent';
240
+ } | {
241
+ status: 'unreadable';
242
+ /**
243
+ * `key-unavailable`, `read-error`, `recovery-journal` and
244
+ * `unsealable-line` (a sealed line that will not unseal while the key is
245
+ * available — §8.1's "unreadable local log") all fall back to the hosted
246
+ * copy. `integrity` (every line unseals, the chain does not verify) is
247
+ * refused with no fallback.
248
+ */
249
+ reason: 'read-error' | 'key-unavailable' | 'recovery-journal' | 'unsealable-line' | 'integrity';
250
+ chainReason?: ChainVerificationResult['reason'];
251
+ };
252
+ /**
253
+ * P61 B-8 (design §8.1, INV-3) — read a session's local log **without ever
254
+ * writing**: no writer is created, nothing is flushed, truncated, rotated or
255
+ * unlinked. A crash-interrupted AGENT_TURN batch is resolved in memory by the
256
+ * writer's own rule (`recoverAgentTurnTransaction`): a pending journal with
257
+ * its matching commit keeps the log only at the journaled post-size; without
258
+ * the commit, only the bytes before the journaled pre-size count. Anything the
259
+ * writer would block on is `unreadable` here, never guessed.
260
+ */
261
+ export declare function readContextLogReadOnly(sessionId: string): Promise<ReadOnlyContextLogResult>;
262
+ /**
263
+ * One context-log line after unsealing: its plaintext JSON and whether it was
264
+ * a sealed envelope, or `null` when it could not be unsealed.
265
+ */
266
+ export type ContextLogLineEntry = {
267
+ json: string;
268
+ wasEnvelope: boolean;
269
+ } | null;
270
+ /**
271
+ * P61 §8.1 — the single, PURE chain verifier (extracted unchanged from the
272
+ * disk reader above). It checks, line by line and in this order: unsealed,
273
+ * sealed envelope, parseable, REQUIRED shape, session match, `prev_hash`
274
+ * linkage, strictly increasing `seq`, content address. The first failing line
275
+ * decides; any failure returns no items. It hashes the plaintext JSON, so it
276
+ * runs unchanged over decrypted hosted rows (pass `wasEnvelope: true`) and
277
+ * over a read-only snapshot that never creates a log writer.
278
+ */
279
+ export declare function verifyContextLogEntries(sessionId: string, entries: ContextLogLineEntry[]): VerifiedContextItemsResult;
216
280
  export declare function readVerifiedContextItems(sessionId: string): Promise<VerifiedContextItemsResult>;
217
281
  /**
218
282
  * §4.2 chain authority (a) — the ON-DEVICE `verify_chain` over decrypted
@@ -29,6 +29,8 @@ export interface AgentCliStatus {
29
29
  installed: boolean;
30
30
  version?: string;
31
31
  binPath?: string;
32
+ model?: string;
33
+ modelIsConfigured?: boolean;
32
34
  error?: string;
33
35
  }
34
36
  export interface AuthPreflightStatus {
@@ -24,6 +24,9 @@ import { AdvisoryAttachmentJournal } from './advisory-attachment-journal';
24
24
  import type { ImageAttachment } from './types';
25
25
  import { type GateDecisionSubmitDeps, type GroupDecisionSubmitDeps } from './gate-decision-submit';
26
26
  import { type WorkspaceOutcomeSinkTap } from './workspace-terminal-coordinator';
27
+ import { runShellResumeInShell } from '../session-resume/shell-resume-io';
28
+ import type { ShellResumeOutcome } from '../session-resume/shell-resume';
29
+ import { type RelaunchRequest } from '../session-resume/relaunch';
27
30
  export { executeGitStatusSummary } from './git-status-summary';
28
31
  import { type ContinuationCliResult, type ContinuationCliDeps } from './slash-routes/continuation';
29
32
  import { loadReviewerWizardData } from './slash-routes/reviewers';
@@ -111,6 +114,35 @@ export interface RunOrchestrationShellArgs {
111
114
  * teardown `finally` is armed; until then the admitting CLI owns retirement.
112
115
  */
113
116
  acceptSessionLifecycleOwnership?: () => void;
117
+ /**
118
+ * P61 §12.4/§5.4 — present when this shell continues a resumed session S.
119
+ * The shell seeds the carried exchanges (`RESTORE_CONVERSATION`) under the
120
+ * §5.4 header before any turn exists, records the carry-over marker's `seq`
121
+ * for the clean-exit flag, and calls `onStarted` once it owns S's lifecycle
122
+ * (the resume entry point then releases P's resume lock).
123
+ */
124
+ resume?: {
125
+ restoredItems: import('./context-items').ContextItem[];
126
+ markerSeq: string;
127
+ header: {
128
+ top: string;
129
+ warnings: string[];
130
+ bottom: string;
131
+ };
132
+ onStarted?: () => void | Promise<void>;
133
+ };
134
+ /**
135
+ * @internal P61 §5.3 — set by `runOrchestrationShell` itself. A `/resume`
136
+ * switch records P and the lock hand-off token here; teardown marks
137
+ * `converged` when every phase finished (the same point as the owner
138
+ * record's finished mark).
139
+ */
140
+ resumeSwitch?: {
141
+ request: RelaunchRequest | null;
142
+ converged: boolean;
143
+ };
144
+ /** @internal Test seam for `/resume` (defaults to the production bindings). */
145
+ runShellResumeFn?: (opts: Parameters<typeof runShellResumeInShell>[0]) => Promise<ShellResumeOutcome>;
114
146
  /** @internal isolated durable advisory-attachment authority for tests. */
115
147
  advisoryAttachmentJournal?: AdvisoryAttachmentJournal;
116
148
  /**
@@ -376,12 +408,22 @@ export declare function wireTeamMergeAuditSummary(store: OrchestrationStore, tar
376
408
  fn?: (summary: any) => void;
377
409
  onTeamMergeAuditSummary?: (summary: any) => void;
378
410
  }): void;
411
+ /** P61 B-12 — what the shell asks its caller to do after it ends. */
412
+ export interface ShellExitResult {
413
+ /** A `/resume` switch whose teardown converged: relaunch as `--resume <P>` (§12.6). */
414
+ relaunch?: RelaunchRequest;
415
+ /** A `/resume` switch whose teardown did not converge: print the command (§5.3 step 2). */
416
+ switchFailedFor?: string;
417
+ /** With `relaunch`: the session C that just ended, named if the child fails (§10). */
418
+ currentSessionId?: string;
419
+ }
379
420
  /**
380
421
  * §5 lines 336-365 LOCK + §16 LOCK #14. Single entrypoint that wires
381
422
  * the store + emit chokepoint + slash router and either mounts the
382
- * Ink TUI (TTY) or falls back to line-log (non-TTY).
423
+ * Ink TUI (TTY) or falls back to line-log (non-TTY). P61 B-12: returns the
424
+ * relaunch for a `/resume` switch only after a converged teardown.
383
425
  */
384
- export declare function runOrchestrationShell(args: RunOrchestrationShellArgs): Promise<void>;
426
+ export declare function runOrchestrationShell(args: RunOrchestrationShellArgs): Promise<ShellExitResult | undefined>;
385
427
  /**
386
428
  * PHASE-CP-1-F.1 Part B (LOCK L7) — the bound on the async per-round summary
387
429
  * fetch fired AFTER a final-approval prompt is queued. The decision never
@@ -489,6 +489,10 @@ export interface QuorumLoopDeps {
489
489
  * `undefined`.
490
490
  */
491
491
  detectedAgents?: string[];
492
+ /** Deterministic active models per agent discovered during local agent detection. */
493
+ detectedAgentModels?: Partial<Record<string, string>> | Record<string, string>;
494
+ /** Explicitly configured models per agent (env or local config only, no synthetic defaults). */
495
+ configuredAgentModels?: Partial<Record<string, string>> | Record<string, string>;
492
496
  /**
493
497
  * #585 auto-continuation (Max) — the resolved authenticated subscription tier
494
498
  * threaded from CLI startup/resume.
@@ -961,13 +965,21 @@ export declare class QuorumLoop {
961
965
  * Enables reconciliation if a user decision arrives or is replayed after promotion.
962
966
  */
963
967
  private readonly completedTaskSummaries;
964
- private readonly expectedRosterByTask;
968
+ private readonly declaredRosterByTask;
969
+ private readonly dispatchedRosterByTask;
965
970
  private readonly taskDegradedMetadataByTask;
966
971
  private getCompletedSummariesPath;
967
972
  private shouldUnlinkAbandonedTmpLockFile;
968
973
  private withCompletedSummariesLock;
969
974
  private loadCompletedSummariesFromDisk;
970
- private recordCompletedTaskSummary;
975
+ /** @internal */
976
+ recordCompletedTaskSummary(taskId: string, info: {
977
+ historySnapshot?: ReviewHistorySnapshot | null;
978
+ filesApplied: number;
979
+ completedAt: string;
980
+ originEpoch?: number;
981
+ workspaceApplicationStatus?: 'pending_merge_gate' | 'applied' | 'unobserved';
982
+ }): void;
971
983
  getCompletedTaskSummary(taskId: string): {
972
984
  historySnapshot?: ReviewHistorySnapshot | null;
973
985
  filesApplied: number;
@@ -1624,6 +1636,8 @@ export declare class QuorumLoop {
1624
1636
  */
1625
1637
  getDurableStore(): RevertManifestStore | undefined;
1626
1638
  getDetectedAgents(): AgentKind[];
1639
+ getDetectedModelForAgent(agent: string): string | null;
1640
+ getExplicitModelForAgent(agent: string): string | null;
1627
1641
  /**
1628
1642
  * #585 auto-continuation (Max) — pick the agent to AUTO-CONTINUE on when the
1629
1643
  * implementor for `taskId` hit a quota wall, or `null` to fall through to the
@@ -2301,7 +2315,7 @@ export declare class QuorumLoop {
2301
2315
  * error propagates (the caller surfaces it fail-closed).
2302
2316
  */
2303
2317
  private submitImplementorOutputWithRetry;
2304
- private handleReviewerDispatch;
2318
+ handleReviewerDispatch(payload: ReviewerDispatchPayload): Promise<void>;
2305
2319
  /**
2306
2320
  * §3.C.21 — spawn + run ONE reviewer seat, then submit its verdict.
2307
2321
  * Double-guarded (§3.C.21b LOCK): a seat already running-in-memory OR
@@ -0,0 +1,19 @@
1
+ /**
2
+ * P61 §5.2/§5.3 — the in-shell `/resume` picker and §8.4 continue/cancel
3
+ * choice. A pre-shell launch mounts its own Ink render (`ResumePicker.tsx`);
4
+ * inside the running shell the same components replace the `<InputBar>`
5
+ * through store state, and the awaiting `/resume` handler is resolved by the
6
+ * component's callbacks. One modal per store at a time.
7
+ */
8
+ import type { PickerRow } from '../session-resume/picker-rows';
9
+ import type { OrchestrationAction } from './types';
10
+ type Dispatch = {
11
+ dispatch(action: OrchestrationAction): void;
12
+ };
13
+ /** The picker: resolves the chosen session ID, or null on Esc. */
14
+ export declare function pickInShell(store: Dispatch, rows: PickerRow[]): Promise<string | null>;
15
+ /** The §8.4 choice: true for "Continue without …"; Cancel (the default) is false. */
16
+ export declare function chooseInShell(store: Dispatch, prompt: string, continueLabel: string): Promise<boolean>;
17
+ /** Called by the rendered modal (and by teardown, with null): close it and resolve the waiter. */
18
+ export declare function settleResumeModal(store: Dispatch, value: string | boolean | null): void;
19
+ export {};
@@ -41,6 +41,8 @@ export interface SlashCommandInfo {
41
41
  */
42
42
  export declare const USER_RULE_PIN_PENDING = "PENDING \u2014 entrypoint pins the rule";
43
43
  export declare const USER_RULES_LIST_PENDING = "PENDING \u2014 entrypoint lists pinned rules";
44
+ /** P61 §5.3 — the entrypoint resolves the target and runs the switch. */
45
+ export declare const RESUME_PENDING = "PENDING \u2014 entrypoint resumes a session";
44
46
  export declare const USER_RULES_FORGET_PENDING = "PENDING \u2014 entrypoint retires a pinned rule";
45
47
  /** True if `command` is a shell-exit command (`/quit` / `/exit`). */
46
48
  export declare function isExitCommand(command: string): boolean;
@@ -339,6 +339,8 @@ export type ConversationEntry = {
339
339
  * rewrite needs. Carried into `pending.attachmentPaths` when a clarification mints.
340
340
  */
341
341
  imagePaths?: string[];
342
+ /** P61 §12.4 — seeded from a resumed session's carry-over; never part of a turn delta. */
343
+ restored?: true;
342
344
  } | {
343
345
  kind: 'planner-decision';
344
346
  id: string;
@@ -348,6 +350,8 @@ export type ConversationEntry = {
348
350
  rationale: string;
349
351
  taskId?: string;
350
352
  brainstorm?: BrainstormContextMetadata;
353
+ /** P61 §12.4 — seeded from a resumed session's carry-over. */
354
+ restored?: true;
351
355
  } | {
352
356
  kind: 'subagent-event';
353
357
  id: string;
@@ -378,6 +382,11 @@ export type ConversationEntry = {
378
382
  final: true;
379
383
  command: string;
380
384
  output: string;
385
+ /**
386
+ * P61 §5.3 — this output ends the shell like `/exit` (a `/resume` switch).
387
+ * The exit detectors honour it as well as `/exit` and `/quit`.
388
+ */
389
+ exit?: true;
381
390
  } | {
382
391
  kind: 'advisory';
383
392
  id: string;
@@ -387,6 +396,8 @@ export type ConversationEntry = {
387
396
  text: string;
388
397
  localOnly?: true;
389
398
  handoffExcluded?: true;
399
+ /** P61 §12.4 — seeded from a resumed session's carry-over. */
400
+ restored?: true;
390
401
  } | {
391
402
  kind: 'gate-prompt';
392
403
  id: string;
@@ -559,8 +570,9 @@ export interface PendingPlannerOffer {
559
570
  * team (`launchTeamFromWorkItems`), so both paths share the numbering. A team
560
571
  * group is one number (its child tasks render `#n.1`, `#n.2` via `state.team`).
561
572
  * `retryOf` links a `#n retry` task (P46a C2/C3) to the `apply_failed` original.
562
- * In-memory only: production has no session resume (design §2.4/A8), so every
563
- * launch is a new session with its own numbering; nothing is persisted.
573
+ * In-memory only; nothing is persisted. Every launch is a new session with its
574
+ * own numbering, and so is a resume: it creates a successor session whose task
575
+ * numbers restart at #1 (P61 SESSION-RESUME-DESIGN §5.4, §7; amends P46 §2.4/A8).
564
576
  */
565
577
  export interface TaskOrdinalRecord {
566
578
  readonly ordinal: number;
@@ -858,6 +870,13 @@ export interface OrchestrationState {
858
870
  * `<OrchestrationApp>` renders `<ImplementorSetupWizard>` in place of `<InputBar>`.
859
871
  */
860
872
  implementorWizard: ImplementorWizardState | null;
873
+ /**
874
+ * P61 §5.2/§5.3 — the in-shell `/resume` picker or §8.4 continue/cancel
875
+ * choice. While non-null, `<OrchestrationApp>` renders it in place of the
876
+ * `<InputBar>`. Optional so existing state fixtures stay valid; absent and
877
+ * null both mean no modal.
878
+ */
879
+ resumeModal?: ResumeModalState | null;
861
880
  /**
862
881
  * `[ADDITIVE WIDENING — O1 C1 (#652)]` per
863
882
  * PHASE-O1-C1-SAFETY-OBSERVABILITY-DESIGN.md §3.1 item 1: the shell used to
@@ -981,11 +1000,23 @@ export interface ImplementorWizardState {
981
1000
  * NEW variant or required field is tagged here before the reducer
982
1001
  * consumes it.
983
1002
  */
1003
+ export type RestoredConversationEntry = Extract<ConversationEntry, {
1004
+ kind: 'user-message';
1005
+ } | {
1006
+ kind: 'advisory';
1007
+ } | {
1008
+ kind: 'planner-decision';
1009
+ }> & {
1010
+ restored: true;
1011
+ };
984
1012
  export type OrchestrationAction = {
985
1013
  type: 'USER_INPUT';
986
1014
  text: string;
987
1015
  attachments?: ImageAttachment[];
988
1016
  imagePaths?: string[];
1017
+ } | {
1018
+ type: 'RESTORE_CONVERSATION';
1019
+ entries: ReadonlyArray<RestoredConversationEntry>;
989
1020
  } | {
990
1021
  type: 'PLANNER_DECISION';
991
1022
  decision: PlannerDecision;
@@ -1012,6 +1043,7 @@ export type OrchestrationAction = {
1012
1043
  type: 'SLASH_OUTPUT';
1013
1044
  command: string;
1014
1045
  output: string;
1046
+ exit?: true;
1015
1047
  } | {
1016
1048
  type: 'CLARIFICATION_ANSWERED';
1017
1049
  answer: string;
@@ -1168,6 +1200,11 @@ export type OrchestrationAction = {
1168
1200
  wizard: ImplementorWizardState;
1169
1201
  } | {
1170
1202
  type: 'IMPLEMENTOR_WIZARD_CLOSE';
1203
+ } | {
1204
+ type: 'RESUME_MODAL_OPEN';
1205
+ modal: ResumeModalState;
1206
+ } | {
1207
+ type: 'RESUME_MODAL_CLOSE';
1171
1208
  } | {
1172
1209
  type: 'AUDIT_FLAG_INDEX_RETAINED';
1173
1210
  index: AuditFlagIndex;
@@ -1193,3 +1230,12 @@ export interface LastModeFile {
1193
1230
  * `src/types/auth.ts` already.
1194
1231
  */
1195
1232
  export type Tier = 'FREE' | 'PRO' | 'MAX';
1233
+ /** P61 §5.2/§5.3 — what the in-shell `/resume` modal shows. */
1234
+ export type ResumeModalState = {
1235
+ kind: 'picker';
1236
+ rows: import('../session-resume/picker-rows').PickerRow[];
1237
+ } | {
1238
+ kind: 'choice';
1239
+ prompt: string;
1240
+ continueLabel: string;
1241
+ };
@@ -21,6 +21,37 @@ declare function fingerprintIntent(intent: PendingSessionCreateIntent): string;
21
21
  declare function readPending(sessionId: string): Promise<PendingSessionCreateRecord | null>;
22
22
  declare function removePending(sessionId: string, record?: PendingSessionCreateRecord): Promise<void>;
23
23
  declare function findPendingForIntent(intent: PendingSessionCreateIntent, logger: Logger): Promise<PendingSessionCreateRecord | null>;
24
+ export declare function localSigningFingerprint(publicKeyB64: string): string;
25
+ /**
26
+ * How the backend resolved a createSession call, relative to the sealed request.
27
+ * - `exact` — the backend admitted THIS sealed create byte-for-byte (it echoed
28
+ * our envelopes): our locally-generated key is authoritative.
29
+ * - `resume` — §4.9 resume-not-overwrite: a same-owner row already existed and
30
+ * the backend returned it verbatim, wrapping a DIFFERENT generation's
31
+ * key (K1) than we sealed (K2). The caller reconciles the key from
32
+ * the returned encryptedKeys (#229 / #230 Finding 1) and fails closed
33
+ * if this device is absent — never caching our local phantom.
34
+ */
35
+ export type CreateResponseClass = 'exact' | 'resume';
36
+ /**
37
+ * Classify a backend createSession response against the sealed request, throwing
38
+ * `SESSION_CREATE_RESPONSE_MISMATCH` for anything that is NEITHER provably our own
39
+ * sealed create NOR a legitimate same-owner resume-not-overwrite.
40
+ *
41
+ * A syntactically valid response is not proof that this exact sealed create was
42
+ * admitted. Kill-switch sentinels (random `__blocked__` userId), cross-owner rows
43
+ * (the backend throws `Unauthorized`, but defense-in-depth here too), and
44
+ * stale/wrong-session responses must remain replayable instead of consuming the
45
+ * only durable request and caching a key under the wrong identity.
46
+ *
47
+ * The fresh-vs-resume signal is `envelopesMatch`. A real resume can NEVER echo our
48
+ * freshly-generated envelopes (K1 ≠ K2, wrapped anew every create), so:
49
+ * - divergent envelopes from the same owner → the resume (accept, `resume`);
50
+ * - matching envelopes (our own key) that still diverge on another identity field
51
+ * → corruption, not a resume (reject).
52
+ * §4.5 X3-AMEND-1 / §4.9 (tool-event consolidation #229 / #230 Finding 1).
53
+ */
54
+ export declare function classifyCreateResponse(session: Session, request: CreateSessionInput): CreateResponseClass;
24
55
  /**
25
56
  * Persist one exact create envelope before network I/O and replay it unchanged
26
57
  * after an ambiguous transport failure or process restart.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * P61 §5.2 — the resume picker and the §8.4 continue/cancel choice (Ink).
3
+ * Callers `await initInkRuntime()` first. Rows come from `toPickerRows`.
4
+ */
5
+ import * as React from 'react';
6
+ import { type PickerRow } from './picker-rows';
7
+ export declare function ResumePicker(props: {
8
+ rows: PickerRow[];
9
+ onSelect: (sessionId: string) => void;
10
+ onCancel: () => void;
11
+ }): React.ReactElement;
12
+ export declare function ResumeChoice(props: {
13
+ prompt: string;
14
+ /** "Continue without them" (default) or, for a missing carry-over, "Continue without it". */
15
+ continueLabel?: string;
16
+ onChoose: (continueWithout: boolean) => void;
17
+ }): React.ReactElement;
18
+ /** Mount the picker before the shell; resolves with the chosen ID, or null on Esc. */
19
+ export declare function runResumePicker(rows: PickerRow[]): Promise<string | null>;
20
+ /** Mount the §8.4 choice; resolves true for "Continue without …". */
21
+ export declare function runResumeChoice(prompt: string, continueLabel?: string): Promise<boolean>;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * P61 session resume — select and re-author the carry-over
3
+ * (SESSION-RESUME-DESIGN.md §7, §8.2, §8.3; DD-2: N = 20).
4
+ *
5
+ * Selection is pure over P's VERIFIED items. Re-authoring appends the chosen
6
+ * items to S's log through the single-writer path in P's seq order, remapping
7
+ * content-id refs and the one body field that names another item (a rule
8
+ * retirement's occurrence `seq`), and ends with one `session_resumed` marker.
9
+ */
10
+ import type { ContextItem, ContextItemDraft } from '../orchestration-shell/context-items';
11
+ export declare const CARRY_OVER_EXCHANGES = 20;
12
+ export declare const CARRY_OVER_MAX_BYTES: number;
13
+ export declare const SESSION_RESUMED_ACTION = "session_resumed";
14
+ /** True when `item` is the carry-over marker of a resume of `fromSessionId` (§8.4 "S's marker"). */
15
+ export declare function isResumeMarkerFor(item: ContextItem, fromSessionId: string): boolean;
16
+ export interface CarryOverPlan {
17
+ /** P's items to re-author, in P's seq order. */
18
+ items: ContextItem[];
19
+ counts: {
20
+ exchanges: number;
21
+ droppedExchanges: number;
22
+ rules: number;
23
+ openQuestions: number;
24
+ taskNotes: number;
25
+ };
26
+ /** True when the rules, closure and marker alone exceeded the cap (§8.2). */
27
+ rulesOnly: boolean;
28
+ }
29
+ /**
30
+ * §8.2 — choose the carry-over from P's verified items.
31
+ * - Every live rule (readLiveUserRules semantics); never dropped.
32
+ * - Exchanges: the N most recent human turns, each with every later
33
+ * session-scoped turn/decision/open question up to the next human turn
34
+ * (rule decisions and earlier markers excluded; they enter only below).
35
+ * - Rule-retirement closure (S2-C1): every retired rule occurrence whose
36
+ * source human turn is carried, with each retirement of a carried occurrence.
37
+ * - Task notes: task-scoped task_spec/verdict/artifact_ref/decision items of
38
+ * tasks first seen inside a carried exchange (display and audit only).
39
+ * - Excluded: repo_state, rationale, earlier `session_resumed` markers.
40
+ * - 1 MiB over every carried item as serialized: rules first, then exchange
41
+ * groups newest first; the oldest are dropped first (DD-2 loss boundary).
42
+ */
43
+ export declare function planCarryOver(items: readonly ContextItem[], exchangesWanted?: number, maxBytes?: number): CarryOverPlan;
44
+ export interface ReauthorResult {
45
+ /** S's items as appended, in order; the last is the marker. */
46
+ appended: ContextItem[];
47
+ marker: ContextItem;
48
+ }
49
+ export declare class CarryOverAppendError extends Error {
50
+ readonly at: number;
51
+ constructor(at: number);
52
+ }
53
+ /**
54
+ * §8.3 — append the plan to S's log, in P's seq order, then the marker. Any
55
+ * null append aborts (durability gate step 1); the caller removes S's
56
+ * directory by exact path and claims nothing.
57
+ */
58
+ export declare function reauthorCarryOver(args: {
59
+ successorSessionId: string;
60
+ predecessorSessionId: string;
61
+ plan: CarryOverPlan;
62
+ sourceComplete: boolean;
63
+ /**
64
+ * §8.4 step 2: the plan's items were read from this session (the
65
+ * predecessor's own predecessor), not from `predecessorSessionId`.
66
+ */
67
+ rebuiltFrom?: string;
68
+ append: (sessionId: string, draft: ContextItemDraft) => Promise<ContextItem | null>;
69
+ }): Promise<ReauthorResult>;
70
+ /** The tail descriptor (`contextTail`) of a context item. */
71
+ export declare function itemTail(item: ContextItem): {
72
+ seq: string;
73
+ itemSha256: string;
74
+ };
75
+ /** §8.2: the header line when only standing rules could be carried (P61B-S1-F7). */
76
+ export declare const RULES_ONLY_WARNING = "Only standing rules were restored; the conversation was too large.";
77
+ /**
78
+ * Whether a carry-over marker records the §8.2 rules-only case: no exchange carried and at least
79
+ * one dropped (the plan's `rulesOnly`, as the ledger-replay path sees it, from the marker alone).
80
+ */
81
+ export declare function markerRecordsRulesOnly(marker: ContextItem): boolean;
@@ -0,0 +1,33 @@
1
+ import type { UpdateSessionInput } from '../types/session';
2
+ /** Characters of the last human prompt kept in the picker preview (§9.4). */
3
+ export declare const LAST_PROMPT_PREVIEW_CHARS = 60;
4
+ /** The backend's cap on the preview's ciphertext. */
5
+ export declare const ENCRYPTED_LAST_PROMPT_MAX_BYTES = 1024;
6
+ export type CleanExitFields = Pick<UpdateSessionInput, 'contextTail' | 'contextTailIsResumeMarker' | 'encryptedLastPrompt'>;
7
+ /** Record the session's most recent human turn (called once it is appended). */
8
+ export declare function recordLastHumanPrompt(sessionId: string, text: string): void;
9
+ /**
10
+ * The `encryptedLastPrompt` for a prompt and a key in hand: a published-then-retired S, whose shell
11
+ * never ran here (P61B-S1-L12). Same preview rule and cap as the shell's. Never throws.
12
+ */
13
+ export declare function encryptedPromptPreview(text: string, sessionKey: string): string | undefined;
14
+ /** Record the `seq` of the carry-over marker a resume wrote into this session's log. */
15
+ export declare function recordResumeMarkerSeq(sessionId: string, seq: string): void;
16
+ /**
17
+ * The fields to add to this session's INACTIVE update, on the ordered
18
+ * retirement and on the last-ditch one alike. Never throws.
19
+ */
20
+ export declare function buildCleanExitFields(sessionId: string): Promise<CleanExitFields>;
21
+ /** Fire-and-forget at a resumed shell's start. Read-only (B-8): never initializes or rotates the writer. Never throws. */
22
+ export declare function primeDurableTail(sessionId: string): Promise<void>;
23
+ /** How long a startup-failure retirement waits for the start-time read (P61B-S1-E1). */
24
+ export declare const PRIME_WAIT_MAX_MS = 1000;
25
+ /**
26
+ * The clean-exit fields for the retirement of a session whose shell failed while starting
27
+ * (P61B-S1-E1, found by E2E-1 round 2): a resumed S can fail right after its seeding, before the
28
+ * fire-and-forget `primeDurableTail` read has finished. Waits at most `PRIME_WAIT_MAX_MS` for that
29
+ * read, then answers as `buildCleanExitFields`. A plain session has no clean-exit state, so it gets
30
+ * `{}` at once. Never throws.
31
+ */
32
+ export declare function cleanExitFieldsAfterPrime(sessionId: string, maxWaitMs?: number): Promise<CleanExitFields>;
33
+ export declare function resetCleanExitStateForTests(): void;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * P61 — the CLI half of `--continue` / `--resume [<id>]` (design §5.1): run the
3
+ * entry point, then hand S to the shell with its seeding, header and lock
4
+ * release. Returns an exit code instead when no shell should start.
5
+ */
6
+ import type { AppSyncClient } from '../appsync/appsync-client';
7
+ import type { Logger } from '../logger';
8
+ import type { Session } from '../types/session';
9
+ import { type ResumeStart } from './resume-entry';
10
+ import type { ResumeEntryPoint } from './resume-target';
11
+ export interface ResumeShellArgs {
12
+ restoredItems: ResumeStart['restoredItems'];
13
+ markerSeq: string;
14
+ header: {
15
+ top: string;
16
+ warnings: string[];
17
+ bottom: string;
18
+ };
19
+ onStarted: () => Promise<void>;
20
+ }
21
+ export declare function resumeEntryFromArgs(args: {
22
+ resume?: {
23
+ id?: string;
24
+ };
25
+ continue?: true;
26
+ }, cwd: string): ResumeEntryPoint | null;
27
+ export declare function startResumedSession(opts: {
28
+ client: AppSyncClient;
29
+ entry: ResumeEntryPoint;
30
+ cwd: string;
31
+ isTTY: boolean;
32
+ logger: Logger;
33
+ ownSessionId?: string;
34
+ }): Promise<{
35
+ kind: 'exit';
36
+ code: number;
37
+ } | {
38
+ kind: 'start';
39
+ session: Session;
40
+ shell: ResumeShellArgs;
41
+ }>;