@bjoernrochel/sevro 0.1.0-rc.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CONTRIBUTING.md +22 -6
  2. package/README.md +5 -1
  3. package/docs/README.md +19 -15
  4. package/docs/architecture.md +138 -17
  5. package/docs/assets/README.md +5 -4
  6. package/docs/builtin-graders-v1.md +17 -3
  7. package/docs/codex-host.md +155 -49
  8. package/docs/contracts.md +2 -1
  9. package/docs/creating-cases.md +9 -2
  10. package/docs/development-cli.md +187 -19
  11. package/docs/documentation-quality.md +12 -2
  12. package/docs/evaluation-workflow.md +110 -0
  13. package/docs/extension-protocol-v1.md +26 -3
  14. package/docs/getting-started.md +23 -2
  15. package/docs/guide-evaluation.md +33 -1
  16. package/docs/identity-v1.md +25 -2
  17. package/docs/installing.md +11 -11
  18. package/docs/native-controls.md +76 -5
  19. package/docs/native-hosts.md +196 -7
  20. package/docs/reading-results.md +102 -0
  21. package/docs/recipes.md +489 -0
  22. package/docs/releases.md +31 -12
  23. package/docs/report-v1.md +29 -1
  24. package/docs/results-v1.md +141 -4
  25. package/docs/run-settings-v1.md +147 -0
  26. package/docs/runtime-v1.md +307 -18
  27. package/docs/troubleshooting.md +12 -12
  28. package/docs/typescript-quality.md +89 -26
  29. package/examples/recipes/candidate.ts +26 -0
  30. package/examples/recipes/cases/coding-change.json +21 -0
  31. package/examples/recipes/cases/host-tools.json +21 -0
  32. package/examples/recipes/cases/mise.json +21 -0
  33. package/examples/recipes/cases/node.json +21 -0
  34. package/examples/recipes/cases/python-uv.json +21 -0
  35. package/examples/recipes/cases/semantic.json +17 -0
  36. package/examples/recipes/coding-change/check.ts +23 -0
  37. package/examples/recipes/extension-artifacts.ts +65 -0
  38. package/examples/recipes/extension-methods.ts +174 -0
  39. package/examples/recipes/extension.ts +37 -0
  40. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  41. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  42. package/examples/recipes/repository-skill/README.md +3 -0
  43. package/examples/recipes/run-inputs.ts +188 -0
  44. package/examples/recipes/run-options.ts +131 -0
  45. package/examples/recipes/run.ts +63 -0
  46. package/examples/recipes/runtime/empty.json +1 -0
  47. package/examples/recipes/runtime/goals.json +1 -0
  48. package/examples/recipes/runtime/mise.json +6 -0
  49. package/examples/recipes/runtime/path.json +1 -0
  50. package/examples/recipes/runtime/plugins.json +4 -0
  51. package/examples/recipes/runtime/python-uv.json +10 -0
  52. package/examples/recipes/semantic-judge.ts +29 -0
  53. package/examples/templates/coding/adapter.ts +17 -0
  54. package/examples/templates/coding/case.json +15 -0
  55. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  56. package/examples/templates/repository-skill/extension-data.ts +64 -0
  57. package/examples/templates/repository-skill/extension.ts +31 -0
  58. package/examples/templates/response/adapter.ts +12 -0
  59. package/examples/templates/response/case.json +13 -0
  60. package/package.json +11 -5
  61. package/schemas/report-v1.schema.json +9 -0
  62. package/schemas/run-evidence-v1.schema.json +2 -1
  63. package/schemas/runtime-v1.schema.json +63 -7
  64. package/src/advisory.ts +2 -2
  65. package/src/candidate-artifacts.ts +206 -0
  66. package/src/candidate-codex-state.ts +185 -0
  67. package/src/case-validation.ts +73 -50
  68. package/src/cli/cancellation.ts +20 -0
  69. package/src/cli/command.ts +87 -0
  70. package/src/cli/completion.ts +15 -0
  71. package/src/cli/demo.ts +60 -0
  72. package/src/cli/evaluation.ts +55 -0
  73. package/src/cli/extension.ts +79 -0
  74. package/src/cli/help.ts +198 -0
  75. package/src/cli/hosts.ts +55 -0
  76. package/src/cli/init-settings.ts +30 -0
  77. package/src/cli/init.ts +111 -0
  78. package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
  79. package/src/cli/loading.ts +151 -0
  80. package/src/cli/preparation.ts +154 -0
  81. package/src/cli/presentation.ts +93 -0
  82. package/src/cli/progress.ts +6 -0
  83. package/src/cli/run-manifest.ts +128 -0
  84. package/src/cli/run-options.ts +403 -0
  85. package/src/cli/run-overrides.ts +69 -0
  86. package/src/cli/settings.ts +154 -0
  87. package/src/cli/summary.ts +43 -0
  88. package/src/cli.ts +13 -591
  89. package/src/contract-types.ts +94 -0
  90. package/src/engine.ts +13 -5
  91. package/src/evaluation/advisory.ts +150 -0
  92. package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
  93. package/src/evaluation/collection.ts +88 -0
  94. package/src/evaluation/context.ts +10 -0
  95. package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
  96. package/src/evaluation/grading.ts +235 -0
  97. package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
  98. package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
  99. package/src/evaluation/progress.ts +19 -0
  100. package/src/evaluation/resources.ts +241 -0
  101. package/src/evaluation/run.ts +206 -0
  102. package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
  103. package/src/evaluation/trial.ts +800 -0
  104. package/src/evaluation/types.ts +207 -0
  105. package/src/evidence/host-results.ts +85 -0
  106. package/src/evidence/retained-artifacts.ts +18 -0
  107. package/src/evidence/trial-writer.ts +203 -0
  108. package/src/evidence/usage.ts +17 -0
  109. package/src/extension-client.ts +34 -96
  110. package/src/extension-session.ts +24 -48
  111. package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
  112. package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
  113. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  114. package/src/fixtures/create.ts +183 -0
  115. package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
  116. package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
  117. package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
  118. package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
  119. package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
  120. package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
  121. package/src/fixtures/workspace-state.ts +212 -0
  122. package/src/generated/cli-result.d.cts +2 -1
  123. package/src/generated/extension.d.cts +2 -1
  124. package/src/generated/report.cjs +1 -1
  125. package/src/generated/report.d.cts +2 -1
  126. package/src/generated/run-evidence.cjs +1 -1
  127. package/src/generated/run-evidence.d.cts +2 -1
  128. package/src/generated/runtime.cjs +1 -1
  129. package/src/generated/runtime.d.cts +2 -1
  130. package/src/git.ts +93 -0
  131. package/src/graders/git-head.ts +29 -84
  132. package/src/graders/shell.ts +57 -68
  133. package/src/host-diagnostic-redaction.ts +52 -0
  134. package/src/host-diagnostics.ts +331 -0
  135. package/src/hosts/claude-continuation.ts +57 -40
  136. package/src/hosts/claude-credential.ts +160 -63
  137. package/src/hosts/claude-events.ts +34 -18
  138. package/src/hosts/claude-keychain.ts +78 -21
  139. package/src/hosts/claude-login-recovery.ts +85 -0
  140. package/src/hosts/claude-login-refresh.ts +178 -0
  141. package/src/hosts/claude-login-store.ts +162 -0
  142. package/src/hosts/claude-login.ts +107 -0
  143. package/src/hosts/claude-native-goal.ts +15 -12
  144. package/src/hosts/claude-process.ts +10 -50
  145. package/src/hosts/claude-refresh-lock.ts +81 -0
  146. package/src/hosts/claude-repository-invocation.ts +9 -34
  147. package/src/hosts/claude-settings.ts +91 -14
  148. package/src/hosts/claude-skill-activations.ts +169 -0
  149. package/src/hosts/claude-state.ts +46 -16
  150. package/src/hosts/claude-tool-calls.ts +13 -10
  151. package/src/hosts/claude.ts +122 -73
  152. package/src/hosts/codex-app-server.ts +78 -52
  153. package/src/hosts/codex-configuration.ts +5 -10
  154. package/src/hosts/codex-exec-controls.ts +516 -0
  155. package/src/hosts/codex-marketplace.ts +12 -10
  156. package/src/hosts/codex-native-calls.ts +15 -122
  157. package/src/hosts/codex-process.ts +10 -54
  158. package/src/hosts/codex-profile.ts +50 -21
  159. package/src/hosts/codex-session-files.ts +1 -9
  160. package/src/hosts/codex-state.ts +141 -19
  161. package/src/hosts/codex.ts +221 -179
  162. package/src/hosts/isolation-common.ts +15 -11
  163. package/src/hosts/isolation-roots.ts +13 -20
  164. package/src/hosts/linux-sandbox.ts +27 -3
  165. package/src/hosts/mac-sandbox.ts +111 -18
  166. package/src/hosts/native-controls.ts +72 -22
  167. package/src/hosts/native-request.ts +55 -0
  168. package/src/hosts/process-lifecycle.ts +221 -45
  169. package/src/hosts/protected-worktrees.ts +1 -1
  170. package/src/hosts/skill-hook-catalog.ts +131 -0
  171. package/src/hosts/skill-hook-handler.ts +374 -0
  172. package/src/hosts/skill-hooks.ts +577 -0
  173. package/src/instrumentation.ts +7 -8
  174. package/src/native-transcript-state.ts +22 -3
  175. package/src/native-transcript-view.ts +52 -13
  176. package/src/native-transcripts.ts +34 -15
  177. package/src/paths.ts +22 -0
  178. package/src/process-lifecycle.ts +150 -0
  179. package/src/provenance.ts +27 -7
  180. package/src/prune.ts +300 -0
  181. package/src/report.ts +63 -36
  182. package/src/resolved-case.ts +1 -1
  183. package/src/results-lock.ts +42 -0
  184. package/src/results.ts +14 -18
  185. package/src/run-owner.ts +4 -4
  186. package/src/{runtime-config.ts → runtime/config.ts} +182 -44
  187. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  188. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  189. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  190. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
  191. package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
  192. package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
  193. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  194. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  195. package/src/runtime/shared-cache.ts +130 -0
  196. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  197. package/src/schema-types.ts +73 -161
  198. package/src/shell.ts +4 -0
  199. package/src/transcript-retention.ts +146 -0
  200. package/src/value-guards.ts +22 -0
  201. package/docs/assets/sevro-logo.png +0 -0
  202. package/docs/assets/sevro-symbol.svg +0 -1
  203. package/docs/assets/sevro-wordmark.svg +0 -1
  204. package/docs/cache-ownership-validation.md +0 -49
  205. package/docs/claude-continuation-validation.md +0 -47
  206. package/docs/concurrency-validation.md +0 -86
  207. package/docs/configuration-root-validation.md +0 -70
  208. package/docs/engine-slice.md +0 -114
  209. package/docs/evidence/runtime-startup-30/README.md +0 -185
  210. package/docs/evidence/runtime-startup-30/after.json +0 -319
  211. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  212. package/docs/evidence/runtime-startup-30/before.json +0 -403
  213. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  214. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  215. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  216. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  217. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  218. package/docs/evidence.md +0 -17
  219. package/docs/fixture-cleanup-validation.md +0 -70
  220. package/docs/guide-verification.json +0 -239
  221. package/src/evaluation-context.ts +0 -257
  222. package/src/evaluation-fixture.ts +0 -313
  223. package/src/evaluation-run.ts +0 -198
  224. package/src/evaluation-trial.ts +0 -946
  225. package/src/evaluation-types.ts +0 -149
  226. package/src/hosts/claude-nested-skills.ts +0 -272
  227. package/src/hosts/codex-command-output.ts +0 -510
  228. package/src/hosts/codex-executor-command.ts +0 -291
  229. package/src/hosts/codex-skill-reads.ts +0 -609
  230. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
@@ -1,6 +1,11 @@
1
1
  import { sha256 } from "../identity";
2
2
  import { isRecord } from "../value-guards";
3
- import { summarizeClaudeEvents } from "./claude-events";
3
+ import {
4
+ summarizeClaudeEvents,
5
+ parseClaudeEvents,
6
+ type ClaudeEvents,
7
+ type ClaudeEventLine,
8
+ } from "./claude-events";
4
9
 
5
10
  const MAX_RETAINED_CALLS = 128;
6
11
  type Entry = Record<string, unknown>;
@@ -51,18 +56,18 @@ type Actor = { actor: "parent" | "nested"; parentToolUseId: string | null };
51
56
 
52
57
  /** Retain ordered host call metadata, never tool prompts or results. */
53
58
  export function claudeToolCallsObservation(
54
- stream: string,
59
+ stream: string | ClaudeEvents,
55
60
  exitCode: number,
56
61
  ): ClaudeToolCallsObservation {
57
- const summary = summarizeClaudeEvents(stream, exitCode);
62
+ const events = parseClaudeEvents(stream);
63
+ const summary = summarizeClaudeEvents(events, exitCode);
58
64
  const state: CallState = {
59
65
  calls: [],
60
66
  malformed: false,
61
67
  truncated: false,
62
68
  ordinal: 0,
63
69
  };
64
- for (const line of stream.split("\n")) {
65
- if (!line.trim()) continue;
70
+ for (const line of events.lines) {
66
71
  collectEvent(line, state);
67
72
  if (state.truncated) break;
68
73
  }
@@ -86,14 +91,12 @@ function callCompleteness(
86
91
  : "partial";
87
92
  }
88
93
 
89
- function collectEvent(line: string, state: CallState): void {
90
- let event: unknown;
91
- try {
92
- event = JSON.parse(line);
93
- } catch {
94
+ function collectEvent(line: ClaudeEventLine, state: CallState): void {
95
+ if (!line.parsed) {
94
96
  state.malformed = true;
95
97
  return;
96
98
  }
99
+ const event = line.value;
97
100
  if (!isRecord(event) || event.type !== "assistant") return;
98
101
  collectAssistant(event, state);
99
102
  }
@@ -2,36 +2,45 @@ import { existsSync } from "node:fs";
2
2
  import { readFile, realpath, rm, stat } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import type { HostAdapter, HostResult } from "../engine";
5
- import {
6
- allocateNativeState,
7
- requireNativeTranscriptView,
8
- } from "../native-transcript-state";
5
+ import { allocateNativeState } from "../native-transcript-state";
9
6
  import {
10
7
  failedNativeTranscripts,
11
8
  retainEnabledNativeTranscripts,
12
9
  } from "../native-transcripts";
13
- import { fixtureParts } from "../preparation";
10
+ import { fixtureParts } from "../fixtures/preparation";
14
11
  import {
15
12
  prepareHookMounts,
16
13
  releaseRuntimeHooks,
17
14
  runtimeHookObservation,
18
15
  runtimeHooksEnabled,
19
16
  type HookMounts,
20
- } from "../runtime-hooks";
21
- import { insideRuntimeRoot as inside } from "../runtime-paths";
22
- import { runtimeObservations } from "../runtime-state";
23
- import { isOptionalNonblankString, isRecord } from "../value-guards";
17
+ } from "../runtime/hooks";
18
+ import { insideRuntimeRoot as inside } from "../runtime/paths";
19
+ import { runtimeObservations } from "../runtime/state";
20
+ import { isRecord } from "../value-guards";
24
21
  import { runClaudeTurns, type ClaudeSession } from "./claude-continuation";
22
+ import { validateNativeHostRequest, validHostTimeout } from "./native-request";
25
23
  import { summarizeClaudeEvents } from "./claude-events";
26
24
  import { observeClaudeNativeGoal } from "./claude-native-goal";
27
- import { claudeNestedSkillsObservation } from "./claude-nested-skills";
28
25
  import { runClaudeProcess } from "./claude-process";
29
26
  import {
30
27
  claudeRepositoryInvocationObservation,
31
28
  verifyClaudeRepositoryInvocation,
32
29
  } from "./claude-repository-invocation";
33
30
  import { prepareClaudeState, type ClaudeHostOptions } from "./claude-state";
31
+ import {
32
+ cleanupNativeState,
33
+ createHostDiagnostics,
34
+ diagnoseHostRequest,
35
+ diagnosticConfiguration,
36
+ type HostDiagnostics,
37
+ } from "../host-diagnostics";
38
+ import {
39
+ CLAUDE_AUTH_VARIABLES,
40
+ CLAUDE_LOGIN_MARGIN_MS,
41
+ } from "./claude-credential";
34
42
  import { claudeToolCallsObservation } from "./claude-tool-calls";
43
+ import { claudeSkillActivationsObservation } from "./claude-skill-activations";
35
44
  import { claudeNativeControls } from "./native-controls";
36
45
  export type { ClaudeHostOptions } from "./claude-state";
37
46
 
@@ -217,12 +226,7 @@ function optionalClaudePaths(options: ClaudeHostOptions): boolean {
217
226
  ].every((path) => path === undefined || isAbsolute(path));
218
227
  }
219
228
  function validClaudeEffortTimeout(effort: string, timeoutMs: number): boolean {
220
- return (
221
- !!effort &&
222
- Number.isSafeInteger(timeoutMs) &&
223
- timeoutMs >= 1 &&
224
- timeoutMs <= 30 * 60_000
225
- );
229
+ return !!effort && validHostTimeout(timeoutMs);
226
230
  }
227
231
 
228
232
  /** Run Claude with explicit plugins and its native tool sandbox. */
@@ -234,11 +238,17 @@ export function createClaudeHost(options: ClaudeHostOptions): HostAdapter {
234
238
  id: "sevro.host.claude",
235
239
  model: options.model,
236
240
  effort: options.effort,
241
+ ...(options.retainHostDiagnostics
242
+ ? {
243
+ configuration: diagnosticConfiguration(options.retainHostDiagnostics),
244
+ }
245
+ : {}),
237
246
  hostCapabilities: [
238
247
  "sevro.host.native-goal",
239
248
  "sevro.host.runtime",
240
249
  "sevro.host.native-transcripts",
241
250
  "sevro.host.hooks",
251
+ "sevro.host.skill-activations",
242
252
  "sevro.claude.plugin-dirs",
243
253
  "sevro.claude.explicit-invocation",
244
254
  ...(options.projectSettings
@@ -246,7 +256,6 @@ export function createClaudeHost(options: ClaudeHostOptions): HostAdapter {
246
256
  : []),
247
257
  "sevro.claude.tool-calls",
248
258
  "sevro.host.native-controls",
249
- "sevro.claude.nested-skills",
250
259
  "sevro.host.continuation",
251
260
  "sevro.claude.continuation",
252
261
  ],
@@ -256,24 +265,6 @@ export function createClaudeHost(options: ClaudeHostOptions): HostAdapter {
256
265
  };
257
266
  }
258
267
 
259
- function requireClaudeFollowUp(request: Request): void {
260
- if (!isOptionalNonblankString(request.followUpPrompt))
261
- throw new Error("Claude follow-up prompt must be nonempty");
262
- }
263
- function requirePassiveClaudeRequest(request: Request): void {
264
- if (request.instrumentation?.length || request.condition !== "passive")
265
- throw new Error("Claude enforcement instrumentation is unavailable");
266
- requireClaudeFixtureBin(request);
267
- }
268
-
269
- function requireClaudeFixtureBin(request: Request): void {
270
- if (
271
- request.fixtureBinDir !== undefined &&
272
- request.fixtureBinDir !== join(request.workspace, ".git", "fixture-bin")
273
- )
274
- throw new Error("fixture binary path is outside the workspace");
275
- }
276
-
277
268
  async function repositoryMount(
278
269
  request: Request,
279
270
  options: ClaudeHostOptions,
@@ -291,42 +282,58 @@ async function runClaudeRequest(
291
282
  timeoutMs: number,
292
283
  request: Request,
293
284
  ): Promise<HostResult> {
294
- requireClaudeFollowUp(request);
295
- await requireNativeTranscriptView(request.candidateTranscriptRoot);
296
- requirePassiveClaudeRequest(request);
297
- const pluginDirs = request.claudePluginDirs
298
- ? await pluginDirectories(request.workspace, request.claudePluginDirs)
299
- : [];
300
- await verifyInvocation(request, pluginDirs);
301
- requireIsolatedHookSources(request, options);
302
- const repositoryInvocation = await repositoryMount(request, options);
285
+ const diagnostics = createHostDiagnostics(
286
+ "claude",
287
+ options.retainHostDiagnostics,
288
+ request,
289
+ );
290
+ return diagnoseHostRequest(diagnostics, async () => {
291
+ diagnostics.stage = "request-validation";
292
+ await diagnostics.readCredentials(options.credentialFile);
293
+ return executeClaudeRequest(options, timeoutMs, request, diagnostics);
294
+ });
295
+ }
296
+
297
+ /** Every turn shares one access token, so it must outlive all of them. */
298
+ function claudeLoginLifetime(timeoutMs: number, request: Request): number {
299
+ const turns = request.followUpPrompt === undefined ? 1 : 2;
300
+ return timeoutMs * turns + CLAUDE_LOGIN_MARGIN_MS;
301
+ }
302
+
303
+ async function executeClaudeRequest(
304
+ options: ClaudeHostOptions,
305
+ timeoutMs: number,
306
+ request: Request,
307
+ diagnostics: HostDiagnostics,
308
+ ): Promise<HostResult> {
309
+ const { pluginDirs, repositoryInvocation } = await validateClaudeRequest(
310
+ options,
311
+ request,
312
+ );
313
+ diagnostics.stage = "state-allocation";
303
314
  const stateRoot = await allocateNativeState(
304
315
  "claude-",
305
316
  request.runtimePolicy ? request.workspace : undefined,
306
317
  );
307
318
  let hooks: HookMounts | undefined;
308
319
  try {
320
+ diagnostics.stage = "state-preparation";
309
321
  const state = await prepareClaudeState(
310
322
  stateRoot,
311
323
  options,
312
324
  request,
313
325
  pluginDirs,
326
+ claudeLoginLifetime(timeoutMs, request),
314
327
  );
328
+ diagnostics.registerCredentials(state.env, CLAUDE_AUTH_VARIABLES);
315
329
  hooks = state.hooks;
316
- const run = (prompt: string, session?: ClaudeSession) =>
317
- runClaudeProcess({
318
- argv: claudeTurnArgs(
319
- options,
320
- prompt,
321
- session,
322
- state.settingsPath,
323
- state.pluginDirs,
324
- ),
325
- cwd: request.workspace,
326
- env: state.env,
327
- timeoutMs,
328
- signal: request.signal,
329
- });
330
+ const run = claudeProcessRunner(
331
+ options,
332
+ timeoutMs,
333
+ request,
334
+ state,
335
+ diagnostics,
336
+ );
330
337
  const execution = await runClaudeTurns({
331
338
  prompt: request.prompt,
332
339
  followUpPrompt: request.followUpPrompt,
@@ -339,6 +346,10 @@ async function runClaudeRequest(
339
346
  request,
340
347
  repositoryInvocation,
341
348
  );
349
+ await diagnostics.retainFailure(
350
+ result,
351
+ "Claude host execution did not complete",
352
+ );
342
353
  await addHookObservation(result, state.hooks);
343
354
  await retainEnabledNativeTranscripts(
344
355
  result,
@@ -355,13 +366,56 @@ async function runClaudeRequest(
355
366
  join(stateRoot, "native-home", "projects"),
356
367
  "claude",
357
368
  request.runtimeRole,
369
+ diagnostics,
358
370
  );
359
371
  } finally {
360
- await releaseRuntimeHooks(hooks);
361
- await rm(stateRoot, { recursive: true, force: true });
372
+ await cleanupNativeState(diagnostics, async () => {
373
+ await releaseRuntimeHooks(hooks);
374
+ await rm(stateRoot, { recursive: true, force: true });
375
+ });
362
376
  }
363
377
  }
364
378
 
379
+ function claudeProcessRunner(
380
+ options: ClaudeHostOptions,
381
+ timeoutMs: number,
382
+ request: Request,
383
+ state: Awaited<ReturnType<typeof prepareClaudeState>>,
384
+ diagnostics: HostDiagnostics,
385
+ ) {
386
+ return (prompt: string, session?: ClaudeSession) => {
387
+ diagnostics.stage = "claude-execution";
388
+ return runClaudeProcess({
389
+ diagnostics,
390
+ argv: claudeTurnArgs(
391
+ options,
392
+ prompt,
393
+ session,
394
+ state.settingsPath,
395
+ state.pluginDirs,
396
+ ),
397
+ cwd: request.workspace,
398
+ env: state.env,
399
+ timeoutMs,
400
+ signal: request.signal,
401
+ });
402
+ };
403
+ }
404
+
405
+ async function validateClaudeRequest(
406
+ options: ClaudeHostOptions,
407
+ request: Request,
408
+ ) {
409
+ await validateNativeHostRequest(request, "Claude");
410
+ const pluginDirs = request.claudePluginDirs
411
+ ? await pluginDirectories(request.workspace, request.claudePluginDirs)
412
+ : [];
413
+ await verifyInvocation(request, pluginDirs);
414
+ requireIsolatedHookSources(request, options);
415
+ const repositoryInvocation = await repositoryMount(request, options);
416
+ return { pluginDirs, repositoryInvocation };
417
+ }
418
+
365
419
  function requireIsolatedHookSources(
366
420
  request: Request,
367
421
  options: ClaudeHostOptions,
@@ -433,11 +487,11 @@ async function repositoryObservation(
433
487
  if (!invocation) return null;
434
488
  return claudeRepositoryInvocationObservation({
435
489
  invocation,
436
- stream: execution.initialOut ?? execution.out,
490
+ stream: execution.initialEvents ?? execution.events,
437
491
  configRoot: configRoot,
438
492
  workspace: request.workspace,
439
- tools: execution.initialOut
440
- ? claudeToolCallsObservation(execution.initialOut, 0)
493
+ tools: execution.initialEvents
494
+ ? claudeToolCallsObservation(execution.initialEvents, 0)
441
495
  : tools,
442
496
  });
443
497
  }
@@ -448,12 +502,7 @@ async function claudeObservations(
448
502
  request: Request,
449
503
  invocation: RepositoryInvocation | null,
450
504
  ) {
451
- const nested = await claudeNestedSkillsObservation(
452
- execution.out,
453
- configRoot,
454
- request.workspace,
455
- );
456
- const tools = claudeToolCallsObservation(execution.out, execution.code);
505
+ const tools = claudeToolCallsObservation(execution.events, execution.code);
457
506
  const repository = await repositoryObservation(
458
507
  execution,
459
508
  configRoot,
@@ -466,12 +515,12 @@ async function claudeObservations(
466
515
  ...runtimeObservations(request.runtimePolicy, request.runtimeRole),
467
516
  await observeClaudeNativeGoal(
468
517
  configRoot,
469
- execution.followUpOut ?? execution.out,
518
+ execution.followUpEvents ?? execution.events,
470
519
  ),
471
- nested,
472
- claudeNativeControls(execution.out, execution.code),
520
+ claudeNativeControls(execution.events, execution.code),
473
521
  ...(repository ? [repository] : []),
474
522
  ...(execution.continuation ? [execution.continuation] : []),
523
+ claudeSkillActivationsObservation(execution.events, execution.code),
475
524
  ];
476
525
  }
477
526
 
@@ -526,7 +575,7 @@ async function claudeResult(
526
575
  request: Request,
527
576
  invocation: RepositoryInvocation | null,
528
577
  ): Promise<HostResult> {
529
- const summary = summarizeClaudeEvents(execution.out, execution.code);
578
+ const summary = summarizeClaudeEvents(execution.events, execution.code);
530
579
  const observations = await claudeObservations(
531
580
  execution,
532
581
  configRoot,