@bjoernrochel/sevro 0.1.0-rc.2 → 0.1.0-rc.21

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 (243) hide show
  1. package/CONTRIBUTING.md +170 -0
  2. package/LICENSE +63 -136
  3. package/README.md +83 -66
  4. package/docs/README.md +44 -0
  5. package/docs/acknowledgements.md +14 -0
  6. package/docs/architecture.md +146 -0
  7. package/docs/assets/README.md +27 -0
  8. package/docs/assets/sevro-logo.png +0 -0
  9. package/docs/assets/sevro-symbol.svg +1 -0
  10. package/docs/assets/sevro-wordmark.svg +1 -0
  11. package/docs/builtin-graders-v1.md +41 -13
  12. package/docs/codex-host.md +184 -52
  13. package/docs/contracts.md +19 -0
  14. package/docs/creating-cases.md +139 -0
  15. package/docs/development-cli.md +190 -30
  16. package/docs/documentation-quality.md +61 -0
  17. package/docs/evaluation-workflow.md +110 -0
  18. package/docs/evidence/code-mode-controls-71.md +64 -0
  19. package/docs/evidence/code-mode-programs-85.md +64 -0
  20. package/docs/evidence/codex-state-73/README.md +204 -0
  21. package/docs/evidence/codex-state-73/darrow-migration.patch +182 -0
  22. package/docs/evidence/codex-state-73/results.json +661 -0
  23. package/docs/evidence/local-http-74.md +104 -0
  24. package/docs/evidence/runtime-startup-30/README.md +185 -0
  25. package/docs/evidence/runtime-startup-30/after.json +319 -0
  26. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +2 -0
  27. package/docs/evidence/runtime-startup-30/before.json +403 -0
  28. package/docs/evidence/runtime-startup-30/consumer-smoke.json +63 -0
  29. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +3 -0
  30. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +3 -0
  31. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +20 -0
  32. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +2 -0
  33. package/docs/evidence/skill-reads-70/results.json +823 -0
  34. package/docs/evidence.md +21 -0
  35. package/docs/extension-protocol-v1.md +50 -16
  36. package/docs/fixture-cleanup-validation.md +3 -0
  37. package/docs/getting-started.md +102 -0
  38. package/docs/guide-evaluation.md +89 -0
  39. package/docs/guide-verification.json +239 -0
  40. package/docs/identity-v1.md +35 -1
  41. package/docs/installing.md +88 -0
  42. package/docs/licenses/BUSL-1.1-0.1.0-rc.2.txt +159 -0
  43. package/docs/licensing.md +42 -0
  44. package/docs/native-controls.md +76 -5
  45. package/docs/native-hosts.md +228 -0
  46. package/docs/reading-results.md +141 -0
  47. package/docs/recipes.md +489 -0
  48. package/docs/releases.md +81 -40
  49. package/docs/report-v1.md +29 -1
  50. package/docs/results-v1.md +180 -0
  51. package/docs/run-settings-v1.md +147 -0
  52. package/docs/running-evaluations.md +31 -0
  53. package/docs/runtime-v1.md +614 -0
  54. package/docs/specs/repository-guide.md +58 -0
  55. package/docs/troubleshooting.md +30 -0
  56. package/docs/typescript-quality.md +234 -0
  57. package/docs/usability-study.md +114 -0
  58. package/examples/basic/host.ts +2 -2
  59. package/examples/recipes/candidate.ts +26 -0
  60. package/examples/recipes/cases/coding-change.json +21 -0
  61. package/examples/recipes/cases/host-tools.json +21 -0
  62. package/examples/recipes/cases/mise.json +21 -0
  63. package/examples/recipes/cases/node.json +21 -0
  64. package/examples/recipes/cases/python-uv.json +21 -0
  65. package/examples/recipes/cases/semantic.json +17 -0
  66. package/examples/recipes/coding-change/check.ts +23 -0
  67. package/examples/recipes/extension-artifacts.ts +65 -0
  68. package/examples/recipes/extension-methods.ts +174 -0
  69. package/examples/recipes/extension.ts +37 -0
  70. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  71. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  72. package/examples/recipes/repository-skill/README.md +3 -0
  73. package/examples/recipes/run-inputs.ts +188 -0
  74. package/examples/recipes/run-options.ts +131 -0
  75. package/examples/recipes/run.ts +63 -0
  76. package/examples/recipes/runtime/empty.json +1 -0
  77. package/examples/recipes/runtime/goals.json +1 -0
  78. package/examples/recipes/runtime/mise.json +6 -0
  79. package/examples/recipes/runtime/path.json +1 -0
  80. package/examples/recipes/runtime/plugins.json +4 -0
  81. package/examples/recipes/runtime/python-uv.json +10 -0
  82. package/examples/recipes/semantic-judge.ts +29 -0
  83. package/examples/templates/coding/adapter.ts +17 -0
  84. package/examples/templates/coding/case.json +15 -0
  85. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  86. package/examples/templates/repository-skill/extension-data.ts +64 -0
  87. package/examples/templates/repository-skill/extension.ts +31 -0
  88. package/examples/templates/response/adapter.ts +12 -0
  89. package/examples/templates/response/case.json +13 -0
  90. package/package.json +39 -6
  91. package/schemas/report-v1.schema.json +9 -0
  92. package/schemas/run-evidence-v1.schema.json +2 -1
  93. package/schemas/runtime-v1.schema.json +193 -0
  94. package/src/advisory.ts +10 -5
  95. package/src/candidate-artifacts.ts +208 -0
  96. package/src/candidate-codex-state.ts +185 -0
  97. package/src/case-validation.ts +105 -0
  98. package/src/cli/cancellation.ts +20 -0
  99. package/src/cli/command.ts +81 -0
  100. package/src/cli/completion.ts +15 -0
  101. package/src/cli/demo.ts +59 -0
  102. package/src/cli/evaluation.ts +55 -0
  103. package/src/cli/extension.ts +79 -0
  104. package/src/cli/help.ts +222 -0
  105. package/src/cli/hosts.ts +59 -0
  106. package/src/cli/init-settings.ts +30 -0
  107. package/src/cli/init.ts +111 -0
  108. package/src/cli/invocation.ts +627 -0
  109. package/src/cli/loading.ts +151 -0
  110. package/src/cli/preparation.ts +163 -0
  111. package/src/cli/presentation.ts +91 -0
  112. package/src/cli/progress.ts +6 -0
  113. package/src/cli/run-manifest.ts +133 -0
  114. package/src/cli/run-options.ts +101 -0
  115. package/src/cli/run-overrides.ts +83 -0
  116. package/src/cli/settings.ts +160 -0
  117. package/src/cli/summary.ts +43 -0
  118. package/src/cli.ts +13 -843
  119. package/src/contract-types.ts +94 -0
  120. package/src/engine.ts +19 -2373
  121. package/src/evaluation/advisory.ts +150 -0
  122. package/src/evaluation/check-plan.ts +510 -0
  123. package/src/evaluation/collection.ts +88 -0
  124. package/src/evaluation/context.ts +10 -0
  125. package/src/evaluation/grading-host.ts +39 -0
  126. package/src/evaluation/grading.ts +258 -0
  127. package/src/evaluation/invocation-plan.ts +548 -0
  128. package/src/evaluation/plan.ts +736 -0
  129. package/src/evaluation/progress.ts +19 -0
  130. package/src/evaluation/resources.ts +241 -0
  131. package/src/evaluation/run.ts +206 -0
  132. package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +3 -1
  133. package/src/evaluation/semantic.ts +312 -0
  134. package/src/evaluation/trial.ts +866 -0
  135. package/src/evaluation/types.ts +207 -0
  136. package/src/evidence/host-results.ts +92 -0
  137. package/src/evidence/retained-artifacts.ts +18 -0
  138. package/src/evidence/trial-writer.ts +203 -0
  139. package/src/evidence/usage.ts +17 -0
  140. package/src/extension-client.ts +228 -148
  141. package/src/extension-session.ts +326 -195
  142. package/src/fixtures/advisory.ts +338 -0
  143. package/src/fixtures/bin.ts +57 -0
  144. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  145. package/src/fixtures/create.ts +187 -0
  146. package/src/fixtures/generated.ts +300 -0
  147. package/src/fixtures/git-hooks.ts +24 -0
  148. package/src/fixtures/preparation.ts +270 -0
  149. package/src/{repository-fixture.ts → fixtures/repository.ts} +207 -85
  150. package/src/fixtures/setup.ts +224 -0
  151. package/src/fixtures/tools.ts +88 -0
  152. package/src/fixtures/workspace-state.ts +212 -0
  153. package/src/generated/cli-result.cjs +2 -0
  154. package/src/generated/cli-result.d.cts +3 -0
  155. package/src/generated/extension.cjs +2 -0
  156. package/src/generated/extension.d.cts +3 -0
  157. package/src/generated/report.cjs +2 -0
  158. package/src/generated/report.d.cts +3 -0
  159. package/src/generated/run-evidence.cjs +2 -0
  160. package/src/generated/run-evidence.d.cts +3 -0
  161. package/src/generated/runtime.cjs +2 -0
  162. package/src/generated/runtime.d.cts +3 -0
  163. package/src/graders/artifact.ts +56 -23
  164. package/src/graders/git-head.ts +79 -75
  165. package/src/graders/output.ts +222 -140
  166. package/src/graders/semantic.ts +102 -43
  167. package/src/graders/shell.ts +379 -207
  168. package/src/host-diagnostic-redaction.ts +52 -0
  169. package/src/host-diagnostics.ts +319 -0
  170. package/src/hosts/claude-continuation.ts +63 -18
  171. package/src/hosts/claude-credential.ts +43 -62
  172. package/src/hosts/claude-events.ts +86 -51
  173. package/src/hosts/claude-keychain.ts +59 -0
  174. package/src/hosts/claude-native-goal.ts +57 -33
  175. package/src/hosts/claude-process.ts +111 -0
  176. package/src/hosts/claude-repository-invocation.ts +208 -117
  177. package/src/hosts/claude-settings.ts +240 -21
  178. package/src/hosts/claude-skill-activations.ts +166 -0
  179. package/src/hosts/claude-state.ts +403 -0
  180. package/src/hosts/claude-tool-calls.ts +171 -106
  181. package/src/hosts/claude.ts +561 -451
  182. package/src/hosts/codex-app-server.ts +326 -178
  183. package/src/hosts/codex-configuration.ts +44 -18
  184. package/src/hosts/codex-events.ts +97 -60
  185. package/src/hosts/codex-exec-controls.ts +516 -0
  186. package/src/hosts/codex-invocation.ts +70 -0
  187. package/src/hosts/codex-marketplace.ts +283 -0
  188. package/src/hosts/codex-native-calls.ts +546 -500
  189. package/src/hosts/codex-process.ts +202 -0
  190. package/src/hosts/codex-profile.ts +257 -28
  191. package/src/hosts/codex-session-files.ts +106 -0
  192. package/src/hosts/codex-state.ts +492 -0
  193. package/src/hosts/codex.ts +783 -832
  194. package/src/hosts/isolation-common.ts +94 -0
  195. package/src/hosts/isolation-roots.ts +54 -27
  196. package/src/hosts/isolation.ts +10 -0
  197. package/src/hosts/linux-sandbox.ts +159 -0
  198. package/src/hosts/mac-sandbox.ts +187 -61
  199. package/src/hosts/native-controls.ts +152 -62
  200. package/src/hosts/process-lifecycle.ts +37 -0
  201. package/src/hosts/protected-worktrees.ts +51 -19
  202. package/src/hosts/skill-hook-catalog.ts +131 -0
  203. package/src/hosts/skill-hook-handler.ts +374 -0
  204. package/src/hosts/skill-hooks.ts +580 -0
  205. package/src/hosts/workspace-fingerprint.ts +55 -35
  206. package/src/identity.ts +102 -48
  207. package/src/instrumentation.ts +109 -48
  208. package/src/native-transcript-state.ts +131 -0
  209. package/src/native-transcript-view.ts +90 -0
  210. package/src/native-transcripts.ts +286 -0
  211. package/src/process-lifecycle.ts +150 -0
  212. package/src/provenance.ts +161 -81
  213. package/src/prune.ts +215 -0
  214. package/src/report.ts +172 -140
  215. package/src/resolved-case.ts +33 -0
  216. package/src/results-lock.ts +29 -0
  217. package/src/results.ts +133 -86
  218. package/src/run-owner.ts +154 -63
  219. package/src/runtime/config.ts +456 -0
  220. package/src/runtime/discovery-apple.ts +90 -0
  221. package/src/runtime/discovery-providers.ts +63 -0
  222. package/src/runtime/discovery.ts +70 -0
  223. package/src/runtime/hook-processes.ts +97 -0
  224. package/src/runtime/hooks.ts +476 -0
  225. package/src/runtime/paths.ts +313 -0
  226. package/src/runtime/query.ts +85 -0
  227. package/src/runtime/seeds.ts +134 -0
  228. package/src/runtime/shared-cache.ts +143 -0
  229. package/src/runtime/state.ts +143 -0
  230. package/src/schema-types.ts +198 -0
  231. package/src/schema.ts +15 -19
  232. package/src/transcript-retention.ts +146 -0
  233. package/src/value-guards.ts +20 -0
  234. package/src/advisory-fixture.ts +0 -282
  235. package/src/fixture-bin.ts +0 -87
  236. package/src/fixture-setup.ts +0 -163
  237. package/src/generated-fixture.ts +0 -220
  238. package/src/git-hooks.ts +0 -63
  239. package/src/hosts/claude-nested-skills.ts +0 -218
  240. package/src/hosts/codex-command-output.ts +0 -399
  241. package/src/hosts/codex-executor-command.ts +0 -195
  242. package/src/hosts/codex-skill-reads.ts +0 -391
  243. package/src/preparation.ts +0 -211
@@ -0,0 +1,548 @@
1
+ import {
2
+ EvaluationConfigurationError,
3
+ type EvaluationOptions,
4
+ type ResolvedCase,
5
+ } from "./types";
6
+ import { fixtureParts, type InlineArtifact } from "../fixtures/preparation";
7
+ import { isStringArray } from "../value-guards";
8
+ import type { PreparationResult } from "../extension-session";
9
+
10
+ interface InvocationArtifacts {
11
+ extensionPreparation: PreparationResult | null;
12
+ inlineArtifacts: InlineArtifact[];
13
+ }
14
+
15
+ type ClaudePluginDirectories = NonNullable<
16
+ NonNullable<InvocationArtifacts["extensionPreparation"]>["claudePluginDirs"]
17
+ >;
18
+ type CodexMarketplace = NonNullable<
19
+ NonNullable<InvocationArtifacts["extensionPreparation"]>["codexMarketplace"]
20
+ >;
21
+
22
+ function uniquePluginRoots(value: unknown): value is string[] {
23
+ return (
24
+ isStringArray(value) &&
25
+ value.length > 0 &&
26
+ new Set(value).size === value.length
27
+ );
28
+ }
29
+
30
+ function validateClaudePluginRoots(roots: string[]): void {
31
+ try {
32
+ if (!uniquePluginRoots(roots)) throw new Error("invalid plugin roots");
33
+ for (const root of roots) fixtureParts(root);
34
+ if (
35
+ roots.some((root) =>
36
+ roots.some((other) => root !== other && root.startsWith(`${other}/`)),
37
+ )
38
+ )
39
+ throw new Error("overlapping plugin roots");
40
+ } catch (cause) {
41
+ throw new EvaluationConfigurationError(
42
+ "invalid Claude plugin directory declaration",
43
+ { cause },
44
+ );
45
+ }
46
+ }
47
+
48
+ function requireNegotiatedCapability(
49
+ options: EvaluationOptions,
50
+ capability: string,
51
+ message: string,
52
+ ): void {
53
+ if (
54
+ !options.extension?.session.identity.capabilities.includes(capability) ||
55
+ !options.host.hostCapabilities?.includes(capability)
56
+ )
57
+ throw new EvaluationConfigurationError(message);
58
+ }
59
+
60
+ function validatePackageManifest(
61
+ files: InlineArtifact[],
62
+ manifest: string,
63
+ message: string,
64
+ ): void {
65
+ if (
66
+ !files.length ||
67
+ !files.every((artifact) => artifact.gitExclude) ||
68
+ !files.some((artifact) => artifact.relativePath === manifest)
69
+ )
70
+ throw new EvaluationConfigurationError(message);
71
+ }
72
+
73
+ function validateClaudePluginPackage(
74
+ options: EvaluationOptions,
75
+ directories: ClaudePluginDirectories | undefined,
76
+ artifacts: InlineArtifact[],
77
+ ): void {
78
+ if (!directories) return;
79
+ const roots = directories.artifactRoots;
80
+ validateClaudePluginRoots(roots);
81
+ requireNegotiatedCapability(
82
+ options,
83
+ "sevro.claude.plugin-dirs",
84
+ "Claude plugin directory capability was not negotiated",
85
+ );
86
+ for (const root of roots) {
87
+ const files = artifacts.filter((artifact) =>
88
+ artifact.relativePath.startsWith(`${root}/`),
89
+ );
90
+ validatePackageManifest(
91
+ files,
92
+ `${root}/.claude-plugin/plugin.json`,
93
+ "Claude plugin directory requires Git-excluded package artifacts and a manifest",
94
+ );
95
+ }
96
+ }
97
+
98
+ function marketplaceNamesValid(name: string, plugins: string[]): boolean {
99
+ return (
100
+ /^[a-z][a-z0-9-]*$/.test(name) &&
101
+ plugins.length > 0 &&
102
+ !plugins.some((plugin) => !/^[a-z][a-z0-9-]*$/.test(plugin)) &&
103
+ new Set(plugins).size === plugins.length
104
+ );
105
+ }
106
+
107
+ function validateMarketplaceDeclaration(marketplace: CodexMarketplace): void {
108
+ try {
109
+ fixtureParts(marketplace.artifactRoot);
110
+ if (
111
+ !marketplaceNamesValid(
112
+ marketplace.marketplaceName,
113
+ marketplace.pluginNames,
114
+ )
115
+ )
116
+ throw new Error("invalid marketplace or plugin name");
117
+ } catch (cause) {
118
+ throw new EvaluationConfigurationError(
119
+ "invalid Codex marketplace declaration",
120
+ { cause },
121
+ );
122
+ }
123
+ }
124
+
125
+ function validateCodexMarketplacePackage(
126
+ options: EvaluationOptions,
127
+ marketplace: CodexMarketplace | undefined,
128
+ artifacts: InlineArtifact[],
129
+ ): void {
130
+ if (!marketplace) return;
131
+ validateMarketplaceDeclaration(marketplace);
132
+ requireNegotiatedCapability(
133
+ options,
134
+ "sevro.codex.plugin-marketplace",
135
+ "Codex marketplace capability was not negotiated",
136
+ );
137
+ const files = artifacts.filter((artifact) =>
138
+ artifact.relativePath.startsWith(`${marketplace.artifactRoot}/`),
139
+ );
140
+ validatePackageManifest(
141
+ files,
142
+ `${marketplace.artifactRoot}/.claude-plugin/marketplace.json`,
143
+ "Codex marketplace requires Git-excluded package artifacts and a manifest",
144
+ );
145
+ }
146
+
147
+ function preparePackageArtifacts<T extends InvocationArtifacts>(
148
+ options: EvaluationOptions,
149
+ state: T,
150
+ ) {
151
+ const codexMarketplace = state.extensionPreparation?.codexMarketplace;
152
+ const claudePluginDirs = state.extensionPreparation?.claudePluginDirs;
153
+ validateClaudePluginPackage(options, claudePluginDirs, state.inlineArtifacts);
154
+ validateCodexMarketplacePackage(
155
+ options,
156
+ codexMarketplace,
157
+ state.inlineArtifacts,
158
+ );
159
+ return { ...state, codexMarketplace, claudePluginDirs };
160
+ }
161
+
162
+ type PackagePlan = ReturnType<
163
+ typeof preparePackageArtifacts<InvocationArtifacts>
164
+ >;
165
+ const INVOCATION_PLACEHOLDER = "{{sevro.skill_invocation}}";
166
+ const LEGACY_CODEX_PLACEHOLDER = "{{sevro.codex.skill_invocation}}";
167
+
168
+ function skillInvocationDeclarations(
169
+ preparation: PackagePlan["extensionPreparation"],
170
+ ) {
171
+ return {
172
+ codexSkillInvocation: preparation?.codexSkillInvocation,
173
+ codexRepositorySkillInvocation: preparation?.codexRepositorySkillInvocation,
174
+ claudeSkillInvocation: preparation?.claudeSkillInvocation,
175
+ claudeRepositorySkillInvocation:
176
+ preparation?.claudeRepositorySkillInvocation,
177
+ };
178
+ }
179
+
180
+ type SkillDeclarations = ReturnType<typeof skillInvocationDeclarations>;
181
+ type PluginSkillInvocation = NonNullable<
182
+ SkillDeclarations["codexSkillInvocation"]
183
+ >;
184
+ type RepositorySkillInvocation = NonNullable<
185
+ SkillDeclarations["codexRepositorySkillInvocation"]
186
+ >;
187
+ type SelectedSkillInvocation =
188
+ | { kind: "codex-plugin"; value: PluginSkillInvocation }
189
+ | { kind: "codex-repository"; value: RepositorySkillInvocation }
190
+ | { kind: "claude-plugin"; value: PluginSkillInvocation }
191
+ | { kind: "claude-repository"; value: RepositorySkillInvocation }
192
+ | { kind: "none" };
193
+
194
+ function selectedSkillInvocation(
195
+ declarations: SkillDeclarations,
196
+ ): SelectedSkillInvocation {
197
+ if (declarations.codexSkillInvocation)
198
+ return { kind: "codex-plugin", value: declarations.codexSkillInvocation };
199
+ if (declarations.codexRepositorySkillInvocation)
200
+ return {
201
+ kind: "codex-repository",
202
+ value: declarations.codexRepositorySkillInvocation,
203
+ };
204
+ if (declarations.claudeRepositorySkillInvocation)
205
+ return {
206
+ kind: "claude-repository",
207
+ value: declarations.claudeRepositorySkillInvocation,
208
+ };
209
+ if (declarations.claudeSkillInvocation)
210
+ return { kind: "claude-plugin", value: declarations.claudeSkillInvocation };
211
+ return { kind: "none" };
212
+ }
213
+
214
+ function invocationTokenFor(declarations: SkillDeclarations): string | null {
215
+ if (declarations.codexRepositorySkillInvocation)
216
+ return `$${declarations.codexRepositorySkillInvocation.skillName}`;
217
+ if (declarations.claudeRepositorySkillInvocation)
218
+ return `/${declarations.claudeRepositorySkillInvocation.skillName}`;
219
+ if (declarations.codexSkillInvocation)
220
+ return `$${declarations.codexSkillInvocation.pluginName}:${declarations.codexSkillInvocation.skillName}`;
221
+ if (declarations.claudeSkillInvocation)
222
+ return `/${declarations.claudeSkillInvocation.pluginName}:${declarations.claudeSkillInvocation.skillName}`;
223
+ return null;
224
+ }
225
+
226
+ function invocationMetadata(declarations: SkillDeclarations) {
227
+ const repositoryInvocation =
228
+ declarations.codexRepositorySkillInvocation ??
229
+ declarations.claudeRepositorySkillInvocation;
230
+ const invocation = repositoryInvocation
231
+ ? { ...repositoryInvocation, scope: "repository" as const }
232
+ : (declarations.codexSkillInvocation ?? declarations.claudeSkillInvocation);
233
+ return { repositoryInvocation, invocation };
234
+ }
235
+
236
+ function promptPlaceholderCount(
237
+ caseData: ResolvedCase,
238
+ placeholder: string,
239
+ ): number {
240
+ return (
241
+ caseData.prompt.split(placeholder).length -
242
+ 1 +
243
+ (caseData.followUpPrompt?.split(placeholder).length ?? 1) -
244
+ 1
245
+ );
246
+ }
247
+
248
+ function validateOnlyOneInvocation(declarations: SkillDeclarations): void {
249
+ if (
250
+ [
251
+ declarations.codexSkillInvocation,
252
+ declarations.codexRepositorySkillInvocation,
253
+ declarations.claudeSkillInvocation,
254
+ declarations.claudeRepositorySkillInvocation,
255
+ ].filter(Boolean).length > 1
256
+ )
257
+ throw new EvaluationConfigurationError(
258
+ "only one explicit skill invocation may be declared",
259
+ );
260
+ }
261
+
262
+ function validateCodexInvocationMembership(
263
+ marketplace: CodexMarketplace | undefined,
264
+ pluginName: string,
265
+ ): asserts marketplace is CodexMarketplace {
266
+ if (
267
+ !marketplace ||
268
+ !marketplace.pluginNames.includes(pluginName) ||
269
+ !/^[a-z][a-z0-9-]*$/.test(pluginName)
270
+ )
271
+ throw new EvaluationConfigurationError(
272
+ "invalid Codex skill invocation declaration",
273
+ );
274
+ }
275
+
276
+ function validateCodexInvocationBody(
277
+ marketplace: CodexMarketplace,
278
+ skillName: string,
279
+ artifacts: InlineArtifact[],
280
+ placeholders: number,
281
+ ): void {
282
+ if (
283
+ !/^[A-Za-z0-9._-]+$/.test(skillName) ||
284
+ placeholders !== 1 ||
285
+ !artifacts.some(
286
+ (artifact) =>
287
+ artifact.relativePath ===
288
+ `${marketplace.artifactRoot}/plugin/skills/${skillName}/SKILL.md`,
289
+ )
290
+ )
291
+ throw new EvaluationConfigurationError(
292
+ "invalid Codex skill invocation declaration",
293
+ );
294
+ }
295
+
296
+ function validateCodexPluginInvocation(
297
+ options: EvaluationOptions,
298
+ context: PackagePlan,
299
+ invocation: PluginSkillInvocation,
300
+ placeholders: number,
301
+ ): void {
302
+ const marketplace = context.codexMarketplace;
303
+ validateCodexInvocationMembership(marketplace, invocation.pluginName);
304
+ validateCodexInvocationBody(
305
+ marketplace,
306
+ invocation.skillName,
307
+ context.inlineArtifacts,
308
+ placeholders,
309
+ );
310
+ requireNegotiatedCapability(
311
+ options,
312
+ "sevro.codex.explicit-invocation",
313
+ "Codex explicit invocation capability was not negotiated",
314
+ );
315
+ }
316
+
317
+ function validRepositoryInvocationIdentity(
318
+ skillName: string,
319
+ placeholders: number,
320
+ ): boolean {
321
+ return (
322
+ /^[A-Za-z0-9._-]+$/.test(skillName) &&
323
+ skillName !== "." &&
324
+ skillName !== ".." &&
325
+ placeholders === 1
326
+ );
327
+ }
328
+
329
+ function validateRepositoryInvocationBody(
330
+ artifacts: InlineArtifact[],
331
+ path: string,
332
+ message: string,
333
+ ): void {
334
+ if (
335
+ !artifacts.some(
336
+ (artifact) => artifact.gitExclude && artifact.relativePath === path,
337
+ )
338
+ )
339
+ throw new EvaluationConfigurationError(message);
340
+ }
341
+
342
+ function validateCodexRepositoryInvocation(
343
+ options: EvaluationOptions,
344
+ context: PackagePlan,
345
+ invocation: RepositorySkillInvocation,
346
+ placeholders: number,
347
+ ): void {
348
+ const message = "invalid Codex repository skill invocation declaration";
349
+ if (!validRepositoryInvocationIdentity(invocation.skillName, placeholders))
350
+ throw new EvaluationConfigurationError(message);
351
+ validateRepositoryInvocationBody(
352
+ context.inlineArtifacts,
353
+ `.agents/skills/${invocation.skillName}/SKILL.md`,
354
+ message,
355
+ );
356
+ requireNegotiatedCapability(
357
+ options,
358
+ "sevro.codex.repository-invocation",
359
+ "Codex repository invocation capability was not negotiated",
360
+ );
361
+ }
362
+
363
+ function validateClaudeRepositoryPrompt(prompt: string): void {
364
+ if (
365
+ !prompt.startsWith(INVOCATION_PLACEHOLDER) ||
366
+ prompt.includes(LEGACY_CODEX_PLACEHOLDER)
367
+ )
368
+ throw new EvaluationConfigurationError(
369
+ "invalid Claude repository skill invocation declaration",
370
+ );
371
+ }
372
+
373
+ function validateClaudeRepositoryInvocation(
374
+ options: EvaluationOptions,
375
+ context: PackagePlan,
376
+ invocation: RepositorySkillInvocation,
377
+ placeholders: number,
378
+ ): void {
379
+ const message = "invalid Claude repository skill invocation declaration";
380
+ if (!validRepositoryInvocationIdentity(invocation.skillName, placeholders))
381
+ throw new EvaluationConfigurationError(message);
382
+ validateClaudeRepositoryPrompt(options.case.prompt);
383
+ validateRepositoryInvocationBody(
384
+ context.inlineArtifacts,
385
+ `.claude/skills/${invocation.skillName}/SKILL.md`,
386
+ message,
387
+ );
388
+ requireNegotiatedCapability(
389
+ options,
390
+ "sevro.claude.repository-invocation",
391
+ "Claude repository invocation capability was not negotiated",
392
+ );
393
+ }
394
+
395
+ function validateClaudeInvocationIdentity(
396
+ directories: ClaudePluginDirectories | undefined,
397
+ invocation: PluginSkillInvocation,
398
+ placeholders: number,
399
+ ): asserts directories is ClaudePluginDirectories {
400
+ if (
401
+ !directories ||
402
+ !/^[a-z][a-z0-9-]*$/.test(invocation.pluginName) ||
403
+ !/^[A-Za-z0-9._-]+$/.test(invocation.skillName) ||
404
+ placeholders !== 1
405
+ )
406
+ throw new EvaluationConfigurationError(
407
+ "invalid Claude skill invocation declaration",
408
+ );
409
+ }
410
+
411
+ function validateClaudePluginPrompt(caseData: ResolvedCase): void {
412
+ if (
413
+ caseData.prompt.includes(LEGACY_CODEX_PLACEHOLDER) ||
414
+ caseData.followUpPrompt?.includes(LEGACY_CODEX_PLACEHOLDER)
415
+ )
416
+ throw new EvaluationConfigurationError(
417
+ "invalid Claude skill invocation declaration",
418
+ );
419
+ }
420
+
421
+ function validateClaudeInvocationBody(
422
+ directories: ClaudePluginDirectories,
423
+ skillName: string,
424
+ artifacts: InlineArtifact[],
425
+ ): void {
426
+ if (
427
+ !directories.artifactRoots.some((root) =>
428
+ artifacts.some(
429
+ (artifact) =>
430
+ artifact.relativePath === `${root}/skills/${skillName}/SKILL.md`,
431
+ ),
432
+ )
433
+ )
434
+ throw new EvaluationConfigurationError(
435
+ "invalid Claude skill invocation declaration",
436
+ );
437
+ }
438
+
439
+ function validateClaudePluginInvocation(
440
+ options: EvaluationOptions,
441
+ context: PackagePlan,
442
+ invocation: PluginSkillInvocation,
443
+ placeholders: number,
444
+ ): void {
445
+ const directories = context.claudePluginDirs;
446
+ validateClaudeInvocationIdentity(directories, invocation, placeholders);
447
+ validateClaudePluginPrompt(options.case);
448
+ validateClaudeInvocationBody(
449
+ directories,
450
+ invocation.skillName,
451
+ context.inlineArtifacts,
452
+ );
453
+ requireNegotiatedCapability(
454
+ options,
455
+ "sevro.claude.explicit-invocation",
456
+ "Claude explicit invocation capability was not negotiated",
457
+ );
458
+ }
459
+
460
+ function validateUndeclaredPlaceholder(placeholders: number): void {
461
+ if (placeholders)
462
+ throw new EvaluationConfigurationError(
463
+ "skill invocation placeholder requires a declaration",
464
+ );
465
+ }
466
+
467
+ function validateSelectedSkillInvocation(
468
+ options: EvaluationOptions,
469
+ context: PackagePlan,
470
+ selected: SelectedSkillInvocation,
471
+ placeholders: number,
472
+ ): void {
473
+ switch (selected.kind) {
474
+ case "codex-plugin":
475
+ validateCodexPluginInvocation(
476
+ options,
477
+ context,
478
+ selected.value,
479
+ placeholders,
480
+ );
481
+ break;
482
+ case "codex-repository":
483
+ validateCodexRepositoryInvocation(
484
+ options,
485
+ context,
486
+ selected.value,
487
+ placeholders,
488
+ );
489
+ break;
490
+ case "claude-repository":
491
+ validateClaudeRepositoryInvocation(
492
+ options,
493
+ context,
494
+ selected.value,
495
+ placeholders,
496
+ );
497
+ break;
498
+ case "claude-plugin":
499
+ validateClaudePluginInvocation(
500
+ options,
501
+ context,
502
+ selected.value,
503
+ placeholders,
504
+ );
505
+ break;
506
+ case "none":
507
+ validateUndeclaredPlaceholder(placeholders);
508
+ break;
509
+ }
510
+ }
511
+
512
+ function prepareSkillInvocation<T extends PackagePlan>(
513
+ options: EvaluationOptions,
514
+ state: T,
515
+ ) {
516
+ const declarations = skillInvocationDeclarations(state.extensionPreparation);
517
+ const metadata = invocationMetadata(declarations);
518
+ const invocationToken = invocationTokenFor(declarations);
519
+ const placeholderCount =
520
+ promptPlaceholderCount(options.case, INVOCATION_PLACEHOLDER) +
521
+ promptPlaceholderCount(options.case, LEGACY_CODEX_PLACEHOLDER);
522
+ validateOnlyOneInvocation(declarations);
523
+ validateSelectedSkillInvocation(
524
+ options,
525
+ state,
526
+ selectedSkillInvocation(declarations),
527
+ placeholderCount,
528
+ );
529
+ return {
530
+ ...state,
531
+ ...declarations,
532
+ ...metadata,
533
+ invocationToken,
534
+ invocationPlaceholder: INVOCATION_PLACEHOLDER,
535
+ legacyCodexPlaceholder: LEGACY_CODEX_PLACEHOLDER,
536
+ placeholderCount,
537
+ };
538
+ }
539
+
540
+ export function prepareInvocationPlan<T extends InvocationArtifacts>(
541
+ options: EvaluationOptions,
542
+ state: T,
543
+ ) {
544
+ return prepareSkillInvocation(
545
+ options,
546
+ preparePackageArtifacts(options, state),
547
+ );
548
+ }