@cleocode/contracts 2026.10.8 → 2026.10.9-rc.ci.676

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 (78) hide show
  1. package/dist/branch-lock.d.ts +5 -2
  2. package/dist/branch-lock.d.ts.map +1 -1
  3. package/dist/branch-lock.js.map +1 -1
  4. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  5. package/dist/dispatch/operations-registry.js +7 -1
  6. package/dist/dispatch/operations-registry.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/invariants/sync-write-invariants.d.ts.map +1 -1
  11. package/dist/invariants/sync-write-invariants.js +10 -1
  12. package/dist/invariants/sync-write-invariants.js.map +1 -1
  13. package/dist/nexus-cloud.d.ts +19 -1
  14. package/dist/nexus-cloud.d.ts.map +1 -1
  15. package/dist/nexus-cloud.js +4 -0
  16. package/dist/nexus-cloud.js.map +1 -1
  17. package/dist/nexus-vault.d.ts +155 -1
  18. package/dist/nexus-vault.d.ts.map +1 -1
  19. package/dist/operations/nexus.d.ts +4 -0
  20. package/dist/operations/nexus.d.ts.map +1 -1
  21. package/dist/operations/nexus.js.map +1 -1
  22. package/dist/operations/orchestrate.d.ts +7 -0
  23. package/dist/operations/orchestrate.d.ts.map +1 -1
  24. package/dist/operations/validate.d.ts +5 -0
  25. package/dist/operations/validate.d.ts.map +1 -1
  26. package/dist/operations/worktree.d.ts +61 -0
  27. package/dist/operations/worktree.d.ts.map +1 -1
  28. package/dist/project-context.d.ts +9 -0
  29. package/dist/project-context.d.ts.map +1 -1
  30. package/dist/project-hook-delivery.d.ts +137 -0
  31. package/dist/project-hook-delivery.d.ts.map +1 -0
  32. package/dist/project-hook-delivery.js +21 -0
  33. package/dist/project-hook-delivery.js.map +1 -0
  34. package/dist/project-hooks.d.ts +276 -0
  35. package/dist/project-hooks.d.ts.map +1 -0
  36. package/dist/project-hooks.js +113 -0
  37. package/dist/project-hooks.js.map +1 -0
  38. package/dist/release/channel.d.ts +1 -1
  39. package/dist/release/channel.d.ts.map +1 -1
  40. package/dist/release/hook-pilot.d.ts +179 -0
  41. package/dist/release/hook-pilot.d.ts.map +1 -0
  42. package/dist/release/hook-pilot.js +112 -0
  43. package/dist/release/hook-pilot.js.map +1 -0
  44. package/dist/release/plan.d.ts +12 -1
  45. package/dist/release/plan.d.ts.map +1 -1
  46. package/dist/release/plan.js +25 -2
  47. package/dist/release/plan.js.map +1 -1
  48. package/dist/release/plan.test.js +14 -3
  49. package/dist/release/plan.test.js.map +1 -1
  50. package/dist/row-identity.d.ts +7 -0
  51. package/dist/row-identity.d.ts.map +1 -1
  52. package/dist/task.d.ts +7 -0
  53. package/dist/task.d.ts.map +1 -1
  54. package/dist/tools/atomic.d.ts +6 -0
  55. package/dist/tools/atomic.d.ts.map +1 -1
  56. package/dist/tools/atomic.js.map +1 -1
  57. package/package.json +2 -2
  58. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  59. package/src/branch-lock.ts +5 -2
  60. package/src/dispatch/operations-registry.ts +7 -1
  61. package/src/index.ts +7 -0
  62. package/src/invariants/sync-write-invariants.ts +10 -1
  63. package/src/nexus-cloud.ts +13 -1
  64. package/src/nexus-vault.ts +138 -1
  65. package/src/operations/nexus.ts +4 -0
  66. package/src/operations/orchestrate.ts +7 -0
  67. package/src/operations/validate.ts +5 -0
  68. package/src/operations/worktree.ts +59 -0
  69. package/src/project-context.ts +9 -0
  70. package/src/project-hook-delivery.ts +144 -0
  71. package/src/project-hooks.ts +256 -0
  72. package/src/release/channel.ts +1 -1
  73. package/src/release/hook-pilot.ts +163 -0
  74. package/src/release/plan.test.ts +15 -3
  75. package/src/release/plan.ts +81 -56
  76. package/src/row-identity.ts +7 -0
  77. package/src/task.ts +7 -0
  78. package/src/tools/atomic.ts +8 -0
