@omnicross/daemon 0.1.10 → 0.2.0

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 (7) hide show
  1. package/dist/cli.cjs +10473 -3551
  2. package/dist/cli.js +11271 -4194
  3. package/dist/index.cjs +7801 -1096
  4. package/dist/index.d.cts +1305 -20
  5. package/dist/index.d.ts +1305 -20
  6. package/dist/index.js +7684 -832
  7. package/package.json +63 -63
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import * as _omnicross_core from '@omnicross/core';
2
- import { OutboundApiServerConfig, Logger, ProviderConfigSource, TransformerService, Transformer, ResolvedTransformerChain, ApiServerSettingsStore, PricingStore, AutomaticPricingSource, OutboundKeyDb, OutboundKeyDbRow, OutboundKeyPolicy, PricingEngine as PricingEngine$1 } from '@omnicross/core';
1
+ import { Logger, OutboundApiServerConfig, ProviderConfigSource, TransformerService, Transformer, ResolvedTransformerChain, ApiServerSettingsStore, PricingStore, AutomaticPricingSource, OutboundPermission, OutboundKeyDb, OutboundKeyDbRow, OutboundKeyPolicy, PricingEngine as PricingEngine$1, OpenAIOperationRegistry } from '@omnicross/core';
3
2
  import { ApiKeyPoolService } from '@omnicross/core/completion/ApiKeyPoolService';
4
- import { AllowanceSchedulingConfig, AccountProbeConfig, OutboundKeyDb as OutboundKeyDb$1, VoucherDb, KeySpendReader, OutboundApiServer } from '@omnicross/core/outbound-api';
3
+ import { AllowanceSchedulingConfig, AccountProbeConfig, OutboundKeyDb as OutboundKeyDb$1, VoucherDb, KeySpendReader, OutboundApiServer, ImagesServerConfig } from '@omnicross/core/outbound-api';
5
4
  import { RouteLeaseManager, ProviderProxy } from '@omnicross/core/provider-proxy';
6
5
  import { UsageRecorder, PricingEngine } from '@omnicross/core/usage';
7
- import { SubscriptionCredentialStore, FetchLike, SubscriptionProviderRegistry, SubscriptionAccountService } from '@omnicross/subscriptions';
6
+ import { SubscriptionCredentialStore, FetchLike, CodexImageCapabilityEvidenceSource, CodexImageCapabilityEvidenceRequest, CodexImageCapabilityEvidence, CodexImageCapabilityObservation, ImageExecutionScheduler, ImageExecutionAccountKey, ImageExecutionSchedulerRequest, ImageExecutionSchedulerGrant, SubscriptionAccountService, SubscriptionProviderRegistry } from '@omnicross/subscriptions';
8
7
  import { AccountAllowanceSnapshot } from '@omnicross/contracts/account-allowance-types';
9
8
  import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-types';
10
9
  import * as _omnicross_contracts_subscription_types from '@omnicross/contracts/subscription-types';
@@ -15,12 +14,17 @@ import { SubscriptionIdentityStore } from '@omnicross/core/provider-proxy/identi
15
14
  import { LoggingConfig, HealthReport } from '@omnicross/contracts/health-logging-types';
16
15
  import { SubscriptionAccountHealth } from '@omnicross/core/pipeline/SubscriptionAccountHealth';
17
16
  import { fetchUpstream } from '@omnicross/core/pipeline/upstreamFetch';
17
+ import { UsageTotals, ModelUsageRow, ApiKeyUsageRow } from '@omnicross/contracts/usage-stats-types';
18
18
  import { ThinkLevel } from '@omnicross/contracts/completion-types';
19
19
  import { AuditRecord, AuditStats, AuditBodyResult, AuditConfig } from '@omnicross/contracts/audit-types';
20
20
  import { BillingDeliveryStatus, BillingConfig, BillingEvent } from '@omnicross/contracts/billing-types';
21
21
  import http from 'node:http';
22
+ import * as _omnicross_contracts_image_generation_types from '@omnicross/contracts/image-generation-types';
23
+ import { ImageCapabilityUnavailableReason, ImageCapabilities, ImageGenerationErrorCode, ImageReferenceMetadata, ImageReferenceId } from '@omnicross/contracts/image-generation-types';
24
+ import { ImageApiContributions, ResponsesImageGenerationContribution, ResponsesImageInspectionInput, ResponsesImageAdmission, ResponsesHostedToolSelection, ResponsesImageRequestScope, ImageTelemetrySink, ImageApiAuditRecord, ImageTelemetryRecord, ImageReferenceStore, ImageReferenceSaveInput, ImageReferenceResolution, ImageTemporaryResourceBudget, ImageTemporaryResourceBudgetLease, ImageApiLimits, ImageRequestResourceScope, ImageApiRuntimeResolver, RemoteImageAssetResolver, ImageProvider, ImageProviderRegistry, ImageOrchestrator } from '@omnicross/core/image-generation';
22
25
  import { LLMProvider, AgentDefaultModels, GlobalModelParameters } from '@omnicross/contracts/llm-config';
23
26
  import { PricingEntry, PricingEntryInput, PricingResolution, PricingSourceRefreshResult } from '@omnicross/contracts/pricing-types';
27
+ import { ResponsesImageStateStore, ResponsesImageStateCommitInput, ResponsesImageCallBinding, ResponsesImageCallId, ResponsesImageCallResolution, ResponsesImageResponseResolution } from '@omnicross/core/image-generation/responses';
24
28
  import { WebhookConfig, WebhookEvent } from '@omnicross/contracts/webhook-types';
25
29
 
26
30
  /**
@@ -86,6 +90,193 @@ declare class SecretBox {
86
90
  encryptMaybe(value: string): string;
87
91
  }
88
92
 
93
+ /**
94
+ * usageRollup — the immutable per-day aggregate that lets a range query skip a
95
+ * day's rows entirely.
96
+ *
97
+ * A rollup is computed ONCE, when a day is closed (strictly before today's local
98
+ * date), and is then never rewritten. It is deliberately kept FOREVER, outliving
99
+ * the raw shard it was built from, for one load-bearing reason:
100
+ * `getSpendByKey().totalUsd` is a LIFETIME number that seeds the outbound key
101
+ * spend policy (`core/outbound-api/keySpendTracker`). Pruning raw rows without
102
+ * keeping a per-key aggregate behind would silently reset every key's lifetime
103
+ * spend and quietly re-open budgets the operator had already spent.
104
+ *
105
+ * WHAT IS EXACT AND WHAT IS NOT
106
+ *
107
+ * Every count, token sum and cost in a rollup is exact — it is a plain sum of
108
+ * the same rows a raw scan would have visited. The ONE approximation is
109
+ * `medianCacheHitRate`: a median cannot be composed from per-day medians, so
110
+ * each day stores a 1000-bin histogram of its per-event hit rates instead. The
111
+ * median recovered from bins is off by at most half a bin — 0.0005, i.e. well
112
+ * under a twentieth of a percentage point. Storing the raw rates instead would
113
+ * make a "permanent" sidecar grow with traffic, which is the property this whole
114
+ * layout exists to avoid.
115
+ *
116
+ * STALENESS: a rollup records the byte size of the shard it was built from.
117
+ * Because a shard is append-only, a size change means rows were added after the
118
+ * rollup was computed (only reachable by inserting into a past day — the tests
119
+ * do it, production does not) and the rollup is recomputed. A shard that is GONE
120
+ * is the pruned case, where the rollup is the sole surviving authority.
121
+ *
122
+ * @module @omnicross/daemon/usage/usageRollup
123
+ */
124
+
125
+ /** Everything in `UsageTotals` except the one field that cannot be summed. */
126
+ type SummableTotals = Omit<UsageTotals, 'medianCacheHitRate'>;
127
+ /** Per-model group inside a rollup. `unpriced` is deliberately absent — it is
128
+ * derived at query time from the injected pricing lookup, so a pricing change
129
+ * never requires rewriting an immutable rollup. */
130
+ type RollupModelRow = Omit<ModelUsageRow, 'unpriced'>;
131
+ /** Per-key group inside a rollup. `label` is absent for the same reason: it is
132
+ * resolved by the admin handler against the live key registry. */
133
+ type RollupApiKeyRow = Omit<ApiKeyUsageRow, 'label'>;
134
+ /** One hour of a day. Only hours that saw traffic are stored. */
135
+ interface RollupHourRow {
136
+ /** LOCAL hour of day, 0–23. */
137
+ hour: number;
138
+ requests: number;
139
+ inputTokens: number;
140
+ outputTokens: number;
141
+ cacheReadTokens: number;
142
+ cacheCreationTokens: number;
143
+ costUsd: number;
144
+ }
145
+ /** The on-disk `usage-YYYY-MM-DD.rollup.json` document. */
146
+ interface UsageDayRollup {
147
+ version: 1;
148
+ /** `YYYY-MM-DD`, LOCAL. Must match the file name. */
149
+ date: string;
150
+ /** Byte size of the shard this was built from; the staleness guard. */
151
+ sourceBytes: number;
152
+ totals: SummableTotals;
153
+ /** Sparse `binIndex -> count` over per-event cache-hit rates. */
154
+ hitRateBins: Record<string, number>;
155
+ byModel: RollupModelRow[];
156
+ byApiKey: RollupApiKeyRow[];
157
+ byHour: RollupHourRow[];
158
+ /** Distinct non-null session ids seen that day (drilldown index). */
159
+ sessionIds: string[];
160
+ /** True when `sessionIds` hit {@link SESSION_INDEX_LIMIT} and is incomplete. */
161
+ sessionIdsTruncated: boolean;
162
+ }
163
+
164
+ /**
165
+ * usageRollupStore — read-or-build the per-day rollup sidecars.
166
+ *
167
+ * A rollup is built LAZILY, the first time a query needs a closed day, and then
168
+ * reused forever. Building it is the only time that day's rows are ever parsed
169
+ * again; every subsequent query for any range containing that day reads a few KB
170
+ * of JSON instead of megabytes of JSONL.
171
+ *
172
+ * STALENESS is checked against the shard's byte size, not its mtime. Shards are
173
+ * append-only, so a size that no longer matches what the rollup was built from
174
+ * means rows were added afterwards — only reachable by inserting an event with a
175
+ * past `ts`, which the store API permits and the tests exercise. A shard that is
176
+ * absent entirely is the PRUNED case: the rollup is then the sole surviving
177
+ * record of that day and is trusted unconditionally.
178
+ *
179
+ * Writes are atomic (temp file + rename) so a crash mid-write cannot leave a
180
+ * half-JSON sidecar that later reads as a corrupt day.
181
+ *
182
+ * @module @omnicross/daemon/usage/usageRollupStore
183
+ */
184
+
185
+ declare class UsageRollupStore {
186
+ private readonly usageDir;
187
+ private readonly maxCached;
188
+ private readonly cache;
189
+ /** In-flight builds, so a burst of concurrent queries builds a day ONCE. */
190
+ private readonly inFlight;
191
+ constructor(usageDir: string, maxCached?: number);
192
+ /**
193
+ * The rollup for a CLOSED day, building it from the shard when absent or
194
+ * stale. `null` means the day has neither a usable rollup nor a shard — i.e.
195
+ * nothing was ever recorded for it.
196
+ *
197
+ * Callers must not ask for TODAY: today's rows are still arriving, so a rollup
198
+ * of them would be wrong the moment it was written.
199
+ */
200
+ get(dayKey: string, hasShard?: boolean): Promise<UsageDayRollup | null>;
201
+ /** Forget a day — used when an insert lands in an already-rolled-up day. */
202
+ invalidate(dayKey?: string): void;
203
+ /**
204
+ * Build (or rebuild) a day's rollup from its shard and persist it. Exposed for
205
+ * the prune sweeper, which must guarantee a rollup exists BEFORE it deletes
206
+ * the rows that rollup is derived from.
207
+ */
208
+ ensure(dayKey: string): Promise<UsageDayRollup | null>;
209
+ private resolve;
210
+ private readSidecar;
211
+ private writeSidecar;
212
+ private remember;
213
+ }
214
+
215
+ /**
216
+ * UsagePruneSweeper — retention for the sharded usage store.
217
+ *
218
+ * Prunes RAW day shards past `retentionDays` and NEVER touches a rollup. That
219
+ * asymmetry is the whole design: aggregates (totals, per-model, per-key, hourly
220
+ * trend) stay complete for the lifetime of the install, while the per-row detail
221
+ * that costs 5 MB a day is bounded. In particular `getSpendByKey().totalUsd` —
222
+ * the LIFETIME figure seeding the outbound key spend policy — keeps summing
223
+ * correctly across pruned days, which a naive "delete old rows" would silently
224
+ * zero, reopening budgets the operator had already spent.
225
+ *
226
+ * ORDERING IS LOAD-BEARING: a shard is only ever deleted AFTER its rollup exists
227
+ * on disk. If the rollup cannot be built, the shard is kept and the day is
228
+ * retried on the next tick. Deleting rows whose aggregate was never computed
229
+ * would destroy that day permanently.
230
+ *
231
+ * Shaped like `audit/AuditPruneSweeper`: `start()` arms an `unref()`ed interval
232
+ * plus one immediate pass, `dispose()` clears it, a re-entrancy guard prevents
233
+ * overlap, and nothing throws — a failed sweep is logged and retried, never
234
+ * fatal to the daemon.
235
+ *
236
+ * @module @omnicross/daemon/usage/UsagePruneSweeper
237
+ */
238
+
239
+ /** The `usage` config segment. */
240
+ interface UsageRetentionConfig {
241
+ /** Days of RAW rows to keep. `0` or absent ⇒ keep everything. */
242
+ retentionDays?: number;
243
+ }
244
+ declare class UsagePruneSweeper {
245
+ private readonly usageDir;
246
+ private readonly rollups;
247
+ private readonly logger;
248
+ private config;
249
+ private readonly intervalMs;
250
+ /** Injectable clock (ms) for deterministic tests. */
251
+ private readonly now;
252
+ private timer;
253
+ private sweeping;
254
+ constructor(usageDir: string, rollups: UsageRollupStore, logger: Logger, config?: UsageRetentionConfig, intervalMs?: number,
255
+ /** Injectable clock (ms) for deterministic tests. */
256
+ now?: () => number);
257
+ /** Whether raw rows are pruned at all. */
258
+ get enabled(): boolean;
259
+ /** Re-apply config to the live instance (boot + admin PUT hot-reload). */
260
+ configure(config: UsageRetentionConfig): void;
261
+ /**
262
+ * Arm the interval AND run one pass immediately (boot cleanup). No-op when
263
+ * retention is off (keep-everything is the zero-regression default).
264
+ * Idempotent.
265
+ */
266
+ start(): void;
267
+ /** Clear the interval (daemon shutdown / test teardown). Idempotent. */
268
+ dispose(): void;
269
+ /**
270
+ * One prune pass: for every day strictly OLDER than the retention cutoff that
271
+ * still has a shard, make sure its rollup exists and only then delete the
272
+ * shard. Exposed for tests; never throws. Returns the number of shards
273
+ * removed.
274
+ */
275
+ sweep(): Promise<number>;
276
+ /** The LOCAL-midnight epoch ms of the current day. */
277
+ private todayMidnight;
278
+ }
279
+
89
280
  /**
90
281
  * config.ts — the daemon's `config.json` schema + load/save (design D9).
91
282
  *
@@ -411,6 +602,12 @@ interface DaemonConfig {
411
602
  * so it is never walked by `decryptConfigSecrets`/`encryptConfigSecrets`.
412
603
  */
