@cleocode/contracts 2026.6.12 → 2026.6.14

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 (216) hide show
  1. package/dist/__tests__/operation-def.test.js +5 -1
  2. package/dist/__tests__/operation-def.test.js.map +1 -1
  3. package/dist/__tests__/provider-def.test.d.ts +12 -0
  4. package/dist/__tests__/provider-def.test.d.ts.map +1 -0
  5. package/dist/__tests__/provider-def.test.js +84 -0
  6. package/dist/__tests__/provider-def.test.js.map +1 -0
  7. package/dist/__tests__/sealed-credential.test.d.ts +19 -0
  8. package/dist/__tests__/sealed-credential.test.d.ts.map +1 -0
  9. package/dist/__tests__/sealed-credential.test.js +74 -0
  10. package/dist/__tests__/sealed-credential.test.js.map +1 -0
  11. package/dist/__tests__/service-provider.test.d.ts +17 -0
  12. package/dist/__tests__/service-provider.test.d.ts.map +1 -0
  13. package/dist/__tests__/service-provider.test.js +108 -0
  14. package/dist/__tests__/service-provider.test.js.map +1 -0
  15. package/dist/__tests__/skill-executor-contract.test.js +3 -0
  16. package/dist/__tests__/skill-executor-contract.test.js.map +1 -1
  17. package/dist/acceptance-gate.d.ts +2 -3
  18. package/dist/acceptance-gate.d.ts.map +1 -1
  19. package/dist/boundary.d.ts +52 -0
  20. package/dist/boundary.d.ts.map +1 -1
  21. package/dist/boundary.js +24 -19
  22. package/dist/boundary.js.map +1 -1
  23. package/dist/conduit.d.ts +171 -0
  24. package/dist/conduit.d.ts.map +1 -1
  25. package/dist/config/index.d.ts +23 -0
  26. package/dist/config/index.d.ts.map +1 -0
  27. package/dist/config/index.js +22 -0
  28. package/dist/config/index.js.map +1 -0
  29. package/dist/config.d.ts +104 -1
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/data-accessor.d.ts +21 -1
  32. package/dist/data-accessor.d.ts.map +1 -1
  33. package/dist/data-accessor.js.map +1 -1
  34. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.d.ts +21 -0
  35. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.d.ts.map +1 -0
  36. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js +84 -0
  37. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js.map +1 -0
  38. package/dist/dispatch/identity.d.ts +1 -1
  39. package/dist/dispatch/identity.d.ts.map +1 -1
  40. package/dist/dispatch/identity.js +25 -0
  41. package/dist/dispatch/identity.js.map +1 -1
  42. package/dist/dispatch/operation-def.d.ts +20 -0
  43. package/dist/dispatch/operation-def.d.ts.map +1 -1
  44. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  45. package/dist/dispatch/operations-registry.js +1044 -182
  46. package/dist/dispatch/operations-registry.js.map +1 -1
  47. package/dist/docs/read.d.ts +2 -2
  48. package/dist/index.d.ts +25 -11
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +17 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/lease-ipc/__tests__/freeze.test.d.ts +15 -0
  53. package/dist/lease-ipc/__tests__/freeze.test.d.ts.map +1 -0
  54. package/dist/lease-ipc/__tests__/freeze.test.js +313 -0
  55. package/dist/lease-ipc/__tests__/freeze.test.js.map +1 -0
  56. package/dist/lease-ipc/index.d.ts +17 -0
  57. package/dist/lease-ipc/index.d.ts.map +1 -0
  58. package/dist/lease-ipc/index.js +16 -0
  59. package/dist/lease-ipc/index.js.map +1 -0
  60. package/dist/lease-ipc/messages.d.ts +918 -0
  61. package/dist/lease-ipc/messages.d.ts.map +1 -0
  62. package/dist/lease-ipc/messages.js +476 -0
  63. package/dist/lease-ipc/messages.js.map +1 -0
  64. package/dist/lease-ipc/version.d.ts +41 -0
  65. package/dist/lease-ipc/version.d.ts.map +1 -0
  66. package/dist/lease-ipc/version.js +43 -0
  67. package/dist/lease-ipc/version.js.map +1 -0
  68. package/dist/llm/catalog-schema.d.ts +242 -0
  69. package/dist/llm/catalog-schema.d.ts.map +1 -0
  70. package/dist/llm/catalog-schema.js +153 -0
  71. package/dist/llm/catalog-schema.js.map +1 -0
  72. package/dist/llm/catalog-schema.json +139 -0
  73. package/dist/llm/index.d.ts +15 -0
  74. package/dist/llm/index.d.ts.map +1 -0
  75. package/dist/llm/index.js +14 -0
  76. package/dist/llm/index.js.map +1 -0
  77. package/dist/llm/interfaces.d.ts +23 -0
  78. package/dist/llm/interfaces.d.ts.map +1 -1
  79. package/dist/llm/oauth.d.ts +16 -2
  80. package/dist/llm/oauth.d.ts.map +1 -1
  81. package/dist/llm/onboarding.d.ts +126 -0
  82. package/dist/llm/onboarding.d.ts.map +1 -0
  83. package/dist/llm/onboarding.js +34 -0
  84. package/dist/llm/onboarding.js.map +1 -0
  85. package/dist/llm/provider-def.d.ts +254 -0
  86. package/dist/llm/provider-def.d.ts.map +1 -0
  87. package/dist/llm/provider-def.js +93 -0
  88. package/dist/llm/provider-def.js.map +1 -0
  89. package/dist/llm/sealed-credential.d.ts +164 -0
  90. package/dist/llm/sealed-credential.d.ts.map +1 -0
  91. package/dist/llm/sealed-credential.js +43 -0
  92. package/dist/llm/sealed-credential.js.map +1 -0
  93. package/dist/llm/system-of-use.d.ts +383 -0
  94. package/dist/llm/system-of-use.d.ts.map +1 -0
  95. package/dist/llm/system-of-use.js +238 -0
  96. package/dist/llm/system-of-use.js.map +1 -0
  97. package/dist/llm/system-resolver.d.ts +87 -11
  98. package/dist/llm/system-resolver.d.ts.map +1 -1
  99. package/dist/llm/system-resolver.js +14 -7
  100. package/dist/llm/system-resolver.js.map +1 -1
  101. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts +17 -0
  102. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts.map +1 -0
  103. package/dist/operations/__tests__/ensures-schema-registry.test.js +113 -0
  104. package/dist/operations/__tests__/ensures-schema-registry.test.js.map +1 -0
  105. package/dist/operations/admin.d.ts +124 -1
  106. package/dist/operations/admin.d.ts.map +1 -1
  107. package/dist/operations/ensures-schema-registry.d.ts +172 -0
  108. package/dist/operations/ensures-schema-registry.d.ts.map +1 -0
  109. package/dist/operations/ensures-schema-registry.js +226 -0
  110. package/dist/operations/ensures-schema-registry.js.map +1 -0
  111. package/dist/operations/entities.d.ts +110 -0
  112. package/dist/operations/entities.d.ts.map +1 -0
  113. package/dist/operations/entities.js +695 -0
  114. package/dist/operations/entities.js.map +1 -0
  115. package/dist/operations/llm.d.ts +116 -9
  116. package/dist/operations/llm.d.ts.map +1 -1
  117. package/dist/operations/output-contracts-data.d.ts.map +1 -1
  118. package/dist/operations/output-contracts-data.js +230 -0
  119. package/dist/operations/output-contracts-data.js.map +1 -1
  120. package/dist/operations/service.d.ts +68 -0
  121. package/dist/operations/service.d.ts.map +1 -0
  122. package/dist/operations/service.js +336 -0
  123. package/dist/operations/service.js.map +1 -0
  124. package/dist/operations/tasks.d.ts +109 -0
  125. package/dist/operations/tasks.d.ts.map +1 -1
  126. package/dist/operations/tasks.js.map +1 -1
  127. package/dist/operations/validate.d.ts +1 -12
  128. package/dist/operations/validate.d.ts.map +1 -1
  129. package/dist/operations/validate.js +1 -1
  130. package/dist/playbook.d.ts +116 -0
  131. package/dist/playbook.d.ts.map +1 -1
  132. package/dist/release/plan.d.ts +5 -5
  133. package/dist/session.d.ts +11 -0
  134. package/dist/session.d.ts.map +1 -1
  135. package/dist/session.js.map +1 -1
  136. package/dist/templates/manifest.d.ts +1 -1
  137. package/dist/tools/agent-tools.d.ts +231 -0
  138. package/dist/tools/agent-tools.d.ts.map +1 -0
  139. package/dist/tools/agent-tools.js +25 -0
  140. package/dist/tools/agent-tools.js.map +1 -0
  141. package/dist/tools/atomic.d.ts +19 -0
  142. package/dist/tools/atomic.d.ts.map +1 -1
  143. package/dist/tools/atomic.js +17 -0
  144. package/dist/tools/atomic.js.map +1 -1
  145. package/dist/tools/index.d.ts +2 -0
  146. package/dist/tools/index.d.ts.map +1 -1
  147. package/dist/tools/skill-executor.d.ts +7 -0
  148. package/dist/tools/skill-executor.d.ts.map +1 -1
  149. package/dist/tools/web-tools.d.ts +217 -0
  150. package/dist/tools/web-tools.d.ts.map +1 -0
  151. package/dist/tools/web-tools.js +31 -0
  152. package/dist/tools/web-tools.js.map +1 -0
  153. package/dist/vault/service-provider.d.ts +343 -0
  154. package/dist/vault/service-provider.d.ts.map +1 -0
  155. package/dist/vault/service-provider.js +857 -0
  156. package/dist/vault/service-provider.js.map +1 -0
  157. package/dist/workgraph.d.ts +12 -12
  158. package/package.json +14 -2
  159. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  160. package/src/__tests__/operation-def.test.ts +7 -1
  161. package/src/__tests__/provider-def.test.ts +93 -0
  162. package/src/__tests__/sealed-credential.test.ts +100 -0
  163. package/src/__tests__/service-provider.test.ts +129 -0
  164. package/src/__tests__/skill-executor-contract.test.ts +3 -0
  165. package/src/acceptance-gate.ts +2 -3
  166. package/src/boundary.ts +70 -20
  167. package/src/conduit.ts +200 -0
  168. package/src/config/index.ts +35 -0
  169. package/src/config.ts +106 -1
  170. package/src/data-accessor.ts +22 -1
  171. package/src/dispatch/__tests__/tasks-gateway-crud-ops.test.ts +99 -0
  172. package/src/dispatch/identity.ts +25 -0
  173. package/src/dispatch/operation-def.ts +21 -0
  174. package/src/dispatch/operations-registry.ts +1083 -176
  175. package/src/index.ts +240 -25
  176. package/src/lease-ipc/__tests__/freeze.test.ts +342 -0
  177. package/src/lease-ipc/index.ts +75 -0
  178. package/src/lease-ipc/messages.ts +588 -0
  179. package/src/lease-ipc/version.ts +46 -0
  180. package/src/llm/catalog-schema.json +139 -0
  181. package/src/llm/catalog-schema.ts +184 -0
  182. package/src/llm/index.ts +29 -0
  183. package/src/llm/interfaces.ts +23 -0
  184. package/src/llm/oauth.ts +16 -2
  185. package/src/llm/onboarding.ts +131 -0
  186. package/src/llm/provider-def.ts +291 -0
  187. package/src/llm/sealed-credential.ts +166 -0
  188. package/src/llm/system-of-use.ts +503 -0
  189. package/src/llm/system-resolver.ts +91 -11
  190. package/src/operations/__tests__/ensures-schema-registry.test.ts +169 -0
  191. package/src/operations/admin.ts +144 -0
  192. package/src/operations/ensures-schema-registry.ts +260 -0
  193. package/src/operations/entities.ts +758 -0
  194. package/src/operations/llm.ts +120 -8
  195. package/src/operations/output-contracts-data.ts +264 -0
  196. package/src/operations/service.ts +360 -0
  197. package/src/operations/tasks.ts +119 -0
  198. package/src/operations/validate.ts +1 -14
  199. package/src/playbook.ts +117 -0
  200. package/src/session.ts +11 -0
  201. package/src/tools/agent-tools.ts +270 -0
  202. package/src/tools/atomic.ts +21 -0
  203. package/src/tools/index.ts +46 -0
  204. package/src/tools/skill-executor.ts +7 -0
  205. package/src/tools/web-tools.ts +247 -0
  206. package/src/vault/service-provider.ts +1137 -0
  207. package/dist/tessera.d.ts +0 -32
  208. package/dist/tessera.d.ts.map +0 -1
  209. package/dist/tessera.js +0 -11
  210. package/dist/tessera.js.map +0 -1
  211. package/dist/warp-chain.d.ts +0 -129
  212. package/dist/warp-chain.d.ts.map +0 -1
  213. package/dist/warp-chain.js +0 -13
  214. package/dist/warp-chain.js.map +0 -1
  215. package/src/tessera.ts +0 -35
  216. package/src/warp-chain.ts +0 -185
package/src/conduit.ts CHANGED
@@ -264,6 +264,206 @@ export interface Conduit {
264
264
  readonly agentId: string;
265
265
  }
266
266
 
267
+ // ============================================================================
268
+ // Channel Adapter (T11952 · epic T11854 · saga T10419)
269
+ // ============================================================================
270
+ //
271
+ // A ChannelAdapter is the boundary between an external conversation surface
272
+ // (a terminal TUI, Telegram chat, Discord channel, …) and the CLEO agent
273
+ // runtime. Each adapter wraps a {@link Transport} (the wire layer) and exposes
274
+ // a uniform, platform-agnostic contract so the Session Store + Delivery Router
275
+ // can route inbound user messages to the agent and route the agent's reply back
276
+ // to the originating channel.
277
+ //
278
+ // The Local-TUI adapter is the FIRST reference adapter (T11952): it rides the
279
+ // in-process {@link Transport} (conduit `LocalTransport`) and works fully
280
+ // offline, daemon-OFF, with no network and no platform credential. Platform
281
+ // adapters (Telegram/Discord/Slack/WhatsApp) implement this same contract but
282
+ // are credential+network gated and out of scope for T11952.
283
+
284
+ /**
285
+ * Liveness verdict for a single channel adapter.
286
+ *
287
+ * - `connected` — the adapter is connected to its transport and ready to
288
+ * receive/send.
289
+ * - `degraded` — the adapter is reachable but operating with reduced capability
290
+ * (e.g. polling fallback, partial subscription).
291
+ * - `disconnected` — the adapter is not currently connected.
292
+ * - `error` — the adapter encountered a fatal error; see {@link ChannelHealth.detail}.
293
+ */
294
+ export type ChannelHealthStatus = 'connected' | 'degraded' | 'disconnected' | 'error';
295
+
296
+ /**
297
+ * Health snapshot for a single {@link ChannelAdapter}.
298
+ *
299
+ * Surfaced by `cleo channels list` as part of the LAFS RenderableEnvelope and
300
+ * consumed by the Delivery Router to skip dead channels.
301
+ */
302
+ export interface ChannelHealth {
303
+ /** Stable channel identifier (matches {@link ChannelAdapter.id}). */
304
+ channelId: string;
305
+ /** Coarse liveness verdict. */
306
+ status: ChannelHealthStatus;
307
+ /** Underlying transport name (e.g. `'local'`, `'http'`, `'sse'`). */
308
+ transport: string;
309
+ /** Human-readable detail for non-`connected` states (error message, reason). */
310
+ detail?: string;
311
+ /** ISO 8601 timestamp the health was sampled. */
312
+ checkedAt: string;
313
+ }
314
+
315
+ /**
316
+ * Per-channel configuration knobs shared by every {@link ChannelAdapter}.
317
+ *
318
+ * Platform adapters extend this with credential fields; the Local-TUI adapter
319
+ * uses only the platform-agnostic subset.
320
+ *
321
+ * @see T11854 AC1 — `home_channel`, `require_mention`, `allowed_users`.
322
+ */
323
+ export interface ChannelConfig {
324
+ /**
325
+ * The home channel address replies fall back to when an inbound message has
326
+ * no explicit reply target (e.g. the default conversation/thread id).
327
+ */
328
+ homeChannel?: string;
329
+ /**
330
+ * When `true`, the adapter only dispatches inbound messages that explicitly
331
+ * mention the agent. Defaults to `false` for the Local-TUI adapter (every
332
+ * line is addressed to the agent).
333
+ */
334
+ requireMention?: boolean;
335
+ /**
336
+ * Allowlist of sender identifiers permitted to reach the agent. When omitted
337
+ * or empty, all senders are allowed (the Local-TUI default — a single local
338
+ * operator).
339
+ */
340
+ allowedUsers?: readonly string[];
341
+ }
342
+
343
+ /**
344
+ * An inbound message arriving from a channel surface, normalized into the
345
+ * platform-agnostic shape the Delivery Router consumes.
346
+ *
347
+ * It is a thin projection over {@link ConduitMessage} carrying the originating
348
+ * channel so the router can preserve session affinity.
349
+ */
350
+ export interface InboundMsg {
351
+ /** Stable message id (from the underlying transport). */
352
+ id: string;
353
+ /** Channel the message originated on (the {@link ChannelAdapter.id}). */
354
+ channelId: string;
355
+ /** Sender identifier within the channel (user/agent id). */
356
+ from: string;
357
+ /** Message text content. */
358
+ content: string;
359
+ /**
360
+ * Channel-native conversation/thread id used for session affinity. The router
361
+ * keys the {@link ChannelSession} on `(channelId, sessionKey)`.
362
+ */
363
+ sessionKey: string;
364
+ /** ISO 8601 timestamp the message was received. */
365
+ timestamp: string;
366
+ /** Optional structured payload carried alongside the message. */
367
+ payload?: Record<string, unknown>;
368
+ }
369
+
370
+ /**
371
+ * An outbound reply the agent emits in response to an {@link InboundMsg}.
372
+ *
373
+ * The Delivery Router resolves the target channel + session from the originating
374
+ * {@link InboundMsg} (or an explicit {@link OutboundReply.channelId}) and calls
375
+ * {@link ChannelAdapter.send}.
376
+ */
377
+ export interface OutboundReply {
378
+ /**
379
+ * Target channel id. When omitted, the router routes to the channel that
380
+ * originated the {@link InboundMsg} being replied to (session affinity).
381
+ */
382
+ channelId?: string;
383
+ /**
384
+ * Target session/thread key. When omitted, the router uses the originating
385
+ * inbound message's {@link InboundMsg.sessionKey}, then the channel's
386
+ * {@link ChannelConfig.homeChannel} fallback.
387
+ */
388
+ sessionKey?: string;
389
+ /** Reply text content. */
390
+ content: string;
391
+ /** Optional structured payload carried with the reply. */
392
+ payload?: Record<string, unknown>;
393
+ }
394
+
395
+ /**
396
+ * A persisted per-channel session-affinity record.
397
+ *
398
+ * The Session Store tracks the most recent inbound exchange per
399
+ * `(channelId, sessionKey)` so the Delivery Router can route an agent reply
400
+ * back to the originating channel without the caller threading routing state.
401
+ *
402
+ * @see T1797 — Session Store + Delivery Router.
403
+ */
404
+ export interface ChannelSession {
405
+ /** Channel the session belongs to. */
406
+ channelId: string;
407
+ /** Channel-native conversation/thread key. */
408
+ sessionKey: string;
409
+ /** Sender identifier of the last inbound message in this session. */
410
+ lastFrom: string;
411
+ /** Id of the last inbound message routed for this session. */
412
+ lastInboundId: string;
413
+ /** ISO 8601 timestamp the session was last touched. */
414
+ updatedAt: string;
415
+ }
416
+
417
+ /**
418
+ * The platform-agnostic channel boundary contract.
419
+ *
420
+ * Every channel (Local-TUI, Telegram, Discord, Slack, WhatsApp, …) implements
421
+ * this interface. Adapters wrap a {@link Transport} and translate between the
422
+ * channel-native surface and the normalized {@link InboundMsg} /
423
+ * {@link OutboundReply} shapes the router consumes.
424
+ *
425
+ * Implementations:
426
+ * - `LocalTuiChannelAdapter` (`@cleocode/core` — rides conduit `LocalTransport`,
427
+ * the first reference adapter, T11952).
428
+ * - Platform adapters (Telegram/Discord/Slack/WhatsApp) — credential+network
429
+ * gated, implemented under T11855 (out of scope for T11952).
430
+ *
431
+ * @see T11854 AC1 — start/stop/onMessage/sendMessage/health.
432
+ */
433
+ export interface ChannelAdapter {
434
+ /** Stable, kebab-case channel identifier (e.g. `'local-tui'`). */
435
+ readonly id: string;
436
+ /** Effective per-channel configuration. */
437
+ readonly config: ChannelConfig;
438
+
439
+ /** Start the adapter: connect its transport and begin receiving. */
440
+ start(): Promise<void>;
441
+
442
+ /** Stop the adapter: detach handlers and disconnect its transport. */
443
+ stop(): Promise<void>;
444
+
445
+ /**
446
+ * Stream of inbound messages from the channel.
447
+ *
448
+ * Yields each {@link InboundMsg} as it arrives until {@link ChannelAdapter.stop}
449
+ * is called (which ends the iterator). Honors {@link ChannelConfig.requireMention}
450
+ * and {@link ChannelConfig.allowedUsers} filtering before yielding.
451
+ */
452
+ receive(): AsyncIterable<InboundMsg>;
453
+
454
+ /**
455
+ * Send a reply back to the channel.
456
+ *
457
+ * @param reply - The outbound reply. The adapter resolves the destination
458
+ * session via {@link OutboundReply.sessionKey} then
459
+ * {@link ChannelConfig.homeChannel}.
460
+ */
461
+ send(reply: OutboundReply): Promise<void>;
462
+
463
+ /** Sample the adapter's current health. */
464
+ health(): ChannelHealth;
465
+ }
466
+
267
467
  // ============================================================================
268
468
  // Factory
269
469
  // ============================================================================
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `@cleocode/contracts/config` — config-domain contract surface.
3
+ *
4
+ * Subpath barrel that surfaces the `ConfigManifest` contract (the
5
+ * {@link ConfigManifestEntry} shape, its Zod validator
6
+ * {@link configManifestEntrySchema}, and the built-in manifest entries) under a
7
+ * stable, config-scoped import path. This is the AC3 surface for the
8
+ * config-as-domain work (T11917): consumers that only need the manifest contract
9
+ * import from `@cleocode/contracts/config` instead of pulling the full index.
10
+ *
11
+ * The types are also re-exported from the package root (`@cleocode/contracts`)
12
+ * for backwards compatibility — this barrel is an additive, narrower entrypoint.
13
+ *
14
+ * @packageDocumentation
15
+ * @module @cleocode/contracts/config
16
+ *
17
+ * @task T11917 — config-as-domain (M5/AC3)
18
+ * @task T9876 — ConfigManifest contract
19
+ * @saga T9855
20
+ */
21
+
22
+ export type {
23
+ ConfigManifestEntry,
24
+ ConfigManifestScope,
25
+ ConfigScope,
26
+ DriftDetection,
27
+ } from './manifest.js';
28
+ export {
29
+ CLEO_CONFIG_MANIFEST,
30
+ CONFIG_MANIFEST_ENTRIES,
31
+ configManifestEntrySchema,
32
+ GLOBAL_CLEO_CONFIG_MANIFEST,
33
+ PROJECT_CONTEXT_MANIFEST,
34
+ PROJECT_INFO_MANIFEST,
35
+ } from './manifest.js';
package/src/config.ts CHANGED
@@ -475,6 +475,29 @@ export interface LlmProfileTuning {
475
475
  temperature?: number;
476
476
  }
477
477
 
478
+ /**
479
+ * Capability lane a named profile occupies — the hermes "global-base +
480
+ * granular-override" tiering model (E9 · T11745 · T11748).
481
+ *
482
+ * A profile's `tier` is a coarse capability hint a runner can use to pick the
483
+ * right lane for a turn independently of the role/system axis:
484
+ *
485
+ * | Tier | Meaning |
486
+ * |--------------|------------------------------------------------------------------|
487
+ * | `prime` | The flagship reasoning lane — orchestration / hard turns. |
488
+ * | `aux` | The auxiliary lane — cheap background subsystems (the hermes |
489
+ * | | `_AUX_TASKS` default model bucket; see {@link LlmProfileConfig.defaultAuxModel}). |
490
+ * | `background` | The lowest lane — fire-and-forget background work (titles, etc.). |
491
+ *
492
+ * Advisory metadata only: it does NOT alter the provider/model resolution
493
+ * chain in {@link LlmConfig} — it lets callers and diagnostics reason about
494
+ * which lane a profile belongs to without re-deriving it from the model id.
495
+ *
496
+ * @task T11748
497
+ * @epic T11745
498
+ */
499
+ export type LlmProfileTier = 'prime' | 'aux' | 'background';
500
+
478
501
  /**
479
502
  * A named, reusable LLM profile.
480
503
  *
@@ -503,6 +526,67 @@ export interface LlmProfileConfig {
503
526
  credentialLabel?: string;
504
527
  /** Optional tuning parameters (max tokens, temperature). */
505
528
  params?: LlmProfileTuning;
529
+ /**
530
+ * Optional capability lane this profile occupies (see {@link LlmProfileTier}).
531
+ *
532
+ * Advisory metadata only — does NOT participate in provider/model resolution.
533
+ * Lets callers/diagnostics reason about a profile's lane (prime/aux/background)
534
+ * without re-deriving it from the model id. The hermes tiering model.
535
+ *
536
+ * @task T11748
537
+ */
538
+ tier?: LlmProfileTier;
539
+ /**
540
+ * Optional default auxiliary model for this profile — the hermes
541
+ * `auxiliary.defaultModel` analog (E9 · T11748).
542
+ *
543
+ * When this profile is a `prime`/global-base binding, `defaultAuxModel` names
544
+ * the cheaper model an aux-tier subsystem should use instead of {@link model}
545
+ * when it has no more-specific binding. Advisory metadata; consumers that want
546
+ * an aux-tier model read this rather than re-selecting a profile.
547
+ *
548
+ * @task T11748
549
+ */
550
+ defaultAuxModel?: string;
551
+ }
552
+
553
+ /**
554
+ * A per-system LLM binding — the granular override half of the hermes
555
+ * "global-base + granular-override" model (E9 · T11745 · T11748).
556
+ *
557
+ * A {@link LlmConfig.systems} entry pins the provider/model (or a named
558
+ * {@link LlmProfileConfig}) for ONE encoded system-of-use key (e.g.
559
+ * `'sentient'`, `'tool:web-search'`, `'skill:ct-cleo'`). It is consulted in the
560
+ * resolution chain AFTER an explicit role/argument override but BEFORE the
561
+ * global {@link LlmConfig.defaultProfile} — giving each subsystem a targeted
562
+ * override without touching the global base.
563
+ *
564
+ * Either `profile` (a key of {@link LlmConfig.profiles}) OR an inline
565
+ * `provider` + `model` tuple supplies the binding; `profile` wins when both are
566
+ * present. An entry that supplies neither a resolvable `profile` nor a complete
567
+ * `provider`+`model` tuple is structurally incomplete and is skipped (resolution
568
+ * falls through to the next tier) — never an error.
569
+ *
570
+ * @task T11748
571
+ * @epic T11745
572
+ */
573
+ export interface SystemBinding {
574
+ /**
575
+ * Optional reference to a named profile in {@link LlmConfig.profiles}. When
576
+ * set and resolvable, the named profile's provider/model/credentialLabel win
577
+ * over this entry's inline `provider`/`model`.
578
+ */
579
+ profile?: string;
580
+ /** Inline LLM provider transport for this system (used when `profile` absent). */
581
+ provider?: LlmProviderTransport;
582
+ /** Inline full model identifier for this system (used when `profile` absent). */
583
+ model?: string;
584
+ /**
585
+ * Optional credential label pinning this system to a specific credential pool
586
+ * entry. When omitted, the standard priority-based credential resolution
587
+ * applies (or the resolved profile's `credentialLabel`).
588
+ */
589
+ credentialLabel?: string;
506
590
  }
507
591
 
508
592
  /**
@@ -554,11 +638,32 @@ export interface LlmConfig {
554
638
  * role that does NOT pin its own provider/model/profile. This is the
555
639
  * user-selectable default binding for background roles — it is NEVER a
556
640
  * hardcoded provider in code; the owner re-selects it with
557
- * `cleo llm bind --default <profileName>`.
641
+ * `cleo config set llm.defaultProfile <profileName>`.
558
642
  *
559
643
  * @task T11617
644
+ * @task T11723
560
645
  */
561
646
  defaultProfile?: string;
647
+ /**
648
+ * Per-system-of-use LLM overrides — the granular-override half of the hermes
649
+ * "global-base + granular-override" model (E9 · T11745 · T11748).
650
+ *
651
+ * Keyed by an encoded system-of-use key: a flat role-mapped label
652
+ * (`'sentient'`, `'memory'`, …) or an open-axis key
653
+ * (`'tool:<name>'` / `'skill:<name>'` / `'cantbook:<node>'` /
654
+ * `'spawn-unit:<id>'`). Each value is a {@link SystemBinding}.
655
+ *
656
+ * Resolution priority for a system-of-use call:
657
+ * explicit-arg (role override) → `systems[key]` → `defaultProfile` →
658
+ * implicit fallback.
659
+ *
660
+ * `systems[key]` is consulted AFTER any explicit role/argument override but
661
+ * BEFORE the global {@link defaultProfile}, so a subsystem can be pointed at a
662
+ * targeted model without disturbing the global base.
663
+ *
664
+ * @task T11748
665
+ */
666
+ systems?: Record<string, SystemBinding>;
562
667
  }
563
668
 
564
669
  /** SignalDock transport mode. */
@@ -468,9 +468,30 @@ export interface DataAccessor {
468
468
 
469
469
  // ---- Fine-grained session operations ----
470
470
 
471
- /** Get the currently active session (status='active', most recent). */
471
+ /**
472
+ * Get the currently active session (status='active', most recent).
473
+ *
474
+ * SCAN-meaning: answers "is there any active session?" — NOT "who am I".
475
+ * Identity-meaning callers MUST use {@link DataAccessor.resolveCurrentSession}
476
+ * (T11640), which resolves the caller's OWN session via the
477
+ * connection-handle → `CLEO_SESSION_ID` → most-recent-active precedence.
478
+ */
472
479
  getActiveSession(): Promise<Session | null>;
473
480
 
481
+ /**
482
+ * Resolve the CALLER's current session (T11640 · Epic T11638).
483
+ *
484
+ * Identity-meaning resolution for accessor-based consumers, mirroring the
485
+ * standalone `resolveCurrentSession` in `@cleocode/core`. Precedence:
486
+ * 1. daemon connection handle (the connection bound at accept-time),
487
+ * 2. env-named session (`CLEO_SESSION_ID`),
488
+ * 3. most-recent-active row (legacy single-process fallback).
489
+ *
490
+ * Use this — NOT {@link DataAccessor.getActiveSession} — anywhere the meaning
491
+ * is "the session of whoever issued THIS request".
492
+ */
493
+ resolveCurrentSession(): Promise<Session | null>;
494
+
474
495
  /** Upsert a single session (targeted write). */
475
496
  upsertSingleSession(session: Session): Promise<void>;
476
497
 
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Registry regression tests for the E1-GATEWAY-CRUD epic (T11556).
3
+ *
4
+ * - T11784: `tasks.update` OperationDef.params enumerates EVERY mutable field
5
+ * declared in `TASKS_UPDATE_INPUT_SCHEMA`, and carries the inline
6
+ * input/output schemas — so dispatch introspection (`--describe`, the SDK
7
+ * `describeOperation`) no longer hides the real update surface behind a
8
+ * `taskId`-only param list.
9
+ * - T11786: the three bulk task mutate ops (`reorder-rank`, `bulk-move`,
10
+ * `assignee`) are registered as `mutate` ops with the expected required
11
+ * params and inline output schemas.
12
+ * - T11785: the first streaming task op (`tasks.subscribe`) is registered as a
13
+ * `query` op flagged `streaming: true`.
14
+ *
15
+ * @task T11784
16
+ * @task T11785
17
+ * @task T11786
18
+ * @epic T11556
19
+ */
20
+
21
+ import { describe, expect, it } from 'vitest';
22
+ import { TASKS_UPDATE_INPUT_SCHEMA } from '../../operations/tasks.js';
23
+ import { OPERATIONS } from '../operations-registry.js';
24
+
25
+ function findOp(gateway: 'query' | 'mutate', operation: string) {
26
+ return OPERATIONS.find(
27
+ (o) => o.domain === 'tasks' && o.gateway === gateway && o.operation === operation,
28
+ );
29
+ }
30
+
31
+ describe('T11784 — tasks.update OperationDef params parity', () => {
32
+ const update = findOp('mutate', 'update');
33
+
34
+ it('registers tasks.update as a mutate op', () => {
35
+ expect(update).toBeDefined();
36
+ });
37
+
38
+ it('enumerates every mutable field from TASKS_UPDATE_INPUT_SCHEMA', () => {
39
+ const schemaFields = Object.keys(
40
+ (TASKS_UPDATE_INPUT_SCHEMA.properties ?? {}) as Record<string, unknown>,
41
+ );
42
+ const paramNames = new Set((update?.params ?? []).map((p) => p.name));
43
+ // Every schema property must be a declared param (no silent-dropped fields).
44
+ for (const field of schemaFields) {
45
+ expect(paramNames.has(field), `tasks.update param "${field}" missing from OperationDef`).toBe(
46
+ true,
47
+ );
48
+ }
49
+ // taskId is the only required param.
50
+ expect(update?.requiredParams).toEqual(['taskId']);
51
+ expect((update?.params ?? []).filter((p) => p.required).map((p) => p.name)).toEqual(['taskId']);
52
+ });
53
+
54
+ it('carries the inline input + output schemas', () => {
55
+ expect(update?.inputSchema?.operation).toBe('tasks.update');
56
+ expect(update?.outputSchema?.operation).toBe('tasks.update');
57
+ // The output contract points at the bare-string updated-id pointer.
58
+ expect(update?.outputSchema?.fieldPointers).toContain('/data/updated/0');
59
+ });
60
+ });
61
+
62
+ describe('T11786 — bulk task mutate ops', () => {
63
+ it('registers tasks.reorder-rank with orderedIds required + output schema', () => {
64
+ const op = findOp('mutate', 'reorder-rank');
65
+ expect(op).toBeDefined();
66
+ expect(op?.requiredParams).toEqual(['orderedIds']);
67
+ expect(op?.outputSchema?.operation).toBe('tasks.reorder-rank');
68
+ expect(op?.outputSchema?.fieldPointers).toContain('/data/ranked/0');
69
+ });
70
+
71
+ it('registers tasks.bulk-move with taskIds required + output schema', () => {
72
+ const op = findOp('mutate', 'bulk-move');
73
+ expect(op).toBeDefined();
74
+ expect(op?.requiredParams).toEqual(['taskIds']);
75
+ expect(op?.outputSchema?.operation).toBe('tasks.bulk-move');
76
+ expect(op?.outputSchema?.fieldPointers).toContain('/data/moved/0');
77
+ });
78
+
79
+ it('registers tasks.assignee with taskId required + output schema', () => {
80
+ const op = findOp('mutate', 'assignee');
81
+ expect(op).toBeDefined();
82
+ expect(op?.requiredParams).toEqual(['taskId']);
83
+ expect(op?.outputSchema?.operation).toBe('tasks.assignee');
84
+ expect(op?.outputSchema?.fieldPointers).toContain('/data/assignee');
85
+ });
86
+ });
87
+
88
+ describe('T11785 — streaming task op', () => {
89
+ it('registers tasks.subscribe as a query op flagged streaming', () => {
90
+ const op = findOp('query', 'subscribe');
91
+ expect(op).toBeDefined();
92
+ expect(op?.streaming).toBe(true);
93
+ // No required params — subscribe is open by default (optional root/ticks scope).
94
+ expect(op?.requiredParams).toEqual([]);
95
+ const paramNames = (op?.params ?? []).map((p) => p.name);
96
+ expect(paramNames).toContain('root');
97
+ expect(paramNames).toContain('ticks');
98
+ });
99
+ });
@@ -99,6 +99,31 @@ export const CANONICAL_DOMAINS = [
99
99
  // T11373: 'cleo attention add|show|list' (alias 'jot') — Tier-2 scope-keyed
100
100
  // working-memory buffer (Epic T11288 · Saga T11283).
101
101
  'attention',
102
+ // T11889 (T11889-D): 'cleo selfimprove run' — the self-dogfooding loop verb.
103
+ // Boots ONE sandbox → replays a canned scenario → diffs vs golden → on a
104
+ // regression emits ONE leased `selfimprove_dhq` row + ONE DRAFT PR. Default
105
+ // OFF (requires --execute); the engine lives in CORE (P5 self-improvement spec).
106
+ 'selfimprove',
107
+ // T11939 (epic T11765 · M2): 'cleo service auth-url|exchange|refresh|self-heal'
108
+ // — the universal service-vault OAuth flow. auth-url builds a PKCE auth URL;
109
+ // exchange persists encrypted tokens; refresh/self-heal renew them honoring the
110
+ // per-provider RefreshConfig. The flow lives in CORE (store/service-oauth.ts),
111
+ // driven by the declarative SERVICE_PROVIDERS registry (contracts).
112
+ 'service',
113
+ // T11700 (epic T11666 · M3 · North-Star §2): the 5-entity provider experience —
114
+ // the addressable Provider / Alias / Account / Model / Profile surface.
115
+ // 'account' — 'cleo account add|list|remove' (the credential pool; add is
116
+ // secret-bearing/cli-only, MCP-default-denied).
117
+ // 'provider' — 'cleo provider list|show|connect' (the declarative providers
118
+ // table #1039 + alias resolution; connect is secret-bearing).
119
+ // 'model' — 'cleo model query|show' (the models_catalog table #1037; QUERY-ONLY).
120
+ // 'profile' — 'cleo profile create|list|pin|use' (the named account+model
121
+ // binding persisted into llm.profiles[name], the resolver SSoT).
122
+ // Each dispatches to the CORE entity-ops layer (llm/entity-ops.ts).
123
+ 'account',
124
+ 'provider',
125
+ 'model',
126
+ 'profile',
102
127
  ] as const;
103
128
 
104
129
  /**
@@ -95,6 +95,27 @@ export interface OperationDef {
95
95
  */
96
96
  mcpExposed?: boolean;
97
97
 
98
+ /**
99
+ * Whether this operation is a **streaming** operation served over the gateway
100
+ * HTTP/SSE transport as `GET /v1/<domain>/<operation>` (T11921).
101
+ *
102
+ * **Default-deny**: absent / `false` means the operation is unary only — it is
103
+ * dispatched via the POST path and returns a single LAFS envelope. When
104
+ * `streaming: true`, the `/v1` HTTP listener routes a `GET` to the same
105
+ * `(domain, operation)` pair through {@link createSseStream}, emitting a
106
+ * sequence of `GatewayStreamEvent` frames (`data` … terminated by `done` or
107
+ * `error`) over a `text/event-stream` body. This is the realtime transport the
108
+ * Studio Kanban (T11929) and Pi-TUI (T11936) consume.
109
+ *
110
+ * A streaming op is still a normal registry entry (it has a `gateway`, a
111
+ * `domain`, an `operation`, a `tier`), so it co-exists with the unary POST
112
+ * routes and is discoverable by every existing registry helper — only the wire
113
+ * framing (a long-lived SSE body vs a single JSON response) differs.
114
+ *
115
+ * @task T11921 — gateway SSE streaming on `/v1`
116
+ */
117
+ streaming?: boolean;
118
+
98
119
  /**
99
120
  * Schema-first INPUT contract for this operation (DHQ-033 / T11692).
100
121
  *