@cleocode/contracts 2026.10.3 → 2026.10.5-rc.ci.672

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 (103) hide show
  1. package/dist/__tests__/cloud-replica-retirement.test.d.ts +10 -0
  2. package/dist/__tests__/cloud-replica-retirement.test.d.ts.map +1 -0
  3. package/dist/__tests__/cloud-replica-retirement.test.js +111 -0
  4. package/dist/__tests__/cloud-replica-retirement.test.js.map +1 -0
  5. package/dist/__tests__/cloud-status-checkpoint-reads.test.d.ts +10 -0
  6. package/dist/__tests__/cloud-status-checkpoint-reads.test.d.ts.map +1 -0
  7. package/dist/__tests__/cloud-status-checkpoint-reads.test.js +49 -0
  8. package/dist/__tests__/cloud-status-checkpoint-reads.test.js.map +1 -0
  9. package/dist/agent-registry.d.ts +5 -1
  10. package/dist/agent-registry.d.ts.map +1 -1
  11. package/dist/cloud/api.d.ts +156 -0
  12. package/dist/cloud/api.d.ts.map +1 -1
  13. package/dist/cloud/api.js +99 -1
  14. package/dist/cloud/api.js.map +1 -1
  15. package/dist/cloud/envelope.d.ts +3 -3
  16. package/dist/dispatch/operations-registry.js +1 -1
  17. package/dist/dispatch/operations-registry.js.map +1 -1
  18. package/dist/doctor.d.ts +79 -7
  19. package/dist/doctor.d.ts.map +1 -1
  20. package/dist/heavy-command-hook.d.ts +134 -5
  21. package/dist/heavy-command-hook.d.ts.map +1 -1
  22. package/dist/index.d.ts +10 -8
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/invariants/sync-write-invariants.d.ts.map +1 -1
  27. package/dist/invariants/sync-write-invariants.js +31 -6
  28. package/dist/invariants/sync-write-invariants.js.map +1 -1
  29. package/dist/ledger/index.d.ts +211 -0
  30. package/dist/ledger/index.d.ts.map +1 -0
  31. package/dist/ledger/index.js +122 -0
  32. package/dist/ledger/index.js.map +1 -0
  33. package/dist/nexus-account.d.ts +72 -1
  34. package/dist/nexus-account.d.ts.map +1 -1
  35. package/dist/nexus-account.js +5 -0
  36. package/dist/nexus-account.js.map +1 -1
  37. package/dist/nexus-cloud.d.ts +175 -0
  38. package/dist/nexus-cloud.d.ts.map +1 -1
  39. package/dist/nexus-cloud.js +17 -0
  40. package/dist/nexus-cloud.js.map +1 -1
  41. package/dist/nexus-first-run.d.ts +145 -0
  42. package/dist/nexus-first-run.d.ts.map +1 -0
  43. package/dist/nexus-first-run.js +54 -0
  44. package/dist/nexus-first-run.js.map +1 -0
  45. package/dist/nexus-vault.d.ts +113 -1
  46. package/dist/nexus-vault.d.ts.map +1 -1
  47. package/dist/nexus-vault.js +27 -0
  48. package/dist/nexus-vault.js.map +1 -1
  49. package/dist/operations/admin.d.ts +14 -1
  50. package/dist/operations/admin.d.ts.map +1 -1
  51. package/dist/operations/docs-lifecycle.d.ts +23 -0
  52. package/dist/operations/docs-lifecycle.d.ts.map +1 -0
  53. package/dist/operations/docs-lifecycle.js +29 -0
  54. package/dist/operations/docs-lifecycle.js.map +1 -0
  55. package/dist/operations/docs.d.ts +2 -11
  56. package/dist/operations/docs.d.ts.map +1 -1
  57. package/dist/operations/docs.js +3 -17
  58. package/dist/operations/docs.js.map +1 -1
  59. package/dist/project-context.d.ts +17 -2
  60. package/dist/project-context.d.ts.map +1 -1
  61. package/dist/release/evidence-atoms.d.ts +15 -7
  62. package/dist/release/evidence-atoms.d.ts.map +1 -1
  63. package/dist/release/evidence-atoms.js +16 -12
  64. package/dist/release/evidence-atoms.js.map +1 -1
  65. package/dist/resource-governor.d.ts +96 -0
  66. package/dist/resource-governor.d.ts.map +1 -1
  67. package/dist/resource-governor.js.map +1 -1
  68. package/dist/row-identity.d.ts +81 -0
  69. package/dist/row-identity.d.ts.map +1 -1
  70. package/dist/store-restore.d.ts +66 -0
  71. package/dist/store-restore.d.ts.map +1 -0
  72. package/dist/store-restore.js +9 -0
  73. package/dist/store-restore.js.map +1 -0
  74. package/dist/task.d.ts +14 -0
  75. package/dist/task.d.ts.map +1 -1
  76. package/dist/task.js.map +1 -1
  77. package/dist/tasks.d.ts +17 -17
  78. package/dist/workgraph.d.ts +28 -28
  79. package/package.json +6 -2
  80. package/src/__tests__/cloud-replica-retirement.test.ts +135 -0
  81. package/src/__tests__/cloud-status-checkpoint-reads.test.ts +60 -0
  82. package/src/agent-registry.ts +5 -1
  83. package/src/cloud/api.ts +125 -1
  84. package/src/dispatch/operations-registry.ts +1 -1
  85. package/src/doctor.ts +85 -7
  86. package/src/heavy-command-hook.ts +175 -5
  87. package/src/index.ts +58 -0
  88. package/src/invariants/sync-write-invariants.ts +81 -42
  89. package/src/ledger/index.ts +141 -0
  90. package/src/nexus-account.ts +82 -0
  91. package/src/nexus-cloud.ts +193 -0
  92. package/src/nexus-first-run.ts +165 -0
  93. package/src/nexus-vault.ts +124 -1
  94. package/src/operations/admin.ts +14 -1
  95. package/src/operations/conduit.ts +1 -1
  96. package/src/operations/docs-lifecycle.ts +31 -0
  97. package/src/operations/docs.ts +4 -19
  98. package/src/project-context.ts +17 -2
  99. package/src/release/evidence-atoms.ts +16 -12
  100. package/src/resource-governor.ts +100 -0
  101. package/src/row-identity.ts +98 -0
  102. package/src/store-restore.ts +70 -0
  103. package/src/task.ts +14 -0