@@ -193,8 +193,11 @@ export interface GitShimEnv {
193
193
  CLEO_ALLOW_BRANCH_OPS?: '1';
194
194
  /** Worktree root path — informational for shim error messages. */
195
195
  CLEO_WORKTREE_ROOT?: string;
196
- /** Branch protection mode. */
197
- CLEO_BRANCH_PROTECTION?: 'strict' | 'permissive' | 'off';
196
+ /**
197
+ * Branch protection mode. `unavailable`: strict was intended but the git
198
+ * shim could not be installed, so nothing enforces it (T13522).
199
+ */
200
+ CLEO_BRANCH_PROTECTION?: 'strict' | 'permissive' | 'off' | 'unavailable';
198
201
  }
199
202
 
200
203
  /**
@@ -9428,7 +9428,7 @@ export const OPERATIONS: OperationDef[] = [
9428
9428
  name: 'channel',
9429
9429
  type: 'string',
9430
9430
  required: false,
9431
- description: 'Release channel: latest | beta | alpha | rc',
9431
+ description: 'Release channel: latest | beta | alpha | rc | canary',
9432
9432
  },
9433
9433
  {
9434
9434
  name: 'hotfix',
@@ -9436,6 +9436,12 @@ export const OPERATIONS: OperationDef[] = [
9436
9436
  required: false,
9437
9437
  description: 'Mark plan as release_kind=hotfix',
9438
9438
  },
9439
+ {
9440
+ name: 'hooksV1Promotion',
9441
+ type: 'boolean',
9442
+ required: false,
9443
+ description: 'Require the unified-hooks pilot for this stable promotion',
9444
+ },
9439
9445
  {
9440
9446
  name: 'dryRun',
9441
9447
  type: 'boolean',
package/src/index.ts CHANGED
@@ -2032,6 +2032,7 @@ export type {
2032
2032
  WorktreeHookResult,
2033
2033
  WorktreeIncludePattern,
2034
2034
  WorktreeInfo,
2035
+ WorktreeInstallOutcome,
2035
2036
  WorktreeLifecycleAction,
2036
2037
  WorktreeLifecycleAuditEntry,
2037
2038
  WorktreeListEntry,
@@ -3163,6 +3164,12 @@ export {
3163
3164
  type CloudConflict,
3164
3165
  type CloudConflictResolveResult,
3165
3166
  type CloudConflictsResult,
3167
+ type CloudDeviceOverviewResult,
3168
+ type CloudDeviceOverviewRow,
3169
+ type CloudJournalActivityDevice,
3170
+ type CloudJournalActivityItem,
3171
+ type CloudJournalActivityResult,
3172
+ type CloudJournalTableOps,
3166
3173
  type CloudLeaseReleaseResult,
3167
3174
  type CloudPushResult,
3168
3175
  type CloudRestoreResult,
@@ -490,7 +490,16 @@ export const SYNC_WRITE_INVARIANTS: readonly SyncWriteInvariant[] = Object.freez
490
490
  {
491
491
  id: 'identity.local-minting',
492
492
  class: 'identity-layer',
493
- tables: ['tasks_tasks', 'tasks_sessions'],
493
+ // T13405: the counter-key re-mint after a uid collision renames these too.
494
+ tables: [
495
+ 'tasks_tasks',
496
+ 'tasks_sessions',
497
+ 'brain_decisions',
498
+ 'brain_sticky_notes',
499
+ 'brain_sticky_tags',
500
+ 'brain_page_nodes',
501
+ 'brain_page_edges',
502
+ ],
494
503
  sites: [],
495
504
  inventory: ['V12', 'B07', 'B12'],
496
505
  reason:
@@ -145,6 +145,10 @@ export const nexusCloudReplicaSchema = z.object({
145
145
  lastSyncAt: isoTime.nullable(),
146
146
  presence: nexusCloudPresenceSchema.nullable(),
147
147
  presenceAt: isoTime.nullable(),
148
+ /** When the replica was retired (cleo-nexus T123), else null. Absent from servers older than retirement. */
149
+ retiredAt: isoTime.nullable().optional(),
150
+ /** The retired replica's named successor, else null (not retired, or retired with none). */
151
+ successor: z.string().nullable().optional(),
148
152
  });
149
153
 
150
154
  /** `DeviceCounts` (§4.1): distinct devices holding a replica. */
@@ -631,7 +635,11 @@ export interface CloudStatusSeenTxns {
631
635
  export interface CloudStatusSyncStream {
632
636
  /** Which store. */
633
637
  readonly scope: 'project' | 'global';
634
- /** Its stream (`project:<id>`, `home:<userId>`) when known locally, else `null`. */
638
+ /**
639
+ * Its stream (`project:<id>`, `home:<userId>`) when known locally: from the
640
+ * project link, else from the store's own genesis cut or pull position
641
+ * (T13370); `null` before either exists.
642
+ */
635
643
  readonly stream: string | null;
636
644
  /** The store file. */
637
645
  readonly dbPath: string;
@@ -645,6 +653,10 @@ export interface CloudStatusSyncStream {
645
653
  readonly oldestUnsealedAtMs: number | null;
646
654
  /** The highest sealed local sequence (`_sync_txn.local_seq`), or `null` before the first seal. */
647
655
  readonly lastSealedSeq: number | null;
656
+ /** The local sequence of this stream's genesis cut, or `null` before the store cut or joined it (T13370). */
657
+ readonly genesisCut: number | null;
658
+ /** The cut's genesis checkpoint is not yet confirmed stored; no segment is sent until it is (T13370). */
659
+ readonly genesisPending: boolean;
648
660
  /** Captures the sealer could not read and holds aside (`_sync_quarantine`), per table. */
649
661
  readonly quarantined: Readonly<Record<string, number>>;
650
662
  /** Tables marked suspect until a repair re-emits them. */
@@ -220,9 +220,31 @@ export interface CloudSyncPushEnableResult {
220
220
  * `enabled`: the cut was recorded and its genesis checkpoint stored; `resumed`: a cut recorded
221
221
  * by an earlier run whose checkpoint push failed was pushed now; `already`: push was already on;
222
222
  * `joined`: this store holds the stream's journal checkpoint another device cut, and now pushes its
223
- * own writes and pulls the stream's after it, with no genesis of its own (T13312).
223
+ * own writes and pulls the stream's after it, with no genesis of its own (T13312). A store that
224
+ * never synced with the stream joins too, keeping its own rows (see `merged`, T13466).
224
225
  */
225
226
  status: 'enabled' | 'resumed' | 'already' | 'joined';
227
+ /**
228
+ * A merge join (T13466): stream rows adopted here, this store's own rows emitted as its first
229
+ * transactions, and stream rows not placed (an unresolved reference). `overwritten` lists fields of
230
+ * this store's own rows whose older value took the stream's newer one (T13503); `safetyBackup` is
231
+ * the bundle of the store as it was before the merge. Null when the reconcile is still due
232
+ * (`cleo cloud sync` finishes it); absent on any other status.
233
+ */
234
+ merged?: {
235
+ adopted: number;
236
+ emitted: number;
237
+ unresolved: number;
238
+ overwritten: Array<{ table: string; uid: string; column: string }>;
239
+ /**
240
+ * Fields of the stream's rows that took this store's newer value on every replica (T13507);
241
+ * column `*` is a whole row this store's newer edit restored over a stream delete (T13508).
242
+ */
243
+ replaced: Array<{ table: string; uid: string; column: string }>;
244
+ /** This store's own rows a newer stream delete removed; the safety bundle holds them (T13508). */
245
+ deleted: Array<{ table: string; uid: string }>;
246
+ safetyBackup: string;
247
+ } | null;
226
248
  /** The genesis cut: the highest capture seq the checkpoint carries. */
227
249
  cut: number;
228
250
  /** Transactions sealed before the cut, folded into the checkpoint, and rows given genesis row meta. */
@@ -427,6 +449,121 @@ export interface CloudActivityResult {
427
449
  warnings: CloudWarning[];
428
450
  }
429
451
 
452
+ /** Per-table op counts of one journal transaction, by op kind. */
453
+ export interface CloudJournalTableOps {
454
+ I: number;
455
+ U: number;
456
+ D: number;
457
+ K: number;
458
+ }
459
+
460
+ /**
461
+ * One transaction another device (or this one, echoed back) wrote, as this
462
+ * store's journal received it (`cleo cloud activity --journal`, T13369).
463
+ */
464
+ export interface CloudJournalActivityItem {
465
+ /** The transaction id, `${replicaId}:${localSeq}`. */
466
+ txn: string;
467
+ stream: string;
468
+ /** The segment's stream sequence number and the transaction's index in it. */
469
+ seq: number;
470
+ txnIdx: number;
471
+ /** The replica (store) that wrote it. */
472
+ replicaId: string;
473
+ /** The Nexus device that signed its segment. */
474
+ deviceId: string;
475
+ /** The device's name from the account's device list; `null` offline or when unknown. */
476
+ deviceName: string | null;
477
+ /** Written by a replica this machine bound. */
478
+ thisDevice: boolean;
479
+ /** The transaction's HLC. */
480
+ hlc: string;
481
+ /** When it was written: the HLC's physical time, ISO-8601. */
482
+ at: string;
483
+ /** When this store staged it, and applied it (`null` until applied). */
484
+ stagedAt: string;
485
+ appliedAt: string | null;
486
+ /** Inbox status: `applied`, `void`, `conflict`, `staged`, `pending`, `held-skew`, `refused-schema`. */
487
+ status: string;
488
+ /** Why it is void, held or refused, when it is. */
489
+ reason: string | null;
490
+ /**
491
+ * Applied as inherited history (§1.5, T13278/T13366): its replica was
492
+ * retired by a server-confirmed retire at a lower stream position, so the
493
+ * merge recorded no conflict for it. Always `false` for an unconfirmed
494
+ * retire or a transaction that was not applied.
495
+ */
496
+ history: boolean;
497
+ /** `write`, `repair`, `retire`, … and how the writes reached the store. */
498
+ kind: string;
499
+ via: string;
500
+ /** The command, agent and session that wrote it, when recorded. */
501
+ actor: { agent?: string; session?: string; op?: string } | null;
502
+ /** The project the transaction is for (`null` for a global-scope txn). */
503
+ project: string | null;
504
+ /** Total ops, and op counts per table. */
505
+ ops: number;
506
+ tables: Record<string, CloudJournalTableOps>;
507
+ }
508
+
509
+ /** One device's share of the journal activity matching the filters. */
510
+ export interface CloudJournalActivityDevice {
511
+ deviceId: string;
512
+ deviceName: string | null;
513
+ thisDevice: boolean;
514
+ /** Matching transactions, and the newest one's write time (ISO-8601). */
515
+ txns: number;
516
+ lastAt: string;
517
+ /** The device's last presence report from the account's device list; `null` offline or when unknown. */
518
+ lastSeenAt: string | null;
519
+ }
520
+
521
+ /** `cleo cloud activity --journal`: what each device changed and when, from the local journal. */
522
+ export interface CloudJournalActivityResult {
523
+ scope: CloudVaultScope;
524
+ /** Newest first. */
525
+ items: CloudJournalActivityItem[];
526
+ /** Per device, over every matching transaction (not just this page). */
527
+ devices: CloudJournalActivityDevice[];
528
+ /** Pass as `--before` for the next older page; `null` at the end. */
529
+ nextBefore: string | null;
530
+ warnings: CloudWarning[];
531
+ }
532
+
533
+ /**
534
+ * One device in `cleo cloud activity --devices` (T13482): its presence, its
535
+ * newest journal change this store received, and its newest server event.
536
+ */
537
+ export interface CloudDeviceOverviewRow {
538
+ deviceId: string;
539
+ /** From the account's device list; `null` offline or when unknown. */
540
+ deviceName: string | null;
541
+ thisDevice: boolean;
542
+ /** `active`, `signed-out` or `revoked`; `null` when not in the device list. */
543
+ state: string | null;
544
+ /** Presence reported within the freshness window; `null` offline or when unknown. */
545
+ online: boolean | null;
546
+ lastSeenAt: string | null;
547
+ /** Its journal transactions in this store, and the newest one; `null` when none. */
548
+ journal: {
549
+ txns: number;
550
+ lastAt: string;
551
+ /** Command (or txn kind), inbox status and tables of the newest transaction on the page read. */
552
+ last: { op: string; status: string; tables: string[] } | null;
553
+ } | null;
554
+ /** Its newest server event (snapshot, lease, enrolment …); `null` offline or when none. */
555
+ server: { at: string; action: string; target: string | null } | null;
556
+ /** The newest of its journal change, server event and presence. */
557
+ lastActivityAt: string | null;
558
+ }
559
+
560
+ /** `cleo cloud activity --devices`: one row per device, newest activity first (T13482). */
561
+ export interface CloudDeviceOverviewResult {
562
+ scope: CloudVaultScope;
563
+ devices: CloudDeviceOverviewRow[];
564
+ warnings: CloudWarning[];
565
+ }
566
+
430
567
  /** One conflict an apply recorded (`cleo cloud conflicts`, T12344 PR-6). */
431
568
  export interface CloudConflict {
432
569
  id: number;
@@ -854,6 +854,8 @@ export interface NexusImpactParams {
854
854
  why?: boolean;
855
855
  /** Maximum reverse traversal depth (default 3, capped at 5). */
856
856
  depth?: number;
857
+ /** `--no-refresh` (T13523): answer from the index as it stands. */
858
+ noRefresh?: boolean;
857
859
  }
858
860
  /** Affected symbol in impact result. */
859
861
  export interface NexusImpactAffectedNode {
@@ -1203,6 +1205,8 @@ export interface NexusContextParams {
1203
1205
  limit?: number;
1204
1206
  /** When true, fetch source code content. */
1205
1207
  content?: boolean;
1208
+ /** `--no-refresh` (T13523): answer from the index as it stands. */
1209
+ noRefresh?: boolean;
1206
1210
  }
1207
1211
  /** A single caller or callee relationship from `nexus.context`. */
1208
1212
  export interface NexusContextRelation {
@@ -630,6 +630,13 @@ export interface OrchestrateSpawnResult {
630
630
  * @task T963
631
631
  */
632
632
  sessionId: string | null;
633
+ /**
634
+ * The worktree's dependency install outcome (`skipped` under `--no-install`,
635
+ * `deferred` when the heavy-work budget was busy, with the command the
636
+ * worker runs instead), or `null` without a worktree.
637
+ * @task T13524
638
+ */
639
+ install?: import('./worktree.js').WorktreeInstallOutcome | null;
633
640
  /**
634
641
  * Legacy mirror of the prompt/protocolType/tier bundle for readers that
635
642
  * still consume `spawnContext.*`.
@@ -228,6 +228,11 @@ export interface ValidateGateParams {
228
228
  * none; a gate without a fresh cached pass refuses with `E_GATE_NOT_CACHED`.
229
229
  */
230
230
  noRun?: boolean;
231
+ /**
232
+ * `--run-typed` (T13521): execute uncached typed gates during this write.
233
+ * Without it a write reads cached passes only (owner decision).
234
+ */
235
+ runTyped?: boolean;
231
236
  /**
232
237
  * `cleo verify --run --req A,B` (T13486): run and cache only the typed gates
233
238
  * with these REQ-IDs (comma-separated); the others are not run.
@@ -17,6 +17,7 @@
17
17
  // ---------------------------------------------------------------------------
18
18
  // Hooks framework
19
19
  // ---------------------------------------------------------------------------
20
+ import type { HookExecutor, HookOutcome } from '../project-hooks.js';
20
21
 
21
22
  /**
22
23
  * A declarative lifecycle hook definition for worktree events.
@@ -28,6 +29,8 @@
28
29
  * @task T1161
29
30
  */
30
31
  export interface WorktreeHook {
32
+ /** Shared hook identity when legacy wiring duplicates a tracked project definition. */
33
+ projectHookId?: string;
31
34
  /** Shell command to run in the worktree dir (`/bin/sh -c` on POSIX, cmd.exe on Windows). */
32
35
  command: string;
33
36
  /**
@@ -239,6 +242,8 @@ export interface CreateWorktreeOptions {
239
242
  reason?: 'subagent' | 'experiment' | 'parallel-wave';
240
243
  /** Declarative hooks to run after creation. */
241
244
  hooks?: WorktreeHook[];
245
+ /** Optional shared project executor injected by CLEO; never imports core into worktree. */
246
+ projectHookExecutor?: HookExecutor;
242
247
  /**
243
248
  * When true, read `.cleo/worktree-include` from the project root and apply
244
249
  * any declared include patterns to the new worktree.
@@ -317,6 +322,50 @@ export interface CreateWorktreeOptions {
317
322
  * @task T12506
318
323
  */
319
324
  lockTtlMs?: number;
325
+ /**
326
+ * The git shim the caller installed for this worktree (T13522). Only when
327
+ * `installed` is the shim dir prepended to PATH and
328
+ * `CLEO_BRANCH_PROTECTION=strict` advertised; otherwise the env says
329
+ * `unavailable`, so nothing claims a fence that is not there.
330
+ *
331
+ * @task T13522
332
+ */
333
+ gitShim?: { readonly dir: string; readonly installed: boolean };
334
+ /**
335
+ * Provision without installing dependencies (`--no-install`, T13524). The
336
+ * result's `install` says so, with the command the worker runs instead.
337
+ *
338
+ * @task T13524
339
+ */
340
+ skipInstall?: boolean;
341
+ /**
342
+ * Admission for the dependency install (T13524): called before `pnpm install`
343
+ * runs; resolves to a release function once admitted, or `null` when the
344
+ * machine budget is busy, in which case the install is deferred to the
345
+ * worker rather than run outside the heavy-work queue. Absent: install
346
+ * unadmitted (legacy callers).
347
+ *
348
+ * @task T13524
349
+ */
350
+ admitInstall?: () => Promise<(() => Promise<void>) | null>;
351
+ }
352
+
353
+ /**
354
+ * What happened to a worktree's dependency install (T13524).
355
+ *
356
+ * @task T13524
357
+ */
358
+ export interface WorktreeInstallOutcome {
359
+ /**
360
+ * `installed`/`failed`: it ran. `skipped`: `--no-install`. `deferred`: the
361
+ * machine budget was busy, so the worker installs. `not-needed`: no
362
+ * lockfile, already installed, or a re-attached worktree.
363
+ */
364
+ status: 'installed' | 'failed' | 'skipped' | 'deferred' | 'not-needed';
365
+ /** Why, for anything but `installed`. */
366
+ reason?: string;
367
+ /** The command the worker runs when the install did not happen here. */
368
+ command?: string;
320
369
  }
321
370
 
322
371
  /**
@@ -365,6 +414,8 @@ export interface CreateWorktreeResult {
365
414
  preamble: string;
366
415
  /** Results of any post-create hooks that were executed. */
367
416
  hookResults: WorktreeHookResult[];
417
+ /** Results from activated project definitions for post-create. */
418
+ projectHookResults?: HookOutcome[];
368
419
  /** Include patterns that were applied (empty if none). */
369
420
  appliedPatterns: WorktreeIncludePattern[];
370
421
  /**
@@ -378,7 +429,15 @@ export interface CreateWorktreeResult {
378
429
  failedPaths: string[];
379
430
  /** Results of post-start hooks. */
380
431
  hookResults: WorktreeHookResult[];
432
+ /** Results from activated project definitions for post-start. */
433
+ projectHookResults?: HookOutcome[];
381
434
  };
435
+ /**
436
+ * The dependency install outcome (T13524).
437
+ *
438
+ * @task T13524
439
+ */
440
+ install?: WorktreeInstallOutcome;
382
441
  }
383
442
 
384
443
  // ---------------------------------------------------------------------------
@@ -97,6 +97,15 @@ export interface ProjectEvidenceContext {
97
97
  * CLEO's own installed hook files are always excluded (T13135, gh#1805).
98
98
  */
99
99
  scopeExcludes?: string[];
100
+ /**
101
+ * Globs of TRACKED files a harness rewrites while a run executes (a goal
102
+ * runtime's `run.json`, its ledger), left out of the evidence tree hash so
103
+ * the run cannot stale its own receipt; they stay tracked for history
104
+ * (T13554). Read from the default branch only. A glob that matches the
105
+ * project context, a `goal.yaml`, an oracle or a typed gate's input is
106
+ * refused (E_EVIDENCE_RUNTIME_PATH_PROTECTED). Default: none.
107
+ */
108
+ runtimePaths?: string[];
100
109
  }
101
110
 
102
111
  /**
@@ -0,0 +1,144 @@
1
+ /** Provider delivery contracts; runtime installers live in adapters and CAAMP. */
2
+ import { z } from 'zod';
3
+ import type { HookJsonValue } from './heavy-command-hook.js';
4
+ import type { HookInvocation } from './project-hooks.js';
5
+
6
+ /** Parsed JSON object used by surgical managed writers. */
7
+ export type HookConfigObject = { readonly [key: string]: HookJsonValue };
8
+ /** One surgical edit; undefined removes exactly the addressed node. */
9
+ export interface HookConfigEdit {
10
+ /** JSON property/index path. */
11
+ path: (string | number)[];
12
+ /** Replacement value, or undefined for removal. */
13
+ value?: HookJsonValue;
14
+ /** Append/insert an array element without replacing its siblings. */
15
+ insert?: boolean;
16
+ }
17
+ /** Receipt authorizing management of one exact config entry. */
18
+ export const ProjectHookDeliveryReceiptSchema = z
19
+ .object({
20
+ schemaVersion: z.literal(1),
21
+ provider: z.string().min(1).max(128),
22
+ configPath: z.string().min(1).max(4096),
23
+ entries: z
24
+ .array(
25
+ z
26
+ .object({
27
+ event: z.string().min(1).max(128),
28
+ hash: z.string().regex(/^[a-f0-9]{64}$/),
29
+ command: z.string().min(1).max(8192),
30
+ })
31
+ .strict(),
32
+ )
33
+ .max(128),
34
+ recordedAt: z.iso.datetime(),
35
+ })
36
+ .strict();
37
+ /** Machine-local source and entry ownership receipt. */
38
+ export type ProjectHookDeliveryReceipt = z.infer<typeof ProjectHookDeliveryReceiptSchema>;
39
+ /** Entry-level provenance without printing commands that may contain secrets. */
40
+ export interface ProjectHookEntryInspection {
41
+ /** Native provider event. */
42
+ event: string;
43
+ /** Index in the provider event's entry array. */
44
+ index: number;
45
+ /** Receipt proves project delivery ownership; other ownership remains unknown. */
46
+ owner: 'project' | 'unknown';
47
+ /** Generated entry is attributable only through its retained local receipt. */
48
+ provenance: 'cleo-receipt' | 'unknown';
49
+ /** Entry digest permits review without retaining its command text. */
50
+ hash: string;
51
+ /** Tracked source, only for receipt-owned project checks. */
52
+ source?: '.cleo/hooks.json';
53
+ }
54
+ /** Inspect/install result; a config file never proves native trust or live support. */
55
+ export interface ProjectHookDeliveryResult {
56
+ /** Harness identifier. */
57
+ provider: string;
58
+ /** Observed local binary version, when discoverable. */
59
+ version?: string;
60
+ /** Safe resolved project-local destination, absent when unsupported. */
61
+ configPath?: string;
62
+ /** Delivery state. */
63
+ state: 'planned' | 'current' | 'installed' | 'conflict' | 'unsupported' | 'disabled' | 'missing';
64
+ /** Independent capability evidence. */
65
+ capability: 'verified' | 'documented' | 'unverified';
66
+ /** Native project/definition trust is never granted by CLEO. */
67
+ nativeTrust: 'unverified';
68
+ /** Receipt-backed provenance; legacy config provenance remains unknown. */
69
+ provenance: 'cleo-receipt' | 'unknown';
70
+ /** Sanitized actionable findings. */
71
+ diagnostics: string[];
72
+ /** Reviewed team-config integration aid when automatic local delivery is ineligible. */
73
+ integrationSnippet?: string;
74
+ /** Per-entry attribution, deliberately excluding raw config command strings. */
75
+ entries?: ProjectHookEntryInspection[];
76
+ }
77
+ /** SDK-owned utilities injected into provider delivery without a runtime SDK dependency. */
78
+ export interface ProjectHookDeliveryServices {
79
+ /**
80
+ * Read bounded regular files through the canonical filesystem primitive.
81
+ * @remarks
82
+ * The caller supplies the SDK implementation, preserving special-file and racing-growth checks.
83
+ * @param path - Resolved file path selected by the delivery adapter.
84
+ * @param maxBytes - Maximum UTF-8 input size in bytes.
85
+ * @returns The file's text after its descriptor and allocation limit have been checked.
86
+ * @example
87
+ * ```typescript
88
+ * const readConfiguration = (services: ProjectHookDeliveryServices) =>
89
+ * services.readTextFile('/repo/.codex/hooks.json', 262144);
90
+ * ```
91
+ */
92
+ readTextFile(path: string, maxBytes: number): Promise<string>;
93
+ /**
94
+ * Git discovery environment with ambient repository overrides removed.
95
+ * @defaultValue No implicit default; the caller supplies the snapshot.
96
+ */
97
+ gitEnvironment: Readonly<Record<string, string | undefined>>;
98
+ }
99
+ /** Explicit cold-path provider sync inputs. */
100
+ export interface ProjectHookDeliveryOptions {
101
+ /** Canonical SDK utilities supplied by the caller through the contracts port. */
102
+ services: ProjectHookDeliveryServices;
103
+ /** Checkout root validated by the core context resolver. */
104
+ projectRoot: string;
105
+ /** Per-checkout Git-private receipt directory. */
106
+ stateDir: string;
107
+ /** Canonical agent events selected from the manifest. */
108
+ events: string[];
109
+ /** Activation was inspected fresh. */
110
+ enabled: boolean;
111
+ /** No files (including lock files) may be written in dry-run mode. */
112
+ dryRun: boolean;
113
+ /**
114
+ * Explicit provider selection; otherwise detect project-local configuration.
115
+ * @defaultValue Providers detected from local configuration and installed harness binaries.
116
+ */
117
+ providers?: string[];
118
+ /**
119
+ * Remove only receipt-owned, unmodified additions instead of synchronizing.
120
+ * @defaultValue false
121
+ */
122
+ rollback?: boolean;
123
+ }
124
+
125
+ /** Provider-normalized request awaiting checkout resolution by core. */
126
+ export interface ProjectHookNativeRequest {
127
+ /** Actual triggering directory, independent of harness-specific environment variables. */
128
+ cwd: string;
129
+ /** Provider wire protocol. */
130
+ provider: string;
131
+ /** Optional native event name from the provider input. */
132
+ nativeEvent?: string;
133
+ /** Shared input fields before Git resolves projectRoot and gitCommonDir. */
134
+ input: Omit<HookInvocation, 'projectRoot' | 'gitCommonDir'>;
135
+ }
136
+ /** Native stdout/stderr and adapter exit code; never an approval rewrite. */
137
+ export interface ProjectHookNativeResult {
138
+ /** Provider-format advisory output. */
139
+ stdout: string;
140
+ /** Safe current-invocation diagnostics. */
141
+ stderr: string;
142
+ /** Git verdict failure or zero for advisory/infra failure. */
143
+ exitCode: number;
144
+ }