@quantiya/codevibe-claude-plugin 2.0.76 → 2.0.77

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 (47) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +41 -10
  3. package/node_modules/@quantiya/codevibe-core/dist/appsync/queries.d.ts +4 -0
  4. package/node_modules/@quantiya/codevibe-core/dist/appsync/session-freshness.d.ts +12 -0
  5. package/node_modules/@quantiya/codevibe-core/dist/index.js +643 -562
  6. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/brainstorm-context.d.ts +11 -0
  7. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +11 -1
  8. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +55627 -53046
  9. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/conductor-reply.d.ts +37 -0
  10. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/context-items.d.ts +64 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +44 -2
  12. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/resume-modal.d.ts +19 -0
  13. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/slash-router.d.ts +2 -0
  14. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +48 -2
  15. package/node_modules/@quantiya/codevibe-core/dist/session/pending-session-create.d.ts +31 -0
  16. package/node_modules/@quantiya/codevibe-core/dist/session-resume/ResumePicker.d.ts +21 -0
  17. package/node_modules/@quantiya/codevibe-core/dist/session-resume/carry-over.d.ts +81 -0
  18. package/node_modules/@quantiya/codevibe-core/dist/session-resume/clean-exit.d.ts +33 -0
  19. package/node_modules/@quantiya/codevibe-core/dist/session-resume/cli-resume.d.ts +41 -0
  20. package/node_modules/@quantiya/codevibe-core/dist/session-resume/completeness-copy.d.ts +52 -0
  21. package/node_modules/@quantiya/codevibe-core/dist/session-resume/create-outcome.d.ts +43 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/session-resume/e2e-fault.d.ts +28 -0
  23. package/node_modules/@quantiya/codevibe-core/dist/session-resume/hosted-loader.d.ts +88 -0
  24. package/node_modules/@quantiya/codevibe-core/dist/session-resume/local-completeness.d.ts +10 -0
  25. package/node_modules/@quantiya/codevibe-core/dist/session-resume/local-records.d.ts +121 -0
  26. package/node_modules/@quantiya/codevibe-core/dist/session-resume/picker-rows.d.ts +17 -0
  27. package/node_modules/@quantiya/codevibe-core/dist/session-resume/publish-retire.d.ts +40 -0
  28. package/node_modules/@quantiya/codevibe-core/dist/session-resume/relaunch.d.ts +72 -0
  29. package/node_modules/@quantiya/codevibe-core/dist/session-resume/restore-conversation.d.ts +27 -0
  30. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-candidates.d.ts +52 -0
  31. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-chosen.d.ts +103 -0
  32. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-create.d.ts +121 -0
  33. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-entry.d.ts +87 -0
  34. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-header.d.ts +25 -0
  35. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-io.d.ts +34 -0
  36. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-ledger.d.ts +107 -0
  37. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-messages.d.ts +68 -0
  38. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-rows.d.ts +24 -0
  39. package/node_modules/@quantiya/codevibe-core/dist/session-resume/resume-target.d.ts +65 -0
  40. package/node_modules/@quantiya/codevibe-core/dist/session-resume/shell-resume-io.d.ts +25 -0
  41. package/node_modules/@quantiya/codevibe-core/dist/session-resume/shell-resume.d.ts +34 -0
  42. package/node_modules/@quantiya/codevibe-core/dist/session-resume/source-choice.d.ts +37 -0
  43. package/node_modules/@quantiya/codevibe-core/dist/session-resume/successor-carry-over.d.ts +54 -0
  44. package/node_modules/@quantiya/codevibe-core/dist/session-resume/will-retire.d.ts +24 -0
  45. package/node_modules/@quantiya/codevibe-core/dist/types/session.d.ts +67 -0
  46. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  47. package/package.json +2 -2
