@agilno-tech/rivet 0.1.0-alpha.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 (283) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +59 -0
  4. package/bin/cli.js +42 -0
  5. package/dist/agents/backend-django-agent/SKILL.md +35 -0
  6. package/dist/agents/backend-nestjs-agent/SKILL.md +36 -0
  7. package/dist/agents/boss-agent/SKILL.md +28 -0
  8. package/dist/agents/bounded-worker/SKILL.md +28 -0
  9. package/dist/agents/data-performance-agent/SKILL.md +32 -0
  10. package/dist/agents/delivery-ticketing-agent/SKILL.md +36 -0
  11. package/dist/agents/devops-agent/SKILL.md +34 -0
  12. package/dist/agents/engineering-manager/SKILL.md +28 -0
  13. package/dist/agents/frontend-nextjs-agent/SKILL.md +34 -0
  14. package/dist/agents/frontend-web-agent/SKILL.md +31 -0
  15. package/dist/agents/incident-response-agent/SKILL.md +42 -0
  16. package/dist/agents/jest-agent/SKILL.md +77 -0
  17. package/dist/agents/mobile-agent/SKILL.md +32 -0
  18. package/dist/agents/playwright-agent/SKILL.md +54 -0
  19. package/dist/agents/product-design-manager/SKILL.md +27 -0
  20. package/dist/agents/qa-agent/SKILL.md +29 -0
  21. package/dist/agents/quality-manager/SKILL.md +28 -0
  22. package/dist/agents/vitest-agent/SKILL.md +77 -0
  23. package/dist/governance/pre-push-rules.md +75 -0
  24. package/dist/governance/prompt-hygiene.md +22 -0
  25. package/dist/governance/review-checklist.md +24 -0
  26. package/dist/governance/safety-and-data.md +27 -0
  27. package/dist/governance/usage-rules.md +21 -0
  28. package/dist/mandatory/address-pr-feedback/SKILL.md +196 -0
  29. package/dist/mandatory/agentic-goal/SKILL.md +42 -0
  30. package/dist/mandatory/agentic-status/SKILL.md +74 -0
  31. package/dist/mandatory/apply-design/SKILL.md +228 -0
  32. package/dist/mandatory/check-ac/SKILL.md +124 -0
  33. package/dist/mandatory/create-pr-and-commit/SKILL.md +304 -0
  34. package/dist/mandatory/design/SKILL.md +312 -0
  35. package/dist/mandatory/feature-workflow/SKILL.md +87 -0
  36. package/dist/mandatory/hotfix/SKILL.md +195 -0
  37. package/dist/mandatory/pre-push/SKILL.md +272 -0
  38. package/dist/mandatory/project-context/SKILL.md +180 -0
  39. package/dist/mandatory/release-docs/SKILL.md +191 -0
  40. package/dist/mandatory/review-pr/SKILL.md +323 -0
  41. package/dist/mandatory/security-review/SKILL.md +157 -0
  42. package/dist/skills/api-contract/SKILL.md +77 -0
  43. package/dist/skills/backend-django/SKILL.md +27 -0
  44. package/dist/skills/backend-nestjs/SKILL.md +65 -0
  45. package/dist/skills/bug-ticket-creation/SKILL.md +43 -0
  46. package/dist/skills/cloudwatch-troubleshooting/SKILL.md +73 -0
  47. package/dist/skills/database-migration/SKILL.md +97 -0
  48. package/dist/skills/debugging/SKILL.md +32 -0
  49. package/dist/skills/devops-infra/SKILL.md +26 -0
  50. package/dist/skills/documentation/SKILL.md +24 -0
  51. package/dist/skills/frontend-nextjs/SKILL.md +27 -0
  52. package/dist/skills/incident-postmortem/SKILL.md +38 -0
  53. package/dist/skills/kubernetes-troubleshooting/SKILL.md +71 -0
  54. package/dist/skills/mobile-react-native/SKILL.md +37 -0
  55. package/dist/skills/postgres-analytics/SKILL.md +29 -0
  56. package/dist/skills/product-jira-ticketing/SKILL.md +28 -0
  57. package/dist/skills/qa-bug-analysis/SKILL.md +27 -0
  58. package/dist/skills/refactoring/SKILL.md +32 -0
  59. package/dist/skills/sprint-planning/SKILL.md +32 -0
  60. package/dist/skills/testing-quality/SKILL.md +62 -0
  61. package/dist/v2/protocols/agent-orchestration.md +49 -0
  62. package/dist/v2/protocols/delivery.md +45 -0
  63. package/dist/v2/protocols/design-authority.md +29 -0
  64. package/dist/v2/protocols/goal-graph.md +44 -0
  65. package/dist/v2/protocols/pattern-first-development.md +29 -0
  66. package/dist/v2/protocols/qa-evidence.md +29 -0
  67. package/dist/v2/protocols/security.md +29 -0
  68. package/dist/v2/schemas/event.schema.json +86 -0
  69. package/dist/v2/schemas/evidence.schema.json +94 -0
  70. package/dist/v2/schemas/feature-decomposition.schema.json +48 -0
  71. package/dist/v2/schemas/feature-plan.schema.json +95 -0
  72. package/dist/v2/schemas/goal-graph.schema.json +61 -0
  73. package/dist/v2/schemas/integration.schema.json +119 -0
  74. package/dist/v2/schemas/orchestration.schema.json +69 -0
  75. package/dist/v2/schemas/project.schema.json +209 -0
  76. package/dist/v2/schemas/providers.schema.json +142 -0
  77. package/dist/v2/schemas/quality.schema.json +50 -0
  78. package/dist/v2/schemas/work-action.schema.json +44 -0
  79. package/dist/v2/schemas/work-request.schema.json +253 -0
  80. package/dist/v2/templates/evidence/qa-bundle.json +194 -0
  81. package/dist/v2/templates/github-actions/rivet-deploy.yml +98 -0
  82. package/dist/v2/templates/harness/SKILL.md +90 -0
  83. package/dist/v2/templates/project/.rivet/orchestration.yaml +42 -0
  84. package/dist/v2/templates/project/.rivet/project.yaml +14 -0
  85. package/dist/v2/templates/project/.rivet/providers.yaml +8 -0
  86. package/dist/v2/templates/project/.rivet/quality.yaml +16 -0
  87. package/package.json +58 -0
  88. package/protocols/agent-orchestration.md +49 -0
  89. package/protocols/delivery.md +45 -0
  90. package/protocols/design-authority.md +29 -0
  91. package/protocols/goal-graph.md +44 -0
  92. package/protocols/pattern-first-development.md +29 -0
  93. package/protocols/qa-evidence.md +29 -0
  94. package/protocols/security.md +29 -0
  95. package/schemas/event.schema.json +86 -0
  96. package/schemas/evidence.schema.json +94 -0
  97. package/schemas/feature-decomposition.schema.json +48 -0
  98. package/schemas/feature-plan.schema.json +95 -0
  99. package/schemas/goal-graph.schema.json +61 -0
  100. package/schemas/integration.schema.json +119 -0
  101. package/schemas/orchestration.schema.json +69 -0
  102. package/schemas/project.schema.json +209 -0
  103. package/schemas/providers.schema.json +142 -0
  104. package/schemas/quality.schema.json +50 -0
  105. package/schemas/work-action.schema.json +44 -0
  106. package/schemas/work-request.schema.json +253 -0
  107. package/src/adapters/confluence.js +138 -0
  108. package/src/adapters/contract.js +657 -0
  109. package/src/adapters/factory.js +122 -0
  110. package/src/adapters/figma.js +178 -0
  111. package/src/adapters/fixtures.js +183 -0
  112. package/src/adapters/github.js +314 -0
  113. package/src/adapters/http.js +704 -0
  114. package/src/adapters/jira.js +195 -0
  115. package/src/adapters/linear.js +171 -0
  116. package/src/adapters/node-transport.js +59 -0
  117. package/src/cli/integration-setup-prompt.js +32 -0
  118. package/src/cli/interrupt.js +25 -0
  119. package/src/cli/main.js +596 -0
  120. package/src/cli/output.js +174 -0
  121. package/src/cli/parse-args.js +246 -0
  122. package/src/cli/project-discovery.js +70 -0
  123. package/src/cli/task-confirmation.js +38 -0
  124. package/src/cli/task-presentation.js +22 -0
  125. package/src/clients/claude.js +214 -0
  126. package/src/clients/codex.js +180 -0
  127. package/src/clients/compatibility.js +23 -0
  128. package/src/clients/contract.js +224 -0
  129. package/src/clients/fake.js +170 -0
  130. package/src/clients/process-runner.js +657 -0
  131. package/src/clients/result-contract.js +78 -0
  132. package/src/commands/delivery-publish.js +80 -0
  133. package/src/commands/delivery-remote.js +230 -0
  134. package/src/commands/delivery-review-update.js +39 -0
  135. package/src/commands/delivery-tracker.js +94 -0
  136. package/src/commands/delivery-transition.js +79 -0
  137. package/src/commands/delivery.js +223 -0
  138. package/src/commands/dependency-approval.js +21 -0
  139. package/src/commands/doctor.js +191 -0
  140. package/src/commands/evidence.js +44 -0
  141. package/src/commands/feature.js +212 -0
  142. package/src/commands/goals.js +231 -0
  143. package/src/commands/human-run.js +208 -0
  144. package/src/commands/human-task.js +278 -0
  145. package/src/commands/init.js +848 -0
  146. package/src/commands/install.js +814 -0
  147. package/src/commands/integration-setup.js +65 -0
  148. package/src/commands/integrations.js +54 -0
  149. package/src/commands/models.js +158 -0
  150. package/src/commands/orchestrate.js +61 -0
  151. package/src/commands/preflight.js +125 -0
  152. package/src/commands/protocols.js +228 -0
  153. package/src/commands/repositories.js +77 -0
  154. package/src/commands/setup-remote.js +41 -0
  155. package/src/commands/setup.js +214 -0
  156. package/src/commands/status.js +224 -0
  157. package/src/commands/support.js +44 -0
  158. package/src/commands/task-completion.js +104 -0
  159. package/src/commands/uninstall.js +234 -0
  160. package/src/commands/verify.js +171 -0
  161. package/src/commands/work.js +220 -0
  162. package/src/commands/worktrees.js +52 -0
  163. package/src/config/command-readiness.js +218 -0
  164. package/src/config/commands.js +206 -0
  165. package/src/config/defaults.js +55 -0
  166. package/src/config/load.js +230 -0
  167. package/src/config/validate.js +560 -0
  168. package/src/delivery/bitbucket-review.js +123 -0
  169. package/src/delivery/branch-publication.js +23 -0
  170. package/src/delivery/contract.js +379 -0
  171. package/src/delivery/github-deployment.js +132 -0
  172. package/src/delivery/github-review.js +5 -0
  173. package/src/delivery/github.js +382 -0
  174. package/src/delivery/gitlab-review.js +5 -0
  175. package/src/delivery/gitlab.js +395 -0
  176. package/src/delivery/prepare.js +90 -0
  177. package/src/delivery/publication-process.js +33 -0
  178. package/src/delivery/publication-transport.js +112 -0
  179. package/src/delivery/review-request.js +184 -0
  180. package/src/delivery/review-update.js +74 -0
  181. package/src/delivery/service.js +400 -0
  182. package/src/delivery/store.js +76 -0
  183. package/src/delivery/tracker-target.js +51 -0
  184. package/src/delivery/tracker-transition.js +158 -0
  185. package/src/delivery/tracker.js +164 -0
  186. package/src/discovery/git.js +85 -0
  187. package/src/discovery/portable.js +108 -0
  188. package/src/discovery/project.js +345 -0
  189. package/src/discovery/tools.js +145 -0
  190. package/src/evaluations/approval.js +20 -0
  191. package/src/evaluations/cost-policy.js +20 -0
  192. package/src/evaluations/harness-attempt.js +113 -0
  193. package/src/evaluations/live-runner.js +55 -0
  194. package/src/evaluations/profile.js +28 -0
  195. package/src/evaluations/report.js +11 -0
  196. package/src/evaluations/text-attempt.js +18 -0
  197. package/src/evidence/checksum.js +87 -0
  198. package/src/evidence/collect.js +727 -0
  199. package/src/evidence/validate.js +354 -0
  200. package/src/feature/accepted-integration.js +46 -0
  201. package/src/feature/actions.js +37 -0
  202. package/src/feature/client-profile.js +56 -0
  203. package/src/feature/decomposition-contract.js +58 -0
  204. package/src/feature/host-execution.js +633 -0
  205. package/src/feature/host-lock-recovery.js +96 -0
  206. package/src/feature/host-run-lock.js +32 -0
  207. package/src/feature/local-approval.js +183 -0
  208. package/src/feature/plan-contract.js +215 -0
  209. package/src/feature/planner.js +253 -0
  210. package/src/feature/run-store.js +241 -0
  211. package/src/feature/runtime-bridge.js +840 -0
  212. package/src/feature/verification-report.js +168 -0
  213. package/src/feature/workflow.js +375 -0
  214. package/src/git/client.js +509 -0
  215. package/src/git/integration-worktree.js +153 -0
  216. package/src/git/reconcile.js +222 -0
  217. package/src/git/reservations.js +450 -0
  218. package/src/git/worktrees.js +478 -0
  219. package/src/graph/completion.js +189 -0
  220. package/src/graph/fixtures.js +116 -0
  221. package/src/graph/reducer.js +124 -0
  222. package/src/graph/scheduler.js +252 -0
  223. package/src/graph/validate.js +106 -0
  224. package/src/install/managed.js +579 -0
  225. package/src/install/project-reference.js +33 -0
  226. package/src/install/project-runtime.js +142 -0
  227. package/src/install/runtime-integrity.cjs +141 -0
  228. package/src/integrations/capabilities.js +69 -0
  229. package/src/integrations/host-observation.js +26 -0
  230. package/src/integrations/registry.js +56 -0
  231. package/src/models/delegate.js +102 -0
  232. package/src/models/profiles.js +53 -0
  233. package/src/models/protocols.js +208 -0
  234. package/src/models/registry.js +83 -0
  235. package/src/models/transport.js +59 -0
  236. package/src/policy/approvals.js +251 -0
  237. package/src/policy/authority.js +282 -0
  238. package/src/policy/budget.js +149 -0
  239. package/src/policy/command-bootstrap.js +219 -0
  240. package/src/policy/commands.js +803 -0
  241. package/src/prompts/launch-contract.js +43 -0
  242. package/src/prompts/planning-contract.js +92 -0
  243. package/src/protocols/presentation.js +53 -0
  244. package/src/protocols/project.js +289 -0
  245. package/src/quality/runner.js +497 -0
  246. package/src/quality/traceability.js +215 -0
  247. package/src/repositories/identity.js +59 -0
  248. package/src/repositories/index.js +2 -0
  249. package/src/repositories/provider.js +210 -0
  250. package/src/runtime/application.js +353 -0
  251. package/src/runtime/dependency-directory.js +33 -0
  252. package/src/runtime/harness-discovery.js +121 -0
  253. package/src/runtime/heartbeat.js +47 -0
  254. package/src/runtime/instance-store.js +112 -0
  255. package/src/runtime/orchestrator.js +1133 -0
  256. package/src/runtime/portable-dependencies.js +94 -0
  257. package/src/runtime/recovery.js +190 -0
  258. package/src/runtime/retry.js +54 -0
  259. package/src/runtime/supervisor.js +166 -0
  260. package/src/runtime/worktree-bootstrap.js +169 -0
  261. package/src/state/event-store.js +333 -0
  262. package/src/state/lock.js +266 -0
  263. package/src/state/paths.js +274 -0
  264. package/src/state/redact.js +106 -0
  265. package/src/state/snapshot-store.js +310 -0
  266. package/src/status/public/app.js +96 -0
  267. package/src/status/public/index.html +37 -0
  268. package/src/status/public/styles.css +50 -0
  269. package/src/status/server.js +323 -0
  270. package/src/status/view-model.js +356 -0
  271. package/src/support/bundle.js +195 -0
  272. package/src/support/failure-report.js +96 -0
  273. package/src/work-request/contract.js +250 -0
  274. package/src/work-request/host.js +78 -0
  275. package/src/work-request/local.js +163 -0
  276. package/src/work-request/tracker.js +105 -0
  277. package/templates/evidence/qa-bundle.json +194 -0
  278. package/templates/github-actions/rivet-deploy.yml +98 -0
  279. package/templates/harness/SKILL.md +90 -0
  280. package/templates/project/.rivet/orchestration.yaml +42 -0
  281. package/templates/project/.rivet/project.yaml +14 -0
  282. package/templates/project/.rivet/providers.yaml +8 -0
  283. package/templates/project/.rivet/quality.yaml +16 -0
@@ -0,0 +1,206 @@
1
+ const SAFE_COMMAND_RUNNERS = new Set([
2
+ 'npm', 'npm.cmd', 'pnpm', 'pnpm.cmd', 'yarn', 'yarn.cmd', 'bun',
3
+ ]);
4
+ const LOGICAL_COMMANDS = Object.freeze(['build', 'test', 'lint', 'typecheck']);
5
+ const LEGACY_COMMANDS = Object.freeze([...LOGICAL_COMMANDS, 'dev']);
6
+ const WINDOWS_RESERVED = /^(?:con|prn|aux|nul|conin\$|conout\$|com[1-9]|lpt[1-9])(?:\..*)?$/i;
7
+ const SCRIPT = /^(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
8
+ const MAX_STEPS = 32;
9
+ const MAX_QUALITY_STEPS = 64;
10
+
11
+ export class CommandConfigurationError extends Error {
12
+ constructor(path, reason) {
13
+ super('Project command configuration is invalid.');
14
+ this.name = 'CommandConfigurationError';
15
+ this.path = path;
16
+ this.reason = reason;
17
+ }
18
+ }
19
+
20
+ function fail(path, reason) {
21
+ throw new CommandConfigurationError(path, reason);
22
+ }
23
+
24
+ export function isExecutionCompatibleCwd(value) {
25
+ if (value === '.') return true;
26
+ if (typeof value !== 'string' || value.length < 1 || value.length > 300
27
+ || value.normalize('NFKC') !== value || /[\\:\u0000-\u001f\u007f]/.test(value)
28
+ || value.startsWith('-') || value.startsWith('/') || value.endsWith('/') || value.includes('//')) return false;
29
+ const parts = value.split('/');
30
+ if (parts.some(part => !part || part === '.' || part === '..' || part.endsWith('.') || part.endsWith(' ')
31
+ || WINDOWS_RESERVED.test(part) || part.toLowerCase() === '.git')) return false;
32
+ return true;
33
+ }
34
+
35
+ function validCwd(value, path) {
36
+ if (!isExecutionCompatibleCwd(value)) fail(path, 'unsafe-path');
37
+ return value;
38
+ }
39
+
40
+ function validArgv(value, logicalId, path, allowTypeCheckAlias) {
41
+ if (!Array.isArray(value) || value.length !== 3
42
+ || Reflect.ownKeys(value).length !== 4) fail(path, 'command-grammar');
43
+ const argv = value.map((item, index) => {
44
+ if (typeof item !== 'string') fail(`${path}/${index}`, 'command-grammar');
45
+ return item;
46
+ });
47
+ const [runner, verb, script] = argv;
48
+ if (!SAFE_COMMAND_RUNNERS.has(runner.toLowerCase())) fail(`${path}/0`, 'unsafe-executable');
49
+ if (verb !== 'run' || !SCRIPT.test(script)) fail(path, 'command-grammar');
50
+ if (script !== logicalId && !(allowTypeCheckAlias && logicalId === 'typecheck' && script === 'type-check')) {
51
+ fail(`${path}/2`, 'command-script-binding');
52
+ }
53
+ return Object.freeze(argv);
54
+ }
55
+
56
+ // Schema 3 grants execution only to these exact reviewed argv values.
57
+ const DIRECT_EXECUTABLE = /^[A-Za-z][A-Za-z0-9._+-]{0,127}$/;
58
+ const SHELL_EXECUTABLE = /^(?:sh|bash|dash|mksh|yash|zsh|fish|nu|elvish|cmd|powershell|pwsh|env|sudo)(?:\.exe|\.cmd)?$/i;
59
+ export function isProjectExecutable(value) {
60
+ return typeof value === 'string' && value.startsWith('./') && value.length > 2
61
+ && isExecutionCompatibleCwd(value.slice(2))
62
+ && !SHELL_EXECUTABLE.test(value.split('/').at(-1));
63
+ }
64
+
65
+ export function commandToolKey(runner, cwd = '.') {
66
+ return runner.startsWith('./') ? JSON.stringify([cwd, runner]) : runner;
67
+ }
68
+
69
+ function directArgv(value, path) {
70
+ if (!Array.isArray(value) || value.length < 1 || value.length > 257
71
+ || Reflect.ownKeys(value).length !== value.length + 1) fail(path, 'command-grammar');
72
+ const argv = [];
73
+ for (let index = 0; index < value.length; index += 1) {
74
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
75
+ if (!descriptor?.enumerable || !Object.hasOwn(descriptor, 'value')
76
+ || typeof descriptor.value !== 'string' || descriptor.value.length > 4096
77
+ || /[\u0000-\u001f\u007f-\u009f]/.test(descriptor.value)) fail(path, 'command-grammar');
78
+ argv.push(descriptor.value);
79
+ }
80
+ if ((!DIRECT_EXECUTABLE.test(argv[0]) && !isProjectExecutable(argv[0])) || SHELL_EXECUTABLE.test(argv[0])) fail(path, 'unsafe-executable');
81
+ return Object.freeze(argv);
82
+ }
83
+
84
+ function compiledGroup(logicalId, rawSteps, path, allowTypeCheckAlias, direct = false) {
85
+ if (!Array.isArray(rawSteps) || rawSteps.length < 1 || rawSteps.length > MAX_STEPS
86
+ || Reflect.ownKeys(rawSteps).length !== rawSteps.length + 1) fail(path, 'command-step-count');
87
+ const seen = new Set();
88
+ const multiple = rawSteps.length > 1;
89
+ const steps = rawSteps.map((raw, index) => {
90
+ const stepPath = `${path}/${index}`;
91
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)
92
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(raw))) fail(stepPath, 'command-step');
93
+ const keys = Reflect.ownKeys(raw);
94
+ if (keys.length !== 2 || !keys.includes('cwd') || !keys.includes('argv')
95
+ || keys.some(key => typeof key !== 'string')) fail(stepPath, 'command-step');
96
+ for (const key of keys) {
97
+ const descriptor = Object.getOwnPropertyDescriptor(raw, key);
98
+ if (!descriptor?.enumerable || !Object.hasOwn(descriptor, 'value')) fail(stepPath, 'command-step');
99
+ }
100
+ const cwd = validCwd(raw.cwd, `${stepPath}/cwd`);
101
+ const argv = direct ? directArgv(raw.argv, `${stepPath}/argv`)
102
+ : validArgv(raw.argv, logicalId, `${stepPath}/argv`, allowTypeCheckAlias);
103
+ const identity = JSON.stringify([cwd, ...argv]);
104
+ if (seen.has(identity)) fail(stepPath, 'duplicate-step');
105
+ seen.add(identity);
106
+ return Object.freeze({ id: multiple ? `${logicalId}-${index + 1}` : logicalId, cwd, argv, ...(direct ? { execution: 'argv' } : {}) });
107
+ });
108
+ return Object.freeze({ logicalId, steps: Object.freeze(steps) });
109
+ }
110
+
111
+ export function compileProjectDependencies(project) {
112
+ if (!project || typeof project !== 'object' || Array.isArray(project)) fail('/project', 'project');
113
+ const descriptor = Object.getOwnPropertyDescriptor(project, 'dependencies');
114
+ if (!descriptor) return undefined;
115
+ if (project.schemaVersion !== 3 || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) {
116
+ fail('/project/dependencies', 'dependencies');
117
+ }
118
+ const value = descriptor.value;
119
+ if (!value || typeof value !== 'object' || Array.isArray(value)
120
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail('/project/dependencies', 'dependencies');
121
+ const keys = Reflect.ownKeys(value);
122
+ if (keys.length !== 3 || keys.some(key => !['inputs', 'provides', 'steps'].includes(key))) fail('/project/dependencies', 'dependencies');
123
+ for (const key of keys) {
124
+ const field = Object.getOwnPropertyDescriptor(value, key);
125
+ if (!field?.enumerable || !Object.hasOwn(field, 'value')) fail(`/project/dependencies/${key}`, 'dependencies');
126
+ }
127
+ const bounded = (array, key) => {
128
+ if (!Array.isArray(array) || array.length > 16 || Reflect.ownKeys(array).length !== array.length + 1) fail(`/project/dependencies/${key}`, 'dependency-count');
129
+ for (let index = 0; index < array.length; index += 1) {
130
+ const item = Object.getOwnPropertyDescriptor(array, String(index));
131
+ if (!item?.enumerable || !Object.hasOwn(item, 'value')) fail(`/project/dependencies/${key}`, 'dependencies');
132
+ }
133
+ return array;
134
+ };
135
+ const inputs = bounded(value.inputs, 'inputs').map(path => {
136
+ if (path === '.' || !isExecutionCompatibleCwd(path)) fail('/project/dependencies/inputs', 'unsafe-path');
137
+ return path;
138
+ });
139
+ const provides = bounded(value.provides, 'provides').map(path => {
140
+ if (!isProjectExecutable(path)) fail('/project/dependencies/provides', 'unsafe-path');
141
+ return path;
142
+ });
143
+ if (new Set(inputs).size !== inputs.length || new Set(provides).size !== provides.length) fail('/project/dependencies', 'duplicate-step');
144
+ const steps = compiledGroup('install', bounded(value.steps, 'steps'), '/project/dependencies/steps', false, true).steps;
145
+ return Object.freeze({ inputs: Object.freeze(inputs), provides: Object.freeze(provides), steps });
146
+ }
147
+
148
+ export function compileProjectCommands(project) {
149
+ if (!project || typeof project !== 'object' || Array.isArray(project)
150
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(project))) fail('/project', 'project');
151
+ const version = project.schemaVersion;
152
+ if (version !== 1 && version !== 2 && version !== 3) fail('/project/schemaVersion', 'schema-version');
153
+ const commands = project.commands;
154
+ if (!commands || typeof commands !== 'object' || Array.isArray(commands)
155
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(commands))) fail('/project/commands', 'commands');
156
+ const allowed = version === 1 ? LEGACY_COMMANDS : version === 3 ? [...LOGICAL_COMMANDS, 'check'] : LOGICAL_COMMANDS;
157
+ const expectedManager = typeof project.stack?.packageManager === 'string'
158
+ ? project.stack.packageManager : null;
159
+ const keys = Reflect.ownKeys(commands);
160
+ if (keys.some(key => typeof key !== 'string' || !allowed.includes(key))) fail('/project/commands', 'command-key');
161
+ const output = Object.create(null);
162
+ for (const logicalId of keys) {
163
+ const descriptor = Object.getOwnPropertyDescriptor(commands, logicalId);
164
+ if (!descriptor?.enumerable || !Object.hasOwn(descriptor, 'value')) fail(`/project/commands/${logicalId}`, 'command');
165
+ if (version === 1) {
166
+ output[logicalId] = compiledGroup(logicalId, [{ cwd: '.', argv: descriptor.value }], `/project/commands/${logicalId}`, false);
167
+ continue;
168
+ }
169
+ const group = descriptor.value;
170
+ if (!group || typeof group !== 'object' || Array.isArray(group)
171
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(group))) fail(`/project/commands/${logicalId}`, 'command-group');
172
+ const groupKeys = Reflect.ownKeys(group);
173
+ const steps = Object.getOwnPropertyDescriptor(group, 'steps');
174
+ if (groupKeys.length !== 1 || groupKeys[0] !== 'steps' || !steps?.enumerable
175
+ || !Object.hasOwn(steps, 'value')) fail(`/project/commands/${logicalId}`, 'command-group');
176
+ output[logicalId] = compiledGroup(logicalId, steps.value, `/project/commands/${logicalId}/steps`, true, version === 3);
177
+ if (version === 2 && expectedManager && output[logicalId].steps.some(step => (
178
+ step.argv[0].toLowerCase().replace(/\.cmd$/, '') !== expectedManager
179
+ ))) fail(`/project/commands/${logicalId}/steps`, 'package-manager-mismatch');
180
+ }
181
+ return Object.freeze(output);
182
+ }
183
+
184
+ export function compileQualitySteps(config) {
185
+ const commands = compileProjectCommands(config.project);
186
+ const expanded = config.quality.commandGates.flatMap(gate => commands[gate.command].steps.map((step, index, steps) => {
187
+ const id = steps.length === 1 ? gate.id : `${gate.id}-${index + 1}`;
188
+ if (id.length > 64) fail(`/quality/commandGates/${gate.id}/id`, 'expanded-command-id');
189
+ return Object.freeze({
190
+ ...step,
191
+ id,
192
+ logicalId: gate.command,
193
+ required: gate.required,
194
+ });
195
+ }));
196
+ if (expanded.length > MAX_QUALITY_STEPS) fail('/quality/commandGates', 'expanded-command-count');
197
+ const ids = new Set();
198
+ for (const step of expanded) {
199
+ const id = step.id.toLowerCase();
200
+ if (ids.has(id)) fail(`/quality/commandGates/${step.id}/id`, 'expanded-command-id-duplicate');
201
+ ids.add(id);
202
+ }
203
+ return Object.freeze(expanded);
204
+ }
205
+
206
+ export { LOGICAL_COMMANDS, MAX_QUALITY_STEPS, MAX_STEPS, SAFE_COMMAND_RUNNERS };
@@ -0,0 +1,55 @@
1
+ function deepFreeze(value) {
2
+ if (value && typeof value === 'object' && !Object.isFrozen(value)) {
3
+ for (const child of Object.values(value)) deepFreeze(child);
4
+ Object.freeze(value);
5
+ }
6
+ return value;
7
+ }
8
+
9
+ export const CONFIG_DIRECTORY = '.rivet';
10
+ export const PROTOCOL_DIRECTORY = 'protocols';
11
+ export const MAX_CONFIG_FILE_BYTES = 256 * 1024;
12
+
13
+ export const CONFIG_FILES = deepFreeze({
14
+ project: 'project.yaml',
15
+ providers: 'providers.yaml',
16
+ orchestration: 'orchestration.yaml',
17
+ quality: 'quality.yaml',
18
+ });
19
+
20
+ export const SCHEMA_FILES = deepFreeze({
21
+ project: 'project.schema.json',
22
+ providers: 'providers.schema.json',
23
+ orchestration: 'orchestration.schema.json',
24
+ quality: 'quality.schema.json',
25
+ goalGraph: 'goal-graph.schema.json',
26
+ event: 'event.schema.json',
27
+ evidence: 'evidence.schema.json',
28
+ });
29
+
30
+ export const EVIDENCE_TYPES = deepFreeze([
31
+ 'commit',
32
+ 'test',
33
+ 'journey',
34
+ 'screenshot',
35
+ 'report',
36
+ 'review',
37
+ 'human-approval',
38
+ 'external-update',
39
+ ]);
40
+
41
+ export const DEFAULT_CONFIG = deepFreeze({
42
+ orchestration: {
43
+ enabled: false,
44
+ },
45
+ externalWrites: {
46
+ enabled: false,
47
+ requireHumanApproval: true,
48
+ },
49
+ activation: {
50
+ automatic: false,
51
+ requireHumanApproval: true,
52
+ },
53
+ });
54
+
55
+ export { deepFreeze };
@@ -0,0 +1,230 @@
1
+ import {
2
+ closeSync,
3
+ constants,
4
+ fstatSync,
5
+ lstatSync,
6
+ openSync,
7
+ readSync,
8
+ readdirSync,
9
+ statSync,
10
+ } from 'node:fs';
11
+ import { join, resolve } from 'node:path';
12
+
13
+ import { parseAllDocuments, visit } from 'yaml';
14
+
15
+ import {
16
+ CONFIG_DIRECTORY,
17
+ CONFIG_FILES,
18
+ DEFAULT_CONFIG,
19
+ MAX_CONFIG_FILE_BYTES,
20
+ PROTOCOL_DIRECTORY,
21
+ deepFreeze,
22
+ } from './defaults.js';
23
+ import { ConfigurationError, validateProjectConfiguration } from './validate.js';
24
+
25
+ const DEFAULT_FILE_SYSTEM = Object.freeze({
26
+ closeSync,
27
+ fstatSync,
28
+ lstatSync,
29
+ openSync,
30
+ readSync,
31
+ readdirSync,
32
+ statSync,
33
+ });
34
+
35
+ function parseYaml(source, filename) {
36
+ let documents;
37
+ try {
38
+ documents = parseAllDocuments(source, {
39
+ maxAliasCount: 0,
40
+ prettyErrors: false,
41
+ strict: true,
42
+ uniqueKeys: true,
43
+ version: '1.2',
44
+ });
45
+ } catch {
46
+ throw new ConfigurationError(`/${filename}`, 'yaml-parse');
47
+ }
48
+ if (documents.length !== 1) throw new ConfigurationError(`/${filename}`, 'yaml-document-count');
49
+ const [document] = documents;
50
+ if (document.errors.length || document.warnings.length) throw new ConfigurationError(`/${filename}`, 'yaml-parse');
51
+
52
+ let unsafe = false;
53
+ visit(document, {
54
+ Alias() {
55
+ unsafe = true;
56
+ return visit.BREAK;
57
+ },
58
+ Pair(_key, pair) {
59
+ if (pair.key?.value === '<<') {
60
+ unsafe = true;
61
+ return visit.BREAK;
62
+ }
63
+ return undefined;
64
+ },
65
+ });
66
+ if (unsafe) throw new ConfigurationError(`/${filename}`, 'yaml-alias');
67
+
68
+ let value;
69
+ try {
70
+ value = document.toJS({ maxAliasCount: 0, mapAsMap: false });
71
+ } catch {
72
+ throw new ConfigurationError(`/${filename}`, 'yaml-conversion');
73
+ }
74
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new ConfigurationError(`/${filename}`, 'yaml-root');
75
+ return value;
76
+ }
77
+
78
+ function sameIdentity(first, second) {
79
+ return first.dev === second.dev && first.ino === second.ino;
80
+ }
81
+
82
+ function readRegularConfigFile(filename, fs) {
83
+ let metadata;
84
+ try {
85
+ metadata = fs.lstatSync(filename);
86
+ } catch {
87
+ throw new ConfigurationError(`/${filename}`, 'missing-file');
88
+ }
89
+ if (metadata.isSymbolicLink() || !metadata.isFile()) throw new ConfigurationError(`/${filename}`, 'not-regular-file');
90
+ if (metadata.size > MAX_CONFIG_FILE_BYTES) throw new ConfigurationError(`/${filename}`, 'file-too-large');
91
+
92
+ let descriptor;
93
+ try {
94
+ descriptor = fs.openSync(filename, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
95
+ const openedMetadata = fs.fstatSync(descriptor);
96
+ if (!sameIdentity(metadata, openedMetadata)) {
97
+ throw new ConfigurationError(`/${filename}`, 'file-identity-changed');
98
+ }
99
+ if (!openedMetadata.isFile() || openedMetadata.size > MAX_CONFIG_FILE_BYTES) {
100
+ throw new ConfigurationError(`/${filename}`, 'invalid-opened-file');
101
+ }
102
+ const data = Buffer.alloc(MAX_CONFIG_FILE_BYTES + 1);
103
+ let offset = 0;
104
+ while (offset < data.length) {
105
+ const bytesRead = fs.readSync(descriptor, data, offset, data.length - offset, null);
106
+ if (!Number.isInteger(bytesRead) || bytesRead < 0 || bytesRead > data.length - offset) {
107
+ throw new ConfigurationError(`/${filename}`, 'invalid-read');
108
+ }
109
+ if (bytesRead === 0) break;
110
+ offset += bytesRead;
111
+ }
112
+ if (offset > MAX_CONFIG_FILE_BYTES) throw new ConfigurationError(`/${filename}`, 'file-too-large');
113
+ return data.subarray(0, offset).toString('utf8');
114
+ } catch (error) {
115
+ if (error instanceof ConfigurationError) throw error;
116
+ throw new ConfigurationError(`/${filename}`, 'read-failed');
117
+ } finally {
118
+ if (descriptor !== undefined) {
119
+ try {
120
+ fs.closeSync(descriptor);
121
+ } catch {
122
+ throw new ConfigurationError(`/${filename}`, 'close-failed');
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ function readTrackedConfiguration(configRoot, fs) {
129
+ let configRootMetadata;
130
+ try {
131
+ configRootMetadata = fs.lstatSync(configRoot);
132
+ } catch {
133
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}`, 'missing-directory');
134
+ }
135
+ if (configRootMetadata.isSymbolicLink() || !configRootMetadata.isDirectory()) {
136
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}`, 'invalid-directory');
137
+ }
138
+
139
+ const originalCwd = process.cwd();
140
+ let changedDirectory = false;
141
+ try {
142
+ process.chdir(configRoot);
143
+ changedDirectory = true;
144
+ const anchoredMetadata = fs.statSync('.');
145
+ if (!anchoredMetadata.isDirectory() || !sameIdentity(configRootMetadata, anchoredMetadata)) {
146
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}`, 'directory-identity-changed');
147
+ }
148
+ const expectedFiles = Object.values(CONFIG_FILES).sort();
149
+ const actualFiles = fs.readdirSync('.').sort();
150
+ const unexpected = actualFiles.filter(entry => !expectedFiles.includes(entry) && entry !== PROTOCOL_DIRECTORY);
151
+ if (unexpected.length > 0) {
152
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}`, 'unexpected-entry');
153
+ }
154
+ const protocolDirectory = fs.lstatSync(PROTOCOL_DIRECTORY, { throwIfNoEntry: false });
155
+ if (protocolDirectory && (protocolDirectory.isSymbolicLink() || !protocolDirectory.isDirectory())) {
156
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}/${PROTOCOL_DIRECTORY}`, 'invalid-directory');
157
+ }
158
+ const parsed = {};
159
+ for (const [name, filename] of Object.entries(CONFIG_FILES)) {
160
+ parsed[name] = parseYaml(readRegularConfigFile(filename, fs), filename);
161
+ }
162
+ return parsed;
163
+ } catch (error) {
164
+ if (error instanceof ConfigurationError) throw error;
165
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}`, 'directory-read-failed');
166
+ } finally {
167
+ if (changedDirectory) {
168
+ try {
169
+ process.chdir(originalCwd);
170
+ } catch {
171
+ throw new ConfigurationError(`/${CONFIG_DIRECTORY}`, 'cwd-restore-failed');
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ function normalize(config) {
178
+ return {
179
+ project: {
180
+ ...config.project,
181
+ repository: {
182
+ sensitivePaths: [],
183
+ ...config.project.repository,
184
+ },
185
+ },
186
+ providers: config.providers,
187
+ orchestration: {
188
+ enabled: DEFAULT_CONFIG.orchestration.enabled,
189
+ ...config.orchestration,
190
+ },
191
+ quality: config.quality,
192
+ };
193
+ }
194
+
195
+ export async function loadProjectConfig(projectRoot, options = {}) {
196
+ if (typeof projectRoot !== 'string' || projectRoot.length === 0 || projectRoot.includes('\0')) {
197
+ throw new ConfigurationError('/', 'project-root');
198
+ }
199
+ const configRoot = join(resolve(projectRoot), CONFIG_DIRECTORY);
200
+ const fs = Object.freeze({ ...DEFAULT_FILE_SYSTEM, ...(options.fs ?? {}) });
201
+ const parsed = readTrackedConfiguration(configRoot, fs);
202
+ const normalized = normalize(parsed);
203
+ validateProjectConfiguration(normalized);
204
+ return deepFreeze(normalized);
205
+ }
206
+
207
+ export function providerCredentialStatus(config, environment = process.env) {
208
+ try {
209
+ const providers = config?.providers?.providers;
210
+ if (!Array.isArray(providers) || !environment || typeof environment !== 'object' || Array.isArray(environment)) {
211
+ throw new TypeError();
212
+ }
213
+ return deepFreeze(providers.flatMap(provider => Object.entries(provider.credentials ?? {})
214
+ .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
215
+ .map(([, name]) => {
216
+ if (typeof name !== 'string' || !/^[A-Z][A-Z0-9_]{1,127}$/.test(name)) throw new TypeError();
217
+ const value = environment[name];
218
+ return {
219
+ provider: provider.id,
220
+ name,
221
+ present: typeof value === 'string' && value.length > 0,
222
+ required: (!provider.projectIds?.length || provider.projectIds.includes(config.project?.id)) && provider.mode !== 'disabled' && provider.transport !== 'harness-mcp' && provider.transport !== 'local-cli',
223
+ };
224
+ })));
225
+ } catch {
226
+ throw new ConfigurationError('/providers/providers', 'credential-reference');
227
+ }
228
+ }
229
+
230
+ export const loadConfig = loadProjectConfig;