@cleocode/contracts 2026.9.15 → 2026.9.17

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 (52) hide show
  1. package/dist/agent-registry.d.ts +11 -1
  2. package/dist/agent-registry.d.ts.map +1 -1
  3. package/dist/code-symbol.d.ts +13 -1
  4. package/dist/code-symbol.d.ts.map +1 -1
  5. package/dist/code-symbol.js +1 -1
  6. package/dist/config.d.ts +42 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/credential-transfer.d.ts +103 -0
  9. package/dist/credential-transfer.d.ts.map +1 -0
  10. package/dist/credential-transfer.js +21 -0
  11. package/dist/credential-transfer.js.map +1 -0
  12. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  13. package/dist/dispatch/operations-registry.js +7 -2
  14. package/dist/dispatch/operations-registry.js.map +1 -1
  15. package/dist/doctor.d.ts +103 -0
  16. package/dist/doctor.d.ts.map +1 -1
  17. package/dist/errors.d.ts +5 -0
  18. package/dist/errors.d.ts.map +1 -1
  19. package/dist/errors.js +2 -1
  20. package/dist/errors.js.map +1 -1
  21. package/dist/graph.d.ts +118 -1
  22. package/dist/graph.d.ts.map +1 -1
  23. package/dist/index.d.ts +6 -4
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/operations/nexus-scope-map.d.ts +16 -0
  27. package/dist/operations/nexus-scope-map.d.ts.map +1 -1
  28. package/dist/operations/nexus-scope-map.js +16 -0
  29. package/dist/operations/nexus-scope-map.js.map +1 -1
  30. package/dist/operations/nexus-scope.d.ts +8 -0
  31. package/dist/operations/nexus-scope.d.ts.map +1 -1
  32. package/dist/operations/nexus.d.ts +77 -5
  33. package/dist/operations/nexus.d.ts.map +1 -1
  34. package/dist/portable-bundle.d.ts +436 -0
  35. package/dist/portable-bundle.d.ts.map +1 -0
  36. package/dist/portable-bundle.js +17 -0
  37. package/dist/portable-bundle.js.map +1 -0
  38. package/package.json +2 -2
  39. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  40. package/src/agent-registry.ts +11 -1
  41. package/src/code-symbol.ts +13 -1
  42. package/src/config.ts +44 -0
  43. package/src/credential-transfer.ts +112 -0
  44. package/src/dispatch/operations-registry.ts +8 -2
  45. package/src/doctor.ts +115 -0
  46. package/src/errors.ts +7 -1
  47. package/src/graph.ts +122 -1
  48. package/src/index.ts +50 -0
  49. package/src/operations/nexus-scope-map.ts +16 -0
  50. package/src/operations/nexus-scope.ts +9 -0
  51. package/src/operations/nexus.ts +87 -5
  52. package/src/portable-bundle.ts +476 -0
@@ -35,8 +35,18 @@ export interface AgentCredential {
35
35
  agentId: string;
36
36
  /** Human-readable display name. */
37
37
  displayName: string;
38
- /** API key for authentication (`sk_live_*`). Stored encrypted at rest. */
38
+ /**
39
+ * API key for authentication (`sk_live_*`). Stored encrypted at rest (global
40
+ * KDF, T12352). When {@link AgentCredential.requiresReauth} is true this is
41
+ * NOT the real key — re-register it.
42
+ */
39
43
  apiKey: string;
44
+ /**
45
+ * True when the stored key is not recoverable (written before T12352, which
46
+ * stored a derived HMAC and discarded the key, or encrypted on another
47
+ * device). Re-register with `cleo agent register --id <id> --api-key <key>`.
48
+ */
49
+ requiresReauth?: boolean;
40
50
  /** Base URL of the messaging API (default: api.signaldock.io). */
41
51
  apiBaseUrl: string;
42
52
  /** Agent classification from the registry (e.g. 'code_dev', 'orchestrator'). */
@@ -87,7 +87,7 @@ export const PARSER_WORKER_HEAP_MIN_MB = 8;
87
87
  export const PARSER_WORKER_HEAP_MAX_MB = 4_096;
88
88
 
89
89
  /** Default V8 old-space per parse worker, in MiB. */
90
- export const PARSER_WORKER_HEAP_DEFAULT_MB = 128;
90
+ export const PARSER_WORKER_HEAP_DEFAULT_MB = 512;
91
91
 
92
92
  /** Bounds for one native parser invocation; cancellation is cooperative in-process. */
93
93
  export interface ParserExecutionLimits {
@@ -111,6 +111,18 @@ export interface ParserProcessHandle {
111
111
  nativeMemory: 'unverified';
112
112
  /** Kill owned execution and wait until it has exited. */
113
113
  stop(): Promise<void>;
114
+ /**
115
+ * The tail of the child's own standard error, bounded in size.
116
+ *
117
+ * The stream was drained and discarded so a full pipe could not block the
118
+ * child. Draining is right; discarding is not — it threw away the only
119
+ * account of why a worker died, leaving the parent to report the broken pipe
120
+ * it noticed afterwards. Retaining a bounded tail keeps the pipe moving and
121
+ * keeps the cause (T12313).
122
+ *
123
+ * @returns Captured stderr, newest content last; empty when the child said nothing.
124
+ */
125
+ stderrTail(): string;
114
126
  }
115
127
 
116
128
  /** Dependency-inverted access to the existing runtime process containment service. */
package/src/config.ts CHANGED
@@ -751,6 +751,43 @@ export interface LeadRollupConfig {
751
751
  mode?: LeadRollupMode;
752
752
  }
753
753
 
754
+ /**
755
+ * Inline refresh policy for code-graph queries (T12316).
756
+ *
757
+ * Before answering, a graph query checks how far the published index lags the
758
+ * working tree. When only a few files are stale it refreshes the index inline
759
+ * (incrementally) and discloses that it did; above these bounds it answers
760
+ * from the stale index with a warning instead of blocking.
761
+ */
762
+ export interface NexusAutoRefreshConfig {
763
+ /**
764
+ * Whether queries may refresh the index inline.
765
+ *
766
+ * @defaultValue true
767
+ */
768
+ enabled?: boolean;
769
+ /**
770
+ * Most stale files a query refreshes inline.
771
+ *
772
+ * @defaultValue 25
773
+ */
774
+ maxFiles?: number;
775
+ /**
776
+ * Wall-clock budget in milliseconds. A refresh whose recorded-cost estimate
777
+ * exceeds it is not started; a started one is cancelled at the budget and
778
+ * leaves the previous graph intact.
779
+ *
780
+ * @defaultValue 60000
781
+ */
782
+ budgetMs?: number;
783
+ }
784
+
785
+ /** Code-graph (`cleo nexus`) configuration. */
786
+ export interface NexusConfig {
787
+ /** Inline refresh of a slightly stale index before graph queries answer. */
788
+ autoRefresh?: NexusAutoRefreshConfig;
789
+ }
790
+
754
791
  /**
755
792
  * Daemon lifecycle configuration.
756
793
  *
@@ -930,6 +967,13 @@ export interface CleoConfig {
930
967
  * @saga T10377
931
968
  */
932
969
  leadRollup?: LeadRollupConfig;
970
+ /**
971
+ * Code-graph behaviour — inline refresh bounds for stale-index queries.
972
+ *
973
+ * @defaultValue undefined (auto-refresh enabled, 25 files, 60 s)
974
+ * @task T12316
975
+ */
976
+ nexus?: NexusConfig;
933
977
  /**
934
978
  * Daemon lifecycle settings.
935
979
  *
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Credential transfer contracts — moving stored secrets between devices
3
+ * without moving the device key.
4
+ *
5
+ * CLEO encrypts stored credentials under a key derived from the local
6
+ * `machine-key`. That key never leaves the device (it is excluded from every
7
+ * backup bundle, encrypted or not). A credential therefore crosses devices in
8
+ * one of two ways:
9
+ *
10
+ * - **Sealed** — an encrypted backup carries the credentials re-sealed under a
11
+ * key derived from the user's passphrase; the importing device unseals them
12
+ * and re-encrypts each one under ITS OWN machine-key.
13
+ * - **Re-entered** — an unencrypted backup carries no usable credential
14
+ * material; the import lists every credential to re-enter, each with the
15
+ * exact command that re-enters it.
16
+ *
17
+ * @task T12326
18
+ * @module credential-transfer
19
+ */
20
+
21
+ /**
22
+ * The store a credential lives in.
23
+ *
24
+ * - `project-agent` — `tasks_agent_credentials.api_key_encrypted` in the
25
+ * project `cleo.db` (project KDF).
26
+ * - `service-connection` — `service_connections.credentials_enc` in the global
27
+ * `cleo.db` (global KDF, id `service:<provider>:<label>`).
28
+ * - `llm-pool` — an entry in `<cleoHome>/llm-credentials.json`.
29
+ * - `agent-registry` — `agent_registry_agents.api_key_encrypted` in the global
30
+ * `cleo.db` (global KDF, id `agent:<agentId>`, `gk1:` prefix — T12352).
31
+ *
32
+ * @task T12326
33
+ * @task T12352
34
+ */
35
+ export type CredentialStoreKind =
36
+ | 'project-agent'
37
+ | 'service-connection'
38
+ | 'llm-pool'
39
+ | 'agent-registry';
40
+
41
+ /**
42
+ * A credential identified WITHOUT its value — safe to print, log, or write
43
+ * into a bundle manifest.
44
+ *
45
+ * @task T12326
46
+ */
47
+ export interface CredentialDescriptor {
48
+ /** Which store holds the credential. */
49
+ readonly store: CredentialStoreKind;
50
+ /**
51
+ * Stable identity within the store: the agent id (`project-agent`,
52
+ * `agent-registry`),
53
+ * `<provider>:<label>` (`service-connection`, `llm-pool`).
54
+ */
55
+ readonly id: string;
56
+ /** Human-readable description of the credential. */
57
+ readonly label: string;
58
+ }
59
+
60
+ /**
61
+ * A credential the target device must re-enter, with the one command that
62
+ * re-enters it.
63
+ *
64
+ * @task T12326
65
+ */
66
+ export interface CredentialReentry extends CredentialDescriptor {
67
+ /** Why the credential could not be carried over. */
68
+ readonly reason: string;
69
+ /** The exact CLI command that re-enters this credential (secret shown as a placeholder). */
70
+ readonly reentryCommand: string;
71
+ }
72
+
73
+ /**
74
+ * Outcome of sealing the local credentials under a passphrase.
75
+ *
76
+ * @task T12326
77
+ */
78
+ export interface SealCredentialsResult {
79
+ /** Sealed payload bytes (passphrase-encrypted). Never contains machine-key material. */
80
+ readonly sealed: Uint8Array;
81
+ /** Credentials carried in the sealed payload. */
82
+ readonly sealedCredentials: readonly CredentialDescriptor[];
83
+ /** Credentials that could not be decrypted locally and must be re-entered after restore. */
84
+ readonly reentry: readonly CredentialReentry[];
85
+ }
86
+
87
+ /**
88
+ * Outcome of unsealing a payload into the local stores.
89
+ *
90
+ * @task T12326
91
+ */
92
+ export interface UnsealCredentialsResult {
93
+ /** Credentials re-encrypted under the local machine-key and written. */
94
+ readonly restored: readonly CredentialDescriptor[];
95
+ /** Credentials the operator must re-enter, each with its command. */
96
+ readonly reentry: readonly CredentialReentry[];
97
+ }
98
+
99
+ /**
100
+ * Outcome of migrating project credentials from the legacy path-bound KDF to
101
+ * the project-identity KDF.
102
+ *
103
+ * @task T12326
104
+ */
105
+ export interface ProjectCredentialMigrationResult {
106
+ /** Credentials re-encrypted from the legacy path KDF to the project-id KDF. */
107
+ readonly migrated: readonly CredentialDescriptor[];
108
+ /** Credentials already under the project-id KDF (untouched). */
109
+ readonly current: readonly CredentialDescriptor[];
110
+ /** Credentials no candidate key could decrypt — ciphertext left untouched. */
111
+ readonly reentry: readonly CredentialReentry[];
112
+ }
@@ -6965,7 +6965,12 @@ export const OPERATIONS: OperationDef[] = [
6965
6965
  required: false,
6966
6966
  description: 'JS regex matched against project_path',
6967
6967
  },
6968
- { name: 'includeTemp', type: 'boolean', required: false, description: 'Match .temp/ paths' },
6968
+ {
6969
+ name: 'includeTemp',
6970
+ type: 'boolean',
6971
+ required: false,
6972
+ description: 'Match .temp/ or OS temp-directory paths',
6973
+ },
6969
6974
  {
6970
6975
  name: 'includeTests',
6971
6976
  type: 'boolean',
@@ -7000,7 +7005,8 @@ export const OPERATIONS: OperationDef[] = [
7000
7005
  name: 'vacuum',
7001
7006
  type: 'boolean',
7002
7007
  required: false,
7003
- description: 'After delete, run sqlite VACUUM on nexus.db to reclaim space (T9117)',
7008
+ description:
7009
+ 'After delete, VACUUM the global registry store (<cleoHome>/cleo.db) to reclaim space (T9117 · T12324)',
7004
7010
  },
7005
7011
  ],
7006
7012
  },
package/src/doctor.ts CHANGED
@@ -1063,3 +1063,118 @@ export interface LegacyBackupScanResult {
1063
1063
  */
1064
1064
  errors: Array<{ path: string; error: string }>;
1065
1065
  }
1066
+
1067
+ // ============================================================================
1068
+ // Superseded-store reconcile — `cleo doctor superseded-store --reconcile` (T12319)
1069
+ // ============================================================================
1070
+
1071
+ /**
1072
+ * Per-table row accounting for one legacy source table reconciled into the
1073
+ * live project `cleo.db`.
1074
+ *
1075
+ * `missingInLive` counts SOURCE rows whose primary key is absent from the live
1076
+ * table — the rows a reconcile must still copy. It is `null` only when the key
1077
+ * cannot be compared (no shared primary key); such a table is judged by counts.
1078
+ *
1079
+ * @task T12319
1080
+ */
1081
+ export interface SupersededStoreTableCount {
1082
+ /** Logical legacy source (`tasks`, `brain (project)`, `conduit`). */
1083
+ sourceDb: string;
1084
+ /** Table name in the legacy file. */
1085
+ sourceTable: string;
1086
+ /** Consolidated table in `cleo.db` the rows land in. */
1087
+ targetTable: string;
1088
+ /** Rows in the legacy table. */
1089
+ sourceRows: number;
1090
+ /** Rows in the live table. */
1091
+ liveRows: number;
1092
+ /** Source rows whose key is absent from the live table, or `null` if not comparable. */
1093
+ missingInLive: number | null;
1094
+ }
1095
+
1096
+ /**
1097
+ * A legacy row set an additive reconcile did NOT copy (T12355). Every one is
1098
+ * listed; none is silently dropped.
1099
+ *
1100
+ * - `live-authoritative` — the table's runtime home is part of the task graph
1101
+ * the project has been editing on the consolidated store; a legacy row
1102
+ * missing there may have been deleted or rewritten since, so copying it
1103
+ * could resurrect or contradict live state.
1104
+ * - `collides-with-live` — inserting the row would violate a live key or
1105
+ * constraint (e.g. a `(task_id, ordinal)` slot a newer criterion now holds),
1106
+ * i.e. it could only land by overwriting live data.
1107
+ *
1108
+ * @task T12355
1109
+ */
1110
+ export interface SupersededStoreConflict {
1111
+ /** Logical legacy source. */
1112
+ sourceDb: string;
1113
+ /** Table in the legacy file. */
1114
+ sourceTable: string;
1115
+ /** Table the runtime reads it from. */
1116
+ targetTable: string;
1117
+ /** Legacy rows left uncopied. */
1118
+ rows: number;
1119
+ /** Why they were left. */
1120
+ reason: 'live-authoritative' | 'collides-with-live';
1121
+ }
1122
+
1123
+ /**
1124
+ * Outcome of `cleo doctor superseded-store --reconcile`.
1125
+ *
1126
+ * - `nothing-to-reconcile` — every legacy row is already present; no write.
1127
+ * - `planned` — dry-run; `before` shows what an apply would copy.
1128
+ * - `reconciled` — rows copied and re-verified; `after` proves zero missing.
1129
+ * - `refused` — the post-copy verification found a mismatch; every row this
1130
+ * run inserted was reverted (see `rolledBack`) and the command exits non-zero.
1131
+ *
1132
+ * @task T12319
1133
+ */
1134
+ export type SupersededStoreReconcileOutcome =
1135
+ | 'nothing-to-reconcile'
1136
+ | 'planned'
1137
+ | 'reconciled'
1138
+ | 'refused';
1139
+
1140
+ /**
1141
+ * Receipt of a superseded-store reconcile. Written verbatim to
1142
+ * `<staging>/reconcile-receipt.json` for an apply.
1143
+ *
1144
+ * @task T12319
1145
+ */
1146
+ export interface SupersededStoreReconcileResult {
1147
+ /** Final outcome; `refused` is the only non-zero exit. */
1148
+ outcome: SupersededStoreReconcileOutcome;
1149
+ /**
1150
+ * `full` — every legacy row must end up present (stranded projects).
1151
+ * `additive` — for a project already running on the consolidated store:
1152
+ * copy only rows whose keys are absent from live history tables, never
1153
+ * touch the live task graph, and list everything left in `conflicts`.
1154
+ */
1155
+ mode: 'full' | 'additive';
1156
+ /** Legacy rows deliberately left uncopied (additive mode); empty in full mode. */
1157
+ conflicts: SupersededStoreConflict[];
1158
+ /** `true` when nothing was written. */
1159
+ dryRun: boolean;
1160
+ /** Absolute project root. */
1161
+ projectRoot: string;
1162
+ /** Absolute path of the live project `cleo.db`. */
1163
+ liveStorePath: string;
1164
+ /** Legacy files read (never modified, moved or deleted). */
1165
+ sourcePaths: string[];
1166
+ /** Per-table accounting before any write. */
1167
+ before: SupersededStoreTableCount[];
1168
+ /** Per-table accounting after the copy; empty unless a copy ran. */
1169
+ after: SupersededStoreTableCount[];
1170
+ /** Rows inserted into `cleo.db` by this run (0 for dry-run / no-op). */
1171
+ rowsCopied: number;
1172
+ /** Rows reverted after a refused verification (0 otherwise). */
1173
+ rolledBack: number;
1174
+ /** Staging directory holding the journal, source backups and receipt, or `null`. */
1175
+ stagingDir: string | null;
1176
+ /** Absolute path of the written receipt, or `null` when none was written. */
1177
+ receiptPath: string | null;
1178
+ /** Human-readable explanation, suitable for printing verbatim. */
1179
+ reason: string;
1180
+ }
package/src/errors.ts CHANGED
@@ -543,6 +543,11 @@ export interface ChangesetYamlInvalidDetails {
543
543
  snippet?: string;
544
544
  /** Underlying parser message, propagated verbatim for diagnostics. */
545
545
  parserMessage: string;
546
+ /**
547
+ * Remediation naming the fix when the failure is a value starting with a
548
+ * YAML-reserved character, e.g. wrap it in double quotes (T12351).
549
+ */
550
+ hint?: string;
546
551
  }
547
552
 
548
553
  /**
@@ -588,7 +593,8 @@ export class ChangesetYamlInvalidError extends Error {
588
593
  constructor(details: ChangesetYamlInvalidDetails) {
589
594
  const linePart = details.line !== null ? `:${details.line}` : '';
590
595
  super(
591
- `E_CHANGESET_YAML_INVALID: ${details.file}${linePart} invalid YAML frontmatter: ${details.parserMessage}`,
596
+ `E_CHANGESET_YAML_INVALID: ${details.file}${linePart} invalid YAML frontmatter: ${details.parserMessage}` +
597
+ (details.hint !== undefined ? `\n hint: ${details.hint}` : ''),
592
598
  );
593
599
  this.name = 'ChangesetYamlInvalidError';
594
600
  this.details = details;
package/src/graph.ts CHANGED
@@ -658,8 +658,19 @@ export interface GraphIndexAssessment {
658
658
  sourceRoots?: GraphSourceRootAssessment;
659
659
  /** Immutable publication identity allocated before extraction; absent on historical indexes. */
660
660
  generation?: string;
661
- /** Unmodeled AST scopes remain explicit limitations instead of fabricated declarations. */
661
+ /**
662
+ * Unmodeled AST scopes remain explicit limitations instead of fabricated declarations.
663
+ * Absent on a summary read: the list is stored beside the assessment (T12348)
664
+ * and loaded only on request; {@link GraphIndexAssessment.referenceCount} still
665
+ * reports how many there are.
666
+ */
662
667
  references?: GraphIndexReferenceReport[];
668
+ /**
669
+ * Number of retained references, recorded whenever the list is stored
670
+ * separately from this summary (T12348). Absent on historical indexes, whose
671
+ * `references` are inline.
672
+ */
673
+ referenceCount?: number;
663
674
  /** Explicit nested repository/worktree scope retained for subsequent rebuilds. */
664
675
  includedRepositories?: string[];
665
676
  /** Canonical root whose relative file paths this graph describes. */
@@ -682,6 +693,116 @@ export interface GraphPublicationRows {
682
693
  nodes: NexusNodeInsertRow[];
683
694
  /** Complete replacement relationship generation. */
684
695
  relations: NexusRelationInsertRow[];
696
+ /**
697
+ * Per-file extraction memo changes committed in the same transaction as the rows.
698
+ * Absent when the producer does not maintain a parse cache.
699
+ */
700
+ parseCache?: GraphParseCacheUpdate;
701
+ }
702
+
703
+ /**
704
+ * One file's memoized extraction output, valid only for the exact bytes and extractor build.
705
+ *
706
+ * Extraction is a pure function of `(path, content, extractor build, publication generation)`;
707
+ * the generation token embedded in anonymous identities is rewritten on reuse, so a reused
708
+ * entry yields exactly what re-parsing the same bytes would.
709
+ */
710
+ export interface GraphParseCacheEntry {
711
+ /** Path relative to the assessed source root. */
712
+ path: string;
713
+ /** SHA-256 of the bytes that produced this extraction. */
714
+ contentHash: string;
715
+ /** Fingerprint of the extractor build (code + grammars) that produced it. */
716
+ fingerprint: string;
717
+ /** Publication generation embedded in the stored payload's anonymous identities. */
718
+ generation: string;
719
+ /** Compressed serialized extraction; opaque to every owner except the producing pipeline. */
720
+ payload: Uint8Array;
721
+ }
722
+
723
+ /** Parse-cache mutation applied atomically with a graph publication. */
724
+ export interface GraphParseCacheUpdate {
725
+ /** Extractor fingerprint every retained and written entry must carry. */
726
+ fingerprint: string;
727
+ /** Delete every existing entry before applying `upserts` (full rebuilds). */
728
+ reset: boolean;
729
+ /** Entries for files parsed during this run. */
730
+ upserts: GraphParseCacheEntry[];
731
+ /** Paths whose entries no longer describe a current, successfully parsed file. */
732
+ deletePaths: string[];
733
+ }
734
+
735
+ /** Why and how an index run chose between reusing prior extraction and parsing everything. */
736
+ export interface GraphIndexRunSummary {
737
+ /**
738
+ * `incremental` parsed only files without a reusable extraction; `full` parsed every file;
739
+ * `unchanged` found no source difference and published nothing.
740
+ */
741
+ mode: 'incremental' | 'full' | 'unchanged';
742
+ /** Human-readable reason for the mode, including every full-rebuild fallback. */
743
+ reason: string;
744
+ /** Previously indexed files whose content hash differs. */
745
+ changedFiles: number;
746
+ /** Files absent from the previous generation. */
747
+ addedFiles: number;
748
+ /** Previously indexed files no longer present. */
749
+ deletedFiles: number;
750
+ /** Files handed to the parser this run. */
751
+ parsedFiles: number;
752
+ /** Files whose extraction was reused from the parse cache instead of re-parsed. */
753
+ reusedFiles: number;
754
+ /**
755
+ * Files whose imports, calls, accesses and heritage were re-resolved. Resolution always runs
756
+ * over the complete merged symbol table, so this equals every extracted file; dependents of
757
+ * changed files are therefore never left pointing at stale targets.
758
+ */
759
+ resolvedFiles: number;
760
+ /** Wall-clock milliseconds per pipeline phase, for cost disclosure. */
761
+ phaseMs: Record<string, number>;
762
+ }
763
+
764
+ /**
765
+ * Freshness of a published code-graph index relative to the working tree.
766
+ *
767
+ * Computed by metadata comparison (mtime + size) with a content-hash fallback on mismatch, so
768
+ * a touched-but-identical file is not reported stale.
769
+ */
770
+ export interface GraphIndexFreshness {
771
+ /** Whether a published index exists at all. */
772
+ indexed: boolean;
773
+ /** `fresh` when no indexed file differs from disk; `stale` otherwise; `unknown` if unassessed. */
774
+ status: 'fresh' | 'stale' | 'unknown';
775
+ /** Assessment timestamp of the published generation. */
776
+ lastIndexedAt: string | null;
777
+ /** Files recorded in the published generation's assessment. */
778
+ fileCount: number;
779
+ /** Modified, added and deleted source files. */
780
+ staleFileCount: number;
781
+ /** Up to a bounded sample of stale paths, for disclosure. */
782
+ stalePaths: string[];
783
+ /** Whether the queried symbol's own file is stale; absent when no symbol file is known. */
784
+ symbolFileStale?: boolean;
785
+ /** The symbol file the `symbolFileStale` verdict describes. */
786
+ symbolFile?: string;
787
+ /** Command that refreshes the index. */
788
+ refreshCommand: string;
789
+ /** Rough cost of that refresh, for the caller to decide whether to wait for it. */
790
+ refreshEstimate: string;
791
+ /** Milliseconds spent assessing freshness. */
792
+ checkMs: number;
793
+ /** Diagnostic reason when `status` is `unknown`. */
794
+ reason?: string;
795
+ /** Present when the query attempted to refresh the index inline before answering. */
796
+ autoRefresh?: {
797
+ /** Whether the inline refresh published a new generation. */
798
+ refreshed: boolean;
799
+ /** Stale files at the time the refresh was attempted. */
800
+ staleFiles: number;
801
+ /** Milliseconds the refresh took (or ran before it was abandoned). */
802
+ durationMs: number;
803
+ /** Why the refresh ran, was skipped, or failed. */
804
+ reason: string;
805
+ };
685
806
  }
686
807
 
687
808
  // ---------------------------------------------------------------------------
package/src/index.ts CHANGED
@@ -291,6 +291,8 @@ export type {
291
291
  LoggingConfig,
292
292
  LogLevel,
293
293
  MemoryBridgeMode,
294
+ NexusAutoRefreshConfig,
295
+ NexusConfig,
294
296
  OutputConfig,
295
297
  OutputFormat,
296
298
  ProviderConfig,
@@ -307,6 +309,15 @@ export type {
307
309
  SystemBinding,
308
310
  } from './config.js';
309
311
  export type { AdapterContextMonitorProvider } from './context-monitor.js';
312
+ // === Credential Transfer Types (T12326 — portable credentials) ===
313
+ export type {
314
+ CredentialDescriptor,
315
+ CredentialReentry,
316
+ CredentialStoreKind,
317
+ ProjectCredentialMigrationResult,
318
+ SealCredentialsResult,
319
+ UnsealCredentialsResult,
320
+ } from './credential-transfer.js';
310
321
  // === Claude Code credential parsing (T9307 — pure helper, no core imports) ===
311
322
  export type {
312
323
  ClaudeCodeOAuthBlock,
@@ -494,6 +505,10 @@ export type {
494
505
  SagaAuditResult,
495
506
  SagaAuditViolation,
496
507
  SagaAuditViolationKind,
508
+ SupersededStoreConflict,
509
+ SupersededStoreReconcileOutcome,
510
+ SupersededStoreReconcileResult,
511
+ SupersededStoreTableCount,
497
512
  WorktreeAnomaly,
498
513
  WorktreeAnomalyKind,
499
514
  } from './doctor.js';
@@ -701,9 +716,13 @@ export type {
701
716
  GraphEdgeConfidenceLabel,
702
717
  GraphIndexAssessment,
703
718
  GraphIndexFileReport,
719
+ GraphIndexFreshness,
704
720
  GraphIndexReferenceReport,
721
+ GraphIndexRunSummary,
705
722
  GraphNode,
706
723
  GraphNodeKind,
724
+ GraphParseCacheEntry,
725
+ GraphParseCacheUpdate,
707
726
  GraphPublicationRows,
708
727
  GraphRelation,
709
728
  GraphRelationType,
@@ -1478,9 +1497,13 @@ export type {
1478
1497
  NexusPermissionLevel,
1479
1498
  NexusPermissionSetParams,
1480
1499
  NexusPermissionSetResult,
1500
+ NexusProjectCheckout,
1481
1501
  NexusProjectRecord,
1482
1502
  NexusProjectStats,
1483
1503
  NexusProjectsCleanParams,
1504
+ NexusProjectsCleanReason,
1505
+ NexusProjectsCleanReceipt,
1506
+ NexusProjectsCleanRemoval,
1484
1507
  NexusProjectsCleanResult,
1485
1508
  NexusProjectsListParams,
1486
1509
  NexusProjectsListResult,
@@ -1498,6 +1521,7 @@ export type {
1498
1521
  NexusRefreshBridgeResult,
1499
1522
  NexusRegisterParams,
1500
1523
  NexusRegisterResult,
1524
+ NexusRegistryClassification,
1501
1525
  NexusResolveParams,
1502
1526
  NexusResolveResult,
1503
1527
  NexusRouteMapParams,
@@ -1950,6 +1974,32 @@ export type {
1950
1974
  PlaybookRun,
1951
1975
  PlaybookRunStatus,
1952
1976
  } from './playbook.js';
1977
+ // === Portable Bundle (manifest v2) Types (T12318 — lossless machine migration) ===
1978
+ export type {
1979
+ PortableBundleManifest,
1980
+ PortableBundleScope,
1981
+ PortableDatabaseEntry,
1982
+ PortableDatabaseRole,
1983
+ PortableExclusion,
1984
+ PortableExportResult,
1985
+ PortableFileEntry,
1986
+ PortableGlobalSection,
1987
+ PortableImportResult,
1988
+ PortableImportSectionResult,
1989
+ PortableKeyCounts,
1990
+ PortableLegacyEvidence,
1991
+ PortablePathFinding,
1992
+ PortableProjectSection,
1993
+ PortableReentryItem,
1994
+ PortableRelocationReport,
1995
+ PortableSealedCredentials,
1996
+ PortableSectionBase,
1997
+ PortableSkippedProject,
1998
+ PortableSkipReason,
1999
+ PortableSymlinkEntry,
2000
+ PortableTableComparison,
2001
+ PortableUnmigratedLegacyReport,
2002
+ } from './portable-bundle.js';
1953
2003
  // === PostgresDataAccessor Contracts — cloud-sync scaffold (T9062) ===
1954
2004
  export type {
1955
2005
  CreatePostgresDataAccessorFn,