@cleocode/contracts 2026.6.13 → 2026.6.15

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 (140) hide show
  1. package/dist/__tests__/operation-def.test.js +4 -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__/service-provider.test.d.ts +17 -0
  8. package/dist/__tests__/service-provider.test.d.ts.map +1 -0
  9. package/dist/__tests__/service-provider.test.js +108 -0
  10. package/dist/__tests__/service-provider.test.js.map +1 -0
  11. package/dist/boundary.d.ts +52 -0
  12. package/dist/boundary.d.ts.map +1 -1
  13. package/dist/boundary.js +23 -0
  14. package/dist/boundary.js.map +1 -1
  15. package/dist/branch-lock.d.ts +4 -0
  16. package/dist/branch-lock.d.ts.map +1 -1
  17. package/dist/branch-lock.js.map +1 -1
  18. package/dist/conduit.d.ts +171 -0
  19. package/dist/conduit.d.ts.map +1 -1
  20. package/dist/config/index.d.ts +23 -0
  21. package/dist/config/index.d.ts.map +1 -0
  22. package/dist/config/index.js +22 -0
  23. package/dist/config/index.js.map +1 -0
  24. package/dist/config.d.ts +109 -1
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.d.ts +21 -0
  27. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.d.ts.map +1 -0
  28. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js +84 -0
  29. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js.map +1 -0
  30. package/dist/dispatch/identity.d.ts +1 -1
  31. package/dist/dispatch/identity.d.ts.map +1 -1
  32. package/dist/dispatch/identity.js +20 -0
  33. package/dist/dispatch/identity.js.map +1 -1
  34. package/dist/dispatch/operation-def.d.ts +20 -0
  35. package/dist/dispatch/operation-def.d.ts.map +1 -1
  36. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  37. package/dist/dispatch/operations-registry.js +1030 -46
  38. package/dist/dispatch/operations-registry.js.map +1 -1
  39. package/dist/index.d.ts +17 -8
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +15 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/lease-ipc/__tests__/freeze.test.js +92 -0
  44. package/dist/lease-ipc/__tests__/freeze.test.js.map +1 -1
  45. package/dist/lease-ipc/index.d.ts +2 -2
  46. package/dist/lease-ipc/index.d.ts.map +1 -1
  47. package/dist/lease-ipc/index.js +1 -1
  48. package/dist/lease-ipc/index.js.map +1 -1
  49. package/dist/lease-ipc/messages.d.ts +167 -3
  50. package/dist/lease-ipc/messages.d.ts.map +1 -1
  51. package/dist/lease-ipc/messages.js +93 -0
  52. package/dist/lease-ipc/messages.js.map +1 -1
  53. package/dist/llm/catalog-schema.d.ts +242 -0
  54. package/dist/llm/catalog-schema.d.ts.map +1 -0
  55. package/dist/llm/catalog-schema.js +153 -0
  56. package/dist/llm/catalog-schema.js.map +1 -0
  57. package/dist/llm/catalog-schema.json +139 -0
  58. package/dist/llm/index.d.ts +15 -0
  59. package/dist/llm/index.d.ts.map +1 -0
  60. package/dist/llm/index.js +14 -0
  61. package/dist/llm/index.js.map +1 -0
  62. package/dist/llm/oauth.d.ts +16 -2
  63. package/dist/llm/oauth.d.ts.map +1 -1
  64. package/dist/llm/onboarding.d.ts +126 -0
  65. package/dist/llm/onboarding.d.ts.map +1 -0
  66. package/dist/llm/onboarding.js +34 -0
  67. package/dist/llm/onboarding.js.map +1 -0
  68. package/dist/llm/provider-def.d.ts +254 -0
  69. package/dist/llm/provider-def.d.ts.map +1 -0
  70. package/dist/llm/provider-def.js +93 -0
  71. package/dist/llm/provider-def.js.map +1 -0
  72. package/dist/llm/system-resolver.d.ts +42 -0
  73. package/dist/llm/system-resolver.d.ts.map +1 -1
  74. package/dist/operations/admin.d.ts +124 -1
  75. package/dist/operations/admin.d.ts.map +1 -1
  76. package/dist/operations/entities.d.ts +110 -0
  77. package/dist/operations/entities.d.ts.map +1 -0
  78. package/dist/operations/entities.js +695 -0
  79. package/dist/operations/entities.js.map +1 -0
  80. package/dist/operations/llm.d.ts +16 -1
  81. package/dist/operations/llm.d.ts.map +1 -1
  82. package/dist/operations/output-contracts-data.d.ts.map +1 -1
  83. package/dist/operations/output-contracts-data.js +230 -0
  84. package/dist/operations/output-contracts-data.js.map +1 -1
  85. package/dist/operations/service.d.ts +68 -0
  86. package/dist/operations/service.d.ts.map +1 -0
  87. package/dist/operations/service.js +336 -0
  88. package/dist/operations/service.js.map +1 -0
  89. package/dist/operations/tasks.d.ts +109 -0
  90. package/dist/operations/tasks.d.ts.map +1 -1
  91. package/dist/operations/tasks.js.map +1 -1
  92. package/dist/operations/worktree.d.ts +11 -1
  93. package/dist/operations/worktree.d.ts.map +1 -1
  94. package/dist/playbook.d.ts +56 -0
  95. package/dist/playbook.d.ts.map +1 -1
  96. package/dist/release/plan.d.ts +5 -5
  97. package/dist/spawn.d.ts +70 -0
  98. package/dist/spawn.d.ts.map +1 -1
  99. package/dist/spawn.js +1 -0
  100. package/dist/spawn.js.map +1 -1
  101. package/dist/vault/service-provider.d.ts +343 -0
  102. package/dist/vault/service-provider.d.ts.map +1 -0
  103. package/dist/vault/service-provider.js +857 -0
  104. package/dist/vault/service-provider.js.map +1 -0
  105. package/dist/workgraph.d.ts +12 -12
  106. package/package.json +10 -2
  107. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  108. package/src/__tests__/operation-def.test.ts +6 -1
  109. package/src/__tests__/provider-def.test.ts +93 -0
  110. package/src/__tests__/service-provider.test.ts +129 -0
  111. package/src/boundary.ts +69 -0
  112. package/src/branch-lock.ts +4 -0
  113. package/src/conduit.ts +200 -0
  114. package/src/config/index.ts +35 -0
  115. package/src/config.ts +116 -1
  116. package/src/dispatch/__tests__/tasks-gateway-crud-ops.test.ts +99 -0
  117. package/src/dispatch/identity.ts +20 -0
  118. package/src/dispatch/operation-def.ts +21 -0
  119. package/src/dispatch/operations-registry.ts +1062 -36
  120. package/src/index.ts +147 -2
  121. package/src/lease-ipc/__tests__/freeze.test.ts +96 -0
  122. package/src/lease-ipc/index.ts +12 -0
  123. package/src/lease-ipc/messages.ts +117 -0
  124. package/src/llm/catalog-schema.json +139 -0
  125. package/src/llm/catalog-schema.ts +184 -0
  126. package/src/llm/index.ts +29 -0
  127. package/src/llm/oauth.ts +16 -2
  128. package/src/llm/onboarding.ts +131 -0
  129. package/src/llm/provider-def.ts +291 -0
  130. package/src/llm/system-resolver.ts +45 -0
  131. package/src/operations/admin.ts +144 -0
  132. package/src/operations/entities.ts +758 -0
  133. package/src/operations/llm.ts +16 -0
  134. package/src/operations/output-contracts-data.ts +264 -0
  135. package/src/operations/service.ts +360 -0
  136. package/src/operations/tasks.ts +119 -0
  137. package/src/operations/worktree.ts +11 -0
  138. package/src/playbook.ts +56 -0
  139. package/src/spawn.ts +79 -0
  140. package/src/vault/service-provider.ts +1137 -0
package/src/config.ts CHANGED
@@ -638,9 +638,10 @@ export interface LlmConfig {
638
638
  * role that does NOT pin its own provider/model/profile. This is the
639
639
  * user-selectable default binding for background roles — it is NEVER a
640
640
  * hardcoded provider in code; the owner re-selects it with
641
- * `cleo llm bind --default <profileName>`.
641
+ * `cleo config set llm.defaultProfile <profileName>`.
642
642
  *
643
643
  * @task T11617
644
+ * @task T11723
644
645
  */
645
646
  defaultProfile?: string;
646
647
  /**
@@ -750,6 +751,98 @@ export interface LeadRollupConfig {
750
751
  mode?: LeadRollupMode;
751
752
  }
752
753
 
754
+ /**
755
+ * Daemon lifecycle configuration.
756
+ *
757
+ * Controls whether the postinstall hook is allowed to auto-enable and start
758
+ * the cleo-daemon service. When `autoStart` is `false`, postinstall NEVER
759
+ * enables or starts the service — even on a fresh install. This flag persists
760
+ * across upgrades so the operator's intent survives `npm install -g`.
761
+ *
762
+ * Configure via `~/.local/share/cleo/config.json` (global config):
763
+ * ```json
764
+ * { "daemon": { "autoStart": false } }
765
+ * ```
766
+ * Or via env var override: `CLEO_DAEMON_DISABLE=1` (per-session, non-persistent).
767
+ *
768
+ * @task T11984
769
+ */
770
+ export interface DaemonConfig {
771
+ /**
772
+ * Whether the postinstall hook may auto-enable and start the cleo-daemon.
773
+ *
774
+ * - `true` (default): first install enables+starts; upgrade respects the
775
+ * current systemd/launchd enabled state.
776
+ * - `false`: postinstall NEVER enables or starts the daemon. The unit file
777
+ * is still written so the operator can activate later via
778
+ * `cleo daemon enable`.
779
+ *
780
+ * @defaultValue true
781
+ */
782
+ autoStart: boolean;
783
+ }
784
+
785
+ // ---------------------------------------------------------------------------
786
+ // Resources config (T11994 · PSI + MemAvailable sensing)
787
+ // ---------------------------------------------------------------------------
788
+
789
+ /**
790
+ * PSI pressure thresholds for the ResourceMonitor.
791
+ *
792
+ * Values are percentage points (0–100) for `avg10` exponential moving averages.
793
+ * Defaults sit far below systemd-oomd's Fedora kill line (80%/20s on
794
+ * user@1000.service).
795
+ *
796
+ * @task T11994
797
+ */
798
+ export interface ResourcesPsiConfig {
799
+ /**
800
+ * Enter `hold` state when `some avg10` exceeds this value.
801
+ * @defaultValue 10
802
+ */
803
+ holdSomeAvg10?: number;
804
+ /**
805
+ * Enter `backoff` state when `some avg10` exceeds this value.
806
+ * @defaultValue 20
807
+ */
808
+ backoffSomeAvg10?: number;
809
+ /**
810
+ * Enter `hold` state when `full avg10` exceeds this value.
811
+ * @defaultValue 5
812
+ */
813
+ holdFullAvg10?: number;
814
+ /**
815
+ * Enter `backoff` state when `full avg10` exceeds this value.
816
+ * @defaultValue 10
817
+ */
818
+ backoffFullAvg10?: number;
819
+ }
820
+
821
+ /**
822
+ * Resource monitor configuration.
823
+ *
824
+ * Optional block — all fields have safe defaults. Maps to the
825
+ * `resources.*` config namespace (Amendment 5 · T11994).
826
+ *
827
+ * @task T11994
828
+ */
829
+ export interface ResourcesConfig {
830
+ /**
831
+ * PSI pressure thresholds (percentage points).
832
+ *
833
+ * @defaultValue undefined (monitor uses built-in defaults)
834
+ */
835
+ psi?: ResourcesPsiConfig;
836
+ /**
837
+ * Minimum free memory headroom in MiB.
838
+ *
839
+ * Used in degraded mode (PSI absent) as the sole availability signal.
840
+ *
841
+ * @defaultValue 256
842
+ */
843
+ headroomMb?: number;
844
+ }
845
+
753
846
  /** CLEO project configuration (config.json). */
754
847
  export interface CleoConfig {
755
848
  /** Configuration schema version string. */
@@ -837,6 +930,28 @@ export interface CleoConfig {
837
930
  * @saga T10377
838
931
  */
839
932
  leadRollup?: LeadRollupConfig;
933
+ /**
934
+ * Daemon lifecycle settings.
935
+ *
936
+ * When absent, defaults to `{ autoStart: true }` — matching the legacy
937
+ * install behaviour. Set `autoStart: false` to prevent postinstall from
938
+ * ever enabling or starting the cleo-daemon (persistent opt-out).
939
+ *
940
+ * @defaultValue undefined (treated as { autoStart: true })
941
+ * @task T11984
942
+ */
943
+ daemon?: DaemonConfig;
944
+ /**
945
+ * Resource monitor configuration — PSI pressure thresholds and memory
946
+ * headroom for the daemon-off governor and continuous supervisor mode.
947
+ *
948
+ * When absent, the ResourceMonitor uses built-in defaults which sit well
949
+ * below systemd-oomd's Fedora kill line (80%/20s on user@1000.service).
950
+ *
951
+ * @defaultValue undefined (ResourceMonitor uses built-in defaults)
952
+ * @task T11994
953
+ */
954
+ resources?: ResourcesConfig;
840
955
  }
841
956
 
842
957
  /**
@@ -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
+ });
@@ -104,6 +104,26 @@ export const CANONICAL_DOMAINS = [
104
104
  // regression emits ONE leased `selfimprove_dhq` row + ONE DRAFT PR. Default
105
105
  // OFF (requires --execute); the engine lives in CORE (P5 self-improvement spec).
106
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',
107
127
  ] as const;
108
128
 
109
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
  *