@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,28 @@
1
+ export declare const E2E_FAULT_ENV = "CODEVIBE_E2E_FAULT";
2
+ export declare const E2E_RELEASE_FILE_ENV = "CODEVIBE_E2E_RELEASE_FILE";
3
+ declare const E2E_FAULTS: readonly ["exit-without-teardown", "hold-after-resume-lock", "exit-after-ledger-seal", "exit-after-resume-create", "fail-resume-publish", "exit-after-retire-note"];
4
+ export type E2eFault = (typeof E2E_FAULTS)[number];
5
+ /** P61B-S1-N1: the hold never outlives a stalled harness. */
6
+ export declare const E2E_HOLD_MAX_MS: number;
7
+ export declare function e2eFault(env?: NodeJS.ProcessEnv): E2eFault | null;
8
+ /** E2E-8 / E2E-9 / E2E-11: end a resume at one of its crash points, naming it on stdout first. */
9
+ export declare function exitAtResumeFaultPoint(point: 'exit-after-ledger-seal' | 'exit-after-resume-create' | 'exit-after-retire-note', predecessorSessionId: string, opts?: {
10
+ env?: NodeJS.ProcessEnv;
11
+ write?: (text: string) => void;
12
+ exit?: (code: number) => void;
13
+ }): void;
14
+ /** E2E-10: fail S's publish while finishing a non-targeted interrupted resume. */
15
+ export declare function failResumePublishIfFaulted(env?: NodeJS.ProcessEnv): void;
16
+ /** Name an active fault on stderr; returns whether one is active. */
17
+ export declare function warnIfE2eFault(env?: NodeJS.ProcessEnv, write?: (text: string) => void): boolean;
18
+ /** E2E-3: hold after taking P's resume lock until the release file appears, or `E2E_HOLD_MAX_MS` passes. */
19
+ export declare function holdAfterResumeLock(predecessorSessionId: string, opts?: {
20
+ env?: NodeJS.ProcessEnv;
21
+ write?: (text: string) => void;
22
+ exists?: (file: string) => boolean;
23
+ sleep?: (ms: number) => Promise<void>;
24
+ now?: () => number;
25
+ }): Promise<void>;
26
+ /** E2E-4: end the process without teardown once the first exchange is durable. */
27
+ export declare function exitWithoutTeardownIfFaulted(env?: NodeJS.ProcessEnv, exit?: (code: number) => void): void;
28
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * P61 session resume — read a session's hosted context copy
3
+ * (SESSION-RESUME-DESIGN.md §8.1 "Following the hosted chain", §8.4, §9.3).
4
+ *
5
+ * The hosted copy can hold rows that are on no chain (an AGENT_TURN batch
6
+ * uploaded before a local rollback, A7). The loader therefore decrypts every
7
+ * row, follows the chain from the item whose `prev_hash` is null, resolves
8
+ * forks against the clean-exit `contextTail`, and runs the selected chain
9
+ * through the same pure verifier the local log uses. It never trusts row
10
+ * order or completeness from the server.
11
+ */
12
+ import { type ContextItem } from '../orchestration-shell/context-items';
13
+ import type { ContextItemRowWire } from '../types/session';
14
+ export interface ContextTail {
15
+ seq: string;
16
+ itemSha256: string;
17
+ }
18
+ /** One decrypted hosted row: its plaintext JSON (hashed for the chain) and parsed item. */
19
+ export interface HostedEntry {
20
+ json: string;
21
+ item: Pick<ContextItem, 'seq' | 'prev_hash'>;
22
+ }
23
+ export type HostedChainSelection =
24
+ /** The chain ends exactly at contextTail. */
25
+ {
26
+ kind: 'complete';
27
+ entries: HostedEntry[];
28
+ }
29
+ /** The chain contains contextTail and continues past it (writes after a last-ditch tail). */
30
+ | {
31
+ kind: 'continues-past-tail';
32
+ entries: HostedEntry[];
33
+ }
34
+ /** Branches fork after contextTail: everything after the fork is left out (one warning line). */
35
+ | {
36
+ kind: 'cut-at-tail';
37
+ entries: HostedEntry[];
38
+ }
39
+ /** The single chain ends before contextTail: the last updates were never uploaded. */
40
+ | {
41
+ kind: 'short';
42
+ entries: HostedEntry[];
43
+ }
44
+ /** No contextTail (crash, signal, sweep, phone end): "didn't end cleanly". */
45
+ | {
46
+ kind: 'no-tail';
47
+ entries: HostedEntry[];
48
+ }
49
+ /** A branch passes contextTail's seq without containing it, or holds a different item at that seq. */
50
+ | {
51
+ kind: 'integrity';
52
+ }
53
+ /** A fork no contextTail can resolve. */
54
+ | {
55
+ kind: 'ambiguous';
56
+ };
57
+ /** Decrypt one hosted row to its plaintext JSON (desktop-side only). */
58
+ export declare function decryptHostedRow(row: Pick<ContextItemRowWire, 'ciphertext'>, sessionKeyBase64: string): string;
59
+ /**
60
+ * §8.4 — choose the chain to resume from, given every decrypted hosted row.
61
+ * Pure: tested over fixtures for each table row.
62
+ */
63
+ export declare function selectHostedChain(entries: HostedEntry[], tail: ContextTail | null): HostedChainSelection;
64
+ export type HostedLoadResult = {
65
+ ok: true;
66
+ selection: Exclude<HostedChainSelection, {
67
+ kind: 'integrity';
68
+ } | {
69
+ kind: 'ambiguous';
70
+ }>;
71
+ items: ContextItem[];
72
+ } | {
73
+ ok: false;
74
+ reason: 'integrity' | 'ambiguous' | 'undecryptable';
75
+ };
76
+ /**
77
+ * Decrypt, select and verify. A row that does not decrypt with P's key is an
78
+ * integrity failure (the key unwrapped P's envelope, so every genuine row
79
+ * decrypts); a row that decrypts but does not parse is ignored as off-chain.
80
+ */
81
+ export declare function loadHostedChain(sessionId: string, rows: ContextItemRowWire[], sessionKeyBase64: string, tail: ContextTail | null): HostedLoadResult;
82
+ /** Page `listContextItems` to the end (design §9.3). */
83
+ export declare function fetchAllHostedRows(client: {
84
+ listContextItems(sessionId: string, nextToken?: string | null): Promise<{
85
+ items: ContextItemRowWire[];
86
+ nextToken: string | null;
87
+ }>;
88
+ }, sessionId: string): Promise<ContextItemRowWire[]>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * P61 §8.4 for P's local log: its verified chain against P's clean-exit
3
+ * `contextTail`. A local log is one chain, so the hash of item i is the next
4
+ * item's `prev_hash`, and the last item's hash is the reader's tail hash. The
5
+ * outcomes are the hosted loader's, minus forks.
6
+ */
7
+ import type { ContextItem } from '../orchestration-shell/context-items';
8
+ import type { ContextTail } from './hosted-loader';
9
+ export type LocalCompleteness = 'complete' | 'continues-past-tail' | 'short' | 'no-tail' | 'integrity';
10
+ export declare function checkLocalCompleteness(items: readonly ContextItem[], localTail: ContextTail | null, contextTail: ContextTail | null): LocalCompleteness;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * P61 session resume — local owner records and resume locks
3
+ * (SESSION-RESUME-DESIGN.md §6.2, §8.5 INV-1/INV-2/INV-8/INV-11, §12.5;
4
+ * Phase B implementation design §2, §4).
5
+ *
6
+ * Every record appears with its full content atomically: it is written to a
7
+ * private temp file, fsynced, then `link()`ed into place, which fails if the
8
+ * name exists. A record that cannot be parsed or validated is classified
9
+ * fail-closed (an unreadable owner record means read-only; an unreadable lock
10
+ * means held) and is never treated as a dead process. Records are never
11
+ * rewritten in place; finishing and releasing add a second record.
12
+ */
13
+ import { type ProcessIdentity } from '../local-executor/process-identity';
14
+ /** `~/.codevibe/resume-state` — a sibling of `context-items`, never inside a session's log directory. */
15
+ export declare function resumeStateRoot(): string;
16
+ export declare function resumeStateDir(sessionId: string): string;
17
+ /** A process identity is usable only when well-formed (code map finding 6). */
18
+ export declare function isValidProcessIdentity(value: unknown): value is ProcessIdentity;
19
+ /**
20
+ * Create `finalPath` with `content` iff it does not exist. The full content is
21
+ * durable before the name appears, so a reader never sees a torn record.
22
+ */
23
+ export declare function createRecordAtomically(finalPath: string, content: string,
24
+ /** `false`: the directory must already exist (a lock release never recreates a swept directory). */
25
+ options?: {
26
+ createDirectory?: boolean;
27
+ }): Promise<'created' | 'exists'>;
28
+ export type OwnerRecordState = 'finished' | 'dead' | 'live' | 'absent' | 'unreadable';
29
+ /**
30
+ * Written once at shell start, before the first context append. A failure
31
+ * never blocks the shell: the record is then absent and resumers stay
32
+ * read-only (INV-3).
33
+ */
34
+ export declare function writeOwnerRecord(sessionId: string, userId: string): Promise<boolean>;
35
+ /**
36
+ * The last teardown step, after the INACTIVE update and the last log write.
37
+ * Never called when teardown did not converge.
38
+ */
39
+ export declare function markOwnerFinished(sessionId: string): Promise<boolean>;
40
+ /**
41
+ * Classify a session's owner record on this computer (design §6.2). A
42
+ * finished mark wins even while its process is alive (S1R8 R8-L1a). A record
43
+ * for another user (INV-11), another session, or with a malformed identity is
44
+ * unreadable, never dead.
45
+ */
46
+ export declare function classifyOwnerRecord(sessionId: string, signedInUserId: string, isAlive?: (identity: ProcessIdentity) => Promise<boolean>): Promise<OwnerRecordState>;
47
+ export type OwnerTakeover = 'taken' | 'live' | 'finished' | 'unreadable' | 'raced';
48
+ /**
49
+ * Make this process S's recorded owner when finishing an interrupted resume
50
+ * (§6.2 step 2, INV-1/INV-2/INV-8): the replaying process starts S's shell or
51
+ * rebuilds its log, so S's owner record must name it, not the dead process
52
+ * that prepared S. Allowed only under P's resume lock, and only when the
53
+ * recorded owner is dead or absent. A dead record is renamed aside by exact
54
+ * path (records are never rewritten in place or deleted); until the new one
55
+ * appears, readers see "absent", which only makes them read-only.
56
+ */
57
+ export declare function takeOverOwnerRecord(sessionId: string, signedInUserId: string, isAlive?: (identity: ProcessIdentity) => Promise<boolean>,
58
+ /**
59
+ * `/resume` hand-off (§12.6): the pid of this process's live parent, passed
60
+ * only after this process adopted P's lock from that parent. A live owner
61
+ * with exactly this pid prepared S and is waiting on this child, so it is
62
+ * handed over like a dead one.
63
+ */
64
+ handOffParentPid?: number): Promise<OwnerTakeover>;
65
+ export interface ResumeLockHandle {
66
+ predecessorSessionId: string;
67
+ generation: number;
68
+ nonce: string;
69
+ path: string;
70
+ }
71
+ export type ResumeLockOutcome = {
72
+ acquired: true;
73
+ handle: ResumeLockHandle;
74
+ } | {
75
+ acquired: false;
76
+ reason: 'held';
77
+ };
78
+ /**
79
+ * At most one resumer of P proceeds on this computer (INV-2). The highest
80
+ * generation decides: released or dead ⇒ create the next generation; live,
81
+ * unreadable or foreign ⇒ held. Creating the next generation is exclusive,
82
+ * and the acquisition is re-checked once it exists (`acquisitionStands`).
83
+ */
84
+ export declare function acquireResumeLock(predecessorSessionId: string, signedInUserId: string, isAlive?: (identity: ProcessIdentity) => Promise<boolean>,
85
+ /**
86
+ * Test seams: `beforeCreate` runs between reading the generations and creating the next one;
87
+ * `afterOwnCheck` between the re-check's read of this process's lock file and its tombstone check.
88
+ */
89
+ seams?: {
90
+ beforeCreate?: () => Promise<void>;
91
+ afterOwnCheck?: () => Promise<void>;
92
+ }): Promise<ResumeLockOutcome>;
93
+ /**
94
+ * Release by adding the generation's released mark (never by deleting the lock), bound to the lock by
95
+ * its nonce. Only while this handle's lock file is still in place, and never by creating a directory:
96
+ * a release after a sweep renamed the directory away must not recreate it (P61B-S1-R4-F1).
97
+ */
98
+ export declare function releaseResumeLock(handle: ResumeLockHandle): Promise<void>;
99
+ /**
100
+ * `/resume` hand-off (Phase B design §4): the child adopts generation `n`
101
+ * only if the lock's nonce matches the one its parent passed and the lock's
102
+ * holder is the child's live parent process.
103
+ */
104
+ export declare function adoptHandedOffResumeLock(token: string, signedInUserId: string, parentPid?: number, isAlive?: (identity: ProcessIdentity) => Promise<boolean>): Promise<ResumeLockHandle | null>;
105
+ export declare function resumeLockHandOffToken(handle: ResumeLockHandle): string;
106
+ /** Same window as the context-items retention sweep (`CONTEXT_ITEMS_RETENTION_MS`). */
107
+ export declare const RESUME_STATE_RETENTION_MS: number;
108
+ /** Fire-and-forget from shell boot, at most once per process (as `scheduleContextItemsGc`). */
109
+ export declare function scheduleResumeStateGc(activeSessionId: string): void;
110
+ /**
111
+ * The resume-state retention sweep: a session directory whose newest file is older than the window
112
+ * is removed, never the active session's, never one with a live owner or a held lock, and never on an
113
+ * unreadable mtime. A tombstone left by an earlier sweep is removed once nothing uses it. Returns the
114
+ * number of session directories removed.
115
+ */
116
+ export declare function gcResumeState(deps: {
117
+ activeSessionId: string;
118
+ nowMs?: number;
119
+ isAlive?: (identity: ProcessIdentity) => Promise<boolean>;
120
+ }): Promise<number>;
121
+ export declare function resetResumeStateGcForTests(): void;
@@ -0,0 +1,17 @@
1
+ import type { UnlockedResumeRow } from './resume-rows';
2
+ export declare const PICKER_TITLE = "Resume a session (ended in the last 7 days)";
3
+ export declare const PICKER_KEYS = "\u2191/\u2193 choose \u00B7 Enter resume \u00B7 Esc cancel";
4
+ export declare const PICKER_EMPTY = "No sessions you can resume from the last 7 days.";
5
+ export declare const PICKER_VISIBLE_ROWS = 20;
6
+ export interface PickerRow {
7
+ sessionId: string;
8
+ label: string;
9
+ locked: boolean;
10
+ }
11
+ export declare function computerLabel(creatorDeviceId: string | null, thisDeviceId: string, platformsByDeviceId: ReadonlyMap<string, string>): string;
12
+ export declare function toPickerRows(rows: readonly UnlockedResumeRow[], ctx: {
13
+ nowMs: number;
14
+ thisDeviceId: string;
15
+ platformsByDeviceId: ReadonlyMap<string, string>;
16
+ home?: string;
17
+ }): PickerRow[];
@@ -0,0 +1,40 @@
1
+ /**
2
+ * P61 publish-then-retire (INV-6; Phase B implementation design §5). For an
3
+ * admitted S whose shell will not start here — a non-targeted interrupted
4
+ * resume finished at a later entry point — S is first published (its hosted
5
+ * outbox drained, bounded), then retired with its marker as the clean-exit
6
+ * tail, so it becomes an ordinary resumable session. A failure leaves a retire
7
+ * note that every later resume entry point retries, draining first.
8
+ */
9
+ import { type UpdateSessionInput } from '../types/session';
10
+ export declare const PUBLISH_DRAIN_TIMEOUT_MS = 30000;
11
+ export interface PublishRetireDeps {
12
+ /** Start hosted sync for S's log and drain it; resolves only when no upload is in flight. */
13
+ drain(successorSessionId: string): Promise<void>;
14
+ updateSession(input: UpdateSessionInput): Promise<unknown>;
15
+ writeRetireNote(note: {
16
+ successorSessionId: string;
17
+ predecessorSessionId: string;
18
+ marker: {
19
+ seq: string;
20
+ itemSha256: string;
21
+ };
22
+ encryptedLastPrompt?: string;
23
+ }): Promise<void>;
24
+ clearRetireNote(successorSessionId: string): Promise<void>;
25
+ timeoutMs?: number;
26
+ }
27
+ export type PublishRetireResult = 'retired' | 'noted';
28
+ /** The message the user sees for a finished non-targeted resume (design §6.2 step 2). */
29
+ export declare function finishedResumeMessage(predecessorSessionId: string, successorSessionId: string): string;
30
+ export declare function publishThenRetire(args: {
31
+ successorSessionId: string;
32
+ predecessorSessionId: string;
33
+ marker: {
34
+ seq: string;
35
+ itemSha256: string;
36
+ };
37
+ /** S's picker preview: its last carried prompt, sealed with S's key (P61B-S1-L12). */
38
+ encryptedLastPrompt?: string;
39
+ deps: PublishRetireDeps;
40
+ }): Promise<PublishRetireResult>;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * P61 `/resume` relaunch (design §5.3 step 2–3, §12.6; Phase B design B-12, §4).
3
+ *
4
+ * After a converged teardown the parent runs `codevibe --resume <P>` as a
5
+ * child with the terminal handed over (`spawnSync`, `stdio: 'inherit'`) and
6
+ * exits with the child's status. The child's environment carries the nesting
7
+ * depth and the hand-off token for P's resume lock, which the child adopts
8
+ * only from its own live parent (`adoptHandedOffResumeLock`).
9
+ */
10
+ import { type SpawnSyncReturns } from 'node:child_process';
11
+ export declare const RESUME_DEPTH_ENV = "CODEVIBE_RESUME_DEPTH";
12
+ export declare const RESUME_LOCK_ENV = "CODEVIBE_RESUME_LOCK";
13
+ /** Switches nest at most this deep (§5.3 step 3). */
14
+ export declare const MAX_RESUME_DEPTH = 8;
15
+ /** An absent or non-numeric value counts as 0 (S2-A5). */
16
+ export declare function parseResumeDepth(value: string | undefined): number;
17
+ export declare function resumeCommandText(predecessorSessionId: string): string;
18
+ /** §5.3 step 2: teardown failed or hit its deadline. */
19
+ export declare function switchFailedMessage(predecessorSessionId: string): string;
20
+ /** §10: a `/resume` switch whose child fails after the current session C has ended. */
21
+ export declare function previousSessionEndedMessage(currentSessionId: string): string;
22
+ /**
23
+ * The exit status of a process that printed the §10 "previous session also ended" line. A parent
24
+ * whose child exits with it passes it on without a second line, so a nested switch names only the
25
+ * most recent ended session.
26
+ */
27
+ export declare const PREVIOUS_SESSION_HINTED_EXIT = 3;
28
+ /** §5.3 step 3: at the depth cap the command is printed instead of relaunching. */
29
+ export declare function depthCapMessage(predecessorSessionId: string): string;
30
+ export interface RelaunchRequest {
31
+ predecessorSessionId: string;
32
+ /** `resumeLockHandOffToken(handle)` for P's lock, held by this process. */
33
+ lockToken: string;
34
+ }
35
+ export type RelaunchOutcome = {
36
+ kind: 'relaunched';
37
+ status: number;
38
+ }
39
+ /** `failed` is false only at the depth cap, where printing the command is the designed outcome. */
40
+ | {
41
+ kind: 'printed';
42
+ text: string;
43
+ failed: boolean;
44
+ };
45
+ /**
46
+ * Relaunch the CLI as `--resume <P>` and return the child's exit status, or the
47
+ * command to print at the depth cap. `cliPath` is this process's entry script.
48
+ */
49
+ export declare function relaunchResume(args: {
50
+ request: RelaunchRequest;
51
+ cliPath: string;
52
+ env?: NodeJS.ProcessEnv;
53
+ execPath?: string;
54
+ spawn?: (command: string, argv: string[], options: {
55
+ stdio: 'inherit';
56
+ env: NodeJS.ProcessEnv;
57
+ }) => Pick<SpawnSyncReturns<Buffer>, 'status' | 'signal' | 'error'>;
58
+ }): RelaunchOutcome;
59
+ /**
60
+ * B-12 — `main`'s last step after the shell returns: relaunch for a converged
61
+ * `/resume` switch and exit with the child's status; print the command when
62
+ * the switch could not run. Otherwise the shell's own code stands.
63
+ */
64
+ export declare function finishShellExit(code: number, exit: {
65
+ relaunch?: RelaunchRequest;
66
+ switchFailedFor?: string;
67
+ currentSessionId?: string;
68
+ }, opts: {
69
+ cliPath: string;
70
+ write(text: string): void;
71
+ relaunch?: typeof relaunchResume;
72
+ }): number;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * P61 §12.4 — the conversation entries a resumed session is seeded with,
3
+ * built from the carried items (P's verified items as the carry-over chose
4
+ * them, in P's seq order). Pure; the shell dispatches the result once as
5
+ * `RESTORE_CONVERSATION` before its first turn.
6
+ *
7
+ * - A human turn → `user-message`.
8
+ * - An agent reply → `advisory` labelled with the agent (§5.4).
9
+ * - A conductor reply → one `advisory` per recorded advisory segment (§12.9),
10
+ * with that segment's source and hand-off flag; without valid segments, one
11
+ * entry flagged `handoffExcluded` (the conservative legacy rule).
12
+ * - A session-scoped conductor decision → `planner-decision` (action and
13
+ * rationale), never a `session_resumed` marker. A `brainstorm` decision's
14
+ * `needsRepositoryContext` is recomputed here, in carried order, with the
15
+ * shipped rule (design §12.4; it is not in the logged decision body).
16
+ * - A carried task → one display-only line under the exchange that started it,
17
+ * `↳ task: <spec> — <verdict>` (§5.4; P61B-S1-F7).
18
+ * Rules, open questions and the marker add no entry here; the §5.4 summary
19
+ * shows the rule count. Entries keep their
20
+ * full text — the hand-off collectors read them exactly as they read P's —
21
+ * and the renderer shortens long restored replies for display only.
22
+ */
23
+ import type { ContextItem } from '../orchestration-shell/context-items';
24
+ import type { RestoredConversationEntry } from '../orchestration-shell/types';
25
+ /** The last carried human prompt, as the restored view shows it (task items excluded); null if none. */
26
+ export declare function lastCarriedHumanText(items: readonly ContextItem[]): string | null;
27
+ export declare function buildRestoredEntries(items: readonly ContextItem[]): RestoredConversationEntry[];
@@ -0,0 +1,52 @@
1
+ /**
2
+ * P61 §6.2 step 1 — read the lists, then build the target-matching view.
3
+ * `listResumableSessions` is paged to the end (B-11). The account's
4
+ * `listSessions` read is the one the pre-listing retire uses, so the
5
+ * `--continue` comparison and the retire evaluate `willRetire` on the same
6
+ * rows (C2-F5). Interrupted resumes come from this computer's ledgers.
7
+ */
8
+ import type { ResumeDetails } from '../types/session';
9
+ import type { OwnerRecordState } from './local-records';
10
+ import type { ResumeLedgerRead } from './resume-ledger';
11
+ import { type UnlockedResumeRow } from './resume-rows';
12
+ import type { TargetLedger, TargetSession } from './resume-target';
13
+ import { type RetireCandidateRow } from './will-retire';
14
+ export interface ResumeCandidateDeps {
15
+ nowMs(): number;
16
+ /** The session running in this process, if any (the `/resume` parent). */
17
+ ownSessionId?: string;
18
+ listResumableAll(): Promise<ResumeDetails[]>;
19
+ listSessions(): Promise<RetireCandidateRow[]>;
20
+ getResumeDetails(sessionId: string): Promise<ResumeDetails | null>;
21
+ unlock(details: ResumeDetails): Promise<UnlockedResumeRow>;
22
+ ownerState(sessionId: string): Promise<OwnerRecordState>;
23
+ listLedgers(): Promise<ResumeLedgerRead[]>;
24
+ }
25
+ export interface ResumeCandidates {
26
+ /** Resumable now, unlocked where this computer can. */
27
+ resumable: UnlockedResumeRow[];
28
+ /** ACTIVE rows this launch retires (`willRetire`), with their details unlocked. */
29
+ retiring: Array<{
30
+ row: RetireCandidateRow;
31
+ unlocked: UnlockedResumeRow | null;
32
+ }>;
33
+ /** The exact `willRetire` set, for step 3. */
34
+ willRetireIds: Set<string>;
35
+ ledgers: Extract<ResumeLedgerRead, {
36
+ state: 'ok';
37
+ }>[];
38
+ /** The target-matching view of all of the above. */
39
+ targetSessions: TargetSession[];
40
+ targetLedgers: TargetLedger[];
41
+ }
42
+ export declare function collectResumeCandidates(deps: ResumeCandidateDeps): Promise<ResumeCandidates>;
43
+ /**
44
+ * §6.2: the steps run "before the picker opens", so a row this launch retires
45
+ * (`willRetire`) is listed with the resumable ones: "Already retired before
46
+ * listing (step 3)" (§6.2 table). It is dated by its freshness timestamp, the
47
+ * value `willRetire` and the `--continue` comparison use (§12.2.3). A retiring
48
+ * row whose details could not be read is left out: it can be neither shown nor
49
+ * unlocked. (E2E-4, other computer, e2e-container-b-r1: both pickers listed only
50
+ * the step-1 resumable read, so a crashed session stayed hidden after 10 min.)
51
+ */
52
+ export declare function pickerCandidates(c: ResumeCandidates): UnlockedResumeRow[];
@@ -0,0 +1,103 @@
1
+ /**
2
+ * P61 — resume one chosen predecessor P (design §6.2 "P still ACTIVE", §8.1,
3
+ * §8.4, §10, §12.2.2). Every effect is injected.
4
+ *
5
+ * Order: refusals from `getResumeDetails` → unlock P here → P's resume lock →
6
+ * source (local or hosted) → completeness → carry-over plan → create S.
7
+ * The lock is kept on success (the caller releases it once S's shell has
8
+ * started) and released on every other outcome.
9
+ *
10
+ * When P is itself a successor (its row names `resumedFromSessionId`), §8.4's
11
+ * "Checking S itself" applies: if P's verified chain lacks its own carry-over
12
+ * marker, the carry-over is rebuilt from P's predecessor while that session's
13
+ * window is open and this computer holds its key (step 2), or the user is
14
+ * asked whether to continue without it (step 3).
15
+ */
16
+ import type { ContextItem, ReadOnlyContextLogResult } from '../orchestration-shell/context-items';
17
+ import { type ContextItemRowWire, type ResumeDetails } from '../types/session';
18
+ import { type CarryOverPlan } from './carry-over';
19
+ import { type CompletenessKind, type ContinueLabel, type CopyContext } from './completeness-copy';
20
+ import type { OwnerRecordState, ResumeLockHandle, ResumeLockOutcome } from './local-records';
21
+ import { type ResumeCreateDeps, type ResumeCreateResult } from './resume-create';
22
+ import type { UnlockedResumeRow } from './resume-rows';
23
+ export interface ResumeChosenDeps {
24
+ userId: string;
25
+ deviceId: string;
26
+ cwd: string;
27
+ isTTY: boolean;
28
+ /** Clock for §8.4 step 2's window check; defaults to `Date.now`. */
29
+ nowMs?: () => number;
30
+ getResumeDetails(sessionId: string): Promise<ResumeDetails | null>;
31
+ unlock(details: ResumeDetails): Promise<UnlockedResumeRow>;
32
+ ownerState(sessionId: string): Promise<OwnerRecordState>;
33
+ /** Retire a still-ACTIVE P whose owner here is finished or dead (as the sweep does). */
34
+ retireActive(sessionId: string): Promise<void>;
35
+ acquireLock(predecessorSessionId: string): Promise<ResumeLockOutcome>;
36
+ releaseLock(handle: ResumeLockHandle): Promise<void>;
37
+ readLocal(predecessorSessionId: string): Promise<ReadOnlyContextLogResult>;
38
+ /** P's post-mortem outbox drain; best effort, must settle (INV-1). */
39
+ drainPredecessorOutbox(predecessorSessionId: string): Promise<void>;
40
+ fetchHosted(predecessorSessionId: string): Promise<ContextItemRowWire[]>;
41
+ deviceLabel(deviceId: string | null): Promise<string>;
42
+ /** Arrow-key choice, Cancel by default; true = continue without them (or it). */
43
+ choose(prompt: string, continueLabel?: ContinueLabel): Promise<boolean>;
44
+ create: ResumeCreateDeps;
45
+ }
46
+ export type ResumeChosenResult = {
47
+ kind: 'resumed';
48
+ created: Extract<ResumeCreateResult, {
49
+ kind: 'admitted';
50
+ }>;
51
+ predecessor: UnlockedResumeRow;
52
+ plan: CarryOverPlan;
53
+ warnings: string[];
54
+ lock: ResumeLockHandle;
55
+ }
56
+ /** `/resume` parent: S prepared and its ledger sealed; P's lock is kept for the hand-off. */
57
+ | {
58
+ kind: 'prepared';
59
+ created: Extract<ResumeCreateResult, {
60
+ kind: 'prepared';
61
+ }>;
62
+ predecessor: UnlockedResumeRow;
63
+ plan: CarryOverPlan;
64
+ warnings: string[];
65
+ lock: ResumeLockHandle;
66
+ } | {
67
+ kind: 'refused';
68
+ text: string | null;
69
+ create?: ResumeCreateResult;
70
+ } | {
71
+ kind: 'cancelled';
72
+ };
73
+ export declare const isComplete: (kind: CompletenessKind) => kind is "complete" | "continues-past-tail";
74
+ export type LoadedChain = {
75
+ ok: true;
76
+ items: ContextItem[];
77
+ kind: CompletenessKind;
78
+ ctx: CopyContext;
79
+ } | {
80
+ ok: false;
81
+ text: string;
82
+ };
83
+ /**
84
+ * §8.1 and §8.4 for one session: choose its source, read the verified chain
85
+ * and classify it against the session's own `contextTail`. Only the session
86
+ * being resumed (whose resume lock this process holds) may act as its
87
+ * post-mortem writer; a §8.4 rebuild reads its source read-only.
88
+ */
89
+ export declare function loadChain(args: {
90
+ sessionId: string;
91
+ details: ResumeDetails;
92
+ sessionKey: string;
93
+ writer: boolean;
94
+ deps: ResumeChosenDeps;
95
+ }): Promise<LoadedChain>;
96
+ export declare function resumeChosenSession(args: {
97
+ predecessorSessionId: string;
98
+ /** True when §6.2 step 3 retires (or retired) this row in this launch. */
99
+ retiring: boolean;
100
+ /** The `/resume` parent: prepare S and seal its ledger, but leave the create to the child. */
101
+ prepareOnly?: boolean;
102
+ deps: ResumeChosenDeps;
103
+ }): Promise<ResumeChosenResult>;