@decibelsystems/tools 2.1.4 → 3.0.0

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 (295) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/README.md +1 -1
  3. package/dist/agentPresence.d.ts +80 -0
  4. package/dist/agentPresence.d.ts.map +1 -1
  5. package/dist/agentPresence.js +177 -0
  6. package/dist/agentPresence.js.map +1 -1
  7. package/dist/agentic/index.d.ts +18 -8
  8. package/dist/agentic/index.d.ts.map +1 -1
  9. package/dist/agentic/index.js +18 -144
  10. package/dist/agentic/index.js.map +1 -1
  11. package/dist/agentic/types.d.ts +106 -0
  12. package/dist/agentic/types.d.ts.map +1 -1
  13. package/dist/agentic/types.js +77 -1
  14. package/dist/agentic/types.js.map +1 -1
  15. package/dist/architectAdrs.d.ts +14 -2
  16. package/dist/architectAdrs.d.ts.map +1 -1
  17. package/dist/architectAdrs.js +9 -3
  18. package/dist/architectAdrs.js.map +1 -1
  19. package/dist/client/transports.d.ts.map +1 -1
  20. package/dist/client/transports.js +13 -3
  21. package/dist/client/transports.js.map +1 -1
  22. package/dist/daemon.d.ts +19 -0
  23. package/dist/daemon.d.ts.map +1 -1
  24. package/dist/daemon.js +181 -38
  25. package/dist/daemon.js.map +1 -1
  26. package/dist/daemonConfig.d.ts +30 -0
  27. package/dist/daemonConfig.d.ts.map +1 -1
  28. package/dist/daemonConfig.js +56 -9
  29. package/dist/daemonConfig.js.map +1 -1
  30. package/dist/decibelPaths.d.ts.map +1 -1
  31. package/dist/decibelPaths.js +39 -6
  32. package/dist/decibelPaths.js.map +1 -1
  33. package/dist/domain/issue.d.ts +77 -0
  34. package/dist/domain/issue.d.ts.map +1 -0
  35. package/dist/domain/issue.js +134 -0
  36. package/dist/domain/issue.js.map +1 -0
  37. package/dist/domain/issueCodec.d.ts +39 -0
  38. package/dist/domain/issueCodec.d.ts.map +1 -0
  39. package/dist/domain/issueCodec.js +298 -0
  40. package/dist/domain/issueCodec.js.map +1 -0
  41. package/dist/domain/issueRepository.d.ts +91 -0
  42. package/dist/domain/issueRepository.d.ts.map +1 -0
  43. package/dist/domain/issueRepository.js +257 -0
  44. package/dist/domain/issueRepository.js.map +1 -0
  45. package/dist/facades/definitions.d.ts +0 -1
  46. package/dist/facades/definitions.d.ts.map +1 -1
  47. package/dist/facades/definitions.js +94 -98
  48. package/dist/facades/definitions.js.map +1 -1
  49. package/dist/facades/index.d.ts +2 -1
  50. package/dist/facades/index.d.ts.map +1 -1
  51. package/dist/facades/index.js +1 -1
  52. package/dist/facades/index.js.map +1 -1
  53. package/dist/facades/types.d.ts +17 -0
  54. package/dist/facades/types.d.ts.map +1 -1
  55. package/dist/httpArgs.d.ts +11 -0
  56. package/dist/httpArgs.d.ts.map +1 -0
  57. package/dist/httpArgs.js +39 -0
  58. package/dist/httpArgs.js.map +1 -0
  59. package/dist/httpServer.d.ts +1 -12
  60. package/dist/httpServer.d.ts.map +1 -1
  61. package/dist/httpServer.js +173 -98
  62. package/dist/httpServer.js.map +1 -1
  63. package/dist/kernel.d.ts +55 -1
  64. package/dist/kernel.d.ts.map +1 -1
  65. package/dist/kernel.js +366 -31
  66. package/dist/kernel.js.map +1 -1
  67. package/dist/lib/agent-services/assumptions.d.ts.map +1 -1
  68. package/dist/lib/agent-services/assumptions.js +8 -2
  69. package/dist/lib/agent-services/assumptions.js.map +1 -1
  70. package/dist/lib/atomicWrite.d.ts +26 -0
  71. package/dist/lib/atomicWrite.d.ts.map +1 -0
  72. package/dist/lib/atomicWrite.js +73 -0
  73. package/dist/lib/atomicWrite.js.map +1 -0
  74. package/dist/lib/envelope.d.ts +31 -0
  75. package/dist/lib/envelope.d.ts.map +1 -0
  76. package/dist/lib/envelope.js +80 -0
  77. package/dist/lib/envelope.js.map +1 -0
  78. package/dist/lib/fileLock.d.ts +38 -0
  79. package/dist/lib/fileLock.d.ts.map +1 -0
  80. package/dist/lib/fileLock.js +124 -0
  81. package/dist/lib/fileLock.js.map +1 -0
  82. package/dist/lib/recordIdAllocator.d.ts +86 -0
  83. package/dist/lib/recordIdAllocator.d.ts.map +1 -0
  84. package/dist/lib/recordIdAllocator.js +161 -0
  85. package/dist/lib/recordIdAllocator.js.map +1 -0
  86. package/dist/license.js +3 -3
  87. package/dist/license.js.map +1 -1
  88. package/dist/projectRegistry.d.ts +65 -3
  89. package/dist/projectRegistry.d.ts.map +1 -1
  90. package/dist/projectRegistry.js +233 -33
  91. package/dist/projectRegistry.js.map +1 -1
  92. package/dist/runtime/circuitBreaker.d.ts +95 -0
  93. package/dist/runtime/circuitBreaker.d.ts.map +1 -0
  94. package/dist/runtime/circuitBreaker.js +178 -0
  95. package/dist/runtime/circuitBreaker.js.map +1 -0
  96. package/dist/runtime/ensureRuntime.d.ts +103 -0
  97. package/dist/runtime/ensureRuntime.d.ts.map +1 -0
  98. package/dist/runtime/ensureRuntime.js +281 -0
  99. package/dist/runtime/ensureRuntime.js.map +1 -0
  100. package/dist/runtime/extensions.d.ts +57 -0
  101. package/dist/runtime/extensions.d.ts.map +1 -0
  102. package/dist/runtime/extensions.js +216 -0
  103. package/dist/runtime/extensions.js.map +1 -0
  104. package/dist/runtime/localHttp.d.ts +24 -0
  105. package/dist/runtime/localHttp.d.ts.map +1 -0
  106. package/dist/runtime/localHttp.js +84 -0
  107. package/dist/runtime/localHttp.js.map +1 -0
  108. package/dist/runtime/projectResolution.d.ts +29 -0
  109. package/dist/runtime/projectResolution.d.ts.map +1 -0
  110. package/dist/runtime/projectResolution.js +67 -0
  111. package/dist/runtime/projectResolution.js.map +1 -0
  112. package/dist/runtime/protocol.d.ts +62 -0
  113. package/dist/runtime/protocol.d.ts.map +1 -0
  114. package/dist/runtime/protocol.js +74 -0
  115. package/dist/runtime/protocol.js.map +1 -0
  116. package/dist/sentinelIssues.d.ts +26 -2
  117. package/dist/sentinelIssues.d.ts.map +1 -1
  118. package/dist/sentinelIssues.js +163 -76
  119. package/dist/sentinelIssues.js.map +1 -1
  120. package/dist/server.js +65 -12
  121. package/dist/server.js.map +1 -1
  122. package/dist/setup.d.ts +83 -0
  123. package/dist/setup.d.ts.map +1 -0
  124. package/dist/setup.js +445 -0
  125. package/dist/setup.js.map +1 -0
  126. package/dist/store/markdown.d.ts +14 -1
  127. package/dist/store/markdown.d.ts.map +1 -1
  128. package/dist/store/markdown.js +36 -3
  129. package/dist/store/markdown.js.map +1 -1
  130. package/dist/tools/agentic/index.d.ts.map +1 -1
  131. package/dist/tools/agentic/index.js +28 -2
  132. package/dist/tools/agentic/index.js.map +1 -1
  133. package/dist/tools/architect/index.d.ts.map +1 -1
  134. package/dist/tools/architect/index.js +7 -2
  135. package/dist/tools/architect/index.js.map +1 -1
  136. package/dist/tools/conductor/index.d.ts +9 -0
  137. package/dist/tools/conductor/index.d.ts.map +1 -0
  138. package/dist/tools/conductor/index.js +242 -0
  139. package/dist/tools/conductor/index.js.map +1 -0
  140. package/dist/tools/context.d.ts +10 -5
  141. package/dist/tools/context.d.ts.map +1 -1
  142. package/dist/tools/context.js +45 -18
  143. package/dist/tools/context.js.map +1 -1
  144. package/dist/tools/coordinator/coordinator.d.ts.map +1 -1
  145. package/dist/tools/coordinator/coordinator.js +48 -19
  146. package/dist/tools/coordinator/coordinator.js.map +1 -1
  147. package/dist/tools/crit.d.ts +2 -1
  148. package/dist/tools/crit.d.ts.map +1 -1
  149. package/dist/tools/crit.js +19 -4
  150. package/dist/tools/crit.js.map +1 -1
  151. package/dist/tools/designer/index.d.ts.map +1 -1
  152. package/dist/tools/designer/index.js +4 -3
  153. package/dist/tools/designer/index.js.map +1 -1
  154. package/dist/tools/designer/lateral-tools.d.ts.map +1 -1
  155. package/dist/tools/designer/lateral-tools.js +4 -2
  156. package/dist/tools/designer/lateral-tools.js.map +1 -1
  157. package/dist/tools/designer.d.ts +2 -1
  158. package/dist/tools/designer.d.ts.map +1 -1
  159. package/dist/tools/designer.js +24 -12
  160. package/dist/tools/designer.js.map +1 -1
  161. package/dist/tools/dojo.d.ts +3 -2
  162. package/dist/tools/dojo.d.ts.map +1 -1
  163. package/dist/tools/dojo.js +64 -22
  164. package/dist/tools/dojo.js.map +1 -1
  165. package/dist/tools/dojoPolicy.js +1 -1
  166. package/dist/tools/dojoPolicy.js.map +1 -1
  167. package/dist/tools/friction.d.ts.map +1 -1
  168. package/dist/tools/friction.js +16 -6
  169. package/dist/tools/friction.js.map +1 -1
  170. package/dist/tools/guardian.d.ts +29 -7
  171. package/dist/tools/guardian.d.ts.map +1 -1
  172. package/dist/tools/guardian.js +90 -75
  173. package/dist/tools/guardian.js.map +1 -1
  174. package/dist/tools/hygiene/oracle-hygiene.d.ts.map +1 -1
  175. package/dist/tools/hygiene/oracle-hygiene.js +8 -2
  176. package/dist/tools/hygiene/oracle-hygiene.js.map +1 -1
  177. package/dist/tools/index.d.ts.map +1 -1
  178. package/dist/tools/index.js +29 -18
  179. package/dist/tools/index.js.map +1 -1
  180. package/dist/tools/learnings.d.ts +2 -1
  181. package/dist/tools/learnings.d.ts.map +1 -1
  182. package/dist/tools/learnings.js +31 -15
  183. package/dist/tools/learnings.js.map +1 -1
  184. package/dist/tools/oracle.d.ts.map +1 -1
  185. package/dist/tools/oracle.js +19 -6
  186. package/dist/tools/oracle.js.map +1 -1
  187. package/dist/tools/policy.d.ts +2 -1
  188. package/dist/tools/policy.d.ts.map +1 -1
  189. package/dist/tools/policy.js +28 -9
  190. package/dist/tools/policy.js.map +1 -1
  191. package/dist/tools/postoffice/index.d.ts +10 -0
  192. package/dist/tools/postoffice/index.d.ts.map +1 -0
  193. package/dist/tools/postoffice/index.js +218 -0
  194. package/dist/tools/postoffice/index.js.map +1 -0
  195. package/dist/tools/postoffice.d.ts +78 -0
  196. package/dist/tools/postoffice.d.ts.map +1 -0
  197. package/dist/tools/postoffice.js +199 -0
  198. package/dist/tools/postoffice.js.map +1 -0
  199. package/dist/tools/provenance.d.ts +7 -0
  200. package/dist/tools/provenance.d.ts.map +1 -1
  201. package/dist/tools/provenance.js +42 -16
  202. package/dist/tools/provenance.js.map +1 -1
  203. package/dist/tools/registry/index.d.ts.map +1 -1
  204. package/dist/tools/registry/index.js +41 -12
  205. package/dist/tools/registry/index.js.map +1 -1
  206. package/dist/tools/sentinel/index.d.ts +1 -0
  207. package/dist/tools/sentinel/index.d.ts.map +1 -1
  208. package/dist/tools/sentinel/index.js +88 -4
  209. package/dist/tools/sentinel/index.js.map +1 -1
  210. package/dist/tools/sentinel.d.ts +117 -1
  211. package/dist/tools/sentinel.d.ts.map +1 -1
  212. package/dist/tools/sentinel.js +761 -339
  213. package/dist/tools/sentinel.js.map +1 -1
  214. package/dist/tools/shared/index.d.ts +2 -1
  215. package/dist/tools/shared/index.d.ts.map +1 -1
  216. package/dist/tools/shared/index.js +2 -1
  217. package/dist/tools/shared/index.js.map +1 -1
  218. package/dist/tools/shared/response.d.ts +24 -0
  219. package/dist/tools/shared/response.d.ts.map +1 -1
  220. package/dist/tools/shared/response.js +61 -7
  221. package/dist/tools/shared/response.js.map +1 -1
  222. package/dist/tools/shared/storeRead.d.ts +153 -0
  223. package/dist/tools/shared/storeRead.d.ts.map +1 -0
  224. package/dist/tools/shared/storeRead.js +242 -0
  225. package/dist/tools/shared/storeRead.js.map +1 -0
  226. package/dist/tools/testSpec.d.ts +3 -2
  227. package/dist/tools/testSpec.d.ts.map +1 -1
  228. package/dist/tools/testSpec.js +51 -14
  229. package/dist/tools/testSpec.js.map +1 -1
  230. package/dist/tools/vector.d.ts +33 -1
  231. package/dist/tools/vector.d.ts.map +1 -1
  232. package/dist/tools/vector.js +31 -5
  233. package/dist/tools/vector.js.map +1 -1
  234. package/dist/tools/zoom/index.d.ts +15 -0
  235. package/dist/tools/zoom/index.d.ts.map +1 -0
  236. package/dist/tools/zoom/index.js +174 -0
  237. package/dist/tools/zoom/index.js.map +1 -0
  238. package/dist/tools/zoom.d.ts +234 -0
  239. package/dist/tools/zoom.d.ts.map +1 -0
  240. package/dist/tools/zoom.js +627 -0
  241. package/dist/tools/zoom.js.map +1 -0
  242. package/dist/transports/bridge.d.ts +1 -1
  243. package/dist/transports/bridge.d.ts.map +1 -1
  244. package/dist/transports/bridge.js +2 -0
  245. package/dist/transports/bridge.js.map +1 -1
  246. package/dist/transports/http.d.ts +1 -1
  247. package/dist/transports/http.d.ts.map +1 -1
  248. package/dist/transports/http.js +3 -1
  249. package/dist/transports/http.js.map +1 -1
  250. package/dist/transports/index.d.ts +1 -0
  251. package/dist/transports/index.d.ts.map +1 -1
  252. package/dist/transports/index.js +1 -0
  253. package/dist/transports/index.js.map +1 -1
  254. package/dist/transports/mcp.d.ts +1 -1
  255. package/dist/transports/mcp.d.ts.map +1 -1
  256. package/dist/transports/mcp.js +7 -3
  257. package/dist/transports/mcp.js.map +1 -1
  258. package/dist/transports/stdio.d.ts +1 -1
  259. package/dist/transports/stdio.d.ts.map +1 -1
  260. package/dist/transports/stdio.js +2 -0
  261. package/dist/transports/stdio.js.map +1 -1
  262. package/dist/transports/thinStdio.d.ts +22 -0
  263. package/dist/transports/thinStdio.d.ts.map +1 -0
  264. package/dist/transports/thinStdio.js +218 -0
  265. package/dist/transports/thinStdio.js.map +1 -0
  266. package/dist/transports/types.d.ts +8 -1
  267. package/dist/transports/types.d.ts.map +1 -1
  268. package/dist/types/agent-services.d.ts +7 -0
  269. package/dist/types/agent-services.d.ts.map +1 -1
  270. package/package.json +9 -4
  271. package/templates/com.decibel.daemon.plist +6 -1
  272. package/dist/tools/deck.d.ts +0 -11
  273. package/dist/tools/deck.d.ts.map +0 -1
  274. package/dist/tools/deck.js +0 -1656
  275. package/dist/tools/deck.js.map +0 -1
  276. package/dist/tools/mother.d.ts +0 -11
  277. package/dist/tools/mother.d.ts.map +0 -1
  278. package/dist/tools/mother.js +0 -503
  279. package/dist/tools/mother.js.map +0 -1
  280. package/dist/tools/registry.d.ts +0 -3
  281. package/dist/tools/registry.d.ts.map +0 -1
  282. package/dist/tools/registry.js +0 -189
  283. package/dist/tools/registry.js.map +0 -1
  284. package/dist/tools/senken.d.ts +0 -11
  285. package/dist/tools/senken.d.ts.map +0 -1
  286. package/dist/tools/senken.js +0 -482
  287. package/dist/tools/senken.js.map +0 -1
  288. package/dist/tools/terminal.d.ts +0 -17
  289. package/dist/tools/terminal.d.ts.map +0 -1
  290. package/dist/tools/terminal.js +0 -968
  291. package/dist/tools/terminal.js.map +0 -1
  292. package/dist/tools/toolsIndex.d.ts +0 -5
  293. package/dist/tools/toolsIndex.d.ts.map +0 -1
  294. package/dist/tools/toolsIndex.js +0 -37
  295. package/dist/tools/toolsIndex.js.map +0 -1
@@ -0,0 +1,216 @@
1
+ // ============================================================================
2
+ // Extension Loader — EPIC-0038 Phase 7
3
+ // ============================================================================
4
+ // Private facades are not compiled into the published package. They arrive at
5
+ // boot as extensions: modules named by absolute path in an allowlist under
6
+ // ~/.decibel/config.yaml, registered into the same registry core facades use.
7
+ //
8
+ // The security model is registration, not permission. A facade that was never
9
+ // registered cannot be dispatched to, cannot appear in tools/list, and cannot
10
+ // be reached by a name-prefix trick — there is nothing on the other end. That
11
+ // is a stronger guarantee than a runtime tier check, which is one missed branch
12
+ // away from failing open. It replaces the DECIBEL_APPS env gate, which had the
13
+ // opposite failure mode: the flag lived in a launchd plist, so regenerating the
14
+ // plist silently dropped four facades with no error anywhere.
15
+ //
16
+ // TRUST BOUNDARY — read before changing anything here.
17
+ // An extension runs in-process with the runtime's full filesystem and database
18
+ // access. There is no sandbox and this loader does not pretend to be one. The
19
+ // allowlist is what makes that proportionate: for single-user private use,
20
+ // "the owner named this exact file" is a real boundary. It stops being one the
21
+ // moment a path can come from somewhere other than the owner's config, so:
22
+ //
23
+ // - Only absolute paths are accepted. A bare specifier ("senken") would be
24
+ // resolved by Node against node_modules, which makes the allowlist
25
+ // decorative — anything that can write a package into the tree chooses
26
+ // what runs.
27
+ // - The path is never read from the environment. Config file only.
28
+ // - An extension may not shadow an already-registered facade name.
29
+ // ============================================================================
30
+ import { existsSync } from 'fs';
31
+ import { isAbsolute, normalize } from 'path';
32
+ import { pathToFileURL } from 'url';
33
+ import { loadConfig } from '../daemonConfig.js';
34
+ import { log } from '../config.js';
35
+ import { RUNTIME_PROTOCOL_VERSION, isProtocolCompatible } from './protocol.js';
36
+ const EMPTY_RESULT = {
37
+ extensions: [],
38
+ facades: [],
39
+ tools: [],
40
+ rejected: [],
41
+ };
42
+ /** Facade names must look like facade names — this is also what the kernel keys on. */
43
+ const NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
44
+ // ============================================================================
45
+ // Manifest validation
46
+ // ============================================================================
47
+ function validateManifest(value) {
48
+ if (!value || typeof value !== 'object') {
49
+ return { ok: false, reason: 'manifest is missing or not an object' };
50
+ }
51
+ const m = value;
52
+ if (typeof m.name !== 'string' || !NAME_PATTERN.test(m.name)) {
53
+ return { ok: false, reason: `manifest.name must match ${NAME_PATTERN} (got ${JSON.stringify(m.name)})` };
54
+ }
55
+ if (typeof m.version !== 'string' || m.version.length === 0) {
56
+ return { ok: false, reason: 'manifest.version must be a non-empty string' };
57
+ }
58
+ if (m.tier !== 'pro' && m.tier !== 'apps') {
59
+ return { ok: false, reason: `manifest.tier must be "pro" or "apps" (got ${JSON.stringify(m.tier)}) — "core" is what ships in the package, so it cannot be an extension` };
60
+ }
61
+ if (typeof m.protocolVersion !== 'string') {
62
+ return { ok: false, reason: 'manifest.protocolVersion must be a string' };
63
+ }
64
+ // The extension is a client of the runtime's wire contract, so the runtime is
65
+ // the "server" side of the comparison. A stale extension fails here with a
66
+ // version mismatch rather than later with a missing endpoint.
67
+ const compat = isProtocolCompatible(RUNTIME_PROTOCOL_VERSION, m.protocolVersion);
68
+ if (!compat.compatible) {
69
+ return { ok: false, reason: `protocol mismatch — ${compat.reason}` };
70
+ }
71
+ return {
72
+ ok: true,
73
+ manifest: {
74
+ name: m.name,
75
+ version: m.version,
76
+ protocolVersion: m.protocolVersion,
77
+ tier: m.tier,
78
+ },
79
+ };
80
+ }
81
+ function validateShape(value) {
82
+ if (!value || typeof value !== 'object') {
83
+ return { ok: false, reason: 'module does not export `extension`' };
84
+ }
85
+ const ext = value;
86
+ const manifestResult = validateManifest(ext.manifest);
87
+ if (!manifestResult.ok)
88
+ return manifestResult;
89
+ if (!Array.isArray(ext.facades) || ext.facades.length === 0) {
90
+ return { ok: false, reason: 'extension.facades must be a non-empty array' };
91
+ }
92
+ if (!Array.isArray(ext.tools) || ext.tools.length === 0) {
93
+ return { ok: false, reason: 'extension.tools must be a non-empty array' };
94
+ }
95
+ const manifest = manifestResult.manifest;
96
+ // Every facade the extension declares must carry the manifest's tier. An
97
+ // extension cannot smuggle a facade in at a laxer tier than it was allowed at.
98
+ for (const facade of ext.facades) {
99
+ if (!facade || typeof facade.name !== 'string' || !NAME_PATTERN.test(facade.name)) {
100
+ return { ok: false, reason: 'every entry in extension.facades must be a FacadeSpec with a valid name' };
101
+ }
102
+ if (facade.tier !== manifest.tier) {
103
+ return {
104
+ ok: false,
105
+ reason: `facade "${facade.name}" declares tier "${facade.tier}" but the manifest declares "${manifest.tier}"`,
106
+ };
107
+ }
108
+ }
109
+ return { ok: true, extension: ext };
110
+ }
111
+ // ============================================================================
112
+ // Allowlist
113
+ // ============================================================================
114
+ /**
115
+ * Read the extension allowlist from ~/.decibel/config.yaml.
116
+ *
117
+ * Deliberately config-only. Reading a path from the environment would let
118
+ * anything that can set an env var — a plist, a shell profile, a parent
119
+ * process — choose code that runs in-process with full access, which is
120
+ * exactly the property the allowlist exists to provide.
121
+ */
122
+ export function readAllowlist() {
123
+ const config = loadConfig();
124
+ const entries = config.extensions?.allow;
125
+ if (!Array.isArray(entries))
126
+ return [];
127
+ return entries.filter((e) => typeof e === 'string');
128
+ }
129
+ /** Path rules, split out so the test suite can exercise them without a filesystem. */
130
+ export function checkAllowlistEntry(entry) {
131
+ const trimmed = entry.trim();
132
+ if (trimmed.length === 0) {
133
+ return { ok: false, reason: 'empty allowlist entry' };
134
+ }
135
+ if (!isAbsolute(trimmed)) {
136
+ return {
137
+ ok: false,
138
+ reason: 'not an absolute path — a bare specifier would be resolved against node_modules, which makes the allowlist decorative',
139
+ };
140
+ }
141
+ // normalize() collapses any ".." segments. Compare against the original so a
142
+ // path that only *looks* like the allowed one is rejected rather than quietly
143
+ // rewritten into something else.
144
+ const normalized = normalize(trimmed);
145
+ if (normalized !== trimmed) {
146
+ return { ok: false, reason: `path is not normalized (resolves to ${normalized})` };
147
+ }
148
+ return { ok: true, path: normalized };
149
+ }
150
+ // ============================================================================
151
+ // Loader
152
+ // ============================================================================
153
+ /**
154
+ * Load every allowlisted extension.
155
+ *
156
+ * Never throws. A broken extension is reported in `rejected` and the runtime
157
+ * boots without it — one bad private module must not take down core. Callers
158
+ * that care (the daemon's startup log, /health) surface the rejections.
159
+ *
160
+ * @param reservedNames Facade names already registered. An extension may not
161
+ * shadow one; that would let a private module silently replace `sentinel`.
162
+ */
163
+ export async function loadExtensions(reservedNames = new Set()) {
164
+ const entries = readAllowlist();
165
+ if (entries.length === 0)
166
+ return EMPTY_RESULT;
167
+ const extensions = [];
168
+ const facades = [];
169
+ const tools = [];
170
+ const rejected = [];
171
+ const claimed = new Set(reservedNames);
172
+ for (const entry of entries) {
173
+ const pathCheck = checkAllowlistEntry(entry);
174
+ if (!pathCheck.ok) {
175
+ rejected.push({ entry, reason: pathCheck.reason });
176
+ continue;
177
+ }
178
+ if (!existsSync(pathCheck.path)) {
179
+ rejected.push({ entry, reason: 'file does not exist' });
180
+ continue;
181
+ }
182
+ let mod;
183
+ try {
184
+ mod = (await import(pathToFileURL(pathCheck.path).href));
185
+ }
186
+ catch (error) {
187
+ rejected.push({ entry, reason: `import failed — ${error instanceof Error ? error.message : String(error)}` });
188
+ continue;
189
+ }
190
+ const shape = validateShape(mod.extension);
191
+ if (!shape.ok) {
192
+ rejected.push({ entry, reason: shape.reason });
193
+ continue;
194
+ }
195
+ const extension = shape.extension;
196
+ const collision = extension.facades.find(f => claimed.has(f.name));
197
+ if (collision) {
198
+ rejected.push({ entry, reason: `facade "${collision.name}" is already registered — an extension may not shadow it` });
199
+ continue;
200
+ }
201
+ for (const facade of extension.facades)
202
+ claimed.add(facade.name);
203
+ extensions.push(extension);
204
+ facades.push(...extension.facades);
205
+ tools.push(...extension.tools);
206
+ }
207
+ for (const r of rejected) {
208
+ log(`Extensions: rejected ${r.entry} — ${r.reason}`);
209
+ }
210
+ if (extensions.length > 0) {
211
+ const names = extensions.map(e => `${e.manifest.name}@${e.manifest.version}`).join(', ');
212
+ log(`Extensions: loaded ${extensions.length} (${names}) — ${facades.length} facades, ${tools.length} tools`);
213
+ }
214
+ return { extensions, facades, tools, rejected };
215
+ }
216
+ //# sourceMappingURL=extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/runtime/extensions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface LocalResponse {
2
+ status: number;
3
+ ok: boolean;
4
+ text: string;
5
+ }
6
+ export interface LocalRequestOptions {
7
+ method?: string;
8
+ headers?: Record<string, string>;
9
+ body?: string;
10
+ timeoutMs: number;
11
+ }
12
+ /**
13
+ * Perform one loopback HTTP request. Rejects on transport failure (nothing
14
+ * listening, socket closed, timeout); resolves with the status for any
15
+ * response the server actually sent, including 4xx and 5xx.
16
+ */
17
+ export declare function localRequest(url: string, opts: LocalRequestOptions): Promise<LocalResponse>;
18
+ /** localRequest + JSON.parse, with the status preserved for the caller. */
19
+ export declare function localJson<T = unknown>(url: string, opts: LocalRequestOptions): Promise<{
20
+ status: number;
21
+ ok: boolean;
22
+ body: T;
23
+ }>;
24
+ //# sourceMappingURL=localHttp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localHttp.d.ts","sourceRoot":"","sources":["../../src/runtime/localHttp.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoD3F;AAED,2EAA2E;AAC3E,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EACzC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAWnD"}
@@ -0,0 +1,84 @@
1
+ // ============================================================================
2
+ // Loopback HTTP for the thin client
3
+ // ============================================================================
4
+ // Node's global fetch() is undici, and undici is initialised lazily on first
5
+ // use: touching fetch() once costs ~10.8 MB resident (measured — see
6
+ // scripts/measure-memory.mjs). node:http is already resident in every process
7
+ // and costs ~2.5 MB for the same work.
8
+ //
9
+ // That trade is only obviously correct because of what these requests are:
10
+ // loopback, plaintext, small JSON bodies, no redirects, no compression, no
11
+ // cookies, no keep-alive pooling worth having. None of undici's surface is in
12
+ // use here, so none of it is worth 10 MB in five client processes.
13
+ //
14
+ // This is deliberately NOT a fetch polyfill. It returns the response body as
15
+ // text and lets callers parse, so a non-JSON error page surfaces as the status
16
+ // code the server actually sent rather than a parse error.
17
+ // ============================================================================
18
+ import { request } from 'http';
19
+ /**
20
+ * Perform one loopback HTTP request. Rejects on transport failure (nothing
21
+ * listening, socket closed, timeout); resolves with the status for any
22
+ * response the server actually sent, including 4xx and 5xx.
23
+ */
24
+ export function localRequest(url, opts) {
25
+ const { method = 'GET', headers = {}, body, timeoutMs } = opts;
26
+ return new Promise((resolve, reject) => {
27
+ let settled = false;
28
+ const finish = (fn) => {
29
+ if (settled)
30
+ return;
31
+ settled = true;
32
+ clearTimeout(timer);
33
+ fn();
34
+ };
35
+ const target = new URL(url);
36
+ const req = request({
37
+ protocol: target.protocol,
38
+ hostname: target.hostname,
39
+ port: target.port,
40
+ path: `${target.pathname}${target.search}`,
41
+ method,
42
+ headers: body === undefined
43
+ ? headers
44
+ : { ...headers, 'Content-Length': String(Buffer.byteLength(body)) },
45
+ }, (res) => {
46
+ const chunks = [];
47
+ res.on('data', (c) => chunks.push(c));
48
+ res.on('error', (err) => finish(() => reject(err)));
49
+ res.on('end', () => finish(() => {
50
+ const status = res.statusCode ?? 0;
51
+ resolve({
52
+ status,
53
+ ok: status >= 200 && status < 300,
54
+ text: Buffer.concat(chunks).toString('utf-8'),
55
+ });
56
+ }));
57
+ });
58
+ // Timeout is enforced here rather than via req.setTimeout, which fires on
59
+ // socket inactivity and so cannot bound a slow-but-trickling response.
60
+ const timer = setTimeout(() => {
61
+ finish(() => {
62
+ req.destroy();
63
+ reject(new Error(`request to ${url} timed out after ${timeoutMs}ms`));
64
+ });
65
+ }, timeoutMs);
66
+ req.on('error', (err) => finish(() => reject(err)));
67
+ if (body !== undefined)
68
+ req.write(body);
69
+ req.end();
70
+ });
71
+ }
72
+ /** localRequest + JSON.parse, with the status preserved for the caller. */
73
+ export async function localJson(url, opts) {
74
+ const res = await localRequest(url, opts);
75
+ let parsed;
76
+ try {
77
+ parsed = JSON.parse(res.text);
78
+ }
79
+ catch {
80
+ throw new Error(`${url} returned ${res.status} with a non-JSON body: ${res.text.slice(0, 200)}`);
81
+ }
82
+ return { status: res.status, ok: res.ok, body: parsed };
83
+ }
84
+ //# sourceMappingURL=localHttp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localHttp.js","sourceRoot":"","sources":["../../src/runtime/localHttp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Which strategy answered. The `matched` flag is the part callers care about:
3
+ * strategies 1-5 and an explicit default all identify the project the caller
4
+ * meant; 6 and 7 substitute a different one.
5
+ */
6
+ export type ResolutionStrategy = 'exact_id' | 'alias' | 'env_root_exact' | 'absolute_path' | 'cwd_exact' | 'env_root_fallback' | 'cwd_fallback' | 'default_project';
7
+ export interface ResolutionRecord {
8
+ /** What the caller asked for. Undefined when the call omitted a project id. */
9
+ requested?: string;
10
+ /** The id of the project actually served. */
11
+ resolvedId: string;
12
+ /** The path actually served — the field strategy 6 substitutes silently. */
13
+ resolvedPath: string;
14
+ strategy: ResolutionStrategy;
15
+ /** False when the resolver substituted a project the caller did not ask for. */
16
+ matched: boolean;
17
+ }
18
+ /**
19
+ * Run `fn` with resolution tracking active. Nested calls share the outermost
20
+ * slot, so a tool that resolves several projects reports the last one — good
21
+ * enough for the single-project calls this exists to protect, and honest about
22
+ * it rather than silently reporting the first.
23
+ */
24
+ export declare function withResolutionTracking<T>(fn: () => Promise<T>): Promise<T>;
25
+ /** Called by the resolver at each of its exits. A no-op outside a tracked scope. */
26
+ export declare function recordResolution(requested: string | undefined, resolvedId: string, resolvedPath: string, strategy: ResolutionStrategy): void;
27
+ /** The resolution that happened during this dispatch, if any. */
28
+ export declare function currentResolution(): ResolutionRecord | undefined;
29
+ //# sourceMappingURL=projectResolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectResolution.d.ts","sourceRoot":"","sources":["../../src/runtime/projectResolution.ts"],"names":[],"mappings":"AAkCA;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,OAAO,GACP,gBAAgB,GAChB,eAAe,GACf,WAAW,GACX,mBAAmB,GACnB,cAAc,GACd,iBAAiB,CAAC;AAOtB,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAC;CAClB;AAQD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAI1E;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,kBAAkB,GAC3B,IAAI,CAUN;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,IAAI,gBAAgB,GAAG,SAAS,CAEhE"}
@@ -0,0 +1,67 @@
1
+ // ============================================================================
2
+ // Project resolution tracking — EPIC-0038 Phase 4
3
+ // ============================================================================
4
+ // Project resolution is forgiving by design. `resolveProject` has seven
5
+ // strategies, and the last two do not match the caller's request at all —
6
+ // they substitute a project discovered from the environment or the working
7
+ // directory so that an interactive agent's typo doesn't hard-fail:
8
+ //
9
+ // // Strategy 7: If we discovered a project from cwd but ID didn't match,
10
+ // // use it anyway. This prevents hard failures when Claude sends a
11
+ // // slightly wrong project ID.
12
+ //
13
+ // That is a reasonable trade for one agent guessing at a name. It is the
14
+ // wrong trade for a programmatic caller sweeping a list of known-good ids:
15
+ // HQ fans out across 34 projects and five facades, and an id that fails to
16
+ // resolve does not come back empty — it comes back carrying ANOTHER project's
17
+ // issues, actions and friction, wearing the requested project's name in the
18
+ // UI. Absence is visible. Misattribution is not.
19
+ //
20
+ // Strategy 6 is subtler still: it returns the REQUESTED id with a substituted
21
+ // path, so comparing the id you asked for against the id you got cannot
22
+ // detect it. Only the resolver knows, and until now it dropped that knowledge
23
+ // on the floor — it logs a line locally and returns a value indistinguishable
24
+ // from a real match.
25
+ //
26
+ // This module keeps the resolver's own account of what happened for the
27
+ // duration of one dispatch, so the runtime can tell the caller when the
28
+ // project it served is not the project that was asked for. It changes no
29
+ // resolution behaviour; whether strategy 6 and 7 should exist at all for a
30
+ // programmatic caller is a separate and larger question.
31
+ // ============================================================================
32
+ import { AsyncLocalStorage } from 'async_hooks';
33
+ const SUBSTITUTING = new Set([
34
+ 'env_root_fallback',
35
+ 'cwd_fallback',
36
+ ]);
37
+ const storage = new AsyncLocalStorage();
38
+ /**
39
+ * Run `fn` with resolution tracking active. Nested calls share the outermost
40
+ * slot, so a tool that resolves several projects reports the last one — good
41
+ * enough for the single-project calls this exists to protect, and honest about
42
+ * it rather than silently reporting the first.
43
+ */
44
+ export function withResolutionTracking(fn) {
45
+ const existing = storage.getStore();
46
+ if (existing)
47
+ return fn();
48
+ return storage.run({}, fn);
49
+ }
50
+ /** Called by the resolver at each of its exits. A no-op outside a tracked scope. */
51
+ export function recordResolution(requested, resolvedId, resolvedPath, strategy) {
52
+ const slot = storage.getStore();
53
+ if (!slot)
54
+ return;
55
+ slot.record = {
56
+ requested,
57
+ resolvedId,
58
+ resolvedPath,
59
+ strategy,
60
+ matched: !SUBSTITUTING.has(strategy),
61
+ };
62
+ }
63
+ /** The resolution that happened during this dispatch, if any. */
64
+ export function currentResolution() {
65
+ return storage.getStore()?.record;
66
+ }
67
+ //# sourceMappingURL=projectResolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectResolution.js","sourceRoot":"","sources":["../../src/runtime/projectResolution.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kDAAkD;AAClD,+EAA+E;AAC/E,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,mEAAmE;AACnE,EAAE;AACF,8EAA8E;AAC9E,wEAAwE;AACxE,oCAAoC;AACpC,EAAE;AACF,yEAAyE;AACzE,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,4EAA4E;AAC5E,iDAAiD;AACjD,EAAE;AACF,8EAA8E;AAC9E,wEAAwE;AACxE,8EAA8E;AAC9E,8EAA8E;AAC9E,qBAAqB;AACrB,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,2EAA2E;AAC3E,yDAAyD;AACzD,+EAA+E;AAE/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBhD,MAAM,YAAY,GAAoC,IAAI,GAAG,CAAqB;IAChF,mBAAmB;IACnB,cAAc;CACf,CAAC,CAAC;AAkBH,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAQ,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAI,EAAoB;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACpC,IAAI,QAAQ;QAAE,OAAO,EAAE,EAAE,CAAC;IAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAC9B,SAA6B,EAC7B,UAAkB,EAClB,YAAoB,EACpB,QAA4B;IAE5B,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAChC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,IAAI,CAAC,MAAM,GAAG;QACZ,SAAS;QACT,UAAU;QACV,YAAY;QACZ,QAAQ;QACR,OAAO,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,iBAAiB;IAC/B,OAAO,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC;AACpC,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Runtime protocol version.
3
+ *
4
+ * A long-lived daemon can outlive the clients that connect to it — a terminal
5
+ * opened yesterday, an editor extension pinned to an older release, a hook
6
+ * script from a previous install. Without an explicit protocol handshake those
7
+ * clients fail in confusing ways (a missing facade, a renamed action, a changed
8
+ * envelope) rather than saying "we disagree about the contract".
9
+ *
10
+ * This version describes the **runtime wire contract**: the shape of `/call`,
11
+ * `/batch`, `/health`, and the dispatch envelope. It is deliberately separate
12
+ * from the package version — most releases do not change the contract, and a
13
+ * client should not be forced to restart a healthy daemon just because a patch
14
+ * shipped.
15
+ *
16
+ * Bump MAJOR when an existing client would break: a removed or renamed
17
+ * endpoint, a changed request/response envelope, changed error semantics.
18
+ * Bump MINOR for backward-compatible additions (a new endpoint, a new optional
19
+ * field). Clients require an exact MAJOR match and tolerate any MINOR at or
20
+ * above their own.
21
+ */
22
+ export declare const RUNTIME_PROTOCOL_VERSION = "1.1";
23
+ /**
24
+ * 1.1 — adds `GET /mcp/tools?tier=`, serving MCP tool definitions verbatim.
25
+ * Additive, so an older client is unaffected. A 1.1 CLIENT against a 1.0
26
+ * runtime is not: the thin stdio adapter has no local kernel and cannot
27
+ * answer tools/list without that endpoint. The minor-version rule
28
+ * (server.minor >= client.minor) turns that into an actionable
29
+ * "restart the runtime" at handshake time rather than a bare 404 on the
30
+ * first request — which is exactly the confusion this file exists to
31
+ * prevent, and which it caught the first time it was exercised for real.
32
+ * 1.0 — initial contract: /call, /batch, /health, dispatch envelope.
33
+ */
34
+ export interface ParsedProtocolVersion {
35
+ major: number;
36
+ minor: number;
37
+ }
38
+ /** Parse `"1.0"` into `{major: 1, minor: 0}`. Returns null if malformed. */
39
+ export declare function parseProtocolVersion(value: string): ParsedProtocolVersion | null;
40
+ export type CompatibilityResult = {
41
+ compatible: true;
42
+ } | {
43
+ compatible: false;
44
+ reason: string;
45
+ };
46
+ /**
47
+ * Is a runtime speaking `serverVersion` usable by a client requiring
48
+ * `clientVersion`?
49
+ *
50
+ * Rules:
51
+ * - MAJOR must match exactly. A different major is a breaking contract change
52
+ * in either direction, so we refuse rather than guess.
53
+ * - The server's MINOR must be >= the client's. A newer server has every
54
+ * endpoint an older client knows about (minor bumps are additive), but an
55
+ * older server may be missing something the client depends on.
56
+ *
57
+ * An unparseable version is treated as incompatible. That is deliberate: a
58
+ * daemon that cannot state its protocol is a daemon we cannot reason about, and
59
+ * failing loudly beats dispatching into an unknown contract.
60
+ */
61
+ export declare function isProtocolCompatible(serverVersion: string | undefined, clientVersion?: string): CompatibilityResult;
62
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAE9C;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAIhF;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,GACpB;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,aAAa,GAAE,MAAiC,GAC/C,mBAAmB,CAiCrB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Runtime protocol version.
3
+ *
4
+ * A long-lived daemon can outlive the clients that connect to it — a terminal
5
+ * opened yesterday, an editor extension pinned to an older release, a hook
6
+ * script from a previous install. Without an explicit protocol handshake those
7
+ * clients fail in confusing ways (a missing facade, a renamed action, a changed
8
+ * envelope) rather than saying "we disagree about the contract".
9
+ *
10
+ * This version describes the **runtime wire contract**: the shape of `/call`,
11
+ * `/batch`, `/health`, and the dispatch envelope. It is deliberately separate
12
+ * from the package version — most releases do not change the contract, and a
13
+ * client should not be forced to restart a healthy daemon just because a patch
14
+ * shipped.
15
+ *
16
+ * Bump MAJOR when an existing client would break: a removed or renamed
17
+ * endpoint, a changed request/response envelope, changed error semantics.
18
+ * Bump MINOR for backward-compatible additions (a new endpoint, a new optional
19
+ * field). Clients require an exact MAJOR match and tolerate any MINOR at or
20
+ * above their own.
21
+ */
22
+ export const RUNTIME_PROTOCOL_VERSION = '1.1';
23
+ /** Parse `"1.0"` into `{major: 1, minor: 0}`. Returns null if malformed. */
24
+ export function parseProtocolVersion(value) {
25
+ const match = /^(\d+)\.(\d+)$/.exec(value.trim());
26
+ if (!match)
27
+ return null;
28
+ return { major: Number(match[1]), minor: Number(match[2]) };
29
+ }
30
+ /**
31
+ * Is a runtime speaking `serverVersion` usable by a client requiring
32
+ * `clientVersion`?
33
+ *
34
+ * Rules:
35
+ * - MAJOR must match exactly. A different major is a breaking contract change
36
+ * in either direction, so we refuse rather than guess.
37
+ * - The server's MINOR must be >= the client's. A newer server has every
38
+ * endpoint an older client knows about (minor bumps are additive), but an
39
+ * older server may be missing something the client depends on.
40
+ *
41
+ * An unparseable version is treated as incompatible. That is deliberate: a
42
+ * daemon that cannot state its protocol is a daemon we cannot reason about, and
43
+ * failing loudly beats dispatching into an unknown contract.
44
+ */
45
+ export function isProtocolCompatible(serverVersion, clientVersion = RUNTIME_PROTOCOL_VERSION) {
46
+ if (!serverVersion) {
47
+ return {
48
+ compatible: false,
49
+ reason: 'runtime did not report a protocol_version (it predates protocol negotiation)',
50
+ };
51
+ }
52
+ const server = parseProtocolVersion(serverVersion);
53
+ const client = parseProtocolVersion(clientVersion);
54
+ if (!server) {
55
+ return { compatible: false, reason: `runtime reported an unparseable protocol_version "${serverVersion}"` };
56
+ }
57
+ if (!client) {
58
+ return { compatible: false, reason: `client requested an unparseable protocol version "${clientVersion}"` };
59
+ }
60
+ if (server.major !== client.major) {
61
+ return {
62
+ compatible: false,
63
+ reason: `runtime speaks protocol ${serverVersion}, client requires ${clientVersion} (major version mismatch)`,
64
+ };
65
+ }
66
+ if (server.minor < client.minor) {
67
+ return {
68
+ compatible: false,
69
+ reason: `runtime speaks protocol ${serverVersion}, client requires at least ${clientVersion} (runtime is older)`,
70
+ };
71
+ }
72
+ return { compatible: true };
73
+ }
74
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAmB9C,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAClC,aAAiC,EACjC,gBAAwB,wBAAwB;IAEhD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO;YACL,UAAU,EAAE,KAAK;YACjB,MAAM,EACJ,8EAA8E;SACjF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;IAEnD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,qDAAqD,aAAa,GAAG,EAAE,CAAC;IAC9G,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,qDAAqD,aAAa,GAAG,EAAE,CAAC;IAC9G,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,OAAO;YACL,UAAU,EAAE,KAAK;YACjB,MAAM,EAAE,2BAA2B,aAAa,qBAAqB,aAAa,2BAA2B;SAC9G,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,OAAO;YACL,UAAU,EAAE,KAAK;YACjB,MAAM,EAAE,2BAA2B,aAAa,8BAA8B,aAAa,qBAAqB;SACjH,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAC9B,CAAC"}
@@ -1,5 +1,6 @@
1
- export type IssueStatus = 'open' | 'in_progress' | 'done' | 'blocked';
2
- export type IssuePriority = 'low' | 'medium' | 'high';
1
+ import type { IssueSeverity, IssueStatus as CanonicalIssueStatus, IssuePriority as CanonicalIssuePriority } from './domain/issue.js';
2
+ export type IssueStatus = CanonicalIssueStatus;
3
+ export type IssuePriority = CanonicalIssuePriority;
3
4
  export interface SentinelIssue {
4
5
  id: string;
5
6
  title: string;
@@ -20,6 +21,8 @@ export interface CreateIssueInput {
20
21
  epicId?: string;
21
22
  priority?: IssuePriority;
22
23
  tags?: string[];
24
+ /** Optional. Defaults to 'med'. Distinct from priority — see createIssue. */
25
+ severity?: IssueSeverity;
23
26
  }
24
27
  export interface CreateIssueOutput extends SentinelIssue {
25
28
  filePath: string;
@@ -37,6 +40,27 @@ export declare function listIssuesForProject(projectId: string): Promise<Sentine
37
40
  /**
38
41
  * Create a new issue for a project
39
42
  */
43
+ /**
44
+ * Create an issue.
45
+ *
46
+ * EPIC-0038 Phase 5: this used to be the second writer. It emitted `.yml` while
47
+ * the facade path (src/tools/sentinel.ts -> FsIssueRepository) emitted `.md`,
48
+ * into the same directory, from the same ISS-NNNN space. Converging the files
49
+ * while both writers ran would have left this one re-emitting `.yml` behind the
50
+ * migration — which is why the epic sequences behaviour before representation.
51
+ *
52
+ * So it now delegates. The name, the input contract and the return shape are
53
+ * all preserved; only the bytes on disk change. `SentinelIssue` is a subset of
54
+ * the canonical `Issue`, so the mapping back is total rather than lossy:
55
+ * `details` carries what this API calls `description`, and everything else is
56
+ * the same field under the same name.
57
+ *
58
+ * One field needs a decision rather than a mapping. This API has no `severity`
59
+ * and the canonical model wants one, so callers may now pass it and it defaults
60
+ * to 'med' — the neutral middle of ['low','med','high','critical'] — rather
61
+ * than inventing a severity from `priority`, which answers a different question
62
+ * (how bad it is, versus when it gets worked on).
63
+ */
40
64
  export declare function createIssue(input: CreateIssueInput): Promise<CreateIssueOutput>;
41
65
  /**
42
66
  * Get a single issue by ID (e.g., "ISS-0005") with full content