@@ -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
@@ -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
@@ -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
+ }>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * P61 §8.4 completeness decisions and the §8.1 failure copy for P's chosen
3
+ * source. Pure. `<device>` is P's creator computer as this account names it;
4
+ * when it is this computer (the resume fell back to the hosted copy here),
5
+ * the copy follows the §8.1 fallback reason instead.
6
+ */
7
+ import type { HostedFallbackReason } from './source-choice';
8
+ export type CompletenessKind = 'complete' | 'continues-past-tail' | 'cut-at-tail' | 'short' | 'no-tail' | 'integrity' | 'ambiguous';
9
+ export interface CopyContext {
10
+ /** P's creator computer as listed for this account, e.g. "MacBook Pro" or "(unknown device)". */
11
+ deviceLabel: string;
12
+ /** True when P's creator computer is this one. */
13
+ deviceIsThisComputer: boolean;
14
+ /** Why the hosted copy is being read on the computer where P ran (null elsewhere, or for a local read). */
15
+ fallbackReason: HostedFallbackReason | null;
16
+ }
17
+ export declare const CONTINUE_WITHOUT_THEM = "Continue without them";
18
+ export declare const CONTINUE_WITHOUT_IT = "Continue without it";
19
+ export type ContinueLabel = typeof CONTINUE_WITHOUT_THEM | typeof CONTINUE_WITHOUT_IT;
20
+ export type CompletenessDecision = {
21
+ kind: 'proceed';
22
+ warning?: string;
23
+ }
24
+ /** Arrow-key choice; Cancel is the default and a launch without a terminal cancels. */
25
+ | {
26
+ kind: 'choose';
27
+ prompt: string;
28
+ continueLabel: typeof CONTINUE_WITHOUT_THEM;
29
+ cancelLabel: 'Cancel';
30
+ } | {
31
+ kind: 'refuse';
32
+ text: string;
33
+ };
34
+ export declare const CUT_AT_TAIL_WARNING = "This session's last moments are ambiguous and were left out.";
35
+ export declare const NO_TAIL_WARNING = "This session didn't end cleanly; its last moments may be missing.";
36
+ export declare const INTEGRITY_REFUSAL = "This session's saved context failed its integrity check, so it can't be resumed.";
37
+ export declare const KEYCHAIN_REFUSAL = "This computer's keychain is locked or unavailable. Unlock it and try again.";
38
+ export declare const UNREADABLE_LOCAL_REFUSAL = "This session's saved context on this computer can't be read (it may be damaged, or this computer's CodeVibe identity may have been reset), and its online copy can't be used, so it can't be resumed.";
39
+ /** §8.1: the refusal when the hosted copy itself cannot be used (integrity or ambiguous). */
40
+ export declare function hostedFailureRefusal(kind: 'integrity' | 'ambiguous', ctx: CopyContext): string;
41
+ /**
42
+ * The "chain ends before contextTail" prompt. Also §8.4 step 2's prompt for a
43
+ * resumed session's own items when its carry-over is rebuilt from P.
44
+ */
45
+ export declare function shortPrompt(ctx: CopyContext): string;
46
+ export declare function completenessDecision(kind: CompletenessKind, ctx: CopyContext): CompletenessDecision;
47
+ /**
48
+ * §8.4 step 3: a resumed session S whose chain lacks its marker, when its
49
+ * carry-over cannot be rebuilt from P. `ctx` describes S's own copy (S's
50
+ * computer and S's fallback reason); `<P>` is S's predecessor.
51
+ */
52
+ export declare function missingCarryOverPrompt(predecessorSessionId: string, ctx: CopyContext): string;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * P61 session resume — classify a resume create's failure (SESSION-RESUME-DESIGN.md
3
+ * §10 class table, §12.2.4; Phase B implementation design B-3).
4
+ *
5
+ * The plain-launch ledger's definitive-rejection sets are deliberately NOT
6
+ * reused: they treat `Unauthorized` as definitive (a resume keeps S's
7
+ * directory and ledger then, because an earlier attempt may have admitted S)
8
+ * and they miss wire codes the resume path must recognise. AppSync strips a
9
+ * Lambda error's type, so every class is matched on `errorType` OR the stable
10
+ * message code that leads the GraphQL error message.
11
+ */
12
+ export type ResumeRefusalReason = 'NOT_FOUND' | 'NOT_ORCHESTRATION' | 'STILL_ACTIVE' | 'ALREADY_RESUMED' | 'WINDOW_CLOSED';
13
+ export type ResumeCreateOutcome =
14
+ /** S was never admitted: remove S's prepared directory by exact path, drop the ledger. */
15
+ {
16
+ kind: 'rejected';
17
+ cause: 'resume-not-allowed';
18
+ reason: ResumeRefusalReason;
19
+ successorSessionId?: string;
20
+ } | {
21
+ kind: 'rejected';
22
+ cause: 'limit' | 'rate' | 'account-deletion' | 'session-id-reserved' | 'invalid-status' | 'old-backend' | 'invalid-request' | 'resume-unavailable';
23
+ }
24
+ /** Keep S's directory and the ledger: the outcome is unknown until a definitive answer. */
25
+ | {
26
+ kind: 'ambiguous';
27
+ cause: 'unauthorized' | 'transient';
28
+ }
29
+ /** Keep S's directory, drop the ledger. */
30
+ | {
31
+ kind: 'conflict';
32
+ cause: 'creation-request-conflict';
33
+ }
34
+ /** S WAS admitted and then retired before its shell ran: keep the directory (its only carry-over), drop the ledger. */
35
+ | {
36
+ kind: 'admitted-then-retired';
37
+ };
38
+ /** Parse the backend's definitive claim refusal, if this error is one. */
39
+ export declare function parseResumeNotAllowed(error: unknown): {
40
+ reason: ResumeRefusalReason;
41
+ successorSessionId?: string;
42
+ } | null;
43
+ export declare function classifyResumeCreateFailure(error: unknown): ResumeCreateOutcome;