@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.
- package/dist/__tests__/cloud-replica-retirement.test.d.ts +10 -0
- package/dist/__tests__/cloud-replica-retirement.test.d.ts.map +1 -0
- package/dist/__tests__/cloud-replica-retirement.test.js +111 -0
- package/dist/__tests__/cloud-replica-retirement.test.js.map +1 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.d.ts +10 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.d.ts.map +1 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.js +49 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.js.map +1 -0
- package/dist/agent-registry.d.ts +5 -1
- package/dist/agent-registry.d.ts.map +1 -1
- package/dist/cloud/api.d.ts +156 -0
- package/dist/cloud/api.d.ts.map +1 -1
- package/dist/cloud/api.js +99 -1
- package/dist/cloud/api.js.map +1 -1
- package/dist/cloud/envelope.d.ts +3 -3
- package/dist/dispatch/operations-registry.js +1 -1
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/doctor.d.ts +79 -7
- package/dist/doctor.d.ts.map +1 -1
- package/dist/heavy-command-hook.d.ts +134 -5
- package/dist/heavy-command-hook.d.ts.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/invariants/sync-write-invariants.d.ts.map +1 -1
- package/dist/invariants/sync-write-invariants.js +31 -6
- package/dist/invariants/sync-write-invariants.js.map +1 -1
- package/dist/ledger/index.d.ts +211 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +122 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/nexus-account.d.ts +72 -1
- package/dist/nexus-account.d.ts.map +1 -1
- package/dist/nexus-account.js +5 -0
- package/dist/nexus-account.js.map +1 -1
- package/dist/nexus-cloud.d.ts +175 -0
- package/dist/nexus-cloud.d.ts.map +1 -1
- package/dist/nexus-cloud.js +17 -0
- package/dist/nexus-cloud.js.map +1 -1
- package/dist/nexus-first-run.d.ts +145 -0
- package/dist/nexus-first-run.d.ts.map +1 -0
- package/dist/nexus-first-run.js +54 -0
- package/dist/nexus-first-run.js.map +1 -0
- package/dist/nexus-vault.d.ts +113 -1
- package/dist/nexus-vault.d.ts.map +1 -1
- package/dist/nexus-vault.js +27 -0
- package/dist/nexus-vault.js.map +1 -1
- package/dist/operations/admin.d.ts +14 -1
- package/dist/operations/admin.d.ts.map +1 -1
- package/dist/operations/docs-lifecycle.d.ts +23 -0
- package/dist/operations/docs-lifecycle.d.ts.map +1 -0
- package/dist/operations/docs-lifecycle.js +29 -0
- package/dist/operations/docs-lifecycle.js.map +1 -0
- package/dist/operations/docs.d.ts +2 -11
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js +3 -17
- package/dist/operations/docs.js.map +1 -1
- package/dist/project-context.d.ts +17 -2
- package/dist/project-context.d.ts.map +1 -1
- package/dist/release/evidence-atoms.d.ts +15 -7
- package/dist/release/evidence-atoms.d.ts.map +1 -1
- package/dist/release/evidence-atoms.js +16 -12
- package/dist/release/evidence-atoms.js.map +1 -1
- package/dist/resource-governor.d.ts +96 -0
- package/dist/resource-governor.d.ts.map +1 -1
- package/dist/resource-governor.js.map +1 -1
- package/dist/row-identity.d.ts +81 -0
- package/dist/row-identity.d.ts.map +1 -1
- package/dist/store-restore.d.ts +66 -0
- package/dist/store-restore.d.ts.map +1 -0
- package/dist/store-restore.js +9 -0
- package/dist/store-restore.js.map +1 -0
- package/dist/task.d.ts +14 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js.map +1 -1
- package/dist/tasks.d.ts +17 -17
- package/dist/workgraph.d.ts +28 -28
- package/package.json +6 -2
- package/src/__tests__/cloud-replica-retirement.test.ts +135 -0
- package/src/__tests__/cloud-status-checkpoint-reads.test.ts +60 -0
- package/src/agent-registry.ts +5 -1
- package/src/cloud/api.ts +125 -1
- package/src/dispatch/operations-registry.ts +1 -1
- package/src/doctor.ts +85 -7
- package/src/heavy-command-hook.ts +175 -5
- package/src/index.ts +58 -0
- package/src/invariants/sync-write-invariants.ts +81 -42
- package/src/ledger/index.ts +141 -0
- package/src/nexus-account.ts +82 -0
- package/src/nexus-cloud.ts +193 -0
- package/src/nexus-first-run.ts +165 -0
- package/src/nexus-vault.ts +124 -1
- package/src/operations/admin.ts +14 -1
- package/src/operations/conduit.ts +1 -1
- package/src/operations/docs-lifecycle.ts +31 -0
- package/src/operations/docs.ts +4 -19
- package/src/project-context.ts +17 -2
- package/src/release/evidence-atoms.ts +16 -12
- package/src/resource-governor.ts +100 -0
- package/src/row-identity.ts +98 -0
- package/src/store-restore.ts +70 -0
- 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({
|
|
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
|
-
*
|
|
703
|
-
*
|
|
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
|
-
/**
|
|
1129
|
-
|
|
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
|
-
/**
|
|
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
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
-
/**
|
|
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,
|