413
604
  logging?: LoggingConfig;
605
+ /**
606
+ * Optional usage-retention config. Absent ⇒ the default 90-day window on RAW
607
+ * usage rows. Per-day ROLLUPS are never pruned regardless — they carry the
608
+ * lifetime per-key spend the outbound key policy seeds from.
609
+ */
610
+ usage?: UsageRetentionConfig;
414
611
  }
415
612
  /** Apply defaults to a (possibly absent) admin block: enabled, port 8766,
416
613
  * loopback, no token. NOTE: an EXPLICIT `port: 0` is honored as "bind an
@@ -1545,6 +1742,254 @@ type AuditCompactor = () => {
1545
1742
  /** The read surface the AdminServer consumes (bootstrap binds it to the ledger dir). */
1546
1743
  type BillingStatusReader = () => BillingDeliveryStatus;
1547
1744
 
1745
+ type PreparedImageRuntimeGeneration = {
1746
+ readonly id: string;
1747
+ readonly enabled: true;
1748
+ readonly imageApi: ImageApiContributions;
1749
+ readonly hosted: ResponsesImageGenerationContribution;
1750
+ readonly hostedRuntime: HostedImageRuntimePolicy;
1751
+ readonly inspectCapability?: (apiKeyId: string) => Promise<Omit<ImageRuntimeCapabilityInspection, 'generationId'>>;
1752
+ readonly readRuntimeStatus?: () => ImageRuntimeResourceStatus;
1753
+ dispose(): void | Promise<void>;
1754
+ } | {
1755
+ readonly id: string;
1756
+ readonly enabled: false;
1757
+ dispose(): void | Promise<void>;
1758
+ };
1759
+ interface HostedImageRuntimeGenerationLease {
1760
+ readonly generationId: string;
1761
+ /** Compatibility/debug view; callers should prefer the deep methods below. */
1762
+ readonly contribution: ResponsesImageGenerationContribution;
1763
+ inspectRequest(input: ResponsesImageInspectionInput): ResponsesImageAdmission;
1764
+ validateSelection(admission: ResponsesImageAdmission, selection: ResponsesHostedToolSelection): void;
1765
+ openRequest(input: HostedImageOpenRequestInput): Promise<ResponsesImageRequestScope>;
1766
+ release(): Promise<void>;
1767
+ }
1768
+ interface HostedImageRuntimePolicy {
1769
+ readonly providerId: string;
1770
+ readonly imageModel: string;
1771
+ readonly referenceTtlMs: number;
1772
+ readonly maxOutputBytes: number;
1773
+ readonly maxTotalOutputBytes: number;
1774
+ readonly preferredAccountId?: string;
1775
+ readonly preferredAccountGroup?: string;
1776
+ readonly boundAccountFallbackPolicy?: 'strict' | 'pool';
1777
+ }
1778
+ interface HostedImageOpenRequestInput {
1779
+ readonly admission: ResponsesImageAdmission;
1780
+ readonly tenantId: string;
1781
+ readonly requestId: string;
1782
+ readonly sessionKey: string;
1783
+ readonly signal: AbortSignal;
1784
+ readonly authorizedPreviousResponseId?: string;
1785
+ /** Trusted affinity fact forwarded unchanged into the contribution scope. */
1786
+ readonly authorizedPreviousResponseKnownEmpty?: boolean;
1787
+ readonly mainProviderId: string;
1788
+ readonly selectedMainAccountId?: string;
1789
+ }
1790
+ /** Dormant integration seam for a later Native Responses owner. */
1791
+ interface HostedImageContributionFactory {
1792
+ acquire(): Promise<HostedImageRuntimeGenerationLease>;
1793
+ }
1794
+ /** Bind a stable factory to one app-session runtime manager without acquiring. */
1795
+ declare function createHostedImageContributionFactory(manager: ImageRuntimeManager): HostedImageContributionFactory;
1796
+ interface ImageRuntimeGenerationStatus {
1797
+ readonly generationId: string;
1798
+ readonly enabled: boolean;
1799
+ readonly httpLeases: number;
1800
+ readonly hostedLeases: number;
1801
+ }
1802
+ interface ImageRuntimeManagerStatus {
1803
+ readonly disposed: boolean;
1804
+ readonly current: ImageRuntimeGenerationStatus;
1805
+ readonly draining: readonly ImageRuntimeGenerationStatus[];
1806
+ }
1807
+ type ImageRuntimeSafeUnavailableReason = ImageCapabilityUnavailableReason | 'disabled' | 'runtime_unavailable';
1808
+ interface ImageRuntimeCapabilityInspection {
1809
+ readonly generationId: string;
1810
+ readonly enabled: boolean;
1811
+ readonly available: boolean;
1812
+ readonly providerId?: 'codex-subscription';
1813
+ readonly model?: string;
1814
+ readonly reason?: ImageRuntimeSafeUnavailableReason;
1815
+ readonly capabilities?: ImageCapabilities;
1816
+ }
1817
+ interface ImageRuntimeResourceStatus {
1818
+ readonly queue: Readonly<{
1819
+ activeJobs: number;
1820
+ waitingJobs: number;
1821
+ activeAccounts: number;
1822
+ waitingAccounts: number;
1823
+ waitingTenants: number;
1824
+ maxConcurrentJobsPerAccount: number;
1825
+ maxQueuedJobs: number;
1826
+ accepting: boolean;
1827
+ shuttingDown: boolean;
1828
+ }>;
1829
+ readonly temporary: Readonly<{
1830
+ activeScopes: number;
1831
+ totalBytes: number;
1832
+ tenantCount: number;
1833
+ maxActiveScopes: number;
1834
+ maxTotalBytes: number;
1835
+ maxTenantBytes: number;
1836
+ }>;
1837
+ readonly storage: Readonly<{
1838
+ mounts: number;
1839
+ retiredMounts: number;
1840
+ referenceEntries: number;
1841
+ referenceBytes: number;
1842
+ referenceTombstones: number;
1843
+ stateCalls: number;
1844
+ stateResponses: number;
1845
+ stateTombstones: number;
1846
+ pendingReferenceDeletes: number;
1847
+ maxReferenceEntries: number;
1848
+ maxReferenceBytes: number;
1849
+ maxTenantReferenceBytes: number;
1850
+ maxStateCalls: number;
1851
+ maxStateResponses: number;
1852
+ }>;
1853
+ }
1854
+ interface PreparedImageRuntimeChange {
1855
+ readonly generationId: string;
1856
+ publish(): void;
1857
+ rollback(): void;
1858
+ dispose(): Promise<void>;
1859
+ }
1860
+ /** App-session owner for stable forwarders and generation-pinned work. */
1861
+ declare class ImageRuntimeManager {
1862
+ #private;
1863
+ readonly contributions: ImageApiContributions;
1864
+ constructor(initial?: PreparedImageRuntimeGeneration);
1865
+ prepare(generation: PreparedImageRuntimeGeneration): PreparedImageRuntimeChange;
1866
+ acquireHosted(): Promise<HostedImageRuntimeGenerationLease>;
1867
+ inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
1868
+ listAvailableModels(apiKeyId: string): Promise<readonly string[]>;
1869
+ resourceStatus(): ImageRuntimeResourceStatus | undefined;
1870
+ status(): ImageRuntimeManagerStatus;
1871
+ dispose(): Promise<void>;
1872
+ }
1873
+
1874
+ type SafeProvider = 'codex-subscription' | 'unknown' | 'other';
1875
+ type SafeModel = 'gpt-image-2' | 'unknown' | 'other';
1876
+ type SafeErrorCode = ImageGenerationErrorCode | 'none' | 'other';
1877
+ type SafeCountOption = 'unknown' | '0' | '1' | '2-4' | '5+';
1878
+ type SafeBooleanOption = boolean | 'unknown';
1879
+ type SafeQuality = 'auto' | 'low' | 'medium' | 'high' | 'unknown' | 'other';
1880
+ type SafeBackground = 'auto' | 'opaque' | 'transparent' | 'unknown' | 'other';
1881
+ type SafeOutputFormat = 'png' | 'jpeg' | 'webp' | 'unknown' | 'other';
1882
+ declare const IMAGE_CONFIGURATION_AUDIT_FIELDS: readonly ["enablement", "provider", "model", "account", "queue", "temporary", "limits", "retention", "storage", "remote", "evidence"];
1883
+ type ImageConfigurationAuditField = typeof IMAGE_CONFIGURATION_AUDIT_FIELDS[number];
1884
+ /** Values are deliberately limited to safe categories and internal generation ids. */
1885
+ interface ImageConfigurationAuditRecord {
1886
+ readonly outcome: 'applied';
1887
+ readonly fields: readonly ImageConfigurationAuditField[];
1888
+ readonly previousGenerationId?: string;
1889
+ readonly generationId?: string;
1890
+ }
1891
+ interface ImageHistogramSnapshot {
1892
+ readonly count: number;
1893
+ readonly sum: number;
1894
+ /** Non-cumulative fixed buckets; `upperBound:null` is the overflow bucket. */
1895
+ readonly buckets: readonly {
1896
+ readonly upperBound: number | null;
1897
+ readonly count: number;
1898
+ }[];
1899
+ }
1900
+ interface ImageApiMetricDimensions {
1901
+ readonly endpoint: 'images.generate' | 'images.edit';
1902
+ readonly provider: SafeProvider;
1903
+ readonly model: SafeModel;
1904
+ readonly action: 'generate' | 'edit';
1905
+ readonly quality: SafeQuality;
1906
+ readonly background: SafeBackground;
1907
+ readonly outputFormat: SafeOutputFormat;
1908
+ readonly streaming: SafeBooleanOption;
1909
+ readonly requestedOutputs: SafeCountOption;
1910
+ readonly partialImages: SafeCountOption;
1911
+ readonly terminal: 'completed' | 'failed' | 'cancelled';
1912
+ readonly errorCode: SafeErrorCode;
1913
+ }
1914
+ interface ImageExecutionMetricDimensions {
1915
+ readonly provider: SafeProvider;
1916
+ readonly model: SafeModel;
1917
+ readonly action: 'generate' | 'edit' | 'other';
1918
+ readonly quality: SafeQuality;
1919
+ readonly background: SafeBackground;
1920
+ readonly outputFormat: SafeOutputFormat;
1921
+ readonly streaming: SafeBooleanOption;
1922
+ readonly requestedOutputs: SafeCountOption;
1923
+ readonly terminal: 'completed' | 'failed' | 'cancelled' | 'other';
1924
+ readonly errorCode: SafeErrorCode;
1925
+ }
1926
+ interface ImageApiMetricSnapshot {
1927
+ readonly dimensions: ImageApiMetricDimensions;
1928
+ readonly requests: number;
1929
+ readonly inputCount?: ImageHistogramSnapshot;
1930
+ readonly inputBytes?: ImageHistogramSnapshot;
1931
+ readonly referenceOutcomes: Readonly<{
1932
+ hits: number;
1933
+ notFound: number;
1934
+ expired: number;
1935
+ failed: number;
1936
+ }>;
1937
+ readonly cleanupOutcomes: Readonly<{
1938
+ completed: number;
1939
+ failed: number;
1940
+ }>;
1941
+ }
1942
+ interface ImageExecutionMetricSnapshot {
1943
+ readonly dimensions: ImageExecutionMetricDimensions;
1944
+ readonly executions: number;
1945
+ readonly finalLatencyMs: ImageHistogramSnapshot;
1946
+ readonly queueWaitMs?: ImageHistogramSnapshot;
1947
+ readonly generationDurationMs?: ImageHistogramSnapshot;
1948
+ readonly firstPartialLatencyMs?: ImageHistogramSnapshot;
1949
+ readonly inputCount: ImageHistogramSnapshot;
1950
+ readonly inputBytes: ImageHistogramSnapshot;
1951
+ readonly outputCount: ImageHistogramSnapshot;
1952
+ readonly outputBytes: ImageHistogramSnapshot;
1953
+ readonly retryCount?: ImageHistogramSnapshot;
1954
+ readonly authRefreshCount?: ImageHistogramSnapshot;
1955
+ readonly referenceSaveCount?: ImageHistogramSnapshot;
1956
+ readonly retentionRollbackFailures?: ImageHistogramSnapshot;
1957
+ }
1958
+ interface ImageObservabilitySnapshot {
1959
+ readonly apiRequests: readonly ImageApiMetricSnapshot[];
1960
+ readonly executions: readonly ImageExecutionMetricSnapshot[];
1961
+ readonly configurationChanges: readonly ImageConfigurationAuditRecord[];
1962
+ readonly overflow: Readonly<{
1963
+ apiRecords: number;
1964
+ telemetryRecords: number;
1965
+ }>;
1966
+ }
1967
+ interface ImageObservabilityOptions {
1968
+ /** Hard cap applied independently to API and execution dimension maps. */
1969
+ readonly maxDimensionSets?: number;
1970
+ }
1971
+ /** Process-local, metadata-only aggregation for HTTP and hosted Images work. */
1972
+ declare class ImageObservability {
1973
+ #private;
1974
+ readonly telemetrySink: ImageTelemetrySink;
1975
+ readonly audit: (record: ImageApiAuditRecord) => void;
1976
+ constructor(options?: ImageObservabilityOptions);
1977
+ recordApiAudit(record: ImageApiAuditRecord): void;
1978
+ recordTelemetry(record: ImageTelemetryRecord): void;
1979
+ recordConfigurationAudit(record: ImageConfigurationAuditRecord): void;
1980
+ snapshot(): ImageObservabilitySnapshot;
1981
+ reset(): void;
1982
+ }
1983
+
1984
+ interface PreparedServerConfigChange {
1985
+ /** Publish the prepared snapshot. Implementations should make this an infallible swap. */
1986
+ publish(): void | Promise<void>;
1987
+ /** Restore the exact runtime snapshot that preceded publish. */
1988
+ rollback(): void | Promise<void>;
1989
+ /** Release an unpublished or rolled-back replacement. */
1990
+ dispose(): void | Promise<void>;
1991
+ }
1992
+
1548
1993
  /**
1549
1994
  * autoDisableStore.ts — the daemon's PROCESS-IN-MEMORY auto-disable store.
1550
1995
  *
@@ -1684,9 +2129,16 @@ declare class ConfigFileProviderConfigSource implements ProviderConfigSource {
1684
2129
  * @module @omnicross/daemon/ports/JsonApiServerSettingsStore
1685
2130
  */
