@cleocode/contracts 2026.6.12 → 2026.6.13

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 (136) hide show
  1. package/dist/__tests__/operation-def.test.js +2 -1
  2. package/dist/__tests__/operation-def.test.js.map +1 -1
  3. package/dist/__tests__/sealed-credential.test.d.ts +19 -0
  4. package/dist/__tests__/sealed-credential.test.d.ts.map +1 -0
  5. package/dist/__tests__/sealed-credential.test.js +74 -0
  6. package/dist/__tests__/sealed-credential.test.js.map +1 -0
  7. package/dist/__tests__/skill-executor-contract.test.js +3 -0
  8. package/dist/__tests__/skill-executor-contract.test.js.map +1 -1
  9. package/dist/acceptance-gate.d.ts +2 -3
  10. package/dist/acceptance-gate.d.ts.map +1 -1
  11. package/dist/boundary.d.ts.map +1 -1
  12. package/dist/boundary.js +1 -19
  13. package/dist/boundary.js.map +1 -1
  14. package/dist/config.d.ts +102 -0
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/data-accessor.d.ts +21 -1
  17. package/dist/data-accessor.d.ts.map +1 -1
  18. package/dist/data-accessor.js.map +1 -1
  19. package/dist/dispatch/identity.d.ts +1 -1
  20. package/dist/dispatch/identity.d.ts.map +1 -1
  21. package/dist/dispatch/identity.js +5 -0
  22. package/dist/dispatch/identity.js.map +1 -1
  23. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  24. package/dist/dispatch/operations-registry.js +46 -168
  25. package/dist/dispatch/operations-registry.js.map +1 -1
  26. package/dist/docs/read.d.ts +2 -2
  27. package/dist/index.d.ts +12 -7
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/lease-ipc/__tests__/freeze.test.d.ts +15 -0
  32. package/dist/lease-ipc/__tests__/freeze.test.d.ts.map +1 -0
  33. package/dist/lease-ipc/__tests__/freeze.test.js +221 -0
  34. package/dist/lease-ipc/__tests__/freeze.test.js.map +1 -0
  35. package/dist/lease-ipc/index.d.ts +17 -0
  36. package/dist/lease-ipc/index.d.ts.map +1 -0
  37. package/dist/lease-ipc/index.js +16 -0
  38. package/dist/lease-ipc/index.js.map +1 -0
  39. package/dist/lease-ipc/messages.d.ts +754 -0
  40. package/dist/lease-ipc/messages.d.ts.map +1 -0
  41. package/dist/lease-ipc/messages.js +383 -0
  42. package/dist/lease-ipc/messages.js.map +1 -0
  43. package/dist/lease-ipc/version.d.ts +41 -0
  44. package/dist/lease-ipc/version.d.ts.map +1 -0
  45. package/dist/lease-ipc/version.js +43 -0
  46. package/dist/lease-ipc/version.js.map +1 -0
  47. package/dist/llm/interfaces.d.ts +23 -0
  48. package/dist/llm/interfaces.d.ts.map +1 -1
  49. package/dist/llm/sealed-credential.d.ts +164 -0
  50. package/dist/llm/sealed-credential.d.ts.map +1 -0
  51. package/dist/llm/sealed-credential.js +43 -0
  52. package/dist/llm/sealed-credential.js.map +1 -0
  53. package/dist/llm/system-of-use.d.ts +383 -0
  54. package/dist/llm/system-of-use.d.ts.map +1 -0
  55. package/dist/llm/system-of-use.js +238 -0
  56. package/dist/llm/system-of-use.js.map +1 -0
  57. package/dist/llm/system-resolver.d.ts +45 -11
  58. package/dist/llm/system-resolver.d.ts.map +1 -1
  59. package/dist/llm/system-resolver.js +14 -7
  60. package/dist/llm/system-resolver.js.map +1 -1
  61. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts +17 -0
  62. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts.map +1 -0
  63. package/dist/operations/__tests__/ensures-schema-registry.test.js +113 -0
  64. package/dist/operations/__tests__/ensures-schema-registry.test.js.map +1 -0
  65. package/dist/operations/ensures-schema-registry.d.ts +172 -0
  66. package/dist/operations/ensures-schema-registry.d.ts.map +1 -0
  67. package/dist/operations/ensures-schema-registry.js +226 -0
  68. package/dist/operations/ensures-schema-registry.js.map +1 -0
  69. package/dist/operations/llm.d.ts +101 -9
  70. package/dist/operations/llm.d.ts.map +1 -1
  71. package/dist/operations/validate.d.ts +1 -12
  72. package/dist/operations/validate.d.ts.map +1 -1
  73. package/dist/operations/validate.js +1 -1
  74. package/dist/playbook.d.ts +60 -0
  75. package/dist/playbook.d.ts.map +1 -1
  76. package/dist/session.d.ts +11 -0
  77. package/dist/session.d.ts.map +1 -1
  78. package/dist/session.js.map +1 -1
  79. package/dist/templates/manifest.d.ts +1 -1
  80. package/dist/tools/agent-tools.d.ts +231 -0
  81. package/dist/tools/agent-tools.d.ts.map +1 -0
  82. package/dist/tools/agent-tools.js +25 -0
  83. package/dist/tools/agent-tools.js.map +1 -0
  84. package/dist/tools/atomic.d.ts +19 -0
  85. package/dist/tools/atomic.d.ts.map +1 -1
  86. package/dist/tools/atomic.js +17 -0
  87. package/dist/tools/atomic.js.map +1 -1
  88. package/dist/tools/index.d.ts +2 -0
  89. package/dist/tools/index.d.ts.map +1 -1
  90. package/dist/tools/skill-executor.d.ts +7 -0
  91. package/dist/tools/skill-executor.d.ts.map +1 -1
  92. package/dist/tools/web-tools.d.ts +217 -0
  93. package/dist/tools/web-tools.d.ts.map +1 -0
  94. package/dist/tools/web-tools.js +31 -0
  95. package/dist/tools/web-tools.js.map +1 -0
  96. package/package.json +6 -2
  97. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  98. package/src/__tests__/operation-def.test.ts +2 -1
  99. package/src/__tests__/sealed-credential.test.ts +100 -0
  100. package/src/__tests__/skill-executor-contract.test.ts +3 -0
  101. package/src/acceptance-gate.ts +2 -3
  102. package/src/boundary.ts +1 -20
  103. package/src/config.ts +104 -0
  104. package/src/data-accessor.ts +22 -1
  105. package/src/dispatch/identity.ts +5 -0
  106. package/src/dispatch/operations-registry.ts +51 -170
  107. package/src/index.ts +103 -24
  108. package/src/lease-ipc/__tests__/freeze.test.ts +246 -0
  109. package/src/lease-ipc/index.ts +63 -0
  110. package/src/lease-ipc/messages.ts +471 -0
  111. package/src/lease-ipc/version.ts +46 -0
  112. package/src/llm/interfaces.ts +23 -0
  113. package/src/llm/sealed-credential.ts +166 -0
  114. package/src/llm/system-of-use.ts +503 -0
  115. package/src/llm/system-resolver.ts +46 -11
  116. package/src/operations/__tests__/ensures-schema-registry.test.ts +169 -0
  117. package/src/operations/ensures-schema-registry.ts +260 -0
  118. package/src/operations/llm.ts +105 -8
  119. package/src/operations/validate.ts +1 -14
  120. package/src/playbook.ts +61 -0
  121. package/src/session.ts +11 -0
  122. package/src/tools/agent-tools.ts +270 -0
  123. package/src/tools/atomic.ts +21 -0
  124. package/src/tools/index.ts +46 -0
  125. package/src/tools/skill-executor.ts +7 -0
  126. package/src/tools/web-tools.ts +247 -0
  127. package/dist/tessera.d.ts +0 -32
  128. package/dist/tessera.d.ts.map +0 -1
  129. package/dist/tessera.js +0 -11
  130. package/dist/tessera.js.map +0 -1
  131. package/dist/warp-chain.d.ts +0 -129
  132. package/dist/warp-chain.d.ts.map +0 -1
  133. package/dist/warp-chain.js +0 -13
  134. package/dist/warp-chain.js.map +0 -1
  135. package/src/tessera.ts +0 -35
  136. package/src/warp-chain.ts +0 -185
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Sealed credential handle — the E10 on-demand-decrypt contract (T11752).
3
+ *
4
+ * ## Why a sealed handle exists
5
+ *
6
+ * Until E10, a resolved credential carried the secret **inline** as a bare
7
+ * `apiKey: string` field on `CredentialResultWire` / `ResolvedLLM`. That
8
+ * plaintext token traveled up the entire resolution call stack — through
9
+ * `resolveLLMForRole` → `resolveLLMForSystem` → consumers — where it could be
10
+ * accidentally logged, serialized into a LAFS envelope, or copied into a
11
+ * diagnostic. Every layer that merely *routed* the credential to the wire still
12
+ * had structural read access to the plaintext.
13
+ *
14
+ * A {@link SealedCredential} inverts that. The resolver returns an **opaque
15
+ * handle** that names the credential (`provider` + `account`) and exposes a
16
+ * single asynchronous {@link SealedCredential.fetch} method. The plaintext token
17
+ * is materialized ONLY when `fetch()` is invoked — and by E10 design that call
18
+ * happens at exactly two trust boundaries:
19
+ *
20
+ * 1. the wire — `transportForProvider` / `session-factory.ts` immediately
21
+ * before building the provider transport / auth headers, or
22
+ * 2. daemon worker-injection — the daemon brokering a short-lived credential
23
+ * into a spawned agent.
24
+ *
25
+ * Between the resolver and those boundaries, NO layer holds a plaintext secret;
26
+ * it holds a handle whose `fetch()` is the sole decryption chokepoint.
27
+ *
28
+ * ## Types-only (Gate 10 contracts purity)
29
+ *
30
+ * This module is **types-only**. {@link SealedCredential.fetch} is a
31
+ * *function-typed field* on an interface — a declaration of shape, NOT a bodied
32
+ * runtime function — so it satisfies the contracts-purity gate
33
+ * (`lint-no-runtime-in-contracts`). The concrete implementation of `fetch()`
34
+ * (the actual `crypto/credentials.ts` decrypt + the vault/daemon round-trip)
35
+ * lives in `@cleocode/core` — never in contracts.
36
+ *
37
+ * @module llm/sealed-credential
38
+ * @task T11752
39
+ * @epic T11746
40
+ * @see ADR-072 §Type lock-in
41
+ */
42
+
43
+ import type { ProviderId } from './provider-id.js';
44
+
45
+ /**
46
+ * The plaintext secret materialized by {@link SealedCredential.fetch}.
47
+ *
48
+ * ## Branded so it cannot be silently created or widened
49
+ *
50
+ * `DecryptedToken` is a *branded* string: an opaque nominal type produced ONLY
51
+ * by the core decrypt chokepoint inside {@link SealedCredential.fetch}. The
52
+ * `readonly __decryptedToken` brand is a phantom marker — it is NEVER assigned a
53
+ * runtime value (there is no runtime field; this is purely a compile-time
54
+ * nominal tag), so a plain `string` cannot be passed where a `DecryptedToken`
55
+ * is expected, and a `DecryptedToken` is not assignable to an arbitrary
56
+ * `string` sink without an explicit, auditable read of `.value`.
57
+ *
58
+ * ## NEVER serialized
59
+ *
60
+ * A `DecryptedToken` MUST NEVER be:
61
+ * - returned up the resolver call stack (it exists only transiently inside
62
+ * the `fetch()` consumer at the wire / daemon worker-injection boundary),
63
+ * - placed on any envelope, `NormalizedResponse.providerData`, log line,
64
+ * diagnostic, or `cleo llm whoami` output,
65
+ * - persisted, cached across calls, or held beyond the single transport
66
+ * construction / auth-header build that consumed it.
67
+ *
68
+ * The CI chokepoint lint (`lint-llm-chokepoint`) plus the E10 redaction tests
69
+ * (T11754) enforce that no bare secret string crosses the resolver boundary.
70
+ *
71
+ * Because the brand is phantom (compile-time only) and there is no enumerable
72
+ * runtime field, `JSON.stringify(token)` over the value field yields only the
73
+ * underlying string — which is precisely why callers MUST treat the value as
74
+ * write-once-to-the-wire and never route it into a serializer.
75
+ *
76
+ * @task T11752
77
+ */
78
+ export type DecryptedToken = {
79
+ /**
80
+ * Phantom brand marker — compile-time only, never materialized at runtime.
81
+ * Prevents a plain `string` from being assigned where a {@link DecryptedToken}
82
+ * is required (nominal typing). Reading this field is meaningless at runtime;
83
+ * it exists solely to make the type opaque.
84
+ *
85
+ * @internal
86
+ */
87
+ readonly __decryptedToken: 'DecryptedToken';
88
+ /**
89
+ * The materialized plaintext secret (API key or OAuth bearer token).
90
+ *
91
+ * SECURITY: read this ONLY at the wire (`transportForProvider`) or daemon
92
+ * worker-injection, build the auth header, and discard. NEVER log, serialize,
93
+ * or return it up the stack. Marked `readonly` so a consumer cannot mutate it
94
+ * in place.
95
+ */
96
+ readonly value: string;
97
+ };
98
+
99
+ /**
100
+ * An opaque, sealed reference to a credential whose plaintext is fetched on
101
+ * demand at the wire — never carried inline up the resolver stack.
102
+ *
103
+ * The resolver returns this *instead of* a bare `apiKey: string` (E10 ·
104
+ * T11753). Consumers route the handle to the transport boundary unchanged;
105
+ * only the boundary calls {@link fetch} to materialize the
106
+ * {@link DecryptedToken}, build the provider auth headers, and discard the
107
+ * plaintext.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * // At the wire boundary (session-factory.ts) — the ONLY place fetch() runs.
112
+ * // Prefer `authHeadersFromSealed` (core), which invokes fetch() internally so
113
+ * // the plaintext is never bound to a caller-visible variable:
114
+ * const headers = await authHeadersFromSealed(sealed, authType);
115
+ * // …or, for diagnostics, name the credential WITHOUT materializing it:
116
+ * log.info({ provider: sealed.provider, preview: sealed.tokenPreview });
117
+ * // The full token is NEVER returned, logged, or serialized.
118
+ * ```
119
+ *
120
+ * @task T11752
121
+ */
122
+ export interface SealedCredential {
123
+ /** Provider this credential targets (e.g. `'anthropic'`, `'openai'`). */
124
+ readonly provider: ProviderId;
125
+ /**
126
+ * Account / store-label identifier this handle resolves against (e.g.
127
+ * `'default'`, `'work'`). Names *which* credential the handle points at
128
+ * without revealing its secret — safe to log and serialize.
129
+ */
130
+ readonly account: string;
131
+ /**
132
+ * Non-secret, redacted preview of the underlying credential (e.g. `'…6789'`
133
+ * or `'oat-…6789'` for OAuth). The SOLE token-derived value safe to place on
134
+ * a log line, LAFS envelope, or `cleo llm whoami` diagnostic.
135
+ *
136
+ * SECURITY: this is computed ONCE at seal time from at most the last 4 token
137
+ * characters via the core redaction chokepoint (`tokenPreviewOf`) — it can
138
+ * never be reversed into the plaintext. Diagnostics that need to *name* a
139
+ * credential without materializing it MUST read this field, never call
140
+ * {@link fetch}. The E10 redaction test (T11754 · AC3) asserts that the
141
+ * preview, not the full token, is the only credential-derived string that
142
+ * crosses any logging/diagnostic boundary.
143
+ *
144
+ * @task T11754
145
+ */
146
+ readonly tokenPreview: string;
147
+ /**
148
+ * Materialize the plaintext secret on demand.
149
+ *
150
+ * SECURITY INVARIANT: the returned {@link DecryptedToken} is the ONLY point
151
+ * at which the secret exists in plaintext. Implementations (in
152
+ * `@cleocode/core`) run the `crypto/credentials.ts` decrypt INSIDE this
153
+ * method and MUST be invoked ONLY at the wire (`transportForProvider` /
154
+ * `session-factory.ts`) or daemon worker-injection — never to surface a key
155
+ * up the resolution stack. The result MUST NOT be cached, persisted, logged,
156
+ * or serialized by any caller.
157
+ *
158
+ * This is a *function-typed field on an interface* (a type, not a bodied
159
+ * function), so the declaration is contracts-purity-safe (Gate 10): the body
160
+ * lives in core.
161
+ *
162
+ * @returns The materialized plaintext token, valid only for immediate
163
+ * wire/daemon use.
164
+ */
165
+ readonly fetch: () => Promise<DecryptedToken>;
166
+ }
@@ -0,0 +1,503 @@
1
+ /**
2
+ * `SystemOfUse` taxonomy — the structured identity of every CLEO subsystem that
3
+ * may request an LLM through the E9 chokepoint (`resolveLLMForSystem`).
4
+ *
5
+ * ## Why a discriminated descriptor (the hermes `_AUX_TASKS` analog)
6
+ *
7
+ * The first cut of the E9 chokepoint (T11749) accepted a flat string-literal
8
+ * label ({@link SystemOfUseLabel} — `'sentient' | 'memory' | …`). That covered
9
+ * the seven role-mapped background subsystems but had no room for the OTHER
10
+ * axes CLEO routes LLMs along:
11
+ *
12
+ * - **orchestration tiers** (`frontier`/`standard`/`fast`/`local`) — the
13
+ * capability lane a delegated turn runs in, independent of any role;
14
+ * - **tools / skills** — atomic tool primitives and `ct-*` skills that each
15
+ * may pin a model (the hermes `_AUX_TASKS` registry: one entry per named
16
+ * auxiliary task, each with its own provider/model binding);
17
+ * - **cantbook nodes** — per-node model selection inside a `.cantbook` flow;
18
+ * - **spawn units** — orchestrator-spawned worker agents that resolve their
19
+ * own LLM.
20
+ *
21
+ * `SystemOfUse` is the discriminated `{ kind, id }` descriptor that unifies all
22
+ * of these axes under ONE identity type. {@link BUILTIN_SYSTEMS_OF_USE} is the
23
+ * static registry of the well-known systems (orchestration tiers + the seven
24
+ * {@link RoleName}s + the auxiliary systems); `tool:` / `skill:` / `cantbook:` /
25
+ * `spawn-unit:` keys are OPEN — any runtime id is a valid `SystemOfUse` of the
26
+ * corresponding kind without a registry edit (see {@link OPEN_SYSTEM_KEY_PREFIXES}).
27
+ *
28
+ * ## Relationship to the resolver input
29
+ *
30
+ * This descriptor is the *taxonomy / identity* layer. The E9 resolver
31
+ * (`resolveLLMForSystem`) still accepts the ergonomic flat {@link SystemOfUseLabel}
32
+ * for the seven role-mapped background subsystems (its locked contract); this
33
+ * file is the SSoT that label set is derived against ({@link AUX_SYSTEM_IDS}),
34
+ * and the home of the richer descriptor that profile-axis routing (tiers,
35
+ * tools, skills, cantbook nodes, spawn units) keys off.
36
+ *
37
+ * Contracts-purity: this module is **types + `as const` DATA + type guards**
38
+ * only — no runtime logic (Gate 10 · T11418). The runtime encode/decode for the
39
+ * open-axis string keys (`parseSystemKey` / `formatSystemKey`) is intentionally
40
+ * NOT defined here; it lives in `@cleocode/core` so this package stays type-only
41
+ * (this module exposes only the {@link OPEN_SYSTEM_KEY_PREFIXES} DATA those
42
+ * helpers read).
43
+ *
44
+ * @module llm/system-of-use
45
+ * @task T11747
46
+ * @epic T11745
47
+ */
48
+
49
+ import type { LlmProviderTransport, RoleName } from '../config.js';
50
+ import type { ProviderTier } from './provider-profile.js';
51
+
52
+ /**
53
+ * The axis a {@link SystemOfUse} belongs to — the discriminant of the
54
+ * descriptor union.
55
+ *
56
+ * | Kind | Meaning | Registry |
57
+ * |-----------------|----------------------------------------------------------------|-----------------|
58
+ * | `role` | One of the seven background {@link RoleName}s. | closed (7) |
59
+ * | `orchestration` | A capability tier lane (frontier/standard/fast/local). | closed (4) |
60
+ * | `aux` | A named auxiliary subsystem (hermes `_AUX_TASKS` analog). | closed (~10) |
61
+ * | `tool` | An atomic tool primitive that pins a model. | open (`tool:`) |
62
+ * | `skill` | A `ct-*` skill that pins a model. | open (`skill:`) |
63
+ * | `cantbook-node` | A node inside a `.cantbook` flow. | open (`cantbook:`) |
64
+ * | `spawn-unit` | An orchestrator-spawned worker agent. | open (`spawn-unit:`) |
65
+ *
66
+ * @task T11747
67
+ */
68
+ export type SystemOfUseKind =
69
+ | 'role'
70
+ | 'orchestration'
71
+ | 'aux'
72
+ | 'tool'
73
+ | 'skill'
74
+ | 'cantbook-node'
75
+ | 'spawn-unit';
76
+
77
+ /**
78
+ * A subsystem whose identity is one of the seven background {@link RoleName}s.
79
+ *
80
+ * `id` is constrained to {@link RoleName} so the role axis can never drift from
81
+ * the config SSoT.
82
+ *
83
+ * @task T11747
84
+ */
85
+ export interface RoleSystem {
86
+ readonly kind: 'role';
87
+ /** The canonical role name (config SSoT — {@link RoleName}). */
88
+ readonly id: RoleName;
89
+ }
90
+
91
+ /**
92
+ * A subsystem identified by an orchestration capability tier.
93
+ *
94
+ * `id` is a {@link ProviderTier} — the lane a delegated/orchestrated turn runs
95
+ * in (frontier flagship reasoning ↔ local on-device), independent of role.
96
+ *
97
+ * @task T11747
98
+ */
99
+ export interface OrchestrationSystem {
100
+ readonly kind: 'orchestration';
101
+ /** Capability tier lane (see {@link ProviderTier}). */
102
+ readonly id: ProviderTier;
103
+ }
104
+
105
+ /**
106
+ * A named auxiliary subsystem — the direct analog of a hermes `_AUX_TASKS`
107
+ * entry. Each id (`sentient`, `memory`, `compression`, …) is a stable,
108
+ * code-referenced subsystem that may carry its own model binding.
109
+ *
110
+ * @task T11747
111
+ */
112
+ export interface AuxSystem {
113
+ readonly kind: 'aux';
114
+ /** Stable auxiliary subsystem id (a member of {@link AUX_SYSTEM_IDS}). */
115
+ readonly id: AuxSystemId;
116
+ }
117
+
118
+ /**
119
+ * A tool primitive that resolves its own LLM. OPEN id — any atomic tool name is
120
+ * valid (encoded `tool:<name>`), so new tools need no registry edit.
121
+ *
122
+ * @task T11747
123
+ */
124
+ export interface ToolSystem {
125
+ readonly kind: 'tool';
126
+ /** Atomic tool name (free-form runtime id). */
127
+ readonly id: string;
128
+ }
129
+
130
+ /**
131
+ * A `ct-*` skill that resolves its own LLM. OPEN id — encoded `skill:<name>`.
132
+ *
133
+ * @task T11747
134
+ */
135
+ export interface SkillSystem {
136
+ readonly kind: 'skill';
137
+ /** Skill name (free-form runtime id). */
138
+ readonly id: string;
139
+ }
140
+
141
+ /**
142
+ * A node inside a `.cantbook` flow that resolves its own LLM. OPEN id — encoded
143
+ * `cantbook:<nodeId>`.
144
+ *
145
+ * @task T11747
146
+ */
147
+ export interface CantbookNodeSystem {
148
+ readonly kind: 'cantbook-node';
149
+ /** Cantbook node id (free-form runtime id). */
150
+ readonly id: string;
151
+ }
152
+
153
+ /**
154
+ * An orchestrator-spawned worker agent that resolves its own LLM. OPEN id —
155
+ * encoded `spawn-unit:<taskId>` (typically the `T####` it executes).
156
+ *
157
+ * @task T11747
158
+ */
159
+ export interface SpawnUnitSystem {
160
+ readonly kind: 'spawn-unit';
161
+ /** Spawn-unit id, typically the executing task id (free-form runtime id). */
162
+ readonly id: string;
163
+ }
164
+
165
+ /**
166
+ * The discriminated `{ kind, id }` identity of any CLEO subsystem that requests
167
+ * an LLM through the E9 chokepoint.
168
+ *
169
+ * Discriminate on {@link SystemOfUseKind} (`system.kind`) to narrow `id` to the
170
+ * axis-specific type. Closed-axis kinds (`role`/`orchestration`/`aux`) carry a
171
+ * typed id; open-axis kinds (`tool`/`skill`/`cantbook-node`/`spawn-unit`) carry
172
+ * a free-form runtime string.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * const sys: SystemOfUse = { kind: 'tool', id: 'web-search' };
177
+ * if (sys.kind === 'role') {
178
+ * // sys.id is narrowed to RoleName here
179
+ * }
180
+ * ```
181
+ *
182
+ * @task T11747
183
+ * @epic T11745
184
+ */
185
+ export type SystemOfUse =
186
+ | RoleSystem
187
+ | OrchestrationSystem
188
+ | AuxSystem
189
+ | ToolSystem
190
+ | SkillSystem
191
+ | CantbookNodeSystem
192
+ | SpawnUnitSystem;
193
+
194
+ // ---------------------------------------------------------------------------
195
+ // Closed-axis value SSoTs (the type↔value mirrors)
196
+ // ---------------------------------------------------------------------------
197
+
198
+ /**
199
+ * Value-level SSoT for the seven {@link RoleName}s.
200
+ *
201
+ * The `RoleName` *type* lives in `config.ts`; this `as const` tuple is the
202
+ * single iterable VALUE mirror used to build the registry (and any other
203
+ * `for (const role of ROLE_SYSTEM_IDS)` loop) so the role axis is enumerated
204
+ * from ONE place. A compile-time `satisfies readonly RoleName[]` keeps it
205
+ * locked to the type — adding a role to `RoleName` without extending this tuple
206
+ * is a type error.
207
+ *
208
+ * @task T11747
209
+ */
210
+ export const ROLE_SYSTEM_IDS = [
211
+ 'extraction',
212
+ 'consolidation',
213
+ 'derivation',
214
+ 'hygiene',
215
+ 'judgement',
216
+ 'plugin',
217
+ 'compression',
218
+ ] as const satisfies readonly RoleName[];
219
+
220
+ /**
221
+ * Value-level SSoT for the user-facing, *enumerable* background {@link RoleName}s
222
+ * — the set `cleo llm whoami` reports and `cleo llm profile` accepts.
223
+ *
224
+ * This is the proper subset of {@link ROLE_SYSTEM_IDS} that excludes the two
225
+ * internal sandbox/utility roles (`plugin`, `compression`): they are resolved
226
+ * by code paths (plugin-scoped single-turn calls, context compression) rather
227
+ * than configured per-call by users, so they are intentionally NOT enumerated
228
+ * by the `whoami` / `profile` CLI surface.
229
+ *
230
+ * Sourcing the `cli-ops.ts` role list from THIS one tuple (instead of a
231
+ * duplicated inline array) is the AC3 "ALL_ROLES from one SSoT" requirement
232
+ * (T11750): the type-checker's `satisfies readonly RoleName[]` keeps every id
233
+ * locked to {@link RoleName}, so the enumerable set can never silently drift
234
+ * from the config vocabulary.
235
+ *
236
+ * @task T11750
237
+ * @epic T11745
238
+ */
239
+ export const WHOAMI_ROLE_IDS = [
240
+ 'extraction',
241
+ 'consolidation',
242
+ 'derivation',
243
+ 'hygiene',
244
+ 'judgement',
245
+ ] as const satisfies readonly RoleName[];
246
+
247
+ /**
248
+ * The id of a `whoami`-enumerable role — the element type of
249
+ * {@link WHOAMI_ROLE_IDS}.
250
+ *
251
+ * @task T11750
252
+ */
253
+ export type WhoamiRoleId = (typeof WHOAMI_ROLE_IDS)[number];
254
+
255
+ /**
256
+ * Value-level SSoT for the four orchestration capability tiers. Mirrors the
257
+ * {@link ProviderTier} type, most→least capable.
258
+ *
259
+ * @task T11747
260
+ */
261
+ export const ORCHESTRATION_TIER_IDS = [
262
+ 'frontier',
263
+ 'standard',
264
+ 'fast',
265
+ 'local',
266
+ ] as const satisfies readonly ProviderTier[];
267
+
268
+ /**
269
+ * Value-level SSoT for the auxiliary subsystem ids (the hermes `_AUX_TASKS`
270
+ * key set). These are the stable, code-referenced background subsystems CLEO
271
+ * resolves an LLM for outside the role/tier/tool axes.
272
+ *
273
+ * The seven role-mapped resolver labels in {@link SystemOfUseLabel} are derived
274
+ * from this set plus `default` — keeping the flat resolver vocabulary and the
275
+ * structured taxonomy in lock-step.
276
+ *
277
+ * @task T11747
278
+ */
279
+ export const AUX_SYSTEM_IDS = [
280
+ /** Dream-cycle / consolidation subsystem (BRAIN sentient layer). */
281
+ 'sentient',
282
+ /** BRAIN memory extraction subsystem. */
283
+ 'memory',
284
+ /** Task-executor judgement subsystem. */
285
+ 'task-executor',
286
+ /** BRAIN deriver subsystem. */
287
+ 'deriver',
288
+ /** Hygiene-scan subsystem. */
289
+ 'hygiene',
290
+ /** Plugin-scoped single-turn calls. */
291
+ 'plugin',
292
+ /** Context-compression / summarization subsystem. */
293
+ 'compression',
294
+ /** Decision conflict-validator (ADR write-gate). */
295
+ 'decision-validator',
296
+ /** Title / summary generation (cheap aux tier). */
297
+ 'title-generation',
298
+ /** Embedding generation for vector retrieval. */
299
+ 'embedding',
300
+ ] as const;
301
+
302
+ /**
303
+ * The id of a registered auxiliary subsystem — the element type of
304
+ * {@link AUX_SYSTEM_IDS}.
305
+ *
306
+ * @task T11747
307
+ */
308
+ export type AuxSystemId = (typeof AUX_SYSTEM_IDS)[number];
309
+
310
+ // ---------------------------------------------------------------------------
311
+ // Open-axis runtime key prefixes
312
+ // ---------------------------------------------------------------------------
313
+
314
+ /**
315
+ * The four OPEN {@link SystemOfUseKind}s and the string-key prefix each is
316
+ * encoded with for transport across string-only boundaries (config keys, CLI
317
+ * args, audit logs). E.g. `{ kind: 'tool', id: 'web-search' }` ↔ `'tool:web-search'`.
318
+ *
319
+ * Declared as DATA so encode/decode never branches on a hardcoded prefix —
320
+ * `@cleocode/core` reads this map (Gate 10 keeps the runtime split out).
321
+ *
322
+ * @task T11747
323
+ */
324
+ export const OPEN_SYSTEM_KEY_PREFIXES = {
325
+ tool: 'tool:',
326
+ skill: 'skill:',
327
+ 'cantbook-node': 'cantbook:',
328
+ 'spawn-unit': 'spawn-unit:',
329
+ } as const satisfies Readonly<Record<OpenSystemKind, string>>;
330
+
331
+ /**
332
+ * The subset of {@link SystemOfUseKind} whose ids are OPEN (any runtime string),
333
+ * encoded via {@link OPEN_SYSTEM_KEY_PREFIXES}.
334
+ *
335
+ * @task T11747
336
+ */
337
+ export type OpenSystemKind = 'tool' | 'skill' | 'cantbook-node' | 'spawn-unit';
338
+
339
+ // ---------------------------------------------------------------------------
340
+ // BUILTIN_SYSTEMS_OF_USE registry
341
+ // ---------------------------------------------------------------------------
342
+
343
+ /**
344
+ * Static registry of every well-known (closed-axis) {@link SystemOfUse}: the
345
+ * four orchestration tiers, the seven {@link RoleName}s, and the ~10 auxiliary
346
+ * subsystems — the hermes `_AUX_TASKS` analog as a single typed table.
347
+ *
348
+ * Open-axis systems (`tool:`/`skill:`/`cantbook:`/`spawn-unit:`) are NOT listed
349
+ * here — they are valid without a registry edit (see {@link OPEN_SYSTEM_KEY_PREFIXES}).
350
+ *
351
+ * Built by spreading the three closed-axis value SSoTs so the registry can
352
+ * never drift from {@link ROLE_SYSTEM_IDS} / {@link ORCHESTRATION_TIER_IDS} /
353
+ * {@link AUX_SYSTEM_IDS}.
354
+ *
355
+ * @task T11747
356
+ * @epic T11745
357
+ */
358
+ export const BUILTIN_SYSTEMS_OF_USE = [
359
+ ...ORCHESTRATION_TIER_IDS.map((id): OrchestrationSystem => ({ kind: 'orchestration', id })),
360
+ ...ROLE_SYSTEM_IDS.map((id): RoleSystem => ({ kind: 'role', id })),
361
+ ...AUX_SYSTEM_IDS.map((id): AuxSystem => ({ kind: 'aux', id })),
362
+ ] as const satisfies readonly SystemOfUse[];
363
+
364
+ // ---------------------------------------------------------------------------
365
+ // Type guards (contracts-pure — Gate 10 allows `v is T` guards)
366
+ // ---------------------------------------------------------------------------
367
+
368
+ /**
369
+ * Type guard: is `value` a {@link SystemOfUse} descriptor (a `{ kind, id }`
370
+ * object whose `kind` is a known {@link SystemOfUseKind})?
371
+ *
372
+ * Structural-only — does NOT assert that closed-axis ids are registered (use
373
+ * {@link isBuiltinSystemOfUse} for that); an open-axis or as-yet-unregistered
374
+ * id still passes.
375
+ *
376
+ * @param value - The candidate value.
377
+ * @returns `true` when `value` is shaped like a {@link SystemOfUse}.
378
+ * @task T11747
379
+ */
380
+ export function isSystemOfUse(value: unknown): value is SystemOfUse {
381
+ if (typeof value !== 'object' || value === null) return false;
382
+ const v = value as { kind?: unknown; id?: unknown };
383
+ if (typeof v.id !== 'string') return false;
384
+ return (
385
+ v.kind === 'role' ||
386
+ v.kind === 'orchestration' ||
387
+ v.kind === 'aux' ||
388
+ v.kind === 'tool' ||
389
+ v.kind === 'skill' ||
390
+ v.kind === 'cantbook-node' ||
391
+ v.kind === 'spawn-unit'
392
+ );
393
+ }
394
+
395
+ /**
396
+ * Type guard: is `value` a closed-axis {@link SystemOfUse} present in
397
+ * {@link BUILTIN_SYSTEMS_OF_USE} (an exact `kind`+`id` match)?
398
+ *
399
+ * @param value - The candidate value.
400
+ * @returns `true` when `value` is a registered builtin system-of-use.
401
+ * @task T11747
402
+ */
403
+ export function isBuiltinSystemOfUse(value: unknown): value is SystemOfUse {
404
+ if (!isSystemOfUse(value)) return false;
405
+ return BUILTIN_SYSTEMS_OF_USE.some((s) => s.kind === value.kind && s.id === value.id);
406
+ }
407
+
408
+ /**
409
+ * Type guard: is `kind` an OPEN-axis {@link OpenSystemKind} (one whose ids are
410
+ * encoded with an {@link OPEN_SYSTEM_KEY_PREFIXES} prefix)?
411
+ *
412
+ * @param kind - The kind discriminant to test.
413
+ * @returns `true` for `tool` / `skill` / `cantbook-node` / `spawn-unit`.
414
+ * @task T11747
415
+ */
416
+ export function isOpenSystemKind(kind: SystemOfUseKind): kind is OpenSystemKind {
417
+ return kind === 'tool' || kind === 'skill' || kind === 'cantbook-node' || kind === 'spawn-unit';
418
+ }
419
+
420
+ // ---------------------------------------------------------------------------
421
+ // Runtime-registered systems-of-use (registerSystemOfUse — T11751)
422
+ // ---------------------------------------------------------------------------
423
+
424
+ /**
425
+ * The default provider/model binding a runtime-registered {@link SystemOfUse}
426
+ * advertises (T11751 · AC1).
427
+ *
428
+ * `registerSystemOfUse(key, displayName, defaults)` lets a plugin / extension /
429
+ * downstream package declare a NEW system-of-use without a registry edit and
430
+ * give it a default binding. That default is consulted strictly BELOW user
431
+ * config (`llm.systems[key]` / `llm.default` / `llm.defaultProfile`) so the user
432
+ * always wins — it only supplies a binding when the user has configured nothing
433
+ * for that key.
434
+ *
435
+ * Either `profile` (a key of `LlmConfig.profiles`) OR an inline `provider` +
436
+ * `model` tuple supplies the binding; `profile` wins when both are present. An
437
+ * entry that supplies neither a resolvable `profile` nor a complete
438
+ * `provider`+`model` tuple is structurally incomplete and is skipped at
439
+ * resolution time (the chain falls through to implicit-fallback) — never an
440
+ * error.
441
+ *
442
+ * @task T11751
443
+ * @epic T11745
444
+ */
445
+ export interface SystemOfUseDefaults {
446
+ /**
447
+ * Optional reference to a named profile in `LlmConfig.profiles`. When set and
448
+ * resolvable, the named profile's provider/model/credentialLabel win over this
449
+ * entry's inline tuple.
450
+ */
451
+ readonly profile?: string;
452
+ /** Inline LLM provider transport (used when `profile` is absent/unresolvable). */
453
+ readonly provider?: LlmProviderTransport;
454
+ /** Inline full model identifier (used when `profile` is absent/unresolvable). */
455
+ readonly model?: string;
456
+ /**
457
+ * Optional credential label pinning this default to a specific credential pool
458
+ * entry. When omitted, standard priority-based credential resolution applies.
459
+ */
460
+ readonly credentialLabel?: string;
461
+ }
462
+
463
+ /**
464
+ * A runtime-registered system-of-use — the merged record surfaced by the
465
+ * profile-picker enumeration (T11751 · AC2).
466
+ *
467
+ * @task T11751
468
+ * @epic T11745
469
+ */
470
+ export interface RegisteredSystemOfUse {
471
+ /** Encoded system-of-use key (e.g. `'sentient'`, `'tool:web-search'`). */
472
+ readonly key: string;
473
+ /** Human-readable display name for the picker surface. */
474
+ readonly displayName: string;
475
+ /** Default binding consulted strictly below user config (user wins). */
476
+ readonly defaults: SystemOfUseDefaults;
477
+ }
478
+
479
+ /**
480
+ * One enumerated entry in the merged profile-picker surface (T11751 · AC2).
481
+ *
482
+ * Combines the structured {@link SystemOfUse} identity with its display name and
483
+ * provenance so the TUI/Studio picker can render every well-known builtin AND
484
+ * every runtime-registered system in one list.
485
+ *
486
+ * @task T11751
487
+ * @epic T11745
488
+ */
489
+ export interface SystemOfUsePickerEntry {
490
+ /** Encoded system-of-use key (the picker value). */
491
+ readonly key: string;
492
+ /** Human-readable display name. */
493
+ readonly displayName: string;
494
+ /** Discriminant axis of this system (see {@link SystemOfUseKind}). */
495
+ readonly kind: SystemOfUseKind;
496
+ /** Where this entry came from: a static builtin or a runtime registration. */
497
+ readonly source: 'builtin' | 'registered';
498
+ /**
499
+ * The default binding when `source === 'registered'`. Absent for builtins
500
+ * (their binding is resolved purely from user config + the role/aux mapping).
501
+ */
502
+ readonly defaults?: SystemOfUseDefaults;
503
+ }