package/src/cloud/api.ts CHANGED
@@ -69,8 +69,45 @@ export type Device = z.infer<typeof Device>;
69
69
  export const ListDevicesResult = z.object({ devices: z.array(Device) });
70
70
  export type ListDevicesResult = z.infer<typeof ListDevicesResult>;
71
71
 
72
+ // ---------- device credential scopes and E3 status (§4.1) ----------
73
+
74
+ /** What a device credential may do (§2.3). A signed-in session holds every scope. */
75
+ export const DeviceScope = z.enum([
76
+ 'account:read',
77
+ 'devices:read',
78
+ 'projects:read',
79
+ 'projects:write',
80
+ 'sync:read',
81
+ 'sync:write',
82
+ 'keys:read',
83
+ 'keys:write',
84
+ ]);
85
+ /** One device credential scope. */
86
+ export type DeviceScope = z.infer<typeof DeviceScope>;
87
+
88
+ /** E3 `GET /v1/status` query (§4.1): `replicaId` (a UUIDv7) requires `projectId`. */
89
+ export const StatusQuery = z
90
+ .object({ projectId: ProjectId.optional(), replicaId: ReplicaId.optional() })
91
+ .refine((q) => q.replicaId === undefined || q.projectId !== undefined, {
92
+ message: 'replicaId requires projectId',
93
+ });
94
+ /** The E3 status query. */
95
+ export type StatusQuery = z.infer<typeof StatusQuery>;
96
+
72
97
  // ---------- projects ----------
73
98
 
99
+ /**
100
+ * A new project's first data key, stored in the same transaction as its registration (onboarding B): key
101
+ * version 1, wrapped by the registrant's account master key. Needs keys:write on top of projects:write, and is
102
+ * honoured only when the call creates the project. Later versions stay rotations
103
+ * (PUT /v1/projects/:projectId/keys/:userId).
104
+ */
105
+ export const InitialProjectKey = z.object({
106
+ /** The project data key, wrapped by the registrant's master key at key version 1. */
107
+ wrappedProjectKey: Base64,
108
+ });
109
+ export type InitialProjectKey = z.infer<typeof InitialProjectKey>;
110
+
74
111
  export const RegisterProjectRequest = z.object({
75
112
  projectId: ProjectId,
76
113
  /**
@@ -87,6 +124,8 @@ export const RegisterProjectRequest = z.object({
87
124
  * caller's personal organization, which every account has.
88
125
  */
89
126
  organizationId: z.string().uuid().optional(),
127
+ /** The project's first data key (onboarding B). Honoured only when this call creates the project. */
128
+ initialKey: InitialProjectKey.optional(),
90
129
  });
91
130
  export type RegisterProjectRequest = z.infer<typeof RegisterProjectRequest>;
92
131
 
@@ -103,7 +142,16 @@ export const Project = z.object({
103
142
  });
104
143
  export type Project = z.infer<typeof Project>;
105
144
 
106
- export const RegisterProjectResult = z.object({ project: Project, streamId: StreamId });
145
+ export const RegisterProjectResult = z.object({
146
+ project: Project,
147
+ streamId: StreamId,
148
+ /**
149
+ * The key version `initialKey` is stored at for the caller: 1 when this call created the project with it,
150
+ * or repeated the caller's identical version 1; null when no initialKey was sent, or the project already
151
+ * existed with no key (initialKey ignored). Absent from servers older than initialKey.
152
+ */
153
+ initialKeyVersion: KeyVersion.nullable().optional(),
154
+ });
107
155
  export type RegisterProjectResult = z.infer<typeof RegisterProjectResult>;