1686
2131
 
2132
+ interface JsonSettingsDocumentSnapshot {
2133
+ readonly existed: boolean;
2134
+ /** Raw persisted bytes; may contain encrypted secrets and must never enter a DTO or log. */
2135
+ readonly bytes?: Uint8Array;
2136
+ }
2137
+ type AtomicDocumentReplace = (targetPath: string, contents: Uint8Array) => void;
1687
2138
  declare class JsonApiServerSettingsStore implements ApiServerSettingsStore {
1688
2139
  private readonly configPath;
1689
2140
  private readonly box;
2141
+ private readonly atomicReplace;
1690
2142
  /**
1691
2143
  * @param configPath the daemon config.json whose `server` field is backed.
1692
2144
  * @param box OPTIONAL at-rest `SecretBox` (upstream-proxy). When set, the
@@ -1695,9 +2147,13 @@ declare class JsonApiServerSettingsStore implements ApiServerSettingsStore {
1695
2147
  * secret-aware — every OTHER server field is non-secret). Null
1696
2148
  * ⇒ passthrough (legacy/pure tests unchanged).
1697
2149
  */
1698
- constructor(configPath: string, box?: SecretBox | null);
2150
+ constructor(configPath: string, box?: SecretBox | null, atomicReplace?: AtomicDocumentReplace);
1699
2151
  get<T = unknown>(key: string): Promise<T | undefined>;
1700
2152
  set<T = unknown>(key: string, value: T): Promise<void>;
2153
+ /** Capture the exact prior document for an admin transaction rollback. */
2154
+ captureDocumentSnapshot(): JsonSettingsDocumentSnapshot;
2155
+ /** Restore exact prior bytes (including unrelated fields and encrypted secrets). */
2156
+ restoreDocumentSnapshot(snapshot: JsonSettingsDocumentSnapshot): void;
1701
2157
  /** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
1702
2158
  private encryptSecrets;
1703
2159
  /** Decrypt the proxy passwords + webhook + billing secrets on read (no-op without a box). */
@@ -1779,7 +2235,31 @@ declare class JsonPricingStore implements PricingStore {
1779
2235
  private replaceFile;
1780
2236
  }
1781
2237
 
2238
+ interface CodexAuthHelperConfig {
2239
+ command: string;
2240
+ args: string[];
2241
+ }
2242
+
1782
2243
  type IntegrationClientId = 'codex' | 'claude';
2244
+ type IntegrationKeyOwnership = 'managed' | 'selected';
2245
+ /** Secret-free pointer to an access key. The plaintext remains in the encrypted key store. */
2246
+ interface IntegrationKeyBinding {
2247
+ keyId: string;
2248
+ ownership: IntegrationKeyOwnership;
2249
+ }
2250
+ /** Redacted access-key state exposed by the integrations admin API. */
2251
+ interface IntegrationKeyBindingStatus {
2252
+ id: string;
2253
+ name: string;
2254
+ keyPrefix: string;
2255
+ ownership: IntegrationKeyOwnership;
2256
+ revealable: boolean;
2257
+ enabled: boolean;
2258
+ revoked: boolean;
2259
+ allowedEndpoints: OutboundPermission[];
2260
+ requiredEndpoints: OutboundPermission[];
2261
+ loopbackOnly: boolean;
2262
+ }
1783
2263
  type IntegrationStatusKind = 'not-installed' | 'enabled' | 'configuration-drift' | 'configuration-missing' | 'key-missing';
1784
2264
  interface IntegrationClientStatus {
1785
2265
  client: IntegrationClientId;
@@ -1788,6 +2268,8 @@ interface IntegrationClientStatus {
1788
2268
  installedAt?: number;
1789
2269
  gatewayBaseUrl?: string;
1790
2270
  message?: string;
2271
+ /** Selected key metadata only; never contains plaintext or the encrypted envelope. */
2272
+ key?: IntegrationKeyBindingStatus;
1791
2273
  }
1792
2274
  interface IntegrationChangePlan {
1793
2275
  client: IntegrationClientId;
@@ -1827,7 +2309,9 @@ interface IntegrationGatewayKeyRecord {
1827
2309
  }
1828
2310
  interface IntegrationState {
1829
2311
  version: 1;
2312
+ /** Legacy shared-key layout. New installs use `keyBindings`; retained for safe migration. */
1830
2313
  gatewayKey?: IntegrationGatewayKeyRecord;
2314
+ keyBindings?: Partial<Record<IntegrationClientId, IntegrationKeyBinding>>;
1831
2315
  clients: Partial<Record<IntegrationClientId, IntegrationInstallRecord>>;
1832
2316
  }
1833
2317
 
@@ -1845,27 +2329,38 @@ interface IntegrationManagerOptions {
1845
2329
  gatewayBaseUrl: string;
1846
2330
  keyDb: OutboundKeyDb;
1847
2331
  stateStore: IntegrationStateStore;
2332
+ codexAuthHelper?: CodexAuthHelperConfig;
1848
2333
  homeDir?: string;
1849
2334
  }
1850
- /** Coordinates a least-privilege gateway key with reversible native CLI config edits. */
2335
+ /** Coordinates per-client least-privilege keys with reversible native CLI config edits. */
1851
2336
  declare class IntegrationManager {
1852
2337
  private readonly options;
1853
2338
  private readonly homeDir;
2339
+ private readonly codexAuthHelper;
1854
2340
  constructor(options: IntegrationManagerOptions);
1855
2341
  listStatus(): Promise<IntegrationClientStatus[]>;
1856
2342
  plan(client: IntegrationClientId, configPath?: string): Promise<IntegrationChangePlan>;
1857
2343
  install(client: IntegrationClientId, configPath?: string): Promise<IntegrationClientStatus>;
1858
2344
  repair(client: IntegrationClientId): Promise<IntegrationClientStatus>;
1859
2345
  remove(client: IntegrationClientId): Promise<IntegrationClientStatus>;
2346
+ /** Bind a user-confirmed access key and grant only this client's required endpoints. */
2347
+ bindIntegrationKey(client: IntegrationClientId, keyId: string): Promise<IntegrationClientStatus>;
2348
+ /** Rotate every Omnicross-managed client binding; user-selected keys remain untouched. */
1860
2349
  rotateGatewayKey(): Promise<{
1861
- keyId: string;
2350
+ keyIds: Partial<Record<IntegrationClientId, string>>;
1862
2351
  }>;
1863
- getGatewayToken(): Promise<string>;
1864
- private ensureGatewayKey;
1865
- private isKeyUsable;
2352
+ /** Resolve the plaintext only for the command-auth helper; callers must not log it. */
2353
+ getIntegrationToken(client: IntegrationClientId): Promise<string>;
2354
+ /** Compatibility alias for callers predating per-client bindings. */
2355
+ getGatewayToken(client?: IntegrationClientId): Promise<string>;
2356
+ private ensureClientKey;
2357
+ private createManagedClientKey;
2358
+ private installedSecret;
2359
+ private rebindInstalledClient;
1866
2360
  private statusFor;
2361
+ private boundKeyDetails;
2362
+ private retireManagedKeys;
1867
2363
  private defaultConfigPath;
1868
- private codexAuthPathForConfig;
1869
2364
  private renderInstalled;
1870
2365
  }
1871
2366
 
@@ -1985,6 +2480,11 @@ interface PoolKeyHealth {
1985
2480
  interface PoolHealthReader {
1986
2481
  getKeyHealth(providerId: string): Promise<Record<string, PoolKeyHealth>>;
1987
2482
  }
2483
+ interface AdminImagesStatusReader {
2484
+ inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
2485
+ status(): ImageRuntimeManagerStatus;
2486
+ resourceStatus(): ImageRuntimeResourceStatus | undefined;
2487
+ }
1988
2488
  /** The live daemon handles the management API operates over. */
1989
2489
  interface AdminApiDeps {
1990
2490
  /** Path to the daemon's `config.json` (provider catalog + `server` field). */
@@ -2009,6 +2509,16 @@ interface AdminApiDeps {
2009
2509
  readonly settingsStore: JsonApiServerSettingsStore;
2010
2510
  /** The running outbound server (status + live applyConfig). */
2011
2511
  readonly outboundApiServer: OutboundApiServer;
2512
+ /** True only when production composed the hardened per-hop remote resolver. */
2513
+ readonly imageRemoteResolverAvailable?: boolean;
2514
+ /** Optional production Images runtime generation participant. */
2515
+ readonly imageRuntimeConfig?: {
2516
+ prepareConfig(config: ImagesServerConfig): Promise<PreparedServerConfigChange>;
2517
+ };
2518
+ /** Narrow metadata-only reader for authenticated Images capability/status. */
2519
+ readonly imageRuntimeStatus?: AdminImagesStatusReader;
2520
+ /** Metadata-only successful Images configuration audit sink. */
2521
+ readonly imageConfigAudit?: (record: ImageConfigurationAuditRecord) => void;
2012
2522
  /** Process-local machine-managed routing leases (optional for light embedders). */
2013
2523
  readonly routeLeaseManager?: RouteLeaseManager;
2014
2524
  /** Subscription accounts (token-free `listAll`). */
@@ -2233,9 +2743,673 @@ declare class AdminServer {
2233
2743
  getStatus(): AdminServerStatus;
2234
2744
  }
2235
2745
 
2746
+ type DaemonImagePathArea = 'temporary' | 'artifacts' | 'state' | 'evidence' | 'mountManifest';
2747
+ interface DaemonImagePaths {
2748
+ readonly applicationDataRoot: string;
2749
+ readonly imagesRoot: string;
2750
+ readonly temporaryRoot: string;
2751
+ readonly durableRoot: string;
2752
+ readonly artifactsRoot: string;
2753
+ readonly stateRoot: string;
2754
+ readonly evidenceRoot: string;
2755
+ readonly mountManifestRoot: string;
2756
+ readonly mountManifestPath: string;
2757
+ }
2758
+ interface ImageRootValidationOptions {
2759
+ readonly label?: string;
2760
+ readonly processDirectory?: string;
2761
+ readonly userHome?: string;
2762
+ readonly temporaryDirectory?: string;
2763
+ }
2764
+ interface CreateDaemonImagePathResolverOptions extends ImageRootValidationOptions {
2765
+ readonly configPath: string;
2766
+ readonly storageRoot?: string;
2767
+ }
2768
+ interface VerifiedDaemonImagePath {
2769
+ readonly area: DaemonImagePathArea;
2770
+ readonly absolutePath: string;
2771
+ readonly kind: 'opaque-file' | 'opaque-directory' | 'mount-manifest';
2772
+ }
2773
+ /**
2774
+ * Owns all daemon Images filesystem names. Callers receive opaque capabilities,
2775
+ * never a filename-accepting delete primitive; destructive methods revalidate
2776
+ * the root identity, descendant relationship, basename, and symlink state.
2777
+ */
2778
+ declare class DaemonImagePathResolver {
2779
+ #private;
2780
+ readonly paths: DaemonImagePaths;
2781
+ constructor(options: CreateDaemonImagePathResolverOptions);
2782
+ createOpaqueFile(area: Exclude<DaemonImagePathArea, 'temporary' | 'mountManifest'>, format?: 'bin' | 'json' | 'tmp'): VerifiedDaemonImagePath;
2783
+ createOpaqueDirectory(area?: Exclude<DaemonImagePathArea, 'mountManifest'>): VerifiedDaemonImagePath;
2784
+ mountManifest(): VerifiedDaemonImagePath;
2785
+ /** Revalidate and return one internal root for store-local bounded I/O. */
2786
+ verifiedRoot(area: DaemonImagePathArea): string;
2787
+ /** Revalidate immediately before unlinking a resolver-issued file capability. */
2788
+ removeFile(target: VerifiedDaemonImagePath): void;
2789
+ /** Only empty opaque directories may be removed until the owned-marker layer is composed. */
2790
+ removeEmptyDirectory(target: VerifiedDaemonImagePath): void;
2791
+ private issue;
2792
+ private verifyDestructiveTarget;
2793
+ }
2794
+
2795
+ interface FileCodexImageCapabilityEvidenceManifestOwnerOptions {
2796
+ readonly paths: DaemonImagePathResolver;
2797
+ readonly maxEntries?: number;
2798
+ readonly now?: () => number;
2799
+ readonly random?: (bytes: number) => Buffer;
2800
+ readonly hmacSalt?: Uint8Array;
2801
+ readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
2802
+ }
2803
+ type FileCodexImageCapabilityEvidenceSourceOptions = Readonly<(FileCodexImageCapabilityEvidenceManifestOwnerOptions & {
2804
+ readonly ttlMs: number;
2805
+ }) | {
2806
+ readonly owner: FileCodexImageCapabilityEvidenceManifestOwner;
2807
+ readonly ttlMs: number;
2808
+ }>;
2809
+ interface FileCodexImageCapabilityEvidenceStatus {
2810
+ readonly entries: number;
2811
+ readonly freshEntries: number;
2812
+ readonly staleEntries: number;
2813
+ readonly bytes: number;
2814
+ }
2815
+ /** Revision-aware manifest owner shared by runtime generations, doctor, and cleanup. */
2816
+ declare class FileCodexImageCapabilityEvidenceManifestOwner {
2817
+ #private;
2818
+ constructor(options: FileCodexImageCapabilityEvidenceManifestOwnerOptions);
2819
+ createSource(ttlMs: number): FileCodexImageCapabilityEvidenceSource;
2820
+ resolveWithTtl(request: CodexImageCapabilityEvidenceRequest, ttlMs: number): Promise<CodexImageCapabilityEvidence>;
2821
+ recordSuccessfulVerificationWithTtl(observation: CodexImageCapabilityObservation, ttlMs: number): Promise<void>;
2822
+ cleanup(now: number, limit: number): Promise<{
2823
+ readonly entriesRemoved: number;
2824
+ readonly bytesRemoved: number;
2825
+ }>;
2826
+ statusWithTtl(ttlMs: number): FileCodexImageCapabilityEvidenceStatus;
2827
+ }
2828
+ /** Immutable TTL view over a revision-aware file-backed evidence manifest owner. */
2829
+ declare class FileCodexImageCapabilityEvidenceSource implements CodexImageCapabilityEvidenceSource {
2830
+ #private;
2831
+ constructor(options: FileCodexImageCapabilityEvidenceSourceOptions);
2832
+ createView(ttlMs: number): FileCodexImageCapabilityEvidenceSource;
2833
+ resolve(request: CodexImageCapabilityEvidenceRequest): Promise<CodexImageCapabilityEvidence>;
2834
+ recordSuccessfulVerification(observation: CodexImageCapabilityObservation): Promise<void>;
2835
+ cleanup(now: number, limit: number): Promise<{
2836
+ readonly entriesRemoved: number;
2837
+ readonly bytesRemoved: number;
2838
+ }>;
2839
+ status(): FileCodexImageCapabilityEvidenceStatus;
2840
+ ttlMs(): number;
2841
+ /** Lifecycle-symmetric no-op; physical safety no longer depends on local leases. */
2842
+ dispose(): void;
2843
+ }
2844
+
2845
+ interface FileImageReferenceStoreLimits {
2846
+ readonly ttlMs: number;
2847
+ readonly maxArtifactBytes: number;
2848
+ readonly maxTotalBytes: number;
2849
+ readonly maxTenantBytes: number;
2850
+ readonly maxEntries: number;
2851
+ readonly maxTombstones: number;
2852
+ readonly tombstoneTtlMs: number;
2853
+ }
2854
+ interface FileImageReferenceStoreOptions {
2855
+ readonly paths: DaemonImagePathResolver;
2856
+ readonly limits: FileImageReferenceStoreLimits;
2857
+ readonly secretBox?: SecretBox;
2858
+ readonly now?: () => number;
2859
+ readonly random?: (bytes: number) => Buffer;
2860
+ readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
2861
+ }
2862
+ interface FileImageReferenceReconciliationResult {
2863
+ readonly metadataRemoved: number;
2864
+ readonly metadataDegradedToProviderReference: number;
2865
+ readonly orphanFilesRemoved: number;
2866
+ readonly incompleteFilesRemoved: number;
2867
+ readonly invalidDescendants: number;
2868
+ }
2869
+ declare class FileImageReferenceStore implements ImageReferenceStore {
2870
+ #private;
2871
+ constructor(options: FileImageReferenceStoreOptions);
2872
+ save(input: ImageReferenceSaveInput): Promise<ImageReferenceMetadata>;
2873
+ /** Generation-bound write entry point; reads and maintenance remain shared. */
2874
+ saveWithLimits(input: ImageReferenceSaveInput, limits: FileImageReferenceStoreLimits): Promise<ImageReferenceMetadata>;
2875
+ /** Updates only app-session maintenance policy; pinned writes pass their own limits. */
2876
+ updateMaintenanceLimits(limits: FileImageReferenceStoreLimits): void;
2877
+ resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
2878
+ delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
2879
+ /** Daemon-internal cleanup path; accepts only the local reference-domain tenant HMAC. */
2880
+ deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
2881
+ cleanup(now?: number): Promise<number>;
2882
+ status(): {
2883
+ readonly entries: number;
2884
+ readonly bytes: number;
2885
+ readonly tombstones: number;
2886
+ };
2887
+ hasLiveReferenceByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId, now?: number): Promise<boolean>;
2888
+ reconcileOwnedFiles(maxEntries: number): Promise<FileImageReferenceReconciliationResult>;
2889
+ openArtifact(fileName: string, byteLength: number, signal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
2890
+ private exclusive;
2891
+ private tenantKey;
2892
+ private newReferenceId;
2893
+ private validateSaveInput;
2894
+ private selectVictims;
2895
+ private nextTombstones;
2896
+ private writeArtifact;
2897
+ private artifactPath;
2898
+ private validArtifact;
2899
+ private removeArtifact;
2900
+ private safeUnlinkArtifactPath;
2901
+ private releaseLease;
2902
+ private manifestPath;
2903
+ private persist;
2904
+ private atomicReplace;
2905
+ private loadManifest;
2906
+ }
2907
+
2908
+ interface FileResponsesImageStateStoreLimits {
2909
+ readonly maxCalls: number;
2910
+ readonly maxResponses: number;
2911
+ readonly maxTombstones: number;
2912
+ readonly tombstoneTtlMs: number;
2913
+ }
2914
+ interface FileResponsesImageStateStoreOptions {
2915
+ readonly paths: DaemonImagePathResolver;
2916
+ readonly limits: FileResponsesImageStateStoreLimits;
2917
+ readonly now?: () => number;
2918
+ readonly random?: (bytes: number) => Buffer;
2919
+ readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
2920
+ }
2921
+ interface PendingResponsesImageReferenceDelete {
2922
+ readonly referenceTenantKey: string;
2923
+ readonly binding: ResponsesImageCallBinding;
2924
+ }
2925
+ /** Durable production implementation of the existing Responses image-state contract. */
2926
+ declare class FileResponsesImageStateStore implements ResponsesImageStateStore {
2927
+ #private;
2928
+ constructor(options: FileResponsesImageStateStoreOptions);
2929
+ commit(input: ResponsesImageStateCommitInput): Promise<readonly ResponsesImageCallBinding[]>;
2930
+ /** Generation-bound write entry point; reads and maintenance remain shared. */
2931
+ commitWithLimits(input: ResponsesImageStateCommitInput, limits: FileResponsesImageStateStoreLimits): Promise<readonly ResponsesImageCallBinding[]>;
2932
+ /** Updates only app-session maintenance policy; pinned commits pass their own limits. */
2933
+ updateMaintenanceLimits(limits: FileResponsesImageStateStoreLimits): void;
2934
+ resolveCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallResolution>;
2935
+ resolveResponse(tenantId: string, responseId: string): Promise<ResponsesImageResponseResolution>;
2936
+ deleteCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallBinding | undefined>;
2937
+ deleteResponse(tenantId: string, responseId: string): Promise<boolean>;
2938
+ cleanup(now?: number): Promise<readonly ResponsesImageCallBinding[]>;
2939
+ pendingReferenceDeletes(limit?: number): readonly PendingResponsesImageReferenceDelete[];
2940
+ acknowledgeReferenceDeletes(completed: readonly PendingResponsesImageReferenceDelete[]): Promise<number>;
2941
+ reconcileBrokenReferenceLinks(hasLiveReference: (referenceTenantKey: string, referenceId: ResponsesImageCallBinding['referenceId']) => Promise<boolean>, maxEntries: number): Promise<readonly ResponsesImageCallBinding[]>;
2942
+ status(): {
2943
+ readonly calls: number;
2944
+ readonly responses: number;
2945
+ readonly tombstones: number;
2946
+ readonly pendingReferenceDeletes: number;
2947
+ };
2948
+ private exclusive;
2949
+ private failure;
2950
+ private assertCommit;
2951
+ private tenantKey;
2952
+ private rememberTombstone;
2953
+ private enqueuePendingReferenceDelete;
2954
+ private hasTombstone;
2955
+ private prunedTombstones;
2956
+ private pruneTombstonesInPlace;
2957
+ private sameTombstones;
2958
+ private touch;
2959
+ private releaseCall;
2960
+ private releaseResponse;
2961
+ private manifestPath;
2962
+ private persist;
2963
+ private atomicReplace;
2964
+ private loadManifest;
2965
+ }
2966
+
2967
+ interface ImageStorageMountBackend {
2968
+ readonly id: string;
2969
+ readonly createdAt: number;
2970
+ readonly resolver: DaemonImagePathResolver;
2971
+ readonly references: FileImageReferenceStore;
2972
+ readonly responsesState: FileResponsesImageStateStore;
2973
+ }
2974
+ interface ImageStorageMountCatalogOptions {
2975
+ readonly pathOptions: Omit<CreateDaemonImagePathResolverOptions, 'storageRoot'>;
2976
+ readonly activeStorageRoot?: string;
2977
+ readonly referenceLimits: FileImageReferenceStoreLimits;
2978
+ readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
2979
+ readonly secretBox?: SecretBox;
2980
+ readonly now?: () => number;
2981
+ readonly random?: (bytes: number) => Buffer;
2982
+ readonly replaceCatalog?: (targetPath: string, contents: Uint8Array) => void;
2983
+ readonly reconcileCorruptManifests?: boolean;
2984
+ }
2985
+ interface ImageStorageMountPolicy {
2986
+ readonly referenceLimits: FileImageReferenceStoreLimits;
2987
+ readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
2988
+ }
2989
+ interface PreparedImageStorageMountActivation {
2990
+ readonly backend: ImageStorageMountBackend;
2991
+ publish(): ImageStorageMountBackend;
2992
+ rollback(): void;
2993
+ dispose(): void;
2994
+ }
2995
+ /** Owns the durable-root set independently from any one runtime generation. */
2996
+ declare class ImageStorageMountCatalog {
2997
+ #private;
2998
+ constructor(options: ImageStorageMountCatalogOptions);
2999
+ active(): ImageStorageMountBackend;
3000
+ mountsForRead(): readonly ImageStorageMountBackend[];
3001
+ status(): {
3002
+ readonly mounts: number;
3003
+ readonly retiredMounts: number;
3004
+ };
3005
+ startupReconciliationStatus(): {
3006
+ readonly corruptManifestsQuarantined: number;
3007
+ };
3008
+ utilization(): {
3009
+ readonly referenceEntries: number;
3010
+ readonly referenceBytes: number;
3011
+ readonly referenceTombstones: number;
3012
+ readonly stateCalls: number;
3013
+ readonly stateResponses: number;
3014
+ readonly stateTombstones: number;
3015
+ readonly pendingReferenceDeletes: number;
3016
+ };
3017
+ /** Pins a backend object until its owning runtime generation drains. */
3018
+ retainBackend(backend: ImageStorageMountBackend): () => void;
3019
+ activate(storageRoot?: string): ImageStorageMountBackend;
3020
+ /** Prepare a validated backend without changing the catalog's active mount. */
3021
+ prepareActivation(storageRoot?: string, policy?: ImageStorageMountPolicy): PreparedImageStorageMountActivation;
3022
+ retireEmptyMount(mountId: string): boolean;
3023
+ private createResolver;
3024
+ private createBackend;
3025
+ private applyMaintenancePolicy;
3026
+ private newMountId;
3027
+ private isManifestError;
3028
+ private quarantineManifest;
3029
+ private isVerifiedEmpty;
3030
+ private catalogPath;
3031
+ private persist;
3032
+ private atomicReplace;
3033
+ private loadCatalog;
3034
+ }
3035
+ declare class MountedImageReferenceStore implements ImageReferenceStore {
3036
+ private readonly catalog;
3037
+ private readonly writeBackend?;
3038
+ private readonly writeLimits?;
3039
+ constructor(catalog: ImageStorageMountCatalog, writeBackend?: ImageStorageMountBackend | undefined, writeLimits?: FileImageReferenceStoreLimits | undefined);
3040
+ bindWriteBackend(backend: ImageStorageMountBackend, limits: FileImageReferenceStoreLimits): MountedImageReferenceStore;
3041
+ status(): Readonly<{
3042
+ referenceEntries: number;
3043
+ referenceBytes: number;
3044
+ referenceTombstones: number;
3045
+ stateCalls: number;
3046
+ stateResponses: number;
3047
+ stateTombstones: number;
3048
+ pendingReferenceDeletes: number;
3049
+ mounts: number;
3050
+ retiredMounts: number;
3051
+ }>;
3052
+ save(input: ImageReferenceSaveInput): Promise<_omnicross_contracts_image_generation_types.ImageReferenceMetadata>;
3053
+ resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
3054
+ delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
3055
+ deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
3056
+ cleanup(now?: number): Promise<number>;
3057
+ }
3058
+ declare class MountedResponsesImageStateStore implements ResponsesImageStateStore {
3059
+ private readonly catalog;
3060
+ private readonly writeBackend?;
3061
+ private readonly writeLimits?;
3062
+ constructor(catalog: ImageStorageMountCatalog, writeBackend?: ImageStorageMountBackend | undefined, writeLimits?: FileResponsesImageStateStoreLimits | undefined);
3063
+ bindWriteBackend(backend: ImageStorageMountBackend, limits: FileResponsesImageStateStoreLimits): MountedResponsesImageStateStore;
3064
+ commit(input: ResponsesImageStateCommitInput): Promise<readonly ResponsesImageCallBinding[]>;
3065
+ resolveCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallResolution>;
3066
+ resolveResponse(tenantId: string, responseId: string): Promise<ResponsesImageResponseResolution>;
3067
+ deleteCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallBinding | undefined>;
3068
+ deleteResponse(tenantId: string, responseId: string): Promise<boolean>;
3069
+ cleanup(now?: number): Promise<readonly ResponsesImageCallBinding[]>;
3070
+ }
3071
+
3072
+ interface ImageDoctorLocalSnapshot {
3073
+ readonly config: Readonly<{
3074
+ enabled: boolean;
3075
+ provider: 'codex-subscription';
3076
+ model: string;
3077
+ valid: boolean;
3078
+ errorCount: number;
3079
+ }>;
3080
+ readonly roots: Readonly<{
3081
+ valid: boolean;
3082
+ verifiedAreas: number;
3083
+ expectedAreas: number;
3084
+ }>;
3085
+ readonly stores: Readonly<{
3086
+ valid: boolean;
3087
+ mounts: number;
3088
+ retiredMounts: number;
3089
+ referenceEntries: number;
3090
+ referenceBytes: number;
3091
+ stateCalls: number;
3092
+ stateResponses: number;
3093
+ corruptManifestsQuarantined: number;
3094
+ }>;
3095
+ readonly permissions: Readonly<{
3096
+ valid: boolean;
3097
+ rows: number;
3098
+ legacyRows: number;
3099
+ invalidRows: number;
3100
+ imagesAuthorizedRows: number;
3101
+ }>;
3102
+ readonly account: Readonly<{
3103
+ present: boolean;
3104
+ usable: boolean;
3105
+ reason: 'ready' | 'missing' | 'unavailable';
3106
+ }>;
3107
+ readonly evidence: Readonly<FileCodexImageCapabilityEvidenceStatus & {
3108
+ valid: boolean;
3109
+ }>;
3110
+ }
3111
+ type ImageDoctorLiveFailureCode = 'images_disabled' | 'codex_account_unavailable' | 'evidence_store_unavailable' | 'evidence_persist_failed' | ImageGenerationErrorCode;
3112
+ type ImageDoctorLiveResult = Readonly<{
3113
+ ok: true;
3114
+ code: 'verified';
3115
+ model: 'gpt-image-2';
3116
+ quality: 'low';
3117
+ outputFormat: 'png';
3118
+ freshEvidenceEntries: number;
3119
+ }> | Readonly<{
3120
+ ok: false;
3121
+ code: ImageDoctorLiveFailureCode;
3122
+ }>;
3123
+ interface ImageDoctorService {
3124
+ inspectLocal(config: ImagesServerConfig): Promise<ImageDoctorLocalSnapshot>;
3125
+ verifyLive(config: ImagesServerConfig, signal: AbortSignal): Promise<ImageDoctorLiveResult>;
3126
+ }
3127
+
3128
+ interface ImageTemporaryBudgetStatus {
3129
+ readonly activeScopes: number;
3130
+ readonly totalBytes: number;
3131
+ readonly tenantCount: number;
3132
+ }
3133
+ /** Process-local atomic budget shared by every scope in one pinned runtime generation. */
3134
+ declare class DaemonImageTemporaryBudget implements ImageTemporaryResourceBudget {
3135
+ #private;
3136
+ constructor(config: ImagesServerConfig['temporary']);
3137
+ acquireScope(tenantId: string): ImageTemporaryResourceBudgetLease;
3138
+ status(): ImageTemporaryBudgetStatus;
3139
+ }
3140
+ interface DaemonImageTemporaryResourceFactoryOptions {
3141
+ readonly paths: DaemonImagePathResolver;
3142
+ readonly config: ImagesServerConfig['temporary'];
3143
+ readonly activeScopes?: DaemonImageActiveScopeRegistry;
3144
+ }
3145
+ /** App-session registry shared by all runtime generations and recurring cleanup. */
3146
+ declare class DaemonImageActiveScopeRegistry {
3147
+ #private;
3148
+ constructor(paths: DaemonImagePathResolver);
3149
+ register(privateDirectory: string): () => void;
3150
+ isActive(privateDirectory: string): boolean;
3151
+ status(): {
3152
+ readonly activeDirectories: number;
3153
+ };
3154
+ }
3155
+ /** Binds the private root, fixed owner marker, tenant, and shared budget in one seam. */
3156
+ declare class DaemonImageTemporaryResourceFactory {
3157
+ #private;
3158
+ readonly budget: DaemonImageTemporaryBudget;
3159
+ constructor(options: DaemonImageTemporaryResourceFactoryOptions);
3160
+ readonly createResourceScope: (limits: ImageApiLimits, signal: AbortSignal, tenantId: string) => Promise<ImageRequestResourceScope>;
3161
+ }
3162
+
3163
+ interface ImageStartupReconcilerOptions {
3164
+ readonly catalog: ImageStorageMountCatalog;
3165
+ readonly temporaryPaths: DaemonImagePathResolver;
3166
+ readonly staleTemporaryAfterMs: number;
3167
+ readonly activeTemporaryScopes?: Pick<DaemonImageActiveScopeRegistry, 'isActive'>;
3168
+ readonly maxMountsPerPass?: number;
3169
+ readonly maxEntriesPerMount?: number;
3170
+ readonly maxTemporaryDirectoriesPerPass?: number;
3171
+ readonly now?: () => number;
3172
+ }
3173
+ interface ImageStartupReconciliationResult {
3174
+ readonly corruptManifestsQuarantined: number;
3175
+ readonly mountsVisited: number;
3176
+ readonly stateBindingsRemoved: number;
3177
+ readonly brokenBindingsRemoved: number;
3178
+ readonly referenceEntriesRemoved: number;
3179
+ readonly metadataRemoved: number;
3180
+ readonly metadataDegradedToProviderReference: number;
3181
+ readonly orphanFilesRemoved: number;
3182
+ readonly incompleteFilesRemoved: number;
3183
+ readonly transactionFilesRemoved: number;
3184
+ readonly temporaryDirectoriesRemoved: number;
3185
+ readonly foreignTemporaryDirectoriesSkipped: number;
3186
+ readonly activeTemporaryDirectoriesSkipped: number;
3187
+ readonly invalidDescendantsSkipped: number;
3188
+ readonly pendingReferenceDeletes: number;
3189
+ }
3190
+ /** One bounded, synchronous-filesystem startup pass; it never follows symlinks. */
3191
+ declare class ImageStartupReconciler {
3192
+ #private;
3193
+ constructor(options: ImageStartupReconcilerOptions);
3194
+ run(): Promise<ImageStartupReconciliationResult>;
3195
+ private hasLiveReference;
3196
+ private removeTransactionFiles;
3197
+ private removeStaleTemporaryDirectories;
3198
+ private removeTreeWithoutFollowingSymlinks;
3199
+ }
3200
+
3201
+ interface BoundedImageEvidenceCleanup {
3202
+ cleanup(now: number, limit: number): Promise<{
3203
+ readonly entriesRemoved: number;
3204
+ readonly bytesRemoved: number;
3205
+ }>;
3206
+ }
3207
+ interface ImageCleanupTimer {
3208
+ unref(): unknown;
3209
+ }
3210
+ interface ImageCleanupServiceOptions {
3211
+ readonly reconciler: ImageStartupReconciler;
3212
+ readonly catalog: ImageStorageMountCatalog;
3213
+ readonly intervalMs: number;
3214
+ readonly maxEvidenceEntriesPerPass?: number;
3215
+ readonly maxRetiredMountsPerPass?: number;
3216
+ readonly evidence?: BoundedImageEvidenceCleanup;
3217
+ readonly now?: () => number;
3218
+ readonly scheduleInterval?: (callback: () => void, intervalMs: number) => ImageCleanupTimer;
3219
+ readonly clearScheduledInterval?: (timer: ImageCleanupTimer) => void;
3220
+ }
3221
+ interface ImageCleanupPolicy {
3222
+ readonly reconciler: ImageStartupReconciler;
3223
+ readonly intervalMs: number;
3224
+ readonly evidence?: BoundedImageEvidenceCleanup;
3225
+ }
3226
+ interface PreparedImageCleanupPolicyChange {
3227
+ publish(): void;
3228
+ rollback(): void;
3229
+ dispose(): void;
3230
+ }
3231
+ interface ImageCleanupServicePassResult {
3232
+ readonly stateBindingsRemoved: number;
3233
+ readonly brokenBindingsRemoved: number;
3234
+ readonly referenceEntriesRemoved: number;
3235
+ readonly orphanFilesRemoved: number;
3236
+ readonly incompleteFilesRemoved: number;
3237
+ readonly transactionFilesRemoved: number;
3238
+ readonly temporaryDirectoriesRemoved: number;
3239
+ readonly evidenceEntriesRemoved: number;
3240
+ readonly evidenceBytesRemoved: number;
3241
+ readonly evidenceCleanupFailures: number;
3242
+ readonly retiredMountsRemoved: number;
3243
+ readonly referenceEntries: number;
3244
+ readonly referenceBytes: number;
3245
+ readonly referenceTombstones: number;
3246
+ readonly stateCalls: number;
3247
+ readonly stateResponses: number;
3248
+ readonly stateTombstones: number;
3249
+ readonly pendingReferenceDeletes: number;
3250
+ readonly finishedAt: number;
3251
+ }
3252
+ interface ImageCleanupServiceStatus extends ImageCleanupServicePassResult {
3253
+ readonly running: boolean;
3254
+ readonly passesCompleted: number;
3255
+ readonly passFailures: number;
3256
+ }
3257
+ /** Unref'ed recurring lifecycle owner with one non-overlapping bounded pass. */
3258
+ declare class ImageCleanupService {
3259
+ #private;
3260
+ constructor(options: ImageCleanupServiceOptions);
3261
+ start(): void;
3262
+ private schedule;
3263
+ /** Prepares a hot-reloadable cadence/reconciler/evidence snapshot. */
3264
+ preparePolicy(policy: ImageCleanupPolicy): PreparedImageCleanupPolicyChange;
3265
+ stop(): Promise<void>;
3266
+ reset(): Promise<void>;
3267
+ runOnce(): Promise<ImageCleanupServicePassResult>;
3268
+ status(): ImageCleanupServiceStatus;
3269
+ private performPass;
3270
+ }
3271
+
3272
+ interface TrustedImageApiRuntimeResolverOptions {
3273
+ readonly config: ImagesServerConfig;
3274
+ readonly referenceStore: ImageReferenceStore;
3275
+ /** Persistent private daemon key material. It is copied on construction. */
3276
+ readonly hmacKey: Uint8Array;
3277
+ /** Present only when the host has proved the complete remote-fetch policy. */
3278
+ readonly provenRemoteResolver?: RemoteImageAssetResolver;
3279
+ }
3280
+ interface TrustedImageApiRuntimeResolver {
3281
+ readonly resolve: ImageApiRuntimeResolver;
3282
+ dispose(): void;
3283
+ }
3284
+ /**
3285
+ * Resolves only daemon-authenticated route state. Named outbound keys retain
3286
+ * their stable key-id tenant; ephemeral Route Lease traffic falls back to the
3287
+ * authenticated lease id and remains isolated from other leases. Request
3288
+ * headers, including an inbound bearer, are deliberately outside the resolver's
3289
+ * identity inputs.
3290
+ */
3291
+ declare function createTrustedImageApiRuntimeResolver(options: TrustedImageApiRuntimeResolverOptions): TrustedImageApiRuntimeResolver;
3292
+
3293
+ interface DaemonImageExecutionSchedulerOptions {
3294
+ readonly config: ImagesServerConfig['queue'];
3295
+ /** Persistent private daemon key material; copied on construction. */
3296
+ readonly hmacKey: Uint8Array;
3297
+ }
3298
+ interface DaemonImageExecutionSchedulerStatus {
3299
+ readonly activeJobs: number;
3300
+ readonly waitingJobs: number;
3301
+ readonly activeAccounts: number;
3302
+ readonly waitingAccounts: number;
3303
+ readonly waitingTenants: number;
3304
+ readonly maxConcurrentJobsPerAccount: number;
3305
+ readonly maxQueuedJobs: number;
3306
+ readonly accepting: boolean;
3307
+ readonly shuttingDown: boolean;
3308
+ }
3309
+ /**
3310
+ * Snapshot-bound image admission with per-account active caps and fair tenant
3311
+ * rotation under one bounded global waiting population.
3312
+ */
3313
+ declare class DaemonImageExecutionScheduler implements ImageExecutionScheduler {
3314
+ #private;
3315
+ constructor(options: DaemonImageExecutionSchedulerOptions);
3316
+ deriveAccountKey(selectedAccountId: string): ImageExecutionAccountKey;
3317
+ acquire(request: ImageExecutionSchedulerRequest): Promise<ImageExecutionSchedulerGrant>;
3318
+ status(): DaemonImageExecutionSchedulerStatus;
3319
+ /** Stop new admissions while allowing already queued and active work to drain. */
3320
+ retire(): void;
3321
+ /** Reject waiters and cancel active grants. Safe to call repeatedly. */
3322
+ shutdown(): void;
3323
+ }
3324
+
3325
+ interface ImageRuntimeGenerationSharedStorage {
3326
+ /** Resolver associated with the catalog's already-active mount. */
3327
+ readonly paths: DaemonImagePathResolver;
3328
+ readonly referenceStore: MountedImageReferenceStore;
3329
+ readonly stateStore: MountedResponsesImageStateStore;
3330
+ }
3331
+ interface ImageRuntimeMetadataObservability {
3332
+ readonly telemetrySink?: ImageTelemetrySink;
3333
+ readonly audit?: (record: ImageApiAuditRecord) => void | Promise<void>;
3334
+ }
3335
+ /**
3336
+ * Explicit deterministic Tier-A seam. It replaces only the provider inside an
3337
+ * otherwise-production runtime generation and is never inferred from config.
3338
+ */
3339
+ interface SyntheticVerifiedImageProviderTestSeam {
3340
+ readonly label: 'synthetic-verified-image-provider-test-only';
3341
+ createProvider(context: Readonly<{
3342
+ generationId: string;
3343
+ scheduler: ImageExecutionScheduler;
3344
+ now: () => number;
3345
+ referenceStore: MountedImageReferenceStore;
3346
+ stateStore: MountedResponsesImageStateStore;
3347
+ }>): ImageProvider;
3348
+ }
3349
+ interface ImageRuntimeGenerationFactoryOptions {
3350
+ readonly generationId: string;
3351
+ readonly config: ImagesServerConfig;
3352
+ readonly subscriptionAccounts: Pick<SubscriptionAccountService, 'getStrategy'>;
3353
+ readonly storage: ImageRuntimeGenerationSharedStorage;
3354
+ readonly provenRemoteResolver?: RemoteImageAssetResolver;
3355
+ readonly observability?: ImageRuntimeMetadataObservability;
3356
+ readonly createRequestId?: () => string;
3357
+ readonly createCallId?: () => `ig_${string}`;
3358
+ readonly now?: () => number;
3359
+ /** Test seam; production loads the persistent daemon-local HMAC salt. */
3360
+ readonly privateHmacKey?: Uint8Array;
3361
+ /** App-session owner used to protect live directories across generations. */
3362
+ readonly activeTemporaryScopes?: DaemonImageActiveScopeRegistry;
3363
+ /** Immutable TTL view scoped to this runtime generation. */
3364
+ readonly evidenceSource?: FileCodexImageCapabilityEvidenceSource;
3365
+ /** App-session catalog lease retained until this generation is disposed. */
3366
+ readonly releaseStorageBackend?: () => void;
3367
+ /** Test-only Tier-A provider; production leaves this absent. */
3368
+ readonly testOnlySyntheticVerifiedProvider?: SyntheticVerifiedImageProviderTestSeam;
3369
+ }
3370
+ interface ProductionImageRuntimeComponents {
3371
+ readonly providerRegistry: ImageProviderRegistry;
3372
+ readonly orchestrator: ImageOrchestrator;
3373
+ readonly scheduler: DaemonImageExecutionScheduler;
3374
+ readonly evidenceSource: FileCodexImageCapabilityEvidenceSource;
3375
+ readonly temporaryResources: DaemonImageTemporaryResourceFactory;
3376
+ readonly runtimeResolver: TrustedImageApiRuntimeResolver;
3377
+ readonly referenceStore: MountedImageReferenceStore;
3378
+ readonly stateStore: MountedResponsesImageStateStore;
3379
+ }
3380
+ type ProductionImageRuntimeGeneration = PreparedImageRuntimeGeneration & {
3381
+ readonly components?: ProductionImageRuntimeComponents;
3382
+ };
3383
+ /**
3384
+ * Builds a complete generation without changing the storage catalog's active
3385
+ * mount. Catalog activation must be composed by a failure-atomic transaction.
3386
+ */
3387
+ declare function createImageRuntimeGeneration(options: ImageRuntimeGenerationFactoryOptions): ProductionImageRuntimeGeneration;
3388
+
3389
+ /**
3390
+ * JsonOutboundKeyDb — the daemon's file-backed `OutboundKeyDb` port impl
3391
+ * (design D3).
3392
+ *
3393
+ * Durable storage for named outbound API keys, backed by a json file (a sibling
3394
+ * of `config.json`, e.g. `keys.json`) holding an `OutboundKeyDbRow[]`. This port
3395
+ * provides ONLY storage — it never generates secrets nor hashes. Core's
3396
+ * `createNamedKey(db, name)` calls `outboundApiKeysCreate` with the sha256
3397
+ * `keyHash` + display `keyPrefix` and returns the one-time plaintext; the hot
3398
+ * auth path uses core's `hashKey(presented)` + `outboundApiKeysGetByHash`.
3399
+ *
3400
+ * OPTIONAL reversible storage: when constructed with a `SecretBox`, each created
3401
+ * key ALSO persists its plaintext as a `keySecret` `enc:` envelope, powering the
3402
+ * operator "view key" affordance (`outboundApiKeysReveal`). The hash remains the
3403
+ * auth index; without a box the store is hash-only (byte-identical to legacy).
3404
+ *
3405
+ * @module @omnicross/daemon/ports/JsonOutboundKeyDb
3406
+ */
3407
+
3408
+ type AtomicFileReplace = (targetPath: string, contents: string) => void;
2236
3409
  declare class JsonOutboundKeyDb implements OutboundKeyDb {
2237
3410
  private readonly keysPath;
2238
3411
  private readonly secretBox?;
3412
+ private readonly atomicReplace;
2239
3413
  /**
2240
3414
  * @param secretBox OPTIONAL reversible-secret codec. When present, a created
2241
3415
  * key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
@@ -2244,7 +3418,7 @@ declare class JsonOutboundKeyDb implements OutboundKeyDb {
2244
3418
  * always returns `null`. Existing 1-arg call sites (tests, lightweight
2245
3419
  * embedders) keep working.
2246
3420
  */
2247
- constructor(keysPath: string, secretBox?: SecretBox | undefined);
3421
+ constructor(keysPath: string, secretBox?: SecretBox | undefined, atomicReplace?: AtomicFileReplace);
2248
3422
  outboundApiKeysList(): Promise<OutboundKeyDbRow[]>;
2249
3423
  outboundApiKeysGetByHash(hash: string): Promise<OutboundKeyDbRow | null>;
2250
3424
  outboundApiKeysCreate(input: {
@@ -2254,7 +3428,7 @@ declare class JsonOutboundKeyDb implements OutboundKeyDb {
2254
3428
  keyPrefix: string;
2255
3429
  createdAt?: number;
2256
3430
  kind?: 'client' | 'integration';
2257
- allowedEndpoints?: _omnicross_core.OutboundEndpoint[];
3431
+ allowedEndpoints?: OutboundPermission[];
2258
3432
  loopbackOnly?: boolean;
2259
3433
  plaintext?: string;
2260
3434
  }): Promise<OutboundKeyDbRow>;
@@ -2263,6 +3437,7 @@ declare class JsonOutboundKeyDb implements OutboundKeyDb {
2263
3437
  outboundApiKeysRevoke(id: string): Promise<boolean>;
2264
3438
  outboundApiKeysTouchLastUsed(id: string): Promise<boolean>;
2265
3439
  outboundApiKeysSetEnabled(id: string, enabled: boolean): Promise<boolean>;
3440
+ outboundApiKeysSetPermissions(id: string, permissions: OutboundPermission[]): Promise<boolean>;
2266
3441
  outboundApiKeysSetMaxConcurrency(id: string, maxConcurrency: number | null): Promise<boolean>;
2267
3442
  outboundApiKeysSetPolicy(id: string, policy: OutboundKeyPolicy): Promise<boolean>;
2268
3443
  outboundApiKeysMarkActivated(id: string, activatedAt: number): Promise<boolean>;
@@ -2424,6 +3599,52 @@ declare class AuditPruneSweeper {
2424
3599
  private archiveDay;
2425
3600
  }
2426
3601
 
3602
+ /**
3603
+ * usageMigrate — the ONE-SHOT fold of the legacy flat `usage-events.jsonl` into
3604
+ * per-LOCAL-day shards plus their immutable rollups.
3605
+ *
3606
+ * Runs at bootstrap, BEFORE any listener is bound, so no request can be served
3607
+ * against a half-migrated store. It streams: the legacy file is never held in
3608
+ * memory as one string (that is the exact shape that put the old store on course
3609
+ * for V8's ~512 MB string ceiling), and each day's rollup is accumulated during
3610
+ * the same pass that writes its shard, so the whole migration is a single read.
3611
+ *
3612
+ * Writes are BATCHED per day (see {@link FLUSH_BYTES}). Awaiting a write per row
3613
+ * turned 199k rows into ~17 s of boot on a real 157 MB store, which the desktop
3614
+ * shell — waiting on the admin port — is right to find suspicious; batching cuts
3615
+ * it to a few seconds without changing what lands on disk.
3616
+ *
3617
+ * CRASH SAFETY. Everything is built inside `usage/.migrating/` and only moved
3618
+ * into place once the row count reconciles. A crash before the commit leaves the
3619
+ * legacy file untouched and the scratch directory to be wiped and rebuilt on the
3620
+ * next attempt. A crash DURING the commit leaves some shards in place and the
3621
+ * legacy file still present; the next attempt refuses to run (a target shard
3622
+ * already exists) and says so, rather than overwriting or double-counting rows.
3623
+ *
3624
+ * RECONCILIATION is the point of the exercise: `linesRead` must equal
3625
+ * `rowsWritten + skipped`, where `skipped` is lines the shared row guard rejects.
3626
+ * If it does not, nothing is committed and the legacy file stays exactly where it
3627
+ * was. Silently losing usage rows would corrupt lifetime key spend, which is the
3628
+ * one number in this store that can never be reconstructed.
3629
+ *
3630
+ * @module @omnicross/daemon/usage/usageMigrate
3631
+ */
3632
+
3633
+ interface UsageMigrationResult {
3634
+ /** True only when the legacy file was folded in and removed. */
3635
+ migrated: boolean;
3636
+ /** Non-empty lines read from the legacy file. */
3637
+ linesRead: number;
3638
+ /** Rows that passed the guard and were written to a shard. */
3639
+ rowsWritten: number;
3640
+ /** Lines the guard rejected (torn tail, hand-edited, foreign writer). */
3641
+ skipped: number;
3642
+ /** Distinct LOCAL days produced. */
3643
+ days: number;
3644
+ /** Present when the migration declined or aborted; nothing was committed. */
3645
+ reason?: string;
3646
+ }
3647
+
2427
3648
  /**
2428
3649
  * AuditWriter — the daemon's file-backed audit sink (request-audit-log design
2429
3650
  * D4/D5, re-laid-out by audit-store-sharding design D2). Registered as
@@ -2824,6 +4045,16 @@ interface DaemonPaths {
2824
4045
  * never invoke a real package manager. Absent → the real `exec`-based runner.
2825
4046
  */
2826
4047
  cliCommandRunner?: CommandRunner;
4048
+ /** TEST/COMPOSITION SEAM: prepared Images runtime generation for config transactions. */
4049
+ imageRuntimeConfig?: AdminApiDeps['imageRuntimeConfig'];
4050
+ /** TEST/COMPOSITION SEAM: metadata-only Images status reader. */
4051
+ imageRuntimeStatus?: AdminApiDeps['imageRuntimeStatus'];
4052
+ /** TEST/COMPOSITION SEAM: metadata-only successful Images config audit sink. */
4053
+ imageConfigAudit?: AdminApiDeps['imageConfigAudit'];
4054
+ /** TEST ONLY: deterministic Tier-A provider inside the production Images composition. */
4055
+ testOnlySyntheticVerifiedImageProvider?: SyntheticVerifiedImageProviderTestSeam;
4056
+ /** TEST SEAM: inject an atomic settings replacement fault. */
4057
+ settingsAtomicReplace?: (targetPath: string, contents: Uint8Array) => void;
2827
4058
  }
2828
4059
  /** The constructed daemon handles the CLI commands operate on. */
2829
4060
  interface Daemon {
@@ -2832,6 +4063,18 @@ interface Daemon {
2832
4063
  readonly llmConfig: ConfigFileProviderConfigSource;
2833
4064
  readonly keyDb: JsonOutboundKeyDb;
2834
4065
  readonly settingsStore: JsonApiServerSettingsStore;
4066
+ /** App-session extension-operation registry shared with the resident proxy. */
4067
+ readonly openAIOperationRegistry: OpenAIOperationRegistry;
4068
+ /** Stable Images forwarders and generation lifecycle owner for this app session. */
4069
+ readonly imageRuntimeManager: ImageRuntimeManager;
4070
+ /** Bounded process-local metadata aggregation shared by every runtime generation. */
4071
+ readonly imageObservability: ImageObservability;
4072
+ /** Startup reconciliation plus recurring bounded cleanup for Images state. */
4073
+ readonly imageCleanupService: ImageCleanupService;
4074
+ /** Local-only diagnostics plus the explicit consuming Images verifier. */
4075
+ readonly imageDoctor: ImageDoctorService;
4076
+ /** Generation-pinned hosted-image lease factory used by Native Responses. */
4077
+ readonly hostedImageContributionFactory: HostedImageContributionFactory;
2835
4078
  readonly providerProxy: ProviderProxy;
2836
4079
  readonly routeLeaseManager: RouteLeaseManager;
2837
4080
  readonly outboundApiServer: OutboundApiServer;
@@ -2909,6 +4152,17 @@ interface Daemon {
2909
4152
  * in cleanup.
2910
4153
  */
2911
4154
  readonly auditPruneSweeper: AuditPruneSweeper;
4155
+ /**
4156
+ * Retention for RAW usage rows (rollups are kept forever). Armed by `start`;
4157
+ * `launch` leaves it off — a short-lived boot has no business pruning.
4158
+ */
4159
+ readonly usagePruneSweeper: UsagePruneSweeper;
4160
+ /**
4161
+ * Fold a legacy flat `usage-events.jsonl` into day shards. MUST be awaited
4162
+ * before any listener binds: it moves files the query path reads, and a
4163
+ * request served mid-migration would see a partial store.
4164
+ */
4165
+ readonly migrateUsageStore: () => Promise<UsageMigrationResult>;
2912
4166
  /**
2913
4167
  * Durable-first billing publisher (billing-event-stream) — appends each event
2914
4168
  * to `billing/billing-YYYY-MM-DD.jsonl` FIRST, then best-effort POSTs it.
@@ -2925,11 +4179,6 @@ interface Daemon {
2925
4179
  */
2926
4180
  readonly billingRetrySweeper: BillingRetrySweeper;
2927
4181
  }
2928
- /**
2929
- * Construct the standalone daemon from a loaded config + on-disk paths. Does NOT
2930
- * start the listeners — the `start` command awaits `providerProxy.start()` then
2931
- * `outboundApiServer.applyConfig(...)`.
2932
- */
2933
4182
  declare function buildDaemon(config: DaemonConfig, paths: DaemonPaths): Daemon;
2934
4183
  /** Reset the core singletons (tests / teardown only). Re-exported for the suite.
2935
4184
  *
@@ -3015,11 +4264,24 @@ declare function buildHealthReport(deps: HealthReportDeps): HealthReport;
3015
4264
  * - SINK: always the console; PLUS an optional append-only file stream when
3016
4265
  * `file` is set (lazy-open; a write/open error is swallowed → the daemon never
3017
4266
  * crashes on a logging failure, it just falls back to the console).
4267
+ * - ROTATION: the file sink is size-capped (`maxFileBytes`, default 8 MB) and
4268
+ * keeps `maxFiles` generations (default 5) as `<file>.1` … `<file>.N`. An
4269
+ * UNBOUNDED append-only log is how a long-lived daemon quietly fills a disk,
4270
+ * so the cap is on by default rather than opt-in. Lines emitted mid-rotation
4271
+ * are queued (bounded) and flushed into the fresh generation, never dropped
4272
+ * silently unless the queue itself overflows.
3018
4273
  *
3019
4274
  * ZERO-REGRESSION DEFAULT: `new ConfigurableLogger()` (no config) = console +
3020
4275
  * all levels + text = behaviorally byte-identical to the legacy `ConsoleLogger`
3021
4276
  * (same `console` method per level, same `(message[, meta])` / error arg shape).
3022
4277
  *
4278
+ * NOTE — the daemon no longer CONSTRUCTS it that way. `bootstrap.ts` defaults the
4279
+ * file sink on (`<configDir>/logs/daemon.log`, level `info`, format `json`)
4280
+ * whenever the config omits it: the desktop app discards the daemon's stdout, so
4281
+ * a console-only logger meant a crash left no evidence at all. The unconfigured
4282
+ * CONSTRUCTOR default above is still console-only — only the daemon's wiring
4283
+ * changed.
4284
+ *
3023
4285
  * CAUTION (per the #3 host:port-only-logging precedent): the JSON serializer
3024
4286
  * reduces an `Error` to `{ message, stack }` and spreads a plain `meta` object,
3025
4287
  * but it is NOT a secret redactor — call sites remain responsible for not passing
@@ -3032,8 +4294,16 @@ declare class ConfigurableLogger implements Logger {
3032
4294
  private readonly threshold;
3033
4295
  private readonly format;
3034
4296
  private readonly filePath;
4297
+ private readonly maxFileBytes;
4298
+ private readonly maxFiles;
3035
4299
  private fileStream;
3036
4300
  private fileDisabled;
4301
+ /** Bytes in the CURRENT generation — seeded from the file on open. */
4302
+ private fileBytes;
4303
+ /** True from the moment a rotation starts until the fresh stream is live. */
4304
+ private rotating;
4305
+ /** Lines emitted while `rotating`; flushed into the new generation. */
4306
+ private rotateQueue;
3037
4307
  constructor(cfg?: LoggingConfig);
3038
4308
  info(message: string, meta?: Record<string, unknown> | Error | object): void;
3039
4309
  warn(message: string, meta?: Record<string, unknown> | Error | object): void;
@@ -3053,7 +4323,22 @@ declare class ConfigurableLogger implements Logger {
3053
4323
  private writeConsole;
3054
4324
  /** Append one line to the file sink; a failure disables the sink (swallowed). */
3055
4325
  private writeFile;
3056
- /** Lazily open the append-only file stream; disable the sink on any error. */
4326
+ /** Write ONE prepared line to the live generation, rotating once it is full. */
4327
+ private emitLine;
4328
+ /**
4329
+ * Close the full generation, shift `<file>.N-1` → `<file>.N` (oldest dropped),
4330
+ * then reopen. Renames run in the `end()` callback so nothing still buffered in
4331
+ * the stream lands in the wrong generation. Any failure disables the sink
4332
+ * rather than throwing — the console sink is unaffected either way.
4333
+ */
4334
+ private rotate;
4335
+ /** `<file>.N` unlinked, `<file>.k` → `<file>.k+1`, `<file>` → `<file>.1`. */
4336
+ private shiftGenerations;
4337
+ /**
4338
+ * Lazily open the append-only file stream; disable the sink on any error. The
4339
+ * byte counter is seeded from the file already on disk so a restart cannot
4340
+ * reset an almost-full generation back to zero and blow past the cap.
4341
+ */
3057
4342
  private getFileStream;
3058
4343
  private consoleFn;
3059
4344
  /** `{ ts, level, msg, ...meta }` (+ `error` when present) as a single line. */
@@ -3143,4 +4428,4 @@ declare function mapCcrToOmnicross(ccr: CcrConfig): {
3143
4428
  notes: string[];
3144
4429
  };
3145
4430
 
3146
- export { type AdminApiDeps, AdminServer, type AdminServerDeps, type AdminServerStatus, type CcrConfig, type CcrProvider, type CcrRouter, ConfigFileProviderConfigSource, ConfigurableLogger, ConsoleLogger, DEFAULT_ADMIN_PORT, type Daemon, type DaemonAdminConfig, type DaemonApiFormat, type DaemonConfig, type DaemonPaths, type DaemonProviderConfig, type HealthReportDeps, JsonApiServerSettingsStore, JsonOutboundKeyDb, JsonSubscriptionCredentialStore, type ResolvedAdminConfig, buildDaemon, buildHealthReport, handleAdminApi, inferApiFormat, loadConfig, mapCcrToOmnicross, parseCcrConfig, resetDaemonSingletonsForTests, resolveAdminConfig, saveConfig, validateConfig };
4431
+ export { type AdminApiDeps, AdminServer, type AdminServerDeps, type AdminServerStatus, type CcrConfig, type CcrProvider, type CcrRouter, ConfigFileProviderConfigSource, ConfigurableLogger, ConsoleLogger, DEFAULT_ADMIN_PORT, type Daemon, type DaemonAdminConfig, type DaemonApiFormat, type DaemonConfig, type DaemonPaths, type DaemonProviderConfig, type HealthReportDeps, type HostedImageContributionFactory, type HostedImageRuntimeGenerationLease, type ImageApiMetricDimensions, type ImageApiMetricSnapshot, type ImageExecutionMetricDimensions, type ImageExecutionMetricSnapshot, type ImageHistogramSnapshot, ImageObservability, type ImageObservabilityOptions, type ImageObservabilitySnapshot, type ImageRuntimeCapabilityInspection, type ImageRuntimeGenerationFactoryOptions, type ImageRuntimeGenerationSharedStorage, ImageRuntimeManager, type ImageRuntimeManagerStatus, type ImageRuntimeMetadataObservability, type ImageRuntimeResourceStatus, type ImageRuntimeSafeUnavailableReason, JsonApiServerSettingsStore, JsonOutboundKeyDb, JsonSubscriptionCredentialStore, type PreparedImageRuntimeChange, type PreparedImageRuntimeGeneration, type ProductionImageRuntimeComponents, type ProductionImageRuntimeGeneration, type ResolvedAdminConfig, type TrustedImageApiRuntimeResolver, type TrustedImageApiRuntimeResolverOptions, buildDaemon, buildHealthReport, createHostedImageContributionFactory, createImageRuntimeGeneration, createTrustedImageApiRuntimeResolver, handleAdminApi, inferApiFormat, loadConfig, mapCcrToOmnicross, parseCcrConfig, resetDaemonSingletonsForTests, resolveAdminConfig, saveConfig, validateConfig };