@cleocode/core 2026.4.11 → 2026.4.12

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 (169) hide show
  1. package/dist/codebase-map/analyzers/architecture.d.ts.map +1 -1
  2. package/dist/codebase-map/analyzers/architecture.js +0 -1
  3. package/dist/codebase-map/analyzers/architecture.js.map +1 -1
  4. package/dist/conduit/local-transport.d.ts +18 -8
  5. package/dist/conduit/local-transport.d.ts.map +1 -1
  6. package/dist/conduit/local-transport.js +23 -13
  7. package/dist/conduit/local-transport.js.map +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +0 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/errors.d.ts +19 -0
  12. package/dist/errors.d.ts.map +1 -1
  13. package/dist/errors.js +6 -0
  14. package/dist/errors.js.map +1 -1
  15. package/dist/index.js +175 -68950
  16. package/dist/index.js.map +1 -7
  17. package/dist/init.d.ts +1 -2
  18. package/dist/init.d.ts.map +1 -1
  19. package/dist/init.js +1 -2
  20. package/dist/init.js.map +1 -1
  21. package/dist/internal.d.ts +8 -3
  22. package/dist/internal.d.ts.map +1 -1
  23. package/dist/internal.js +13 -6
  24. package/dist/internal.js.map +1 -1
  25. package/dist/memory/learnings.d.ts +2 -2
  26. package/dist/memory/patterns.d.ts +6 -6
  27. package/dist/output.d.ts +32 -11
  28. package/dist/output.d.ts.map +1 -1
  29. package/dist/output.js +67 -67
  30. package/dist/output.js.map +1 -1
  31. package/dist/paths.js +80 -14
  32. package/dist/paths.js.map +1 -1
  33. package/dist/skills/dynamic-skill-generator.d.ts +0 -2
  34. package/dist/skills/dynamic-skill-generator.d.ts.map +1 -1
  35. package/dist/skills/dynamic-skill-generator.js.map +1 -1
  36. package/dist/store/agent-registry-accessor.d.ts +203 -12
  37. package/dist/store/agent-registry-accessor.d.ts.map +1 -1
  38. package/dist/store/agent-registry-accessor.js +618 -100
  39. package/dist/store/agent-registry-accessor.js.map +1 -1
  40. package/dist/store/api-key-kdf.d.ts +73 -0
  41. package/dist/store/api-key-kdf.d.ts.map +1 -0
  42. package/dist/store/api-key-kdf.js +84 -0
  43. package/dist/store/api-key-kdf.js.map +1 -0
  44. package/dist/store/cleanup-legacy.js +171 -0
  45. package/dist/store/cleanup-legacy.js.map +1 -0
  46. package/dist/store/conduit-sqlite.d.ts +184 -0
  47. package/dist/store/conduit-sqlite.d.ts.map +1 -0
  48. package/dist/store/conduit-sqlite.js +570 -0
  49. package/dist/store/conduit-sqlite.js.map +1 -0
  50. package/dist/store/global-salt.d.ts +78 -0
  51. package/dist/store/global-salt.d.ts.map +1 -0
  52. package/dist/store/global-salt.js +147 -0
  53. package/dist/store/global-salt.js.map +1 -0
  54. package/dist/store/migrate-signaldock-to-conduit.d.ts +81 -0
  55. package/dist/store/migrate-signaldock-to-conduit.d.ts.map +1 -0
  56. package/dist/store/migrate-signaldock-to-conduit.js +555 -0
  57. package/dist/store/migrate-signaldock-to-conduit.js.map +1 -0
  58. package/dist/store/nexus-sqlite.js +28 -3
  59. package/dist/store/nexus-sqlite.js.map +1 -1
  60. package/dist/store/signaldock-sqlite.d.ts +122 -19
  61. package/dist/store/signaldock-sqlite.d.ts.map +1 -1
  62. package/dist/store/signaldock-sqlite.js +401 -251
  63. package/dist/store/signaldock-sqlite.js.map +1 -1
  64. package/dist/store/sqlite-backup.js +122 -4
  65. package/dist/store/sqlite-backup.js.map +1 -1
  66. package/dist/system/backup.d.ts +0 -26
  67. package/dist/system/backup.d.ts.map +1 -1
  68. package/dist/system/runtime.d.ts +0 -2
  69. package/dist/system/runtime.d.ts.map +1 -1
  70. package/dist/system/runtime.js +3 -3
  71. package/dist/system/runtime.js.map +1 -1
  72. package/dist/tasks/add.d.ts +1 -1
  73. package/dist/tasks/add.d.ts.map +1 -1
  74. package/dist/tasks/add.js +98 -23
  75. package/dist/tasks/add.js.map +1 -1
  76. package/dist/tasks/complete.d.ts.map +1 -1
  77. package/dist/tasks/complete.js +4 -1
  78. package/dist/tasks/complete.js.map +1 -1
  79. package/dist/tasks/find.d.ts.map +1 -1
  80. package/dist/tasks/find.js +4 -1
  81. package/dist/tasks/find.js.map +1 -1
  82. package/dist/tasks/labels.d.ts.map +1 -1
  83. package/dist/tasks/labels.js +4 -1
  84. package/dist/tasks/labels.js.map +1 -1
  85. package/dist/tasks/relates.d.ts.map +1 -1
  86. package/dist/tasks/relates.js +16 -4
  87. package/dist/tasks/relates.js.map +1 -1
  88. package/dist/tasks/show.d.ts.map +1 -1
  89. package/dist/tasks/show.js +4 -1
  90. package/dist/tasks/show.js.map +1 -1
  91. package/dist/tasks/update.d.ts.map +1 -1
  92. package/dist/tasks/update.js +32 -6
  93. package/dist/tasks/update.js.map +1 -1
  94. package/dist/validation/engine.d.ts.map +1 -1
  95. package/dist/validation/engine.js +16 -4
  96. package/dist/validation/engine.js.map +1 -1
  97. package/dist/validation/param-utils.d.ts +5 -3
  98. package/dist/validation/param-utils.d.ts.map +1 -1
  99. package/dist/validation/param-utils.js +8 -6
  100. package/dist/validation/param-utils.js.map +1 -1
  101. package/dist/validation/protocols/_shared.d.ts.map +1 -1
  102. package/dist/validation/protocols/_shared.js +13 -6
  103. package/dist/validation/protocols/_shared.js.map +1 -1
  104. package/package.json +7 -7
  105. package/src/adapters/__tests__/manager.test.ts +0 -1
  106. package/src/codebase-map/analyzers/architecture.ts +0 -1
  107. package/src/conduit/__tests__/local-credential-flow.test.ts +20 -18
  108. package/src/conduit/__tests__/local-transport.test.ts +14 -12
  109. package/src/conduit/local-transport.ts +23 -13
  110. package/src/config.ts +0 -1
  111. package/src/errors.ts +24 -0
  112. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +2 -5
  113. package/src/init.ts +1 -2
  114. package/src/internal.ts +49 -2
  115. package/src/lifecycle/cant/lifecycle-rcasd.cant +133 -0
  116. package/src/memory/__tests__/engine-compat.test.ts +2 -2
  117. package/src/memory/__tests__/pipeline-manifest-sqlite.test.ts +4 -4
  118. package/src/observability/__tests__/index.test.ts +4 -4
  119. package/src/observability/__tests__/log-filter.test.ts +4 -4
  120. package/src/output.ts +73 -75
  121. package/src/sessions/__tests__/session-grade.integration.test.ts +1 -1
  122. package/src/sessions/__tests__/session-grade.test.ts +2 -2
  123. package/src/skills/__tests__/dynamic-skill-generator.test.ts +0 -2
  124. package/src/skills/dynamic-skill-generator.ts +0 -2
  125. package/src/store/__tests__/agent-registry-accessor.test.ts +807 -0
  126. package/src/store/__tests__/api-key-kdf.test.ts +113 -0
  127. package/src/store/__tests__/conduit-sqlite.test.ts +413 -0
  128. package/src/store/__tests__/global-salt.test.ts +195 -0
  129. package/src/store/__tests__/migrate-signaldock-to-conduit.test.ts +715 -0
  130. package/src/store/__tests__/signaldock-sqlite.test.ts +652 -0
  131. package/src/store/__tests__/sqlite-backup-global.test.ts +307 -3
  132. package/src/store/__tests__/sqlite-backup.test.ts +5 -1
  133. package/src/store/__tests__/t310-integration.test.ts +1150 -0
  134. package/src/store/agent-registry-accessor.ts +847 -140
  135. package/src/store/api-key-kdf.ts +104 -0
  136. package/src/store/conduit-sqlite.ts +655 -0
  137. package/src/store/global-salt.ts +175 -0
  138. package/src/store/migrate-signaldock-to-conduit.ts +669 -0
  139. package/src/store/signaldock-sqlite.ts +431 -254
  140. package/src/store/sqlite-backup.ts +185 -10
  141. package/src/system/backup.ts +2 -62
  142. package/src/system/runtime.ts +4 -6
  143. package/src/tasks/__tests__/error-hints.test.ts +256 -0
  144. package/src/tasks/add.ts +99 -9
  145. package/src/tasks/complete.ts +4 -1
  146. package/src/tasks/find.ts +4 -1
  147. package/src/tasks/labels.ts +4 -1
  148. package/src/tasks/relates.ts +16 -4
  149. package/src/tasks/show.ts +4 -1
  150. package/src/tasks/update.ts +32 -3
  151. package/src/validation/__tests__/error-hints.test.ts +97 -0
  152. package/src/validation/engine.ts +16 -1
  153. package/src/validation/param-utils.ts +10 -7
  154. package/src/validation/protocols/_shared.ts +14 -6
  155. package/src/validation/protocols/cant/architecture-decision.cant +80 -0
  156. package/src/validation/protocols/cant/artifact-publish.cant +95 -0
  157. package/src/validation/protocols/cant/consensus.cant +74 -0
  158. package/src/validation/protocols/cant/contribution.cant +82 -0
  159. package/src/validation/protocols/cant/decomposition.cant +92 -0
  160. package/src/validation/protocols/cant/implementation.cant +67 -0
  161. package/src/validation/protocols/cant/provenance.cant +88 -0
  162. package/src/validation/protocols/cant/release.cant +96 -0
  163. package/src/validation/protocols/cant/research.cant +66 -0
  164. package/src/validation/protocols/cant/specification.cant +67 -0
  165. package/src/validation/protocols/cant/testing.cant +88 -0
  166. package/src/validation/protocols/cant/validation.cant +65 -0
  167. package/src/validation/protocols/protocols-markdown/decomposition.md +0 -4
  168. package/templates/config.template.json +0 -1
  169. package/templates/global-config.template.json +0 -1
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Unit tests for global-salt subsystem.
3
+ *
4
+ * @task T348
5
+ * @epic T310
6
+ */
7
+
8
+ import crypto from 'node:crypto';
9
+ import fs from 'node:fs';
10
+ import os from 'node:os';
11
+ import path from 'node:path';
12
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
13
+ import {
14
+ __clearGlobalSaltCache,
15
+ GLOBAL_SALT_FILENAME,
16
+ GLOBAL_SALT_SIZE,
17
+ getGlobalSalt,
18
+ getGlobalSaltPath,
19
+ validateGlobalSalt,
20
+ } from '../global-salt.js';
21
+
22
+ // Mock getCleoHome to use a per-test tmp directory
23
+ let tmpHome: string;
24
+ vi.mock('../../paths.js', () => ({
25
+ getCleoHome: () => tmpHome,
26
+ }));
27
+
28
+ describe('global-salt', () => {
29
+ beforeEach(() => {
30
+ tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'cleo-t348-'));
31
+ __clearGlobalSaltCache();
32
+ });
33
+
34
+ afterEach(() => {
35
+ fs.rmSync(tmpHome, { recursive: true, force: true });
36
+ });
37
+
38
+ // -------------------------------------------------------------------------
39
+ // getGlobalSaltPath
40
+ // -------------------------------------------------------------------------
41
+
42
+ describe('getGlobalSaltPath', () => {
43
+ it('returns <cleoHome>/global-salt', () => {
44
+ expect(getGlobalSaltPath()).toBe(path.join(tmpHome, GLOBAL_SALT_FILENAME));
45
+ });
46
+ });
47
+
48
+ // -------------------------------------------------------------------------
49
+ // getGlobalSalt — first-run generation
50
+ // -------------------------------------------------------------------------
51
+
52
+ describe('getGlobalSalt — first-run generation', () => {
53
+ it('generates a Buffer of exactly 32 bytes on first call', () => {
54
+ const salt = getGlobalSalt();
55
+ expect(salt).toBeInstanceOf(Buffer);
56
+ expect(salt.length).toBe(GLOBAL_SALT_SIZE);
57
+ });
58
+
59
+ it('persists the salt file to disk', () => {
60
+ getGlobalSalt();
61
+ expect(fs.existsSync(getGlobalSaltPath())).toBe(true);
62
+ });
63
+
64
+ it('writes the salt file with mode 0o600 on POSIX', () => {
65
+ if (process.platform === 'win32') return;
66
+ getGlobalSalt();
67
+ const mode = fs.statSync(getGlobalSaltPath()).mode & 0o777;
68
+ expect(mode).toBe(0o600);
69
+ });
70
+
71
+ it('creates the cleoHome directory if it does not exist', () => {
72
+ // Remove the tmp dir to simulate a missing cleoHome
73
+ fs.rmSync(tmpHome, { recursive: true, force: true });
74
+ expect(() => getGlobalSalt()).not.toThrow();
75
+ expect(fs.existsSync(tmpHome)).toBe(true);
76
+ expect(fs.existsSync(getGlobalSaltPath())).toBe(true);
77
+ });
78
+
79
+ it('leaves no lingering .tmp- files after a successful write', () => {
80
+ getGlobalSalt();
81
+ const files = fs.readdirSync(tmpHome);
82
+ const lingering = files.filter((f) => f.includes('.tmp-'));
83
+ expect(lingering).toEqual([]);
84
+ });
85
+ });
86
+
87
+ // -------------------------------------------------------------------------
88
+ // getGlobalSalt — memoization
89
+ // -------------------------------------------------------------------------
90
+
91
+ describe('getGlobalSalt — memoization', () => {
92
+ it('returns the same Buffer instance on repeated calls (memoized)', () => {
93
+ const first = getGlobalSalt();
94
+ const second = getGlobalSalt();
95
+ expect(second.equals(first)).toBe(true);
96
+ });
97
+
98
+ it('returns the same bytes after a cache clear (simulated process restart)', () => {
99
+ const first = getGlobalSalt();
100
+ __clearGlobalSaltCache();
101
+ const second = getGlobalSalt();
102
+ expect(second.equals(first)).toBe(true);
103
+ });
104
+
105
+ it('NEVER overwrites an existing file on subsequent calls', () => {
106
+ getGlobalSalt();
107
+ const mtime1 = fs.statSync(getGlobalSaltPath()).mtimeMs;
108
+
109
+ // Spin-wait 2 ms so mtime could differ if the file were rewritten
110
+ const until = Date.now() + 2;
111
+ while (Date.now() < until) {
112
+ // busy wait
113
+ }
114
+
115
+ __clearGlobalSaltCache();
116
+ getGlobalSalt();
117
+ const mtime2 = fs.statSync(getGlobalSaltPath()).mtimeMs;
118
+ expect(mtime2).toBe(mtime1);
119
+ });
120
+ });
121
+
122
+ // -------------------------------------------------------------------------
123
+ // getGlobalSalt — validation on existing file
124
+ // -------------------------------------------------------------------------
125
+
126
+ describe('getGlobalSalt — validation on existing file', () => {
127
+ it('throws with "wrong size" when the file has incorrect byte count', () => {
128
+ fs.mkdirSync(tmpHome, { recursive: true });
129
+ fs.writeFileSync(getGlobalSaltPath(), Buffer.alloc(16), { mode: 0o600 });
130
+ __clearGlobalSaltCache();
131
+ expect(() => getGlobalSalt()).toThrow(/wrong size/);
132
+ });
133
+
134
+ it('throws with "wrong permissions" on POSIX when mode is too permissive', () => {
135
+ if (process.platform === 'win32') return;
136
+ fs.mkdirSync(tmpHome, { recursive: true });
137
+ const randomBytes = crypto.randomBytes(GLOBAL_SALT_SIZE);
138
+ fs.writeFileSync(getGlobalSaltPath(), randomBytes, { mode: 0o644 });
139
+ __clearGlobalSaltCache();
140
+ expect(() => getGlobalSalt()).toThrow(/wrong permissions/);
141
+ });
142
+ });
143
+
144
+ // -------------------------------------------------------------------------
145
+ // validateGlobalSalt
146
+ // -------------------------------------------------------------------------
147
+
148
+ describe('validateGlobalSalt', () => {
149
+ it('is a no-op when the salt file does not exist', () => {
150
+ expect(() => validateGlobalSalt()).not.toThrow();
151
+ });
152
+
153
+ it('does not throw when the salt file is valid', () => {
154
+ getGlobalSalt(); // generates a valid file
155
+ expect(() => validateGlobalSalt()).not.toThrow();
156
+ });
157
+
158
+ it('throws with "validation failed" and "size" when size is wrong', () => {
159
+ fs.mkdirSync(tmpHome, { recursive: true });
160
+ fs.writeFileSync(getGlobalSaltPath(), Buffer.alloc(10), { mode: 0o600 });
161
+ expect(() => validateGlobalSalt()).toThrow(/validation failed.*size/i);
162
+ });
163
+
164
+ it('throws with "validation failed" and "permissions" on POSIX when mode is wrong', () => {
165
+ if (process.platform === 'win32') return;
166
+ fs.mkdirSync(tmpHome, { recursive: true });
167
+ const randomBytes = crypto.randomBytes(GLOBAL_SALT_SIZE);
168
+ fs.writeFileSync(getGlobalSaltPath(), randomBytes, { mode: 0o644 });
169
+ expect(() => validateGlobalSalt()).toThrow(/validation failed.*permissions/i);
170
+ });
171
+ });
172
+
173
+ // -------------------------------------------------------------------------
174
+ // __clearGlobalSaltCache — test utility
175
+ // -------------------------------------------------------------------------
176
+
177
+ describe('__clearGlobalSaltCache', () => {
178
+ it('allows re-generation in a fresh tmp dir after cache is cleared', () => {
179
+ const first = getGlobalSalt();
180
+
181
+ // Simulate switching to a different machine / user by using a new tmpHome
182
+ fs.rmSync(tmpHome, { recursive: true, force: true });
183
+ tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'cleo-t348-fresh-'));
184
+ __clearGlobalSaltCache();
185
+
186
+ const second = getGlobalSalt();
187
+ // Different directory => different (freshly generated) salt
188
+ expect(fs.existsSync(getGlobalSaltPath())).toBe(true);
189
+ // The new salt is a valid 32-byte buffer (may or may not equal first by chance)
190
+ expect(second.length).toBe(GLOBAL_SALT_SIZE);
191
+ // Suppress unused variable warning
192
+ expect(first.length).toBe(GLOBAL_SALT_SIZE);
193
+ });
194
+ });
195
+ });