108
156
 
109
157
  /**
@@ -162,6 +210,10 @@ export const HomeReplica = z.object({
162
210
  lastSyncAt: z.iso.datetime().nullable(),
163
211
  presence: ReplicaPresence.nullable(),
164
212
  presenceAt: z.iso.datetime().nullable(),
213
+ /** When the replica was retired (T123), else null. Absent from servers older than retirement. */
214
+ retiredAt: z.iso.datetime().nullable().optional(),
215
+ /** The retired replica's named successor, else null (not retired, or retired with none). */
216
+ successor: ReplicaId.nullable().optional(),
165
217
  });
166
218
  export type HomeReplica = z.infer<typeof HomeReplica>;
167
219
 
@@ -501,6 +553,15 @@ export type CreateCheckpointResult = z.infer<typeof CreateCheckpointResult>;
501
553
  export const ListCheckpointsResult = z.object({ checkpoints: z.array(Checkpoint) });
502
554
  export type ListCheckpointsResult = z.infer<typeof ListCheckpointsResult>;
503
555
 
556
+ /**
557
+ * E29 `GET …/checkpoints/head` and E30 `GET …/checkpoints/:checkpointId` (cleo-nexus T122, contract
558
+ * v2.27): one checkpoint, shaped as a list item. Both answer 404 when there is no head, or the id is
559
+ * unknown, malformed or on another stream.
560
+ */
561
+ export const GetCheckpointResult = z.object({ checkpoint: Checkpoint });
562
+ /** One checkpoint read by id or as the head. */
563
+ export type GetCheckpointResult = z.infer<typeof GetCheckpointResult>;
564
+
504
565
  // ---------- blobs ----------
505
566
 
506
567
  export const PresignUploadRequest = z.object({
@@ -585,6 +646,69 @@ export type ListedLease = z.infer<typeof ListedLease>;
585
646
  export const ListLeasesResult = z.object({ leases: z.array(ListedLease) });
586
647
  export type ListLeasesResult = z.infer<typeof ListLeasesResult>;
587
648
 
649
+ // ---------- replica retirement (T123; cleocode journal spec §1.3, §3.5 D5) ----------
650
+
651
+ /**
652
+ * A journal transaction id, `<emittingReplicaId>:<localTxnSeq>`: the replica that emitted the transaction and its own
653
+ * transaction counter. Only the format is checked: the emitting replica may be the successor (a rebind), not the
654
+ * retired one.
655
+ */
656
+ const JournalTxnId = z
657
+ .string()
658
+ .regex(
659
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}:(0|[1-9][0-9]{0,15})$/,
660
+ 'expected <uuid>:<non-negative integer>',
661
+ );
662
+
663
+ /**
664
+ * E31 `POST /v1/streams/:streamId/replicas/:replicaId/retirements` (cleo-nexus T123, contract v2.28): the replica
665
+ * writes no segment past `lastReplicaSeq` (none at all when it is null). The journal's own `retire` transaction
666
+ * travels E2E-encrypted; this is the server's signed copy, so it can refuse later appends and show the retirement in
667
+ * the reads.
668
+ */
669
+ export const RetireReplicaRequest = z.object({
670
+ /** The signing device. It must be the calling device. */
671
+ deviceId: DeviceId,
672
+ /** The replica that carries on this one's history, or null. Never the retired replica itself. */
673
+ successor: ReplicaId.nullable(),
674
+ /**
675
+ * The replica's last segment: at least its highest appended replicaSeq, and late segments up to it still land. Null
676
+ * when the replica wrote nothing: then no segment of it is stored, and none may land.
677
+ */
678
+ lastReplicaSeq: z.number().int().nonnegative().nullable(),
679
+ /**
680
+ * The journal transaction that carries the retire (`${emittingReplicaId}:${localTxnSeq}`), or null for an owner's
681
+ * retirement with no journal transaction.
682
+ */
683
+ txnId: JournalTxnId.nullable(),
684
+ /** Ed25519 by the calling device over replicaRetireMessage(...). */
685
+ signature: Base64,
686
+ });
687
+ /** The E31 request body. */
688
+ export type RetireReplicaRequest = z.infer<typeof RetireReplicaRequest>;
689
+
690
+ /** A stored retirement: one per (stream, replica), insert-only. */
691
+ export const ReplicaRetirement = z.object({
692
+ streamId: StreamId,
693
+ replicaId: ReplicaId,
694
+ successor: ReplicaId.nullable(),
695
+ /** Null: the replica wrote nothing. */
696
+ lastReplicaSeq: z.number().int().nonnegative().nullable(),
697
+ /** The device that signed it: the replica's own, a project owner's, or (home streams) another of the account's. */
698
+ signerDeviceId: DeviceId,
699
+ txnId: JournalTxnId.nullable(),
700
+ /** The signer's Ed25519 over replicaRetireMessage(...), so a client or an auditor can verify the record. */
701
+ signature: Base64,
702
+ retiredAt: z.iso.datetime(),
703
+ });
704
+ /** A stored replica retirement. */
705
+ export type ReplicaRetirement = z.infer<typeof ReplicaRetirement>;
706
+
707
+ /** 201 for a new retirement, 200 with the stored one for a repeat with the same successor and lastReplicaSeq. */
708
+ export const RetireReplicaResult = z.object({ retirement: ReplicaRetirement });
709
+ /** The E31 answer. */
710
+ export type RetireReplicaResult = z.infer<typeof RetireReplicaResult>;
711
+
588
712
  // ---------- keys (wrapped project keys; the account master key is escrowed on the server, T082: encryption at rest, not end-to-end) ----------
589
713
 
590
714
  /**
@@ -9,7 +9,7 @@
9
9
  * @since SG-ARCH-SOLID Saga T9831 · E-CLI-BOUNDARY T9833 · T10061
10
10
  */
11
11
 
12
- import { DOCS_LIFECYCLE_STATUSES } from '../operations/docs.js';
12
+ import { DOCS_LIFECYCLE_STATUSES } from '../operations/docs-lifecycle.js';
13
13
  import { OUTPUT_CONTRACTS } from '../operations/output-contracts-data.js';
14
14
  import { tasksUpdateInputContract } from '../operations/tasks.js';
15
15
  import type { CanonicalDomain, Gateway, OperationDef, ParamDef, Tier } from './operation-def.js';
package/src/doctor.ts CHANGED
@@ -699,11 +699,10 @@ export interface DbSubstrateProjectSurvey {
699
699
  /** Absolute path to the project root that was surveyed. */
700
700
  projectRoot: string;
701
701
  /**
702
- * Stable identifier for the project — currently the `base64url(path)`
703
- * truncated to 32 chars, matching the convention used by `cleo nexus`
704
- * project resolution.
702
+ * Declared portable project identity, or null when identity is missing.
703
+ * A survey never creates an identity or derives it from a filesystem path.
705
704
  */
706
- projectId: string;
705
+ projectId: string | null;
707
706
  /** Per-role survey result, keyed by canonical role name. */
708
707
  dbs: Readonly<Record<string, DbSubstrateEntry>>;
709
708
  }
@@ -1125,8 +1124,71 @@ export interface SupersededStoreConflict {
1125
1124
  targetTable: string;
1126
1125
  /** Legacy rows left uncopied. */
1127
1126
  rows: number;
1128
- /** Why they were left. */
1129
- reason: 'live-authoritative' | 'collides-with-live';
1127
+ /**
1128
+ * Why they were left. `id-collision-undecided` (full mode, T13172): a live
1129
+ * task holds the legacy task's id and one of the two creation times does
1130
+ * not parse, so the run cannot tell whether they are the same task; it
1131
+ * neither renumbers nor copies the legacy task. `withheld-undecided`: a
1132
+ * task-graph table such a run did not copy at all, so the undecided task's
1133
+ * children, dependencies and criteria cannot attach to the live task.
1134
+ */
1135
+ reason:
1136
+ | 'live-authoritative'
1137
+ | 'collides-with-live'
1138
+ | 'id-collision-undecided'
1139
+ | 'withheld-undecided';
1140
+ /** The legacy ids left uncopied, when the run can name them. */
1141
+ ids?: string[];
1142
+ }
1143
+
1144
+ /**
1145
+ * A legacy task recovered under a fresh id because a DIFFERENT live task holds
1146
+ * its id (a task written into the empty store after a deferred exodus, T13172).
1147
+ * The renumbering happens in a scratch copy of the legacy file, with every
1148
+ * reference to the task re-pointed; the legacy file and live rows are untouched.
1149
+ *
1150
+ * @task T13172
1151
+ */
1152
+ export interface SupersededStoreIdRemap {
1153
+ /** Logical legacy source. */
1154
+ sourceDb: string;
1155
+ /** The legacy task's id, which the live store uses for another task. */
1156
+ legacyId: string;
1157
+ /** The id the legacy task is recovered under. */
1158
+ newId: string;
1159
+ /** The legacy task's title. */
1160
+ legacyTitle: string;
1161
+ /** Title of the live task that holds `legacyId`. */
1162
+ liveTitle: string;
1163
+ /** `true` when a previous run already recovered it as `newId` (nothing new is copied). */
1164
+ alreadyRecovered: boolean;
1165
+ /** Legacy rows re-pointed from `legacyId` to `newId` (children, dependencies, criteria, …). */
1166
+ referencesRepointed: number;
1167
+ /**
1168
+ * The legacy task's creation time and type, which a later run (and
1169
+ * `cleo show <legacy id>`) checks against the recovered task before trusting
1170
+ * this record (T13183). Absent in receipts written before T13183.
1171
+ */
1172
+ legacyCreatedAt?: string;
1173
+ legacyType?: string | null;
1174
+ }
1175
+
1176
+ /**
1177
+ * One bare legacy table of the live store a reconcile carried: its row count
1178
+ * and key digest when the run verified (T13319). The run records it in the
1179
+ * store (T13320) and in its receipt; the sync refusal for stranded bare rows
1180
+ * stands down for a table that still matches, so a row the runtime later
1181
+ * deletes from the prefixed twin is never mistaken for one never carried.
1182
+ *
1183
+ * @task T13319
1184
+ */
1185
+ export interface SupersededStoreBareAccount {
1186
+ /** The bare table, e.g. `task_labels`. */
1187
+ table: string;
1188
+ /** Its rows when the run carried it. */
1189
+ rows: number;
1190
+ /** sha256 of its primary-key values in order (every column if keyless). */
1191
+ digest: string;
1130
1192
  }
1131
1193
 
1132
1194
  /**
@@ -1162,8 +1224,17 @@ export interface SupersededStoreReconcileResult {
1162
1224
  * touch the live task graph, and list everything left in `conflicts`.
1163
1225
  */
1164
1226
  mode: 'full' | 'additive';
1165
- /** Legacy rows deliberately left uncopied (additive mode); empty in full mode. */
1227
+ /**
1228
+ * Legacy rows deliberately left uncopied: additive mode's live-authoritative
1229
+ * tables, and in full mode the task-id collisions the run cannot decide.
1230
+ */
1166
1231
  conflicts: SupersededStoreConflict[];
1232
+ /**
1233
+ * Legacy tasks whose id a different live task holds, recovered (full mode)
1234
+ * or to be recovered (dry-run) under a new id. Empty in additive mode, which
1235
+ * never writes the task graph.
1236
+ */
1237
+ remaps: SupersededStoreIdRemap[];
1167
1238
  /** `true` when nothing was written. */
1168
1239
  dryRun: boolean;
1169
1240
  /** Absolute project root. */
@@ -1186,4 +1257,11 @@ export interface SupersededStoreReconcileResult {
1186
1257
  receiptPath: string | null;
1187
1258
  /** Human-readable explanation, suitable for printing verbatim. */
1188
1259
  reason: string;
1260
+ /**
1261
+ * The live store's bare tables this run carried, on a `reconciled` receipt
1262
+ * that copied the bare family (T13319). Audit only: the sync check reads the
1263
+ * record the run wrote into the store itself, which travels with backups
1264
+ * (T13320).
1265
+ */
1266
+ accounted?: SupersededStoreBareAccount[];
1189
1267
  }
@@ -19,9 +19,10 @@
19
19
  *
20
20
  * - `rewrite` — DEFAULT. Wrap it in place as
21
21
  * `cleo run --wait --passthrough --class <c> -- <cmd>`, when the harness
22
- * runs in `bypassPermissions` or `auto` mode and no deny or ask rule names
23
- * the command (otherwise the hook warns: a rewrite would change which
24
- * permission rules match).
22
+ * runs in `bypassPermissions` or `auto` mode, or (Claude Code `default`,
23
+ * `acceptEdits`, `dontAsk`) when the user's allow rules already approve the
24
+ * command, and no deny or ask rule names it. Otherwise the hook warns: a
25
+ * rewrite would change which permission rules match.
25
26
  * - `warn` — leave the command alone; add a context line naming the governed
26
27
  * form.
27
28
  * - `off` — do nothing (the installer also removes the hook entry).
@@ -124,9 +125,13 @@ export interface OpencodeHeavyCommandAnswer {
124
125
  export interface PreToolUseHookOutput {
125
126
  readonly hookSpecificOutput: {
126
127
  readonly hookEventName: 'PreToolUse';
127
- /** Omitted unless the provider requires it (Codex rewrites need `allow`). */
128
+ /**
129
+ * Omitted unless needed: Codex rewrites need `allow`, Kimi's "re-run it"
130
+ * is a `deny`, and a Claude Code rewrite carries `allow` only when the
131
+ * user's allow rules already approve the original command (T13124).
132
+ */
128
133
  readonly permissionDecision?: 'allow' | 'deny';
129
- /** Shown to the agent on `deny`. */
134
+ /** Shown to the agent on `deny`; written to Claude Code's debug log on `allow`. */
130
135
  readonly permissionDecisionReason?: string;
131
136
  /** Replaces the whole tool input. */
132
137
  readonly updatedInput?: ShellToolInput;
@@ -134,3 +139,168 @@ export interface PreToolUseHookOutput {
134
139
  readonly additionalContext?: string;
135
140
  };
136
141
  }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Project-level delivery (T13124)
145
+ // ---------------------------------------------------------------------------
146
+
147
+ /**
148
+ * What delivering the hook to one provider did in one project.
149
+ *
150
+ * - `installed` / `updated` / `removed` / `unchanged` — the config file was
151
+ * written, refreshed, cleaned (mode `off`) or already current.
152
+ * - `skipped` — not applicable: the provider is not in use on this machine or
153
+ * project, or the project is the user's home directory (whose provider
154
+ * configs are user-global, which CLEO never writes).
155
+ * - `blocked` — a path the hook must live under exists but is not a
156
+ * directory (a stray `.codex` file, say); nothing was written.
157
+ * - `unsupported` — the provider cannot take a project-level hook (Kimi reads
158
+ * hooks only from its global config).
159
+ * - `needs-consent` — the config is the project's own (Codex's committable
160
+ * `.codex/hooks.json`, tracked or holding anything CLEO did not write), so
161
+ * CLEO does not write or hide it; `snippet` is the entry to add by hand.
162
+ * - `failed` — any other error, such as a config file that is not valid JSON
163
+ * (left untouched).
164
+ */
165
+ export type HeavyHookDeliveryStatus =
166
+ | 'installed'
167
+ | 'updated'
168
+ | 'removed'
169
+ | 'unchanged'
170
+ | 'skipped'
171
+ | 'blocked'
172
+ | 'unsupported'
173
+ | 'needs-consent'
174
+ | 'failed';
175
+
176
+ /** One provider's result from syncing the hook into a project. */
177
+ export interface HeavyHookDeliveryOutcome {
178
+ /** The harness. */
179
+ readonly provider: HeavyCommandHookProvider;
180
+ /** What the sync did. */
181
+ readonly status: HeavyHookDeliveryStatus;
182
+ /** The config file (or plugin file) the hook lives in, absolute. */
183
+ readonly target: string;
184
+ /** Why it was skipped, blocked, unsupported or failed. */
185
+ readonly reason?: string;
186
+ /** The exact step that fixes a `blocked`, `unsupported`, `needs-consent` or `failed` outcome. */
187
+ readonly remedy?: string;
188
+ /** `needs-consent`: the exact JSON entry to add to the config by hand. */
189
+ readonly snippet?: string;
190
+ }
191
+
192
+ /**
193
+ * The hook's state for one provider in one project, as `cleo doctor` reports it.
194
+ *
195
+ * - `installed` — present and identical to what this CLEO would write.
196
+ * - `outdated` — present but written by another CLEO build, only in a legacy
197
+ * location, or visible to git as an untracked file (gh#1805: it widens
198
+ * `cleo verify`'s evidence scope); `cleo upgrade` refreshes it.
199
+ * - `missing` — the provider is in use and the hook is absent.
200
+ * - `blocked` — see {@link HeavyHookDeliveryStatus}.
201
+ * - `unreadable` — the config file exists but cannot be read or parsed.
202
+ * - `unsupported` — the provider cannot take a project-level hook.
203
+ * - `needs-consent` — see {@link HeavyHookDeliveryStatus}; `snippet` holds the entry.
204
+ * - `disabled` — `resources.heavyCommandHook` is `off`, and no CLEO hook is left.
205
+ * - `not-detected` — the provider is not in use here; nothing is expected.
206
+ */
207
+ export type HeavyHookInstallState =
208
+ | 'installed'
209
+ | 'outdated'
210
+ | 'missing'
211
+ | 'blocked'
212
+ | 'unreadable'
213
+ | 'unsupported'
214
+ | 'needs-consent'
215
+ | 'disabled'
216
+ | 'not-detected';
217
+
218
+ /** One provider's hook state in one project. */
219
+ export interface HeavyHookInspection {
220
+ /** The harness. */
221
+ readonly provider: HeavyCommandHookProvider;
222
+ /** Whether the provider is in use on this machine or in this project. */
223
+ readonly detected: boolean;
224
+ /** The hook's state. */
225
+ readonly state: HeavyHookInstallState;
226
+ /** The config file (or plugin file) the hook lives in, absolute. */
227
+ readonly target: string;
228
+ /** One line saying what was found. */
229
+ readonly detail: string;
230
+ /** The exact step that fixes the state, when one is needed. */
231
+ readonly remedy?: string;
232
+ /** `needs-consent`: the exact JSON entry to add to the config by hand. */
233
+ readonly snippet?: string;
234
+ }
235
+
236
+ /**
237
+ * Whether the `cleo` a hook finds on PATH can answer it.
238
+ *
239
+ * - `current` — it answers `cleo hook heavy-command`.
240
+ * - `older` — it predates `cleo hook` (and `cleo run`): the hook lets every
241
+ * command run ungoverned. The hook's own marker (exit 127, "Unknown
242
+ * command") or a direct probe says so.
243
+ * - `missing` — no `cleo` on PATH: the hook stays silent.
244
+ * - `unknown` — the probe failed some other way (a broken version-manager
245
+ * shim, a timeout, a crash). The hook fails open, so it governs nothing
246
+ * while this lasts.
247
+ */
248
+ export type HeavyHookCliState = 'current' | 'older' | 'missing' | 'unknown';
249
+
250
+ /** The result of probing the `cleo` on PATH from a project directory. */
251
+ export interface HeavyHookCliProbe {
252
+ /** What the probe found. */
253
+ readonly state: HeavyHookCliState;
254
+ /** The `cleo` the hook resolves on PATH, or `null`. */
255
+ readonly path: string | null;
256
+ /** One line saying what was found. */
257
+ readonly detail: string;
258
+ /** The exact step that fixes (or diagnoses) a CLI that is not `current`. */
259
+ readonly remedy?: string;
260
+ }
261
+
262
+ /** Options for the project-level delivery functions (all injectable for tests). */
263
+ export interface HeavyHookDeliveryOptions {
264
+ /** Environment for provider detection (`PATH`, `HOME`, `CODEX_HOME`, …). Default `process.env`. */
265
+ readonly env?: Readonly<Record<string, string | undefined>>;
266
+ /** Providers to consider. Default: every provider CLEO knows. */
267
+ readonly providers?: readonly HeavyCommandHookProvider[];
268
+ /**
269
+ * Inspection only: run the git checks (a hook file git can see, a shared
270
+ * Codex `hooks.json`). Default `true`; the session briefing passes `false`
271
+ * to skip their git spawns (`cleo doctor` keeps them).
272
+ */
273
+ readonly gitChecks?: boolean;
274
+ }
275
+
276
+ /**
277
+ * The project-level delivery surface `@cleocode/adapters` exports at
278
+ * `@cleocode/adapters/heavy-command-hook`. `@cleocode/core` loads it at run
279
+ * time (adapters builds on core, so core cannot import it statically) for
280
+ * `cleo init`, `cleo upgrade`, `cleo doctor` and the session briefing.
281
+ */
282
+ export interface HeavyHookDeliveryApi {
283
+ /**
284
+ * Install, refresh or (mode `off`) remove CLEO's hook for every provider in
285
+ * use, each independently. Never throws: every provider gets an outcome.
286
+ */
287
+ readonly syncProjectHeavyCommandHooks: (
288
+ projectDir: string,
289
+ mode: HeavyCommandHookMode,
290
+ options?: HeavyHookDeliveryOptions,
291
+ ) => Promise<readonly HeavyHookDeliveryOutcome[]>;
292
+ /** Read-only: the hook's state for every provider. Never throws. */
293
+ readonly inspectProjectHeavyCommandHooks: (
294
+ projectDir: string,
295
+ mode: HeavyCommandHookMode,
296
+ options?: HeavyHookDeliveryOptions,
297
+ ) => readonly HeavyHookInspection[];
298
+ /**
299
+ * Whether the `cleo` on PATH (as the hook resolves it from `projectDir`)
300
+ * can answer the hook. Starts that `cleo` once. Never throws.
301
+ */
302
+ readonly probeHeavyHookCli: (
303
+ projectDir: string,
304
+ options?: HeavyHookDeliveryOptions,
305
+ ) => HeavyHookCliProbe;
306
+ }
package/src/index.ts CHANGED
@@ -514,7 +514,9 @@ export type {
514
514
  SagaAuditResult,
515
515
  SagaAuditViolation,
516
516
  SagaAuditViolationKind,
517
+ SupersededStoreBareAccount,
517
518
  SupersededStoreConflict,
519
+ SupersededStoreIdRemap,
518
520
  SupersededStoreReconcileOutcome,
519
521
  SupersededStoreReconcileResult,
520
522
  SupersededStoreTableCount,
@@ -780,6 +782,14 @@ export type {
780
782
  HeavyCommandHookProvider,
781
783
  HeavyCommandPlan,
782
784
  HeavyCommandSegment,
785
+ HeavyHookCliProbe,
786
+ HeavyHookCliState,
787
+ HeavyHookDeliveryApi,
788
+ HeavyHookDeliveryOptions,
789
+ HeavyHookDeliveryOutcome,
790
+ HeavyHookDeliveryStatus,
791
+ HeavyHookInspection,
792
+ HeavyHookInstallState,
783
793
  HookJsonValue,
784
794
  OpencodeHeavyCommandAnswer,
785
795
  PreToolUseHookInput,
@@ -2270,6 +2280,10 @@ export * from './render/index.js';
2270
2280
  export type {
2271
2281
  AdmissionResult,
2272
2282
  GovernorMode,
2283
+ HeavyHeapSource,
2284
+ HeavyLeverChange,
2285
+ HeavyToolResourcePlan,
2286
+ MemoryPressureReading,
2273
2287
  ResourceClass,
2274
2288
  ResourceDeferral,
2275
2289
  ResourceGrant,
@@ -2309,7 +2323,13 @@ export type {
2309
2323
  // === Row Identity Types (T12341 — uid merge keys) ===
2310
2324
  export type {
2311
2325
  RowIdentityKind,
2326
+ RowIdentityNexusAnswer,
2312
2327
  RowIdentityRef,
2328
+ RowIdentityRefillCounts,
2329
+ RowIdentityRefillReport,
2330
+ RowIdentityShareSignal,
2331
+ RowIdentityShareSignalCode,
2332
+ RowIdentityShareState,
2313
2333
  RowIdentitySpec,
2314
2334
  StoredRefUid,
2315
2335
  SymmetricEdge,
@@ -2450,6 +2470,14 @@ export {
2450
2470
  // Terminal state sets
2451
2471
  TERMINAL_TASK_STATUSES,
2452
2472
  } from './status-registry.js';
2473
+ // === Store restore (T13240 — restore cleo.db from a named snapshot or backup id) ===
2474
+ export type {
2475
+ StoreRecoverResult,
2476
+ StoreRestoreKept,
2477
+ StoreRestoreResult,
2478
+ StoreRestoreSource,
2479
+ StoreRestoreVerification,
2480
+ } from './store-restore.js';
2453
2481
  // === Sub-Accessor Contracts (T9188) ===
2454
2482
  export type {
2455
2483
  AgentRegistrySubAccessor,
@@ -3009,6 +3037,8 @@ export type {
3009
3037
  NexusAccountErrorCode,
3010
3038
  NexusAccountMe,
3011
3039
  NexusAccountOrganization,
3040
+ NexusAccountSetup,
3041
+ NexusAccountSetupStep,
3012
3042
  NexusAccountStatus,
3013
3043
  NexusAccountUser,
3014
3044
  NexusDeviceEndOutcome,
@@ -3036,12 +3066,21 @@ export type {
3036
3066
  CloudPaging,
3037
3067
  CloudProjectShowResult,
3038
3068
  CloudProjectsResult,
3069
+ CloudRetiredReplica,
3039
3070
  CloudStatusGlobalStore,
3071
+ CloudStatusHolder,
3040
3072
  CloudStatusLocal,
3041
3073
  CloudStatusOfflineDetails,
3042
3074
  CloudStatusResult,
3043
3075
  CloudStatusSummary,
3076
+ CloudStatusSync,
3077
+ CloudStatusSyncStream,
3044
3078
  CloudStatusVerdict,
3079
+ CloudSyncFact,
3080
+ CloudSyncFlags,
3081
+ CloudSyncKnown,
3082
+ CloudSyncLag,
3083
+ CloudSyncUnknown,
3045
3084
  CloudWarning,
3046
3085
  CloudWhoamiResult,
3047
3086
  NexusCloudCredential,
@@ -3065,6 +3104,7 @@ export {
3065
3104
  NEXUS_DEVICE_LIST_STATES,
3066
3105
  NEXUS_DEVICE_STATE_UNKNOWN,
3067
3106
  NEXUS_DEVICE_STATES,
3107
+ NEXUS_FEATURE_PROJECT_INITIAL_KEY,
3068
3108
  NEXUS_FORBIDDEN_ERRORS,
3069
3109
  NEXUS_FORBIDDEN_REASONS,
3070
3110
  NEXUS_PAGE_LIMIT_MAX,
@@ -3092,9 +3132,24 @@ export {
3092
3132
  nexusCloudUserSchema,
3093
3133
  nexusCloudWhoamiSchema,
3094
3134
  } from './nexus-cloud.js';
3135
+ // === Guided first run of `cleo login nexus` (T13102) ===
3136
+ export {
3137
+ NEXUS_FIRST_RUN_STATES,
3138
+ type NexusFirstRunBackup,
3139
+ type NexusFirstRunChoice,
3140
+ type NexusFirstRunOffer,
3141
+ type NexusFirstRunRestore,
3142
+ type NexusFirstRunResult,
3143
+ type NexusFirstRunState,
3144
+ type NexusNamedProject,
3145
+ type NexusProjectNameSource,
3146
+ } from './nexus-first-run.js';
3095
3147
  export {
3096
3148
  type CloudActivityItem,
3097
3149
  type CloudActivityResult,
3150
+ type CloudConflict,
3151
+ type CloudConflictResolveResult,
3152
+ type CloudConflictsResult,
3098
3153
  type CloudLeaseReleaseResult,
3099
3154
  type CloudPushResult,
3100
3155
  type CloudRestoreResult,
@@ -3104,9 +3159,12 @@ export {
3104
3159
  type CloudVaultStatusResult,
3105
3160
  type CloudVaultTable,
3106
3161
  type CloudVaultTableDiff,
3162
+ type CloudVerifyDeepCheck,
3107
3163
  type CloudVerifyResult,
3164
+ NEXUS_REPLICA_RETIREMENT_REASONS,
3108
3165
  NEXUS_VAULT_LEASE_ROLE,
3109
3166
  type NexusActivityEvent,
3167
+ type NexusReplicaRetirementReason,
3110
3168
  nexusActivityEventSchema,
3111
3169
  nexusActivityPageSchema,
3112
3170
  nexusLeaseAcquireSchema,