@b00dea/cockpit 0.0.0-stage → 0.1.1

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 (426) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/LICENSE +21 -0
  3. package/README.md +25 -2
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2644 -0
  6. package/dist/autonomy.d.ts +45 -0
  7. package/dist/autonomy.js +173 -0
  8. package/dist/bounded-read.d.ts +14 -0
  9. package/dist/bounded-read.js +42 -0
  10. package/dist/browser/commands.d.ts +117 -0
  11. package/dist/browser/commands.js +453 -0
  12. package/dist/browser/package-paths.d.ts +1 -0
  13. package/dist/browser/package-paths.js +6 -0
  14. package/dist/browser/proxy.d.ts +22 -0
  15. package/dist/browser/proxy.js +37 -0
  16. package/dist/browser/runtime.d.ts +118 -0
  17. package/dist/browser/runtime.js +186 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +561 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/canonical-json.d.ts +2 -0
  23. package/dist/canonical-json.js +5 -0
  24. package/dist/cli-guard.d.ts +5 -0
  25. package/dist/cli-guard.js +71 -0
  26. package/dist/cli-main.d.ts +176 -0
  27. package/dist/cli-main.js +1584 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.js +16 -0
  30. package/dist/command-table.d.ts +121 -0
  31. package/dist/command-table.js +664 -0
  32. package/dist/config/load.d.ts +183 -0
  33. package/dist/config/load.js +367 -0
  34. package/dist/config/sibling-toml.d.ts +48 -0
  35. package/dist/config/sibling-toml.js +160 -0
  36. package/dist/config/surface-tools-toml.d.ts +9 -0
  37. package/dist/config/surface-tools-toml.js +37 -0
  38. package/dist/config/unconfirmed-toml.d.ts +9 -0
  39. package/dist/config/unconfirmed-toml.js +29 -0
  40. package/dist/config/verified-write.d.ts +43 -0
  41. package/dist/config/verified-write.js +89 -0
  42. package/dist/diagnostics.d.ts +26 -0
  43. package/dist/diagnostics.js +344 -0
  44. package/dist/docs/check.d.ts +16 -0
  45. package/dist/docs/check.js +197 -0
  46. package/dist/docs/decision-record.d.ts +34 -0
  47. package/dist/docs/decision-record.js +135 -0
  48. package/dist/docs/decisions.d.ts +16 -0
  49. package/dist/docs/decisions.js +103 -0
  50. package/dist/docs/markdown.d.ts +36 -0
  51. package/dist/docs/markdown.js +88 -0
  52. package/dist/docs/references.d.ts +32 -0
  53. package/dist/docs/references.js +148 -0
  54. package/dist/emit.d.ts +22 -0
  55. package/dist/emit.js +43 -0
  56. package/dist/entrypoint-kinds.d.ts +3 -0
  57. package/dist/entrypoint-kinds.js +11 -0
  58. package/dist/environment/application/confirm.d.ts +35 -0
  59. package/dist/environment/application/confirm.js +122 -0
  60. package/dist/environment/application/destructive.d.ts +6 -0
  61. package/dist/environment/application/destructive.js +429 -0
  62. package/dist/environment/application/doctor.d.ts +94 -0
  63. package/dist/environment/application/doctor.js +247 -0
  64. package/dist/environment/application/guard.d.ts +49 -0
  65. package/dist/environment/application/guard.js +677 -0
  66. package/dist/environment/application/health.d.ts +143 -0
  67. package/dist/environment/application/health.js +385 -0
  68. package/dist/environment/application/lifecycle-context.d.ts +235 -0
  69. package/dist/environment/application/lifecycle-context.js +154 -0
  70. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  71. package/dist/environment/application/lifecycle-state.js +202 -0
  72. package/dist/environment/application/lifecycle.d.ts +18 -0
  73. package/dist/environment/application/lifecycle.js +737 -0
  74. package/dist/environment/application/logs.d.ts +45 -0
  75. package/dist/environment/application/logs.js +127 -0
  76. package/dist/environment/application/services.d.ts +60 -0
  77. package/dist/environment/application/services.js +89 -0
  78. package/dist/environment/application/setup.d.ts +129 -0
  79. package/dist/environment/application/setup.js +741 -0
  80. package/dist/environment/application/shared.d.ts +11 -0
  81. package/dist/environment/application/shared.js +61 -0
  82. package/dist/environment/application/siblings.d.ts +43 -0
  83. package/dist/environment/application/siblings.js +269 -0
  84. package/dist/environment/claims.d.ts +389 -0
  85. package/dist/environment/claims.js +1701 -0
  86. package/dist/environment/codex-hook-trust.d.ts +11 -0
  87. package/dist/environment/codex-hook-trust.js +185 -0
  88. package/dist/environment/compose-environment.d.ts +1 -0
  89. package/dist/environment/compose-environment.js +6 -0
  90. package/dist/environment/compose-ownership.d.ts +21 -0
  91. package/dist/environment/compose-ownership.js +117 -0
  92. package/dist/environment/compose-topology.d.ts +125 -0
  93. package/dist/environment/compose-topology.js +490 -0
  94. package/dist/environment/contract.d.ts +522 -0
  95. package/dist/environment/contract.js +794 -0
  96. package/dist/environment/declaration.d.ts +14 -0
  97. package/dist/environment/declaration.js +39 -0
  98. package/dist/environment/destructive-composition.d.ts +29 -0
  99. package/dist/environment/destructive-composition.js +333 -0
  100. package/dist/environment/docker-daemon.d.ts +30 -0
  101. package/dist/environment/docker-daemon.js +78 -0
  102. package/dist/environment/docker-endpoint.d.ts +10 -0
  103. package/dist/environment/docker-endpoint.js +19 -0
  104. package/dist/environment/env-schema-observation.d.ts +46 -0
  105. package/dist/environment/env-schema-observation.js +241 -0
  106. package/dist/environment/evidence.d.ts +136 -0
  107. package/dist/environment/evidence.js +437 -0
  108. package/dist/environment/executable.d.ts +38 -0
  109. package/dist/environment/executable.js +190 -0
  110. package/dist/environment/generated-application/authorities.d.ts +5 -0
  111. package/dist/environment/generated-application/authorities.js +61 -0
  112. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  113. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  114. package/dist/environment/generated-application/check.d.ts +13 -0
  115. package/dist/environment/generated-application/check.js +191 -0
  116. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  117. package/dist/environment/generated-application/custom-surface.js +289 -0
  118. package/dist/environment/generated-application/prepare.d.ts +29 -0
  119. package/dist/environment/generated-application/prepare.js +278 -0
  120. package/dist/environment/generated-application/publication.d.ts +33 -0
  121. package/dist/environment/generated-application/publication.js +547 -0
  122. package/dist/environment/generated-application/shared.d.ts +492 -0
  123. package/dist/environment/generated-application/shared.js +354 -0
  124. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  125. package/dist/environment/generated-application/surface-observation.js +69 -0
  126. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  127. package/dist/environment/generated-application/surface-outputs.js +400 -0
  128. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  129. package/dist/environment/generated-application/surfaces.js +704 -0
  130. package/dist/environment/generated-application/sync.d.ts +6 -0
  131. package/dist/environment/generated-application/sync.js +124 -0
  132. package/dist/environment/git-observation.d.ts +202 -0
  133. package/dist/environment/git-observation.js +553 -0
  134. package/dist/environment/health-composition.d.ts +12 -0
  135. package/dist/environment/health-composition.js +645 -0
  136. package/dist/environment/host-observation.d.ts +56 -0
  137. package/dist/environment/host-observation.js +52 -0
  138. package/dist/environment/identity.d.ts +48 -0
  139. package/dist/environment/identity.js +85 -0
  140. package/dist/environment/init-application.d.ts +333 -0
  141. package/dist/environment/init-application.js +726 -0
  142. package/dist/environment/install-policy-observation.d.ts +71 -0
  143. package/dist/environment/install-policy-observation.js +284 -0
  144. package/dist/environment/install-policy.d.ts +48 -0
  145. package/dist/environment/install-policy.js +96 -0
  146. package/dist/environment/instance.d.ts +33 -0
  147. package/dist/environment/instance.js +122 -0
  148. package/dist/environment/instruction-pointer.d.ts +19 -0
  149. package/dist/environment/instruction-pointer.js +144 -0
  150. package/dist/environment/lifecycle-composition.d.ts +31 -0
  151. package/dist/environment/lifecycle-composition.js +742 -0
  152. package/dist/environment/lifecycle-stage.d.ts +120 -0
  153. package/dist/environment/lifecycle-stage.js +408 -0
  154. package/dist/environment/lifecycle.d.ts +75 -0
  155. package/dist/environment/lifecycle.js +354 -0
  156. package/dist/environment/logs.d.ts +132 -0
  157. package/dist/environment/logs.js +519 -0
  158. package/dist/environment/map-limit.d.ts +2 -0
  159. package/dist/environment/map-limit.js +13 -0
  160. package/dist/environment/mcp-observation.d.ts +9 -0
  161. package/dist/environment/mcp-observation.js +46 -0
  162. package/dist/environment/observations.d.ts +46 -0
  163. package/dist/environment/observations.js +346 -0
  164. package/dist/environment/operator-application.d.ts +79 -0
  165. package/dist/environment/operator-application.js +197 -0
  166. package/dist/environment/ports.d.ts +182 -0
  167. package/dist/environment/ports.js +1205 -0
  168. package/dist/environment/posture-observation.d.ts +68 -0
  169. package/dist/environment/posture-observation.js +253 -0
  170. package/dist/environment/presence.d.ts +4 -0
  171. package/dist/environment/presence.js +26 -0
  172. package/dist/environment/read-composition.d.ts +32 -0
  173. package/dist/environment/read-composition.js +397 -0
  174. package/dist/environment/readiness-composition.d.ts +57 -0
  175. package/dist/environment/readiness-composition.js +151 -0
  176. package/dist/environment/reclaim.d.ts +370 -0
  177. package/dist/environment/reclaim.js +1940 -0
  178. package/dist/environment/repository-identity.d.ts +13 -0
  179. package/dist/environment/repository-identity.js +108 -0
  180. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  181. package/dist/environment/runtime-measurement-record.js +111 -0
  182. package/dist/environment/runtime-measurement.d.ts +27 -0
  183. package/dist/environment/runtime-measurement.js +224 -0
  184. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  185. package/dist/environment/runtime-probe-residue.js +148 -0
  186. package/dist/environment/runtime-probe.d.ts +72 -0
  187. package/dist/environment/runtime-probe.js +725 -0
  188. package/dist/environment/runtime.d.ts +551 -0
  189. package/dist/environment/runtime.js +2405 -0
  190. package/dist/environment/seed.d.ts +16 -0
  191. package/dist/environment/seed.js +43 -0
  192. package/dist/environment/selftest-application.d.ts +86 -0
  193. package/dist/environment/selftest-application.js +713 -0
  194. package/dist/environment/sensitive.d.ts +3 -0
  195. package/dist/environment/sensitive.js +48 -0
  196. package/dist/environment/served-checkout-grants.d.ts +53 -0
  197. package/dist/environment/served-checkout-grants.js +182 -0
  198. package/dist/environment/setup-observation.d.ts +62 -0
  199. package/dist/environment/setup-observation.js +1473 -0
  200. package/dist/environment/setup.d.ts +43 -0
  201. package/dist/environment/setup.js +176 -0
  202. package/dist/environment/sibling-checkouts.d.ts +119 -0
  203. package/dist/environment/sibling-checkouts.js +254 -0
  204. package/dist/environment/sibling-instructions.d.ts +33 -0
  205. package/dist/environment/sibling-instructions.js +224 -0
  206. package/dist/environment/sibling-observation.d.ts +27 -0
  207. package/dist/environment/sibling-observation.js +45 -0
  208. package/dist/environment/sibling-pull.d.ts +23 -0
  209. package/dist/environment/sibling-pull.js +95 -0
  210. package/dist/environment/sibling-registration.d.ts +45 -0
  211. package/dist/environment/sibling-registration.js +538 -0
  212. package/dist/environment/stack-inputs.d.ts +18 -0
  213. package/dist/environment/stack-inputs.js +210 -0
  214. package/dist/environment/stack-inventory-prune.d.ts +31 -0
  215. package/dist/environment/stack-inventory-prune.js +129 -0
  216. package/dist/environment/stack-inventory-report.d.ts +44 -0
  217. package/dist/environment/stack-inventory-report.js +78 -0
  218. package/dist/environment/stack-inventory.d.ts +94 -0
  219. package/dist/environment/stack-inventory.js +418 -0
  220. package/dist/environment/state.d.ts +410 -0
  221. package/dist/environment/state.js +2153 -0
  222. package/dist/environment/templates.d.ts +140 -0
  223. package/dist/environment/templates.js +685 -0
  224. package/dist/environment/tool-registration.d.ts +9 -0
  225. package/dist/environment/tool-registration.js +92 -0
  226. package/dist/environment/upgrade-application.d.ts +19 -0
  227. package/dist/environment/upgrade-application.js +175 -0
  228. package/dist/environment-exec/execute.d.ts +105 -0
  229. package/dist/environment-exec/execute.js +1179 -0
  230. package/dist/environment-exec/libexec-program.d.ts +6 -0
  231. package/dist/environment-exec/libexec-program.js +22 -0
  232. package/dist/environment-exec/verified-digest.d.ts +4 -0
  233. package/dist/environment-exec/verified-digest.js +16 -0
  234. package/dist/exact-keys.d.ts +7 -0
  235. package/dist/exact-keys.js +15 -0
  236. package/dist/fix-catalogue.d.ts +32 -0
  237. package/dist/fix-catalogue.js +60 -0
  238. package/dist/freshness.d.ts +29 -0
  239. package/dist/freshness.js +75 -0
  240. package/dist/generated-environment/adopt.d.ts +16 -0
  241. package/dist/generated-environment/adopt.js +271 -0
  242. package/dist/generated-environment/command.d.ts +47 -0
  243. package/dist/generated-environment/command.js +75 -0
  244. package/dist/generated-environment/transaction.d.ts +186 -0
  245. package/dist/generated-environment/transaction.js +1770 -0
  246. package/dist/grading/evaluate.d.ts +71 -0
  247. package/dist/grading/evaluate.js +403 -0
  248. package/dist/grading/grade.d.ts +33 -0
  249. package/dist/grading/grade.js +57 -0
  250. package/dist/guards/answer-confirmation.d.ts +41 -0
  251. package/dist/guards/answer-confirmation.js +109 -0
  252. package/dist/guards/evaluator.d.ts +130 -0
  253. package/dist/guards/evaluator.js +1258 -0
  254. package/dist/guards/harness-adapter.d.ts +17 -0
  255. package/dist/guards/harness-adapter.js +57 -0
  256. package/dist/guards/host.d.ts +8 -0
  257. package/dist/guards/host.js +10 -0
  258. package/dist/guards/invocation-parser.d.ts +66 -0
  259. package/dist/guards/invocation-parser.js +595 -0
  260. package/dist/guards/local-identity.d.ts +44 -0
  261. package/dist/guards/local-identity.js +129 -0
  262. package/dist/guards/policy-change.d.ts +43 -0
  263. package/dist/guards/policy-change.js +159 -0
  264. package/dist/guards/policy-pin.d.ts +13 -0
  265. package/dist/guards/policy-pin.js +177 -0
  266. package/dist/guards/protected-locations.d.ts +62 -0
  267. package/dist/guards/protected-locations.js +461 -0
  268. package/dist/guards/read-only.d.ts +37 -0
  269. package/dist/guards/read-only.js +315 -0
  270. package/dist/init/command.d.ts +41 -0
  271. package/dist/init/command.js +330 -0
  272. package/dist/init/derive.d.ts +53 -0
  273. package/dist/init/derive.js +819 -0
  274. package/dist/init/draft.d.ts +140 -0
  275. package/dist/init/draft.js +1228 -0
  276. package/dist/init/render.d.ts +47 -0
  277. package/dist/init/render.js +104 -0
  278. package/dist/init/scaffold.d.ts +53 -0
  279. package/dist/init/scaffold.js +121 -0
  280. package/dist/invocation.d.ts +13 -0
  281. package/dist/invocation.js +20 -0
  282. package/dist/package-metadata.d.ts +9 -0
  283. package/dist/package-metadata.js +19 -0
  284. package/dist/readiness.d.ts +53 -0
  285. package/dist/readiness.js +88 -0
  286. package/dist/references/named.d.ts +24 -0
  287. package/dist/references/named.js +27 -0
  288. package/dist/refusal.d.ts +15 -0
  289. package/dist/refusal.js +7 -0
  290. package/dist/registry-derivation.d.ts +31 -0
  291. package/dist/registry-derivation.js +32 -0
  292. package/dist/registry-resolve/command.d.ts +47 -0
  293. package/dist/registry-resolve/command.js +226 -0
  294. package/dist/registry-resolve/registry.d.ts +68 -0
  295. package/dist/registry-resolve/registry.js +292 -0
  296. package/dist/registry-resolve/render-shell.d.ts +18 -0
  297. package/dist/registry-resolve/render-shell.js +36 -0
  298. package/dist/repair.d.ts +29 -0
  299. package/dist/repair.js +8 -0
  300. package/dist/repository-path.d.ts +9 -0
  301. package/dist/repository-path.js +16 -0
  302. package/dist/schema-json.d.ts +1 -0
  303. package/dist/schema-json.js +177 -0
  304. package/dist/schema.d.ts +1289 -0
  305. package/dist/schema.js +985 -0
  306. package/dist/setup/command.d.ts +4 -0
  307. package/dist/setup/command.js +4 -0
  308. package/dist/sha256.d.ts +1 -0
  309. package/dist/sha256.js +4 -0
  310. package/dist/surfaces/adoption.d.ts +41 -0
  311. package/dist/surfaces/adoption.js +217 -0
  312. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  313. package/dist/surfaces/consumer-instructions.js +43 -0
  314. package/dist/surfaces/contract.d.ts +42 -0
  315. package/dist/surfaces/contract.js +169 -0
  316. package/dist/surfaces/core-declarations.d.ts +24 -0
  317. package/dist/surfaces/core-declarations.js +79 -0
  318. package/dist/surfaces/development-markers.d.ts +1 -0
  319. package/dist/surfaces/development-markers.js +18 -0
  320. package/dist/surfaces/generator-adapter.d.ts +71 -0
  321. package/dist/surfaces/generator-adapter.js +809 -0
  322. package/dist/surfaces/harness-registry.d.ts +260 -0
  323. package/dist/surfaces/harness-registry.js +146 -0
  324. package/dist/surfaces/mcp.d.ts +145 -0
  325. package/dist/surfaces/mcp.js +349 -0
  326. package/dist/surfaces/merge.d.ts +30 -0
  327. package/dist/surfaces/merge.js +105 -0
  328. package/dist/surfaces/ownership.d.ts +190 -0
  329. package/dist/surfaces/ownership.js +795 -0
  330. package/dist/surfaces/projection.d.ts +136 -0
  331. package/dist/surfaces/projection.js +716 -0
  332. package/dist/surfaces/render.d.ts +79 -0
  333. package/dist/surfaces/render.js +636 -0
  334. package/dist/surfaces/restrictions.d.ts +130 -0
  335. package/dist/surfaces/restrictions.js +447 -0
  336. package/dist/surfaces/selftest.d.ts +162 -0
  337. package/dist/surfaces/selftest.js +393 -0
  338. package/dist/surfaces/skill-contract.d.ts +85 -0
  339. package/dist/surfaces/skill-contract.js +449 -0
  340. package/dist/surfaces/sync.d.ts +57 -0
  341. package/dist/surfaces/sync.js +164 -0
  342. package/dist/team-metadata.d.ts +41 -0
  343. package/dist/team-metadata.js +513 -0
  344. package/dist/testing/gates.d.ts +91 -0
  345. package/dist/testing/gates.js +182 -0
  346. package/dist/testing/hooks.d.ts +59 -0
  347. package/dist/testing/hooks.js +139 -0
  348. package/dist/testing/test-protection.d.ts +34 -0
  349. package/dist/testing/test-protection.js +137 -0
  350. package/dist/testing/verdict.d.ts +29 -0
  351. package/dist/testing/verdict.js +62 -0
  352. package/dist/testing/weakening.d.ts +15 -0
  353. package/dist/testing/weakening.js +30 -0
  354. package/dist/work-orders/application-closure.d.ts +7 -0
  355. package/dist/work-orders/application-closure.js +76 -0
  356. package/dist/work-orders/application-context.d.ts +162 -0
  357. package/dist/work-orders/application-context.js +137 -0
  358. package/dist/work-orders/application-decisions.d.ts +27 -0
  359. package/dist/work-orders/application-decisions.js +121 -0
  360. package/dist/work-orders/application-grading.d.ts +11 -0
  361. package/dist/work-orders/application-grading.js +212 -0
  362. package/dist/work-orders/application.d.ts +6 -0
  363. package/dist/work-orders/application.js +223 -0
  364. package/dist/work-orders/composition.d.ts +101 -0
  365. package/dist/work-orders/composition.js +1202 -0
  366. package/dist/work-orders/contract.d.ts +77 -0
  367. package/dist/work-orders/contract.js +140 -0
  368. package/dist/work-orders/decisions.d.ts +23 -0
  369. package/dist/work-orders/decisions.js +55 -0
  370. package/dist/work-orders/store.d.ts +207 -0
  371. package/dist/work-orders/store.js +291 -0
  372. package/docs/README.md +68 -0
  373. package/docs/architecture.md +136 -0
  374. package/docs/capability-matrix.md +58 -0
  375. package/docs/cli-reference.md +807 -0
  376. package/docs/command-list.md +62 -0
  377. package/docs/compose-patterns.md +99 -0
  378. package/docs/concepts.md +173 -0
  379. package/docs/configuration-essentials.md +177 -0
  380. package/docs/configuration-reference.md +636 -0
  381. package/docs/docs-check.md +76 -0
  382. package/docs/environment.md +948 -0
  383. package/docs/extending.md +43 -0
  384. package/docs/faq.md +113 -0
  385. package/docs/getting-started.md +692 -0
  386. package/docs/harness-surfaces.md +500 -0
  387. package/docs/joining-a-team.md +76 -0
  388. package/docs/release-policy.md +49 -0
  389. package/docs/safety-and-autonomy.md +463 -0
  390. package/docs/troubleshooting.md +382 -0
  391. package/docs/tutorial.md +171 -0
  392. package/docs/work-and-browser.md +420 -0
  393. package/libexec/browser-operation.cjs +270 -0
  394. package/libexec/fence-marker.cjs +101 -0
  395. package/libexec/fence-marker.d.cts +24 -0
  396. package/libexec/fenced-group-runner.cjs +77 -0
  397. package/libexec/fenced-supervisor.cjs +137 -0
  398. package/libexec/generated-filesystem.cjs +731 -0
  399. package/libexec/install-filesystem.cjs +225 -0
  400. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  401. package/libexec/rulesync-generator.cjs +743 -0
  402. package/libexec/surface-generator-worker.cjs +266 -0
  403. package/libexec/surface-output-reader.cjs +99 -0
  404. package/libexec/surface-scratch-cleanup.cjs +14 -0
  405. package/libexec/validate-harness-evidence.d.mts +183 -0
  406. package/libexec/validate-harness-evidence.mjs +1312 -0
  407. package/package.json +74 -4
  408. package/surfaces/README.md +33 -0
  409. package/surfaces/agents/evaluator.agent.md +76 -0
  410. package/surfaces/guards/bash-guard.sh +48 -0
  411. package/surfaces/guards/claude.adapter.json +25 -0
  412. package/surfaces/guards/codex.adapter.json +24 -0
  413. package/surfaces/guards/opencode.adapter.json +25 -0
  414. package/surfaces/references/browser.md +63 -0
  415. package/surfaces/references/test-data-and-personas.md +84 -0
  416. package/surfaces/references/testing-tiers.md +11 -0
  417. package/surfaces/skills/cockpit-browser.skill.md +59 -0
  418. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  419. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  420. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  421. package/surfaces/skills/cockpit-work.skill.md +34 -0
  422. package/surfaces/skills/collection.json +5 -0
  423. package/surfaces/skills/procedures/explain.md +66 -0
  424. package/surfaces/skills/procedures/fix.md +95 -0
  425. package/surfaces/skills/procedures/setup.md +452 -0
  426. package/surfaces/skills/procedures/work.md +134 -0
@@ -0,0 +1,130 @@
1
+ import { type GuardSurfaceTarget } from './harness-registry.js';
2
+ export declare const RESTRICTION_OUTPUT_LIMITS: Readonly<{
3
+ readonly scalarBytes: 65536;
4
+ readonly aggregateBytes: 102400;
5
+ readonly fileCount: 8;
6
+ }>;
7
+ export interface InstancePathTarget {
8
+ readonly kind: 'instance';
9
+ readonly name: string;
10
+ readonly root?: string;
11
+ readonly path: string;
12
+ }
13
+ export interface RestrictionPolicy {
14
+ readonly hosted_db_hosts: readonly string[];
15
+ readonly protected_branches: readonly string[];
16
+ readonly readonly: {
17
+ readonly kind: 'agent';
18
+ readonly name: string;
19
+ };
20
+ }
21
+ interface NativeRestrictionAdapter {
22
+ readonly format: 'cockpit-restriction-adapter/v1';
23
+ readonly target: GuardSurfaceTarget;
24
+ readonly mechanism: 'native';
25
+ readonly promptTier: boolean;
26
+ readonly measuredOn: string;
27
+ readonly measuredVersion: string;
28
+ readonly provenance: string;
29
+ readonly registration: {
30
+ readonly kind: 'hooks-json' | 'opencode-plugin';
31
+ readonly path: string;
32
+ readonly event: 'PreToolUse' | 'tool.execute.before';
33
+ readonly matcher: string;
34
+ };
35
+ readonly payload: {
36
+ readonly kind: 'pre-tool-use' | 'opencode-before';
37
+ readonly bashTool: 'Bash' | 'bash';
38
+ readonly editTools: readonly string[];
39
+ };
40
+ readonly readonly: {
41
+ readonly kind: 'agent-frontmatter' | 'instruction-only';
42
+ readonly pathPattern?: string;
43
+ readonly bytes?: string;
44
+ readonly reason?: string;
45
+ };
46
+ }
47
+ interface UnavailableRestrictionAdapter {
48
+ readonly format: 'cockpit-restriction-adapter/v1';
49
+ readonly target: GuardSurfaceTarget;
50
+ readonly mechanism: 'unavailable';
51
+ readonly measuredOn: string;
52
+ readonly measuredVersion: null;
53
+ readonly provenance: string;
54
+ readonly reason: string;
55
+ }
56
+ export type RestrictionAdapter = NativeRestrictionAdapter | UnavailableRestrictionAdapter;
57
+ export interface RestrictionExpansionInput {
58
+ readonly canonicalProjectRoot: string;
59
+ readonly allowedProjectRoots?: readonly string[];
60
+ readonly guardTemplate: string;
61
+ readonly guardPath: string;
62
+ readonly instances: readonly InstancePathTarget[];
63
+ readonly policy: RestrictionPolicy;
64
+ readonly adapters: readonly unknown[];
65
+ /** The configuration's path relative to the project root; the guard names it in its fix command. */
66
+ readonly configFile: string;
67
+ }
68
+ export interface RestrictionFile {
69
+ readonly path: string;
70
+ readonly bytes: string;
71
+ readonly sha256: string;
72
+ }
73
+ export interface RestrictionReadonlyOutput {
74
+ readonly agent: string;
75
+ readonly capability: 'instructed' | 'unavailable';
76
+ readonly evidenceStatus: 'pending_evidence' | 'not_applicable';
77
+ readonly reason: string;
78
+ readonly fragment: RestrictionFile | null;
79
+ }
80
+ export interface RestrictionTargetOutput {
81
+ readonly target: GuardSurfaceTarget;
82
+ readonly capability: 'instructed' | 'unavailable';
83
+ readonly evidenceStatus: 'pending_evidence' | 'not_applicable';
84
+ readonly measuredOn: string;
85
+ readonly measuredVersion: string | null;
86
+ readonly provenance: string;
87
+ readonly files: readonly RestrictionFile[];
88
+ readonly readonly: RestrictionReadonlyOutput;
89
+ }
90
+ export interface ShippedRestrictionCapability {
91
+ readonly target: GuardSurfaceTarget;
92
+ readonly capability: 'instructed' | 'unavailable';
93
+ readonly evidenceStatus: 'pending_evidence' | 'not_applicable';
94
+ readonly measuredOn: string;
95
+ readonly measuredVersion: string | null;
96
+ readonly provenance: string;
97
+ }
98
+ export type ShippedRestrictionCapabilities = Readonly<{
99
+ readonly kind: 'complete';
100
+ readonly targets: readonly ShippedRestrictionCapability[];
101
+ }> | RestrictionExpansionRefusal;
102
+ export interface RestrictionExpansionComplete {
103
+ readonly kind: 'complete';
104
+ readonly sharedGuard: RestrictionFile;
105
+ readonly targets: readonly RestrictionTargetOutput[];
106
+ }
107
+ export interface RestrictionExpansionRefusal {
108
+ readonly kind: 'refusal';
109
+ readonly code: 'RESTRICTION_INPUT_INVALID' | 'RESTRICTION_ADAPTER_INVALID' | 'RESTRICTION_EVENT_UNADAPTED' | 'RESTRICTION_TEMPLATE_INVALID' | 'RESTRICTION_OUTPUT_LIMIT';
110
+ readonly message: string;
111
+ }
112
+ export type RestrictionExpansionResult = RestrictionExpansionComplete | RestrictionExpansionRefusal;
113
+ export declare function inspectShippedRestrictionCapabilities(input: unknown): ShippedRestrictionCapabilities;
114
+ export declare function expandRestrictions(input: unknown): RestrictionExpansionResult;
115
+ /** Capture the future registration without changing the operative installed guard. */
116
+ export declare function validateGuardRegistration(input: unknown): RestrictionExpansionRefusal | {
117
+ kind: 'complete';
118
+ target: string;
119
+ handler: string;
120
+ cwd: string;
121
+ argv: any[];
122
+ registration: RestrictionFile;
123
+ };
124
+ export declare function compareRestrictionOutputs(expectedInput: unknown, actualInput: unknown): RestrictionExpansionRefusal | {
125
+ kind: string;
126
+ currentTargets: ("claudecode" | "codexcli" | "opencode")[];
127
+ staleTargets: ("claudecode" | "codexcli" | "opencode")[];
128
+ sharedGuardCurrent: boolean;
129
+ };
130
+ export {};
@@ -0,0 +1,447 @@
1
+ import { sha256Hex } from '../sha256.js';
2
+ import { isAbsolute, posix } from 'node:path';
3
+ import { snapshotJson } from '../environment/contract.js';
4
+ import { GUARD_SURFACE_TARGETS } from './harness-registry.js';
5
+ import { GUARD_HANDLER_ARGV } from '../guards/harness-adapter.js';
6
+ import { GUARD_HOST_LIMITS, isGuardHost, normalizeGuardHost } from '../guards/host.js';
7
+ import { exactKeys } from '../exact-keys.js';
8
+ export const RESTRICTION_OUTPUT_LIMITS = Object.freeze({
9
+ scalarBytes: 65_536,
10
+ aggregateBytes: 102_400,
11
+ fileCount: 8,
12
+ });
13
+ const FORMAT = 'cockpit-restriction-adapter/v1';
14
+ // Generated bytes are committed, so the only value rendered into the guard is root-relative; the
15
+ // handler identity that names this machine is written to the ignored local file instead.
16
+ const MARKERS = Object.freeze({
17
+ config: '__COCKPIT_GUARD_CONFIG__',
18
+ });
19
+ const DATE = /^\d{4}-\d{2}-\d{2}$/;
20
+ const VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$/;
21
+ const NAME = /^[a-z0-9][a-z0-9-]{0,63}$/;
22
+ const BRANCH = /^(?!.*\.\.)(?!\/)(?!.*\/$)[A-Za-z0-9][A-Za-z0-9._/-]{0,254}$/;
23
+ const GUARD_PATH = '.cockpit/guards/bash-guard.sh';
24
+ const MAX_INSTANCES = 256;
25
+ const MAX_POLICY_ROWS = 128;
26
+ const NATIVE_PROVENANCE = Object.freeze({
27
+ claudecode: 'paired native PreToolUse payload and decision probe',
28
+ codexcli: 'paired native PreToolUse payload, apply_patch and ask-decision probe',
29
+ opencode: 'paired native tool.execute.before payload, apply_patch and thrown-refusal probe',
30
+ });
31
+ const MEASURED = Object.freeze({
32
+ claudecode: { date: '2026-08-25', version: '2.1.220' },
33
+ codexcli: { date: '2026-08-24', version: '0.149.0' },
34
+ opencode: { date: '2026-08-24', version: '1.18.15' },
35
+ });
36
+ function object(value) {
37
+ return value !== undefined && value !== null && typeof value === 'object' && !Array.isArray(value);
38
+ }
39
+ function frozen(value) {
40
+ const captured = snapshotJson(value);
41
+ return captured === undefined ? undefined : captured;
42
+ }
43
+ function refusal(code, message) {
44
+ return frozen({ kind: 'refusal', code, message }) ?? { kind: 'refusal', code, message };
45
+ }
46
+ function safeScalar(value, max = 4_096) {
47
+ return value.length > 0 && Buffer.byteLength(value) <= max && !/[\x00-\x1f\x7f]/.test(value);
48
+ }
49
+ function canonicalRoot(value) {
50
+ return safeScalar(value) && isAbsolute(value) && value !== '/' && !value.endsWith('/')
51
+ && posix.normalize(value) === value && !value.includes('\\');
52
+ }
53
+ function confinedRelativePath(value) {
54
+ return safeScalar(value) && !isAbsolute(value) && !value.includes('\\') && posix.normalize(value) === value
55
+ && value !== '.' && !value.split('/').some((part) => part === '..' || part.length === 0);
56
+ }
57
+ function boundedUniqueStrings(value, pattern, max) {
58
+ if (!Array.isArray(value) || value.length > max || !value.every((item) => typeof item === 'string' && pattern.test(item)))
59
+ return undefined;
60
+ const strings = value;
61
+ return new Set(strings).size === strings.length ? strings : undefined;
62
+ }
63
+ function capturePolicy(value) {
64
+ if (!object(value) || !exactKeys(value, ['hosted_db_hosts', 'protected_branches', 'readonly']))
65
+ return undefined;
66
+ const hosted = value.hosted_db_hosts;
67
+ if (!Array.isArray(hosted) || hosted.length > GUARD_HOST_LIMITS.rows || !hosted.every(isGuardHost)
68
+ || new Set(hosted.map(normalizeGuardHost)).size !== hosted.length)
69
+ return undefined;
70
+ const branches = boundedUniqueStrings(value.protected_branches, BRANCH, MAX_POLICY_ROWS);
71
+ if (!hosted || !branches
72
+ || !object(value.readonly) || !exactKeys(value.readonly, ['kind', 'name']) || value.readonly.kind !== 'agent'
73
+ || typeof value.readonly.name !== 'string' || !NAME.test(value.readonly.name))
74
+ return undefined;
75
+ return value;
76
+ }
77
+ function capturedInput(input) {
78
+ const value = snapshotJson(input);
79
+ if (!object(value))
80
+ return undefined;
81
+ const inputKeys = ['canonicalProjectRoot', 'guardTemplate', 'guardPath', 'instances', 'policy', 'adapters', 'configFile'];
82
+ const hasAllowedRoots = Object.hasOwn(value, 'allowedProjectRoots');
83
+ if (!exactKeys(value, hasAllowedRoots ? [...inputKeys, 'allowedProjectRoots'] : inputKeys)
84
+ || typeof value.canonicalProjectRoot !== 'string' || !canonicalRoot(value.canonicalProjectRoot)
85
+ || typeof value.guardTemplate !== 'string' || value.guardPath !== GUARD_PATH
86
+ || !Array.isArray(value.instances) || value.instances.length > MAX_INSTANCES || !Array.isArray(value.adapters)
87
+ || typeof value.configFile !== 'string' || !confinedRelativePath(value.configFile))
88
+ return undefined;
89
+ const allowedProjectRoots = hasAllowedRoots ? value.allowedProjectRoots : [value.canonicalProjectRoot];
90
+ if (!Array.isArray(allowedProjectRoots) || allowedProjectRoots.length === 0 || allowedProjectRoots.length > MAX_INSTANCES
91
+ || !allowedProjectRoots.every((root) => typeof root === 'string' && canonicalRoot(root))
92
+ || new Set(allowedProjectRoots).size !== allowedProjectRoots.length
93
+ || !allowedProjectRoots.includes(value.canonicalProjectRoot))
94
+ return undefined;
95
+ const names = new Set();
96
+ const paths = new Set();
97
+ const instances = [];
98
+ for (const target of value.instances) {
99
+ if (!object(target))
100
+ return undefined;
101
+ const hasRoot = Object.hasOwn(target, 'root');
102
+ if (!exactKeys(target, hasRoot ? ['kind', 'name', 'root', 'path'] : ['kind', 'name', 'path']) || target.kind !== 'instance'
103
+ || typeof target.name !== 'string' || !NAME.test(target.name) || names.has(target.name)
104
+ || typeof target.path !== 'string' || !confinedRelativePath(target.path))
105
+ return undefined;
106
+ const root = hasRoot ? target.root : value.canonicalProjectRoot;
107
+ if (typeof root !== 'string' || !allowedProjectRoots.includes(root))
108
+ return undefined;
109
+ const path = posix.join(root, target.path);
110
+ if (paths.has(path))
111
+ return undefined;
112
+ names.add(target.name);
113
+ paths.add(path);
114
+ instances.push({ kind: 'instance', name: target.name, root, path: target.path });
115
+ }
116
+ const policy = capturePolicy(value.policy);
117
+ return policy ? {
118
+ ...value,
119
+ allowedProjectRoots: allowedProjectRoots,
120
+ instances,
121
+ } : undefined;
122
+ }
123
+ function stringArray(value) {
124
+ return Array.isArray(value) && value.length > 0 && value.length <= 8
125
+ && value.every((item) => typeof item === 'string') ? value : undefined;
126
+ }
127
+ function measuredFields(value) {
128
+ return typeof value.measuredOn === 'string' && DATE.test(value.measuredOn)
129
+ && typeof value.measuredVersion === 'string' && VERSION.test(value.measuredVersion)
130
+ && typeof value.provenance === 'string' && safeScalar(value.provenance, 200)
131
+ && !/credential|secret|token|password|private[ _-]?key/i.test(value.provenance);
132
+ }
133
+ function unavailableAdapter(value) {
134
+ if (!exactKeys(value, ['format', 'target', 'mechanism', 'measuredOn', 'measuredVersion', 'provenance', 'reason'])
135
+ || value.format !== FORMAT || value.mechanism !== 'unavailable' || value.measuredVersion !== null
136
+ || typeof value.target !== 'string' || !GUARD_SURFACE_TARGETS.includes(value.target)
137
+ || typeof value.measuredOn !== 'string' || !DATE.test(value.measuredOn)
138
+ || typeof value.provenance !== 'string' || !safeScalar(value.provenance, 200)
139
+ || /credential|secret|token|password|private[ _-]?key/i.test(value.provenance)
140
+ || typeof value.reason !== 'string' || !safeScalar(value.reason, 300))
141
+ return undefined;
142
+ return value;
143
+ }
144
+ function nativeShapeMatches(adapter) {
145
+ if (adapter.target === 'claudecode')
146
+ return adapter.promptTier
147
+ && adapter.registration.kind === 'hooks-json' && adapter.registration.path === '.claude/settings.json'
148
+ && adapter.registration.event === 'PreToolUse' && adapter.registration.matcher === 'Bash|Edit|Write|NotebookEdit'
149
+ && adapter.payload.kind === 'pre-tool-use' && adapter.payload.bashTool === 'Bash'
150
+ && JSON.stringify(adapter.payload.editTools) === JSON.stringify(['Edit', 'Write', 'NotebookEdit'])
151
+ && adapter.readonly.kind === 'agent-frontmatter' && adapter.readonly.pathPattern === '.claude/agents/{agent}.md'
152
+ && adapter.readonly.bytes === 'disallowedTools: Edit, Write, NotebookEdit\n';
153
+ if (adapter.target === 'codexcli')
154
+ return !adapter.promptTier
155
+ && adapter.registration.kind === 'hooks-json' && adapter.registration.path === '.codex/hooks.json'
156
+ && adapter.registration.event === 'PreToolUse' && adapter.registration.matcher === 'Bash|Edit|Write|apply_patch'
157
+ && adapter.payload.kind === 'pre-tool-use' && adapter.payload.bashTool === 'Bash'
158
+ && JSON.stringify(adapter.payload.editTools) === JSON.stringify(['Edit', 'Write', 'apply_patch'])
159
+ && adapter.readonly.kind === 'instruction-only' && adapter.readonly.reason === 'agent role edit restrictions were measured inert';
160
+ return !adapter.promptTier && adapter.registration.kind === 'opencode-plugin'
161
+ && adapter.registration.path === '.opencode/plugin/cockpit-guards.js'
162
+ && adapter.registration.event === 'tool.execute.before' && adapter.registration.matcher === 'bash|edit|write|apply_patch'
163
+ && adapter.payload.kind === 'opencode-before' && adapter.payload.bashTool === 'bash'
164
+ && JSON.stringify(adapter.payload.editTools) === JSON.stringify(['edit', 'write', 'apply_patch'])
165
+ && adapter.readonly.kind === 'agent-frontmatter' && adapter.readonly.pathPattern === '.opencode/agents/{agent}.md'
166
+ && adapter.readonly.bytes === 'permission:\n edit: deny\n';
167
+ }
168
+ function nativeAdapter(value) {
169
+ if (!exactKeys(value, ['format', 'target', 'mechanism', 'promptTier', 'measuredOn', 'measuredVersion', 'provenance', 'registration', 'payload', 'readonly'])
170
+ || value.format !== FORMAT || value.mechanism !== 'native' || typeof value.promptTier !== 'boolean'
171
+ || typeof value.target !== 'string' || !GUARD_SURFACE_TARGETS.includes(value.target) || !measuredFields(value)
172
+ || !object(value.registration) || !exactKeys(value.registration, ['kind', 'path', 'event', 'matcher'])
173
+ || typeof value.registration.kind !== 'string' || typeof value.registration.path !== 'string'
174
+ || typeof value.registration.event !== 'string' || typeof value.registration.matcher !== 'string'
175
+ || !object(value.payload) || !exactKeys(value.payload, ['kind', 'bashTool', 'editTools'])
176
+ || typeof value.payload.kind !== 'string' || typeof value.payload.bashTool !== 'string' || !stringArray(value.payload.editTools)
177
+ || !object(value.readonly))
178
+ return undefined;
179
+ const readonlyKeys = value.readonly.kind === 'agent-frontmatter' ? ['kind', 'pathPattern', 'bytes'] : ['kind', 'reason'];
180
+ if (!exactKeys(value.readonly, readonlyKeys))
181
+ return undefined;
182
+ const adapter = value;
183
+ const measurement = MEASURED[adapter.target];
184
+ return nativeShapeMatches(adapter) && adapter.measuredOn === measurement.date
185
+ && adapter.measuredVersion === measurement.version && adapter.provenance === NATIVE_PROVENANCE[adapter.target]
186
+ ? adapter : undefined;
187
+ }
188
+ function captureAdapter(input) {
189
+ const value = snapshotJson(input);
190
+ if (!object(value))
191
+ return undefined;
192
+ return value.mechanism === 'unavailable' ? unavailableAdapter(value) : nativeAdapter(value);
193
+ }
194
+ const issuedShippedRestrictionCapabilities = new WeakSet();
195
+ const shippedRestrictionBindings = new WeakMap();
196
+ function validGuardTemplate(value) {
197
+ return Buffer.byteLength(value) <= RESTRICTION_OUTPUT_LIMITS.scalarBytes
198
+ && !/[\uD800-\uDFFF]/u.test(value)
199
+ && Object.values(MARKERS).every(marker => value.split(marker).length === 2);
200
+ }
201
+ export function inspectShippedRestrictionCapabilities(input) {
202
+ const value = snapshotJson(input);
203
+ if (!object(value) || !exactKeys(value, ['guardTemplate', 'adapters'])
204
+ || typeof value.guardTemplate !== 'string' || !Array.isArray(value.adapters)) {
205
+ return refusal('RESTRICTION_INPUT_INVALID', 'shipped restriction inspection requires bounded guard template and adapter bytes');
206
+ }
207
+ if (!validGuardTemplate(value.guardTemplate)) {
208
+ return refusal('RESTRICTION_TEMPLATE_INVALID', 'guard template must contain every transport identity marker exactly once');
209
+ }
210
+ const adapters = new Map();
211
+ for (const candidate of value.adapters) {
212
+ const adapter = captureAdapter(candidate);
213
+ if (!adapter || adapters.has(adapter.target)) {
214
+ return refusal('RESTRICTION_ADAPTER_INVALID', 'restriction adapters must use one closed measured transport shape per target');
215
+ }
216
+ adapters.set(adapter.target, adapter);
217
+ }
218
+ const missing = GUARD_SURFACE_TARGETS.find(target => !adapters.has(target));
219
+ if (missing)
220
+ return refusal('RESTRICTION_EVENT_UNADAPTED', `restriction event has no adapter for ${missing}`);
221
+ const targets = GUARD_SURFACE_TARGETS.map((target) => {
222
+ const adapter = adapters.get(target);
223
+ const row = {
224
+ target,
225
+ capability: adapter.mechanism === 'unavailable' ? 'unavailable' : 'instructed',
226
+ evidenceStatus: adapter.mechanism === 'unavailable' ? 'not_applicable' : 'pending_evidence',
227
+ measuredOn: adapter.measuredOn,
228
+ measuredVersion: adapter.measuredVersion,
229
+ provenance: adapter.provenance,
230
+ };
231
+ issuedShippedRestrictionCapabilities.add(row);
232
+ return row;
233
+ });
234
+ const result = { kind: 'complete', targets };
235
+ issuedShippedRestrictionCapabilities.add(result);
236
+ shippedRestrictionBindings.set(result, Object.freeze({ adapters }));
237
+ return result;
238
+ }
239
+ function file(path, bytes) {
240
+ return { path, bytes, sha256: sha256Hex(bytes) };
241
+ }
242
+ function shellLiteral(value) {
243
+ return value.replaceAll("'", "'\\''");
244
+ }
245
+ // Registrations are committed, so each finds the cockpit checkout at run time through the
246
+ // mechanism its harness documents: Claude Code exports the project directory to hooks, and Codex runs
247
+ // hooks in the session working directory. The Git root can be above a cockpit rooted in a subdirectory.
248
+ const HOOK_ROOT = Object.freeze({
249
+ claudecode: { expression: '${CLAUDE_PROJECT_DIR:-}', source: 'CLAUDE_PROJECT_DIR' },
250
+ codexcli: { expression: '$(pwd -P 2>/dev/null)', source: 'the canonical Codex session directory' },
251
+ });
252
+ function hooksBytes(target, matcher, guardPath, askIsDeny, configFile) {
253
+ const root = HOOK_ROOT[target];
254
+ // Both hook runners block only on exit 2; any other failure (a handler that cannot load, a killed
255
+ // process, a missing shell) would let the tool call run, so it is folded into a blocking exit.
256
+ const guard = `R="${root.expression}"; H="$R/${guardPath}"; test -n "$R" && test -f "$H" && test -r "$H" || { printf 'cockpit restriction guard missing: %s (the hook finds the cockpit checkout through ${root.source}), so every tool call is held. Fix: start the agent from the cockpit checkout; if the file is gone, run git restore ${guardPath} there (it is committed), or have a human run npx --no cockpit sync --config <your cockpit.toml> in a terminal.\\n' "$H" >&2; exit 2; }; ${askIsDeny ? 'COCKPIT_ASK_IS_DENY=1 ' : ''}/bin/bash "$H" --harness ${target}; s=$?; case $s in 0|2) exit $s;; esac; printf 'cockpit guard failed (exit %s)\\n' "$s" >&2; exit 2`;
257
+ // Claude Code 2.1.317 and Codex 0.166.0 (a trusted hook) each ran a SessionStart command and put its
258
+ // stdout in the agent's context, measured 2026-11-03 beside a misspelled event that never ran. A
259
+ // drift report there tells the agent what to re-sync before it works; the hook never blocks.
260
+ const sessionStart = `R="${root.expression}"; test -n "$R" && test -x "$R/node_modules/.bin/cockpit" || exit 0; cd "$R" && ./node_modules/.bin/cockpit check --config ${shellLiteral(configFile)} 2>&1 | tail -n 40; exit 0`;
261
+ const hooks = { PreToolUse: [{ matcher, hooks: [{ type: 'command', command: guard }] }], SessionStart: [{ hooks: [{ type: 'command', command: sessionStart }] }] };
262
+ const value = target === 'codexcli' ? { description: 'Generated authenticated restriction transport and session-start drift check.', hooks } : { hooks };
263
+ return `${JSON.stringify(value, null, 2)}\n`;
264
+ }
265
+ function opencodeBytes(guardPath, configFile) {
266
+ // The plugin is committed, so it locates the guard from its own published location.
267
+ const guard = `fileURLToPath(new URL(${JSON.stringify(`../../${guardPath}`)}, import.meta.url))`;
268
+ // OpenCode's native read, grep and glob tools never reach a shell, and OpenCode 1.19.4 returned a
269
+ // secrets file through read while the plugin mapped only shell, edit and patch tools (2026-11-01).
270
+ // The guard parses shell, edit and patch payloads only, so each native read is restated as the
271
+ // shell command that reads at least the same paths: `cat` for read, a recursive `grep` for grep
272
+ // (OpenCode searches hidden files too) and `ls` for glob, which reports names only.
273
+ const readTools = ` if (tool === "Read") return typeof args.filePath === "string" ? shell(\`cat -- \${quote(args.filePath)}\`) : null\n if (tool === "Grep" || tool === "Glob") {\n if (typeof args.pattern !== "string" || args.path !== undefined && typeof args.path !== "string") return null\n const path = quote(args.path ?? ".")\n return shell(tool === "Grep" ? \`grep -r -e \${quote(args.pattern)} -- \${path}\` : \`ls -- \${path}\`)\n }\n`;
274
+ return `import { spawnSync } from "node:child_process"\nimport { fileURLToPath } from "node:url"\n\nconst tools = { bash: "Bash", edit: "Edit", write: "Write", apply_patch: "apply_patch", read: "Read", grep: "Grep", glob: "Glob" }\n\nconst quote = (value) => \`'\${value.replaceAll("'", "'\\\\''")}'\`\nconst shell = (command) => ({ hook_event_name: "PreToolUse", tool_name: "Bash", tool_input: { command } })\n\nfunction payload(tool, args) {\n if (tool === "Bash") return typeof args.command === "string" ? shell(args.command) : null\n if (tool === "apply_patch") return typeof args.patchText === "string" ? { hook_event_name: "PreToolUse", tool_name: "apply_patch", tool_input: { command: args.patchText } } : null\n${readTools} const file_path = args.filePath ?? args.path ?? args.file\n if (typeof file_path !== "string") return null\n // The guard checks an edit of the configuration against its pinned policy tables, so that file's new text is sent.\n const text = file_path.split("/").pop() !== ${JSON.stringify(configFile.split('/').pop())} ? {}\n : tool === "Edit" ? { old_string: args.oldString, new_string: args.newString, replace_all: args.replaceAll === true } : { content: args.content }\n return { hook_event_name: "PreToolUse", tool_name: tool, tool_input: { file_path, ...text } }\n}\n\nexport const CockpitRestrictions = async () => ({\n "tool.execute.before": async (input, output) => {\n const tool = tools[String(input?.tool ?? "").toLowerCase()]\n if (!tool) return\n const normalized = payload(tool, output?.args ?? {})\n if (!normalized) throw new Error("cockpit restriction guard: malformed applicable payload")\n const result = spawnSync("/bin/bash", [${guard}, "--harness", "opencode"], {\n input: JSON.stringify(normalized), encoding: "utf8",\n env: { ...process.env, COCKPIT_ASK_IS_DENY: "1" },\n })\n if (result.error) throw new Error(\`cockpit restriction guard failed: \${result.error.message}\`)\n // A restated read names the command the guard judged, so the agent can narrow its path.\n const checked = tool === "Read" || tool === "Grep" || tool === "Glob" ? \` (OpenCode \${tool.toLowerCase()} is checked as: \${normalized.tool_input.command})\` : ""\n if (result.status !== 0) throw new Error((result.stderr.trim() || "cockpit restriction guard refused without a reason") + checked)\n let decision\n try { decision = JSON.parse(result.stdout || "{}")?.hookSpecificOutput }\n catch { throw new Error("cockpit restriction guard returned malformed output") }\n if (decision?.permissionDecision === "deny" || decision?.permissionDecision === "ask") {\n throw new Error(\`cockpit restriction guard: \${decision.permissionDecisionReason}\${checked}\`)\n }\n },\n})\n`;
275
+ }
276
+ function registration(adapter, input) {
277
+ // The measured shapes pair OpenCode with its plugin and the other targets with hooks.json.
278
+ // Codex and OpenCode have no ask tier, so a prompt they cannot show refuses instead of proceeding;
279
+ // Claude Code shows the prompt.
280
+ const bytes = adapter.target === 'opencode'
281
+ ? opencodeBytes(input.guardPath, input.configFile)
282
+ : hooksBytes(adapter.target, adapter.registration.matcher, input.guardPath, adapter.target === 'codexcli', input.configFile);
283
+ return file(adapter.registration.path, bytes);
284
+ }
285
+ function readonlyOutput(adapter, agent) {
286
+ if (adapter.mechanism === 'unavailable')
287
+ return {
288
+ agent, capability: 'unavailable', evidenceStatus: 'not_applicable', reason: adapter.reason, fragment: null,
289
+ };
290
+ if (adapter.readonly.kind === 'instruction-only')
291
+ return {
292
+ agent, capability: 'instructed', evidenceStatus: 'pending_evidence', reason: adapter.readonly.reason, fragment: null,
293
+ };
294
+ const path = adapter.readonly.pathPattern.replace('{agent}', agent);
295
+ return {
296
+ agent, capability: 'instructed', evidenceStatus: 'pending_evidence',
297
+ reason: 'native spelling generated; operator proof remains pending', fragment: file(path, adapter.readonly.bytes),
298
+ };
299
+ }
300
+ function renderGuard(input) {
301
+ if (!validGuardTemplate(input.guardTemplate))
302
+ return undefined;
303
+ let rendered = input.guardTemplate;
304
+ const replacements = {
305
+ [MARKERS.config]: input.configFile,
306
+ };
307
+ for (const [marker, value] of Object.entries(replacements)) {
308
+ if (rendered.split(marker).length !== 2)
309
+ return undefined;
310
+ rendered = rendered.replace(marker, () => shellLiteral(value));
311
+ }
312
+ return rendered;
313
+ }
314
+ function outputWithinLimits(value) {
315
+ let files = 1;
316
+ const visit = (candidate) => {
317
+ if (typeof candidate === 'string')
318
+ return Buffer.byteLength(candidate) <= RESTRICTION_OUTPUT_LIMITS.scalarBytes;
319
+ if (candidate === null || typeof candidate !== 'object')
320
+ return true;
321
+ if (Array.isArray(candidate))
322
+ return candidate.every(visit);
323
+ return Object.values(candidate).every(visit);
324
+ };
325
+ for (const row of value.targets) {
326
+ files += row.files.length + (row.readonly.fragment === null ? 0 : 1);
327
+ }
328
+ return files <= RESTRICTION_OUTPUT_LIMITS.fileCount && visit(value)
329
+ && Buffer.byteLength(JSON.stringify(value)) <= RESTRICTION_OUTPUT_LIMITS.aggregateBytes;
330
+ }
331
+ export function expandRestrictions(input) {
332
+ const captured = capturedInput(input);
333
+ if (!captured)
334
+ return refusal('RESTRICTION_INPUT_INVALID', 'restriction input must be bounded own data with confined instance paths and an authenticated root');
335
+ const capabilities = inspectShippedRestrictionCapabilities({
336
+ guardTemplate: captured.guardTemplate,
337
+ adapters: captured.adapters,
338
+ });
339
+ if (capabilities.kind === 'refusal')
340
+ return capabilities;
341
+ const binding = issuedShippedRestrictionCapabilities.has(capabilities)
342
+ && capabilities.targets.every(row => issuedShippedRestrictionCapabilities.has(row))
343
+ ? shippedRestrictionBindings.get(capabilities) : undefined;
344
+ if (!binding)
345
+ return refusal('RESTRICTION_ADAPTER_INVALID', 'restriction capabilities were not issued by the shipped restriction owner');
346
+ const guardBytes = renderGuard(captured);
347
+ if (guardBytes === undefined) {
348
+ return refusal('RESTRICTION_TEMPLATE_INVALID', 'guard template must contain every transport identity marker exactly once');
349
+ }
350
+ const targets = GUARD_SURFACE_TARGETS.map((target) => {
351
+ const adapter = binding.adapters.get(target);
352
+ const capability = capabilities.targets.find(row => row.target === target);
353
+ if (adapter.mechanism === 'unavailable')
354
+ return {
355
+ ...capability, files: [],
356
+ readonly: readonlyOutput(adapter, captured.policy.readonly.name),
357
+ };
358
+ return {
359
+ ...capability,
360
+ files: [registration(adapter, captured)], readonly: readonlyOutput(adapter, captured.policy.readonly.name),
361
+ };
362
+ });
363
+ const result = {
364
+ kind: 'complete', sharedGuard: file(captured.guardPath, guardBytes), targets,
365
+ };
366
+ if (!outputWithinLimits(result)) {
367
+ return refusal('RESTRICTION_OUTPUT_LIMIT', 'rendered restriction output exceeds scalar, aggregate, or file-count limits');
368
+ }
369
+ return frozen(result) ?? refusal('RESTRICTION_OUTPUT_LIMIT', 'rendered restriction output cannot be captured within bounded JSON limits');
370
+ }
371
+ function validFile(value) {
372
+ return object(value) && exactKeys(value, ['path', 'bytes', 'sha256'])
373
+ && typeof value.path === 'string' && safeScalar(value.path) && typeof value.bytes === 'string'
374
+ && Buffer.byteLength(value.bytes) <= RESTRICTION_OUTPUT_LIMITS.scalarBytes
375
+ && typeof value.sha256 === 'string' && value.sha256 === sha256Hex(value.bytes);
376
+ }
377
+ /** Capture the future registration without changing the operative installed guard. */
378
+ export function validateGuardRegistration(input) {
379
+ const value = snapshotJson(input);
380
+ if (!object(value) || !exactKeys(value, ['target', 'handler', 'cwd', 'argv', 'registration'])
381
+ || typeof value.target !== 'string' || !GUARD_SURFACE_TARGETS.includes(value.target)
382
+ || typeof value.handler !== 'string' || !canonicalRoot(value.handler)
383
+ || typeof value.cwd !== 'string' || !canonicalRoot(value.cwd)
384
+ || !Array.isArray(value.argv) || JSON.stringify(value.argv) !== JSON.stringify([...GUARD_HANDLER_ARGV, '--harness', value.target])
385
+ || !object(value.registration) || !exactKeys(value.registration, ['path', 'bytes'])
386
+ || typeof value.registration.path !== 'string' || !confinedRelativePath(value.registration.path)
387
+ || typeof value.registration.bytes !== 'string' || !value.registration.bytes
388
+ || Buffer.byteLength(value.registration.bytes) > RESTRICTION_OUTPUT_LIMITS.scalarBytes
389
+ || /[\uD800-\uDFFF]/u.test(value.registration.bytes)) {
390
+ return refusal('RESTRICTION_ADAPTER_INVALID', 'staged registration requires exact handler argv and bounded confined output bytes');
391
+ }
392
+ const paths = { claudecode: '.claude/settings.json', codexcli: '.codex/hooks.json', opencode: '.opencode/plugin/cockpit-guards.js' };
393
+ if (value.registration.path !== paths[value.target]) {
394
+ return refusal('RESTRICTION_ADAPTER_INVALID', 'staged registration path does not match its harness');
395
+ }
396
+ return frozen({ kind: 'complete', target: value.target, handler: value.handler, cwd: value.cwd, argv: value.argv,
397
+ registration: file(value.registration.path, value.registration.bytes) })
398
+ ?? refusal('RESTRICTION_OUTPUT_LIMIT', 'staged registration cannot be captured within output limits');
399
+ }
400
+ function validReadonly(value) {
401
+ return object(value) && exactKeys(value, ['agent', 'capability', 'evidenceStatus', 'reason', 'fragment'])
402
+ && typeof value.agent === 'string' && NAME.test(value.agent) && typeof value.reason === 'string'
403
+ && (value.fragment === null || validFile(value.fragment))
404
+ && (value.capability === 'unavailable' && value.evidenceStatus === 'not_applicable' && value.fragment === null
405
+ || value.capability === 'instructed' && value.evidenceStatus === 'pending_evidence');
406
+ }
407
+ function validExpansion(value) {
408
+ const captured = snapshotJson(value);
409
+ if (!object(captured) || !exactKeys(captured, ['kind', 'sharedGuard', 'targets']) || captured.kind !== 'complete'
410
+ || !validFile(captured.sharedGuard) || !Array.isArray(captured.targets) || captured.targets.length !== GUARD_SURFACE_TARGETS.length)
411
+ return undefined;
412
+ const seen = new Set();
413
+ for (const row of captured.targets) {
414
+ if (!object(row) || !exactKeys(row, ['target', 'capability', 'evidenceStatus', 'measuredOn', 'measuredVersion', 'provenance', 'files', 'readonly'])
415
+ || typeof row.target !== 'string' || !GUARD_SURFACE_TARGETS.includes(row.target) || seen.has(row.target)
416
+ || typeof row.measuredOn !== 'string' || !DATE.test(row.measuredOn)
417
+ || row.measuredVersion !== null && (typeof row.measuredVersion !== 'string' || !VERSION.test(row.measuredVersion))
418
+ || typeof row.provenance !== 'string' || !Array.isArray(row.files) || !row.files.every(validFile) || !validReadonly(row.readonly)
419
+ || row.capability === 'unavailable' && (row.evidenceStatus !== 'not_applicable' || row.files.length !== 0)
420
+ || row.capability === 'instructed' && (row.evidenceStatus !== 'pending_evidence' || row.files.length !== 1)
421
+ || row.capability !== 'unavailable' && row.capability !== 'instructed')
422
+ return undefined;
423
+ seen.add(row.target);
424
+ }
425
+ const result = captured;
426
+ return outputWithinLimits(result) ? result : undefined;
427
+ }
428
+ export function compareRestrictionOutputs(expectedInput, actualInput) {
429
+ const expected = validExpansion(expectedInput);
430
+ const actual = validExpansion(actualInput);
431
+ if (!expected || !actual)
432
+ return refusal('RESTRICTION_INPUT_INVALID', 'restriction comparison requires two bounded complete expansions');
433
+ const actualTargets = new Map(actual.targets.map((row) => [row.target, row]));
434
+ const staleTargets = [];
435
+ const currentTargets = [];
436
+ for (const row of expected.targets) {
437
+ const actualRow = actualTargets.get(row.target);
438
+ if (JSON.stringify(row) === JSON.stringify(actualRow))
439
+ currentTargets.push(row.target);
440
+ else
441
+ staleTargets.push(row.target);
442
+ }
443
+ return frozen({
444
+ kind: 'complete', currentTargets, staleTargets,
445
+ sharedGuardCurrent: JSON.stringify(expected.sharedGuard) === JSON.stringify(actual.sharedGuard),
446
+ }) ?? refusal('RESTRICTION_OUTPUT_LIMIT', 'restriction comparison output exceeds bounded JSON limits');
447
+ }