@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
@@ -68,6 +68,8 @@ type _OperationDefShape = {
68
68
  requiredParams: string[];
69
69
  params?: ParamDef[];
70
70
  mcpExposed?: boolean;
71
+ // T11921 — opt-in streaming marker for the gateway HTTP/SSE transport.
72
+ streaming?: boolean;
71
73
  // T11692 (DHQ-033 / DHQ-057) — optional schema-first input/output contracts.
72
74
  inputSchema?: OperationInputContract<unknown>;
73
75
  outputSchema?: OperationOutputContract;
@@ -102,7 +104,11 @@ describe('dispatch/operation-def contracts', () => {
102
104
  expect(CANONICAL_DOMAINS.length).toBeGreaterThan(0);
103
105
  // T9954 — snapshot the current count so accidental additions/removals
104
106
  // trip this test and force an explicit human review.
105
- expect(CANONICAL_DOMAINS.length).toBe(23);
107
+ // T11889-D — +1 for the new 'selfimprove' domain (self-dogfooding loop verb).
108
+ // T11939 — +1 for the new 'service' domain (universal service-vault OAuth flow).
109
+ // T11700 — +4 for the 5-entity provider experience: 'account', 'provider',
110
+ // 'model', 'profile' (North-Star §2 addressable provider surface).
111
+ expect(CANONICAL_DOMAINS.length).toBe(29);
106
112
  });
107
113
 
108
114
  it('CANONICAL_DOMAINS contains the four sentinel newly-promoted domains', () => {
@@ -0,0 +1,93 @@
1
+ /**
2
+ * T11702 — `ProviderDef` declarative contract shape.
3
+ *
4
+ * Type-level + value-level assertions that the contract is a closed, discriminated,
5
+ * serializable shape (the endpoint union is keyed on the `transport` literal, no
6
+ * `any`/`unknown` shortcut). No DB, no network.
7
+ *
8
+ * @task T11702
9
+ * @epic T11667
10
+ */
11
+
12
+ import { describe, expect, it } from 'vitest';
13
+ import {
14
+ PROVIDER_TRANSPORTS,
15
+ type ProviderDef,
16
+ type ProviderEndpoint,
17
+ REQUEST_QUIRK_KINDS,
18
+ } from '../llm/provider-def.js';
19
+
20
+ describe('ProviderDef contract (T11702)', () => {
21
+ it('exposes the closed transport discriminant set', () => {
22
+ expect([...PROVIDER_TRANSPORTS]).toEqual([
23
+ 'openai-completions',
24
+ 'openai-responses',
25
+ 'anthropic-messages',
26
+ 'aisdk',
27
+ ]);
28
+ });
29
+
30
+ it('exposes the closed request-quirk-kind set', () => {
31
+ expect(REQUEST_QUIRK_KINDS).toContain('grok-conv-id');
32
+ expect(REQUEST_QUIRK_KINDS).toContain('gemini-thinking-config');
33
+ expect(REQUEST_QUIRK_KINDS).toContain('moonshot-schema-sanitize');
34
+ });
35
+
36
+ it('accepts a fully-populated declarative provider def with an OAuth flow + alt endpoint', () => {
37
+ const def: ProviderDef = {
38
+ id: 'openai',
39
+ displayName: 'OpenAI Codex (ChatGPT)',
40
+ aliases: ['codex', 'chatgpt', 'openai-codex'],
41
+ authMethods: ['api_key', 'oauth'],
42
+ endpoint: { transport: 'openai-completions', baseUrl: 'https://api.openai.com/v1' },
43
+ altEndpoints: [{ transport: 'openai-responses', baseUrl: 'https://api.openai.com/v1' }],
44
+ modelsDevId: 'openai',
45
+ oauth: {
46
+ mode: 'pkce',
47
+ clientId: 'app_test',
48
+ tokenEndpoint: 'https://auth.openai.com/oauth/token',
49
+ authorizationEndpoint: 'https://auth.openai.com/oauth/authorize',
50
+ },
51
+ requestQuirks: [{ kind: 'openrouter-pareto' }],
52
+ };
53
+ // Round-trips through JSON — the shape is serializable (no closures).
54
+ const roundTripped = JSON.parse(JSON.stringify(def)) as ProviderDef;
55
+ expect(roundTripped.id).toBe('openai');
56
+ expect(roundTripped.endpoint.transport).toBe('openai-completions');
57
+ expect(roundTripped.altEndpoints?.[0]?.transport).toBe('openai-responses');
58
+ expect(roundTripped.oauth?.mode).toBe('pkce');
59
+ });
60
+
61
+ it('narrows the endpoint discriminated union on the transport literal', () => {
62
+ const endpoints: ProviderEndpoint[] = [
63
+ { transport: 'anthropic-messages', baseUrl: 'https://api.anthropic.com' },
64
+ { transport: 'aisdk', baseUrl: 'https://example.com', aiSdkProvider: 'google' },
65
+ ];
66
+ for (const ep of endpoints) {
67
+ if (ep.transport === 'aisdk') {
68
+ // Only the aisdk variant carries aiSdkProvider — narrowing proves the union.
69
+ expect(ep.aiSdkProvider).toBe('google');
70
+ } else {
71
+ expect(ep.baseUrl.startsWith('https://')).toBe(true);
72
+ }
73
+ }
74
+ });
75
+
76
+ it('accepts a minimal def (no optional fields)', () => {
77
+ const def: ProviderDef = {
78
+ id: 'bedrock',
79
+ displayName: 'AWS Bedrock',
80
+ aliases: ['aws-bedrock'],
81
+ authMethods: ['aws_sdk'],
82
+ endpoint: {
83
+ transport: 'aisdk',
84
+ baseUrl: 'https://bedrock.example',
85
+ aiSdkProvider: 'bedrock',
86
+ },
87
+ modelsDevId: 'bedrock',
88
+ };
89
+ expect(def.oauth).toBeUndefined();
90
+ expect(def.requestQuirks).toBeUndefined();
91
+ expect(def.altEndpoints).toBeUndefined();
92
+ });
93
+ });
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Type + purity tests for the E10 sealed credential handle (T11752).
3
+ *
4
+ * Pins the {@link SealedCredential} / {@link DecryptedToken} contract so that:
5
+ * - `SealedCredential` is exactly `{ provider, account, fetch }` with
6
+ * `fetch: () => Promise<DecryptedToken>` (AC1).
7
+ * - `ResolvedLLM` carries a sealed-credential-typed field (AC2).
8
+ * - `DecryptedToken` is a *branded* string — a plain `string` is NOT
9
+ * assignable to it, so the secret cannot be silently fabricated and a
10
+ * `DecryptedToken` cannot leak into an arbitrary `string` sink without an
11
+ * explicit `.value` read (the "never serialized" invariant, AC2).
12
+ * - The module is types-only (no runtime export), satisfying contracts
13
+ * purity (Gate 10 · AC3).
14
+ *
15
+ * @task T11752
16
+ * @epic T11746
17
+ */
18
+
19
+ import { describe, expect, expectTypeOf, it } from 'vitest';
20
+ import type { ProviderId } from '../llm/provider-id.js';
21
+ import type { DecryptedToken, SealedCredential } from '../llm/sealed-credential.js';
22
+ import type { ResolvedLLM } from '../operations/llm.js';
23
+
24
+ describe('SealedCredential shape (AC1)', () => {
25
+ it('is exactly { provider, account, tokenPreview, fetch } — pins the field set', () => {
26
+ expectTypeOf<keyof SealedCredential>().toEqualTypeOf<
27
+ 'provider' | 'account' | 'tokenPreview' | 'fetch'
28
+ >();
29
+ });
30
+
31
+ it('provider is a ProviderId', () => {
32
+ expectTypeOf<SealedCredential['provider']>().toEqualTypeOf<ProviderId>();
33
+ });
34
+
35
+ it('account is a plain (loggable) string', () => {
36
+ expectTypeOf<SealedCredential['account']>().toEqualTypeOf<string>();
37
+ });
38
+
39
+ it('tokenPreview is a plain (loggable, non-secret) string (T11754)', () => {
40
+ expectTypeOf<SealedCredential['tokenPreview']>().toEqualTypeOf<string>();
41
+ });
42
+
43
+ it('fetch resolves to a DecryptedToken (never a bare string)', () => {
44
+ expectTypeOf<SealedCredential['fetch']>().toEqualTypeOf<() => Promise<DecryptedToken>>();
45
+ expectTypeOf<ReturnType<SealedCredential['fetch']>>().resolves.toEqualTypeOf<DecryptedToken>();
46
+ });
47
+
48
+ it('all fields are readonly (a handle cannot be mutated in place)', () => {
49
+ // Removing `readonly` from any field would make this fail to compile.
50
+ expectTypeOf<SealedCredential>().toEqualTypeOf<{
51
+ readonly provider: ProviderId;
52
+ readonly account: string;
53
+ readonly tokenPreview: string;
54
+ readonly fetch: () => Promise<DecryptedToken>;
55
+ }>();
56
+ });
57
+ });
58
+
59
+ describe('DecryptedToken branding (AC2 — never serialized / never fabricated)', () => {
60
+ it('is NOT assignable from a plain string (nominal brand)', () => {
61
+ // A plain string lacks the phantom `__decryptedToken` brand, so it cannot
62
+ // be passed where a DecryptedToken is required.
63
+ expectTypeOf<string>().not.toMatchTypeOf<DecryptedToken>();
64
+ });
65
+
66
+ it('is NOT a plain string sink (must read .value explicitly to serialize)', () => {
67
+ expectTypeOf<DecryptedToken>().not.toEqualTypeOf<string>();
68
+ });
69
+
70
+ it('exposes a readonly plaintext .value of type string', () => {
71
+ expectTypeOf<DecryptedToken['value']>().toEqualTypeOf<string>();
72
+ });
73
+
74
+ it('carries the phantom brand marker (compile-time only)', () => {
75
+ expectTypeOf<DecryptedToken['__decryptedToken']>().toEqualTypeOf<'DecryptedToken'>();
76
+ });
77
+ });
78
+
79
+ describe('ResolvedLLM.sealedCredential (AC2 · T11753)', () => {
80
+ it('is the required (non-optional) canonical credential surface', () => {
81
+ // T11753 promotes the field from optional to required: the resolver ALWAYS
82
+ // populates it (a handle when a credential resolved, `null` otherwise).
83
+ expectTypeOf<ResolvedLLM['sealedCredential']>().toEqualTypeOf<SealedCredential | null>();
84
+ });
85
+
86
+ it('the inline credential metadata carries NO plaintext apiKey (T11753)', () => {
87
+ // The secret-bearing `apiKey` field is structurally removed from the inline
88
+ // credential — the plaintext crosses ONLY through `sealedCredential.fetch()`.
89
+ type Cred = NonNullable<ResolvedLLM['credential']>;
90
+ expectTypeOf<Cred>().not.toHaveProperty('apiKey');
91
+ });
92
+ });
93
+
94
+ describe('contracts purity (AC3) — sealed-credential is types-only', () => {
95
+ it('the module exposes no runtime exports (no decrypt logic in contracts)', async () => {
96
+ const mod = await import('../llm/sealed-credential.js');
97
+ // A pure type module compiles to an empty ESM namespace at runtime.
98
+ expect(Object.keys(mod)).toHaveLength(0);
99
+ });
100
+ });
@@ -0,0 +1,129 @@
1
+ /**
2
+ * T11937 seed + T11938 BREADTH — service-provider registry contract.
3
+ *
4
+ * Asserts the declarative `SERVICE_PROVIDERS` registry: the two pattern seeds
5
+ * (github + google) keep their precise shape (T11937), and the full ~40-service
6
+ * census (T11938) is well-formed — every entry self-consistent on its key, every
7
+ * provider carrying a NON-EMPTY `hostRules` array, and every OAuth (non-`api-key`)
8
+ * provider carrying either a `refresh` config or an explicit re-auth shape. The
9
+ * registry is DATA (no runtime helper) — this test is the only consumer that
10
+ * exercises its shape.
11
+ *
12
+ * @task T11937
13
+ * @task T11938
14
+ * @epic T11765
15
+ */
16
+
17
+ import { describe, expect, it } from 'vitest';
18
+ import {
19
+ REFRESH_KINDS,
20
+ SERVICE_AUTH_KINDS,
21
+ SERVICE_PROVIDERS,
22
+ type ServiceProviderDef,
23
+ } from '../vault/service-provider.js';
24
+
25
+ describe('SERVICE_PROVIDERS registry (T11937 seed + T11938 breadth)', () => {
26
+ it('contains the full ~40-service census (≥ 38 providers, T11938 AC1)', () => {
27
+ // 2 seeds (github, google) + ~37 breadth entries = ~40 declarative providers.
28
+ expect(Object.keys(SERVICE_PROVIDERS).length).toBeGreaterThanOrEqual(38);
29
+ });
30
+
31
+ it('includes a representative spread across provider families', () => {
32
+ const keys = Object.keys(SERVICE_PROVIDERS);
33
+ // Seeds + Google family + Atlassian + api-key + JWT/client-credentials.
34
+ for (const expected of [
35
+ 'github',
36
+ 'google',
37
+ 'gmail',
38
+ 'notion',
39
+ 'dropbox',
40
+ 'jira',
41
+ 'confluence',
42
+ 'supabase',
43
+ 'todoist',
44
+ 'resend',
45
+ 'cloudflare',
46
+ 'github-app',
47
+ 'mongodb-atlas',
48
+ 'vertex-ai',
49
+ ]) {
50
+ expect(keys).toContain(expected);
51
+ }
52
+ });
53
+
54
+ it('each entry is well-formed and self-consistent on its key', () => {
55
+ for (const [key, def] of Object.entries(SERVICE_PROVIDERS) as Array<
56
+ [string, ServiceProviderDef]
57
+ >) {
58
+ expect(def.provider).toBe(key);
59
+ expect(def.displayName.length).toBeGreaterThan(0);
60
+ expect(def.description.length).toBeGreaterThan(0);
61
+ expect(SERVICE_AUTH_KINDS).toContain(def.authKind);
62
+ }
63
+ });
64
+
65
+ it('every provider declares a NON-EMPTY hostRules array (T11938 AC4)', () => {
66
+ for (const [key, def] of Object.entries(SERVICE_PROVIDERS) as Array<
67
+ [string, ServiceProviderDef]
68
+ >) {
69
+ expect(Array.isArray(def.hostRules), `${key} must declare hostRules`).toBe(true);
70
+ expect(def.hostRules.length, `${key} hostRules must be non-empty`).toBeGreaterThan(0);
71
+ for (const rule of def.hostRules) {
72
+ expect(rule.host.length).toBeGreaterThan(0);
73
+ expect(['bearer', 'basic-x-access-token', 'header']).toContain(rule.strategy);
74
+ }
75
+ }
76
+ });
77
+
78
+ it('every OAuth (non api-key) provider declares a refresh config OR an oauth re-auth shape (T11938 AC4)', () => {
79
+ for (const [key, def] of Object.entries(SERVICE_PROVIDERS) as Array<
80
+ [string, ServiceProviderDef]
81
+ >) {
82
+ if (def.authKind === 'api-key') {
83
+ // api-key services have no token to refresh — refresh MUST be absent.
84
+ expect(def.refresh, `${key} (api-key) must not declare refresh`).toBeUndefined();
85
+ continue;
86
+ }
87
+ // An OAuth provider must be refreshable OR at least carry a full oauth
88
+ // block the flow can re-authorize against.
89
+ const hasRefresh = def.refresh !== undefined;
90
+ const hasOAuth = def.oauth !== undefined;
91
+ expect(hasRefresh || hasOAuth, `${key} must declare refresh or oauth`).toBe(true);
92
+ if (hasRefresh && def.refresh) {
93
+ expect(REFRESH_KINDS).toContain(def.refresh.kind);
94
+ expect(def.refresh.tokenUrl.length).toBeGreaterThan(0);
95
+ }
96
+ }
97
+ });
98
+
99
+ it('declares the three special-case refresh-kind discriminants (T11938 AC3)', () => {
100
+ const kinds = new Set(
101
+ Object.values(SERVICE_PROVIDERS)
102
+ .map((d) => d.refresh?.kind)
103
+ .filter((k): k is NonNullable<typeof k> => k !== undefined),
104
+ );
105
+ expect(kinds.has('github-app')).toBe(true);
106
+ expect(kinds.has('client-credentials')).toBe(true);
107
+ expect(kinds.has('refresh-token')).toBe(true);
108
+ });
109
+
110
+ it('github is a classic OAuth2 web flow with complete endpoints (T11937)', () => {
111
+ const gh = SERVICE_PROVIDERS['github'];
112
+ expect(gh).toBeDefined();
113
+ expect(gh?.authKind).toBe('oauth2');
114
+ expect(gh?.oauth?.authorizationEndpoint).toContain('github.com');
115
+ expect(gh?.oauth?.tokenEndpoint).toContain('github.com');
116
+ expect(gh?.oauth?.clientId.length).toBeGreaterThan(0);
117
+ expect(gh?.defaultScopes).toContain('repo');
118
+ });
119
+
120
+ it('google is an installed-app OAuth2 + PKCE flow that requests offline access (T11937)', () => {
121
+ const g = SERVICE_PROVIDERS['google'];
122
+ expect(g).toBeDefined();
123
+ expect(g?.authKind).toBe('oauth2-pkce');
124
+ expect(g?.oauth?.authorizationEndpoint).toContain('accounts.google.com');
125
+ expect(g?.oauth?.tokenEndpoint).toContain('oauth2.googleapis.com');
126
+ // Google needs access_type=offline + prompt=consent to issue a refresh token.
127
+ expect(g?.oauth?.extraAuthParams?.['access_type']).toBe('offline');
128
+ });
129
+ });
@@ -43,6 +43,9 @@ const fakeTools: GuardedToolSurface = {
43
43
  async executeShell(_input): Promise<ExecuteShellResult> {
44
44
  return { stdout: '', stderr: '', code: 0 };
45
45
  },
46
+ async executePty(_input) {
47
+ return { stdout: '', stderr: '', code: 0, mode: 'spawn', ptyFellBack: false };
48
+ },
46
49
  async runGit(_input): Promise<ExecuteShellResult> {
47
50
  return { stdout: '', stderr: '', code: 0 };
48
51
  },
@@ -325,9 +325,8 @@ export type GateResultDetails =
325
325
  * Persisted to `lifecycle_gate_results` and summarised in
326
326
  * `task.verification.gateResults`.
327
327
  *
328
- * NOTE: Named `AcceptanceGateResult` (not `GateResult`) to avoid collision
329
- * with the existing {@link GateResult} from `./warp-chain.js` which describes
330
- * WarpChain LOOM-gate evaluation outcomes.
328
+ * NOTE: Named `AcceptanceGateResult` (not `GateResult`) for historical
329
+ * clarity — it describes acceptance-criterion gate outcomes specifically.
331
330
  */
332
331
  export interface AcceptanceGateResult {
333
332
  /** Zero-based index in the task's acceptance array. */
package/src/boundary.ts CHANGED
@@ -266,25 +266,6 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
266
266
  rationale:
267
267
  'napi binding shim for cant-core + cant-runtime. Loaded via packages/cant/src/native-loader.ts and packages/core/src/system/dependencies.ts. Internal-only — shipped via per-platform packages/cant-napi-* npm packages, not crates.io. cant-router bindings removed (E8 T11432).',
268
268
  },
269
- {
270
- module: 'cant-router',
271
- intent: 'cpu-bound',
272
- rustCore: 'crates/cant-router',
273
- canonicalHome: 'cleocode',
274
- perfBudget: {
275
- latency_p50_ms: 5,
276
- latency_p99_ms: 50,
277
- },
278
- safetyBudget: {
279
- panic_unwind: 'forbidden',
280
- root_escape: 'forbidden',
281
- },
282
- amendments: [],
283
- rationale:
284
- 'Cant Router (984 LOC) — RETIRED from cant-napi dep-set (E8 T11432: zero live TS consumers). ' +
285
- 'Preserved in the workspace for reference; not linked into the published napi binary. ' +
286
- 'publish=false. Relocation/deletion tracked in T11432.',
287
- },
288
269
  {
289
270
  module: 'cant-runtime',
290
271
  intent: 'cpu-bound',
@@ -368,7 +349,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
368
349
  },
369
350
  amendments: [],
370
351
  rationale:
371
- 'Internal Rust test harness (241 LOC) over lafs-core/cleo-conduit-core/cant-core/cant-router/signaldock-core. publish=false intentional — test code never ships.',
352
+ 'Internal Rust test harness over lafs-core/cleo-conduit-core/cant-core. publish=false intentional — test code never ships.',
372
353
  },
373
354
  {
374
355
  module: 'lafs-core',
@@ -910,6 +891,75 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
910
891
  },
911
892
  ] as const;
912
893
 
894
+ // ============================================================
895
+ // Channel Boundary Registry (T11952 · epic T11854 · saga T10419)
896
+ // ============================================================
897
+
898
+ /**
899
+ * Trust posture for an inbound channel sender.
900
+ *
901
+ * - `verified` — sender messages bypass the HITL approval gate and dispatch
902
+ * directly to the agent.
903
+ * - `unverified` — sender messages route through the resumeToken approval gate
904
+ * before dispatch (T11854 AC5).
905
+ */
906
+ export type ChannelTrustPosture = 'verified' | 'unverified';
907
+
908
+ /**
909
+ * One row in {@link CHANNEL_BOUNDARY_REGISTRY}. Declares a messaging channel's
910
+ * delivery boundary: which transport it rides, whether it crosses the network,
911
+ * whether a platform credential is required, and the default trust posture for
912
+ * inbound senders.
913
+ *
914
+ * This is the channel-layer companion to the ADR-078 module {@link BoundaryEntry}.
915
+ * Where {@link BoundaryEntry} governs Rust/TS package layering (one row per
916
+ * crate/package, validated against the filesystem), a {@link ChannelBoundaryEntry}
917
+ * governs the conversation-surface boundary (one row per channel adapter) and is
918
+ * NOT filesystem-validated — channels are runtime concepts, not on-disk modules.
919
+ *
920
+ * @see T11854 AC4 — every channel registers a boundary entry in boundary.ts.
921
+ * @see ConduitMessage — the wire shape channels exchange.
922
+ */
923
+ export interface ChannelBoundaryEntry {
924
+ /** Stable, kebab-case channel id (e.g. `'local-tui'`, `'telegram'`). */
925
+ channel: string;
926
+ /** Underlying conduit transport the adapter rides (e.g. `'local'`, `'http'`). */
927
+ transport: string;
928
+ /** Whether the channel originates outbound network traffic. */
929
+ networkEgress: 'none' | 'required';
930
+ /** Whether the channel requires a platform credential to operate. */
931
+ credentialRequired: boolean;
932
+ /** Default trust posture applied to inbound senders lacking explicit verification. */
933
+ defaultTrust: ChannelTrustPosture;
934
+ /** 1-2 sentence rationale for the channel's boundary posture. */
935
+ rationale: string;
936
+ }
937
+
938
+ /**
939
+ * Canonical registry of messaging-channel boundaries.
940
+ *
941
+ * The Local-TUI adapter (T11952) is the first reference channel: it rides the
942
+ * in-process conduit `LocalTransport`, never touches the network, needs no
943
+ * platform credential, and treats the single local operator as a verified
944
+ * sender. Platform adapters (Telegram/Discord/Slack/WhatsApp — T11855) append
945
+ * their own entries as they land.
946
+ *
947
+ * Entries are kept alphabetical by `channel` for diff-friendliness.
948
+ *
949
+ * @see T11854 AC4
950
+ */
951
+ export const CHANNEL_BOUNDARY_REGISTRY: readonly ChannelBoundaryEntry[] = [
952
+ {
953
+ channel: 'local-tui',
954
+ transport: 'local',
955
+ networkEgress: 'none',
956
+ credentialRequired: false,
957
+ defaultTrust: 'verified',
958
+ rationale:
959
+ 'In-process stdin/stdout terminal channel riding the conduit LocalTransport (conduit.db). Fully offline, daemon-OFF, no network, no platform credential; the single local operator is a verified sender.',
960
+ },
961
+ ] as const;
962
+
913
963
  // ============================================================
914
964
  // TOOLS-vs-SKILLS boundary (E3 · T11409 · SG-PACKAGE-ARCH)
915
965
  // ============================================================