@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.
- package/CONTRIBUTING.md +22 -6
- package/README.md +5 -1
- package/docs/README.md +19 -15
- package/docs/architecture.md +138 -17
- package/docs/assets/README.md +5 -4
- package/docs/builtin-graders-v1.md +17 -3
- package/docs/codex-host.md +155 -49
- package/docs/contracts.md +2 -1
- package/docs/creating-cases.md +9 -2
- package/docs/development-cli.md +187 -19
- package/docs/documentation-quality.md +12 -2
- package/docs/evaluation-workflow.md +110 -0
- package/docs/extension-protocol-v1.md +26 -3
- package/docs/getting-started.md +23 -2
- package/docs/guide-evaluation.md +33 -1
- package/docs/identity-v1.md +25 -2
- package/docs/installing.md +11 -11
- package/docs/native-controls.md +76 -5
- package/docs/native-hosts.md +196 -7
- package/docs/reading-results.md +102 -0
- package/docs/recipes.md +489 -0
- package/docs/releases.md +31 -12
- package/docs/report-v1.md +29 -1
- package/docs/results-v1.md +141 -4
- package/docs/run-settings-v1.md +147 -0
- package/docs/runtime-v1.md +307 -18
- package/docs/troubleshooting.md +12 -12
- package/docs/typescript-quality.md +89 -26
- package/examples/recipes/candidate.ts +26 -0
- package/examples/recipes/cases/coding-change.json +21 -0
- package/examples/recipes/cases/host-tools.json +21 -0
- package/examples/recipes/cases/mise.json +21 -0
- package/examples/recipes/cases/node.json +21 -0
- package/examples/recipes/cases/python-uv.json +21 -0
- package/examples/recipes/cases/semantic.json +17 -0
- package/examples/recipes/coding-change/check.ts +23 -0
- package/examples/recipes/extension-artifacts.ts +65 -0
- package/examples/recipes/extension-methods.ts +174 -0
- package/examples/recipes/extension.ts +37 -0
- package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/README.md +3 -0
- package/examples/recipes/run-inputs.ts +188 -0
- package/examples/recipes/run-options.ts +131 -0
- package/examples/recipes/run.ts +63 -0
- package/examples/recipes/runtime/empty.json +1 -0
- package/examples/recipes/runtime/goals.json +1 -0
- package/examples/recipes/runtime/mise.json +6 -0
- package/examples/recipes/runtime/path.json +1 -0
- package/examples/recipes/runtime/plugins.json +4 -0
- package/examples/recipes/runtime/python-uv.json +10 -0
- package/examples/recipes/semantic-judge.ts +29 -0
- package/examples/templates/coding/adapter.ts +17 -0
- package/examples/templates/coding/case.json +15 -0
- package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
- package/examples/templates/repository-skill/extension-data.ts +64 -0
- package/examples/templates/repository-skill/extension.ts +31 -0
- package/examples/templates/response/adapter.ts +12 -0
- package/examples/templates/response/case.json +13 -0
- package/package.json +11 -5
- package/schemas/report-v1.schema.json +9 -0
- package/schemas/run-evidence-v1.schema.json +2 -1
- package/schemas/runtime-v1.schema.json +63 -7
- package/src/advisory.ts +2 -2
- package/src/candidate-artifacts.ts +206 -0
- package/src/candidate-codex-state.ts +185 -0
- package/src/case-validation.ts +73 -50
- package/src/cli/cancellation.ts +20 -0
- package/src/cli/command.ts +87 -0
- package/src/cli/completion.ts +15 -0
- package/src/cli/demo.ts +60 -0
- package/src/cli/evaluation.ts +55 -0
- package/src/cli/extension.ts +79 -0
- package/src/cli/help.ts +198 -0
- package/src/cli/hosts.ts +55 -0
- package/src/cli/init-settings.ts +30 -0
- package/src/cli/init.ts +111 -0
- package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
- package/src/cli/loading.ts +151 -0
- package/src/cli/preparation.ts +154 -0
- package/src/cli/presentation.ts +93 -0
- package/src/cli/progress.ts +6 -0
- package/src/cli/run-manifest.ts +128 -0
- package/src/cli/run-options.ts +403 -0
- package/src/cli/run-overrides.ts +69 -0
- package/src/cli/settings.ts +154 -0
- package/src/cli/summary.ts +43 -0
- package/src/cli.ts +13 -591
- package/src/contract-types.ts +94 -0
- package/src/engine.ts +13 -5
- package/src/evaluation/advisory.ts +150 -0
- package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
- package/src/evaluation/collection.ts +88 -0
- package/src/evaluation/context.ts +10 -0
- package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
- package/src/evaluation/grading.ts +235 -0
- package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
- package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
- package/src/evaluation/progress.ts +19 -0
- package/src/evaluation/resources.ts +241 -0
- package/src/evaluation/run.ts +206 -0
- package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
- package/src/evaluation/trial.ts +800 -0
- package/src/evaluation/types.ts +207 -0
- package/src/evidence/host-results.ts +85 -0
- package/src/evidence/retained-artifacts.ts +18 -0
- package/src/evidence/trial-writer.ts +203 -0
- package/src/evidence/usage.ts +17 -0
- package/src/extension-client.ts +34 -96
- package/src/extension-session.ts +24 -48
- package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
- package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
- package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
- package/src/fixtures/create.ts +183 -0
- package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
- package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
- package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
- package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
- package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
- package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
- package/src/fixtures/workspace-state.ts +212 -0
- package/src/generated/cli-result.d.cts +2 -1
- package/src/generated/extension.d.cts +2 -1
- package/src/generated/report.cjs +1 -1
- package/src/generated/report.d.cts +2 -1
- package/src/generated/run-evidence.cjs +1 -1
- package/src/generated/run-evidence.d.cts +2 -1
- package/src/generated/runtime.cjs +1 -1
- package/src/generated/runtime.d.cts +2 -1
- package/src/git.ts +93 -0
- package/src/graders/git-head.ts +29 -84
- package/src/graders/shell.ts +57 -68
- package/src/host-diagnostic-redaction.ts +52 -0
- package/src/host-diagnostics.ts +331 -0
- package/src/hosts/claude-continuation.ts +57 -40
- package/src/hosts/claude-credential.ts +160 -63
- package/src/hosts/claude-events.ts +34 -18
- package/src/hosts/claude-keychain.ts +78 -21
- package/src/hosts/claude-login-recovery.ts +85 -0
- package/src/hosts/claude-login-refresh.ts +178 -0
- package/src/hosts/claude-login-store.ts +162 -0
- package/src/hosts/claude-login.ts +107 -0
- package/src/hosts/claude-native-goal.ts +15 -12
- package/src/hosts/claude-process.ts +10 -50
- package/src/hosts/claude-refresh-lock.ts +81 -0
- package/src/hosts/claude-repository-invocation.ts +9 -34
- package/src/hosts/claude-settings.ts +91 -14
- package/src/hosts/claude-skill-activations.ts +169 -0
- package/src/hosts/claude-state.ts +46 -16
- package/src/hosts/claude-tool-calls.ts +13 -10
- package/src/hosts/claude.ts +122 -73
- package/src/hosts/codex-app-server.ts +78 -52
- package/src/hosts/codex-configuration.ts +5 -10
- package/src/hosts/codex-exec-controls.ts +516 -0
- package/src/hosts/codex-marketplace.ts +12 -10
- package/src/hosts/codex-native-calls.ts +15 -122
- package/src/hosts/codex-process.ts +10 -54
- package/src/hosts/codex-profile.ts +50 -21
- package/src/hosts/codex-session-files.ts +1 -9
- package/src/hosts/codex-state.ts +141 -19
- package/src/hosts/codex.ts +221 -179
- package/src/hosts/isolation-common.ts +15 -11
- package/src/hosts/isolation-roots.ts +13 -20
- package/src/hosts/linux-sandbox.ts +27 -3
- package/src/hosts/mac-sandbox.ts +111 -18
- package/src/hosts/native-controls.ts +72 -22
- package/src/hosts/native-request.ts +55 -0
- package/src/hosts/process-lifecycle.ts +221 -45
- package/src/hosts/protected-worktrees.ts +1 -1
- package/src/hosts/skill-hook-catalog.ts +131 -0
- package/src/hosts/skill-hook-handler.ts +374 -0
- package/src/hosts/skill-hooks.ts +577 -0
- package/src/instrumentation.ts +7 -8
- package/src/native-transcript-state.ts +22 -3
- package/src/native-transcript-view.ts +52 -13
- package/src/native-transcripts.ts +34 -15
- package/src/paths.ts +22 -0
- package/src/process-lifecycle.ts +150 -0
- package/src/provenance.ts +27 -7
- package/src/prune.ts +300 -0
- package/src/report.ts +63 -36
- package/src/resolved-case.ts +1 -1
- package/src/results-lock.ts +42 -0
- package/src/results.ts +14 -18
- package/src/run-owner.ts +4 -4
- package/src/{runtime-config.ts → runtime/config.ts} +182 -44
- package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
- package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
- package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
- package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
- package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
- package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
- package/src/{runtime-query.ts → runtime/query.ts} +5 -5
- package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
- package/src/runtime/shared-cache.ts +130 -0
- package/src/{runtime-state.ts → runtime/state.ts} +28 -11
- package/src/schema-types.ts +73 -161
- package/src/shell.ts +4 -0
- package/src/transcript-retention.ts +146 -0
- package/src/value-guards.ts +22 -0
- package/docs/assets/sevro-logo.png +0 -0
- package/docs/assets/sevro-symbol.svg +0 -1
- package/docs/assets/sevro-wordmark.svg +0 -1
- package/docs/cache-ownership-validation.md +0 -49
- package/docs/claude-continuation-validation.md +0 -47
- package/docs/concurrency-validation.md +0 -86
- package/docs/configuration-root-validation.md +0 -70
- package/docs/engine-slice.md +0 -114
- package/docs/evidence/runtime-startup-30/README.md +0 -185
- package/docs/evidence/runtime-startup-30/after.json +0 -319
- package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
- package/docs/evidence/runtime-startup-30/before.json +0 -403
- package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
- package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
- package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
- package/docs/evidence.md +0 -17
- package/docs/fixture-cleanup-validation.md +0 -70
- package/docs/guide-verification.json +0 -239
- package/src/evaluation-context.ts +0 -257
- package/src/evaluation-fixture.ts +0 -313
- package/src/evaluation-run.ts +0 -198
- package/src/evaluation-trial.ts +0 -946
- package/src/evaluation-types.ts +0 -149
- package/src/hosts/claude-nested-skills.ts +0 -272
- package/src/hosts/codex-command-output.ts +0 -510
- package/src/hosts/codex-executor-command.ts +0 -291
- package/src/hosts/codex-skill-reads.ts +0 -609
- /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
package/src/evaluation-types.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
InstrumentationCapability,
|
|
3
|
-
InstrumentationRequest,
|
|
4
|
-
} from "./instrumentation";
|
|
5
|
-
import type { GeneratedFixture } from "./generated-fixture";
|
|
6
|
-
import type { PreparationSources } from "./preparation";
|
|
7
|
-
import type { RepositoryFixture } from "./repository-fixture";
|
|
8
|
-
import type { EvaluationResult, ExtensionCase } from "./extension-session";
|
|
9
|
-
import type { openExtensionSession } from "./extension-session";
|
|
10
|
-
import type { Assessment, CheckOutcome } from "./results";
|
|
11
|
-
import type { RuntimePolicy } from "./runtime-config";
|
|
12
|
-
import type { RuntimeRole } from "./runtime-state";
|
|
13
|
-
|
|
14
|
-
export class EvaluationConfigurationError extends Error {
|
|
15
|
-
constructor(message: string, options?: ErrorOptions) {
|
|
16
|
-
super(message, options);
|
|
17
|
-
this.name = "EvaluationConfigurationError";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface HostResult {
|
|
22
|
-
finalMessage: string | null;
|
|
23
|
-
complete: boolean;
|
|
24
|
-
executionFailed?: boolean;
|
|
25
|
-
artifacts?: { id: string; bytes: Uint8Array }[];
|
|
26
|
-
observations?: {
|
|
27
|
-
id: string;
|
|
28
|
-
completeness: "complete" | "partial" | "unavailable";
|
|
29
|
-
data: Record<string, unknown>;
|
|
30
|
-
}[];
|
|
31
|
-
actualCondition?: "passive" | "enforced" | "unknown";
|
|
32
|
-
appliedInstrumentation?: InstrumentationRequest[];
|
|
33
|
-
inputTokens?: number | null;
|
|
34
|
-
outputTokens?: number | null;
|
|
35
|
-
costUsd?: number | null;
|
|
36
|
-
usageComplete?: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface HostAdapter {
|
|
40
|
-
id: string;
|
|
41
|
-
model: string;
|
|
42
|
-
effort: string;
|
|
43
|
-
instrumentation?: InstrumentationCapability[];
|
|
44
|
-
hostCapabilities?: string[];
|
|
45
|
-
configuration?: Record<string, unknown>;
|
|
46
|
-
run(request: {
|
|
47
|
-
runtimePolicy?: RuntimePolicy;
|
|
48
|
-
runtimeRole?: RuntimeRole;
|
|
49
|
-
candidateTranscriptRoot?: string;
|
|
50
|
-
prompt: string;
|
|
51
|
-
followUpPrompt?: string;
|
|
52
|
-
workspace: string;
|
|
53
|
-
condition: "passive" | "enforced";
|
|
54
|
-
fixtureBinDir?: string;
|
|
55
|
-
instrumentation?: InstrumentationRequest[];
|
|
56
|
-
codexMarketplace?: {
|
|
57
|
-
artifactRoot: string;
|
|
58
|
-
marketplaceName: string;
|
|
59
|
-
pluginNames: string[];
|
|
60
|
-
artifactPaths: string[];
|
|
61
|
-
};
|
|
62
|
-
claudePluginDirs?: {
|
|
63
|
-
artifactRoots: string[];
|
|
64
|
-
artifactPaths: string[];
|
|
65
|
-
};
|
|
66
|
-
explicitSkillInvocation?:
|
|
67
|
-
| {
|
|
68
|
-
scope?: "plugin";
|
|
69
|
-
pluginName: string;
|
|
70
|
-
skillName: string;
|
|
71
|
-
token: string;
|
|
72
|
-
}
|
|
73
|
-
| {
|
|
74
|
-
scope: "repository";
|
|
75
|
-
pluginName?: never;
|
|
76
|
-
skillName: string;
|
|
77
|
-
token: string;
|
|
78
|
-
};
|
|
79
|
-
signal?: AbortSignal;
|
|
80
|
-
}): Promise<HostResult>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface ResolvedCase {
|
|
84
|
-
id: string;
|
|
85
|
-
prompt: string;
|
|
86
|
-
followUpPrompt?: string;
|
|
87
|
-
fixture:
|
|
88
|
-
| { files: Record<string, string>; sourceRef?: never }
|
|
89
|
-
| Omit<RepositoryFixture, "kind">
|
|
90
|
-
| (GeneratedFixture & { sourceRef?: never });
|
|
91
|
-
checks: {
|
|
92
|
-
id: string;
|
|
93
|
-
grader: string;
|
|
94
|
-
configuration: Record<string, unknown>;
|
|
95
|
-
}[];
|
|
96
|
-
requiredEvidence: string[];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface EvaluationOptions {
|
|
100
|
-
runtimePolicy?: RuntimePolicy;
|
|
101
|
-
projectRoot: string;
|
|
102
|
-
resultsRoot: string;
|
|
103
|
-
runStateRoot?: string;
|
|
104
|
-
case: ResolvedCase;
|
|
105
|
-
host: HostAdapter;
|
|
106
|
-
semanticHost?: HostAdapter;
|
|
107
|
-
advisoryHost?: HostAdapter;
|
|
108
|
-
advisoryExcludedPaths?: string[];
|
|
109
|
-
runnerBuildDigest: string;
|
|
110
|
-
runnerCheckoutRoot?: string;
|
|
111
|
-
projectDigest: string;
|
|
112
|
-
condition: "passive" | "enforced";
|
|
113
|
-
trialCount: number;
|
|
114
|
-
jobs?: number;
|
|
115
|
-
passThreshold: number;
|
|
116
|
-
dry?: boolean;
|
|
117
|
-
signal?: AbortSignal;
|
|
118
|
-
extension?: {
|
|
119
|
-
session: Awaited<ReturnType<typeof openExtensionSession>>;
|
|
120
|
-
resolvedCase: ExtensionCase;
|
|
121
|
-
};
|
|
122
|
-
preparationSources?: PreparationSources;
|
|
123
|
-
shellIsolation?: {
|
|
124
|
-
protectedRoots: string[];
|
|
125
|
-
toolchainBinDir?: string;
|
|
126
|
-
uvRuntimeCache?: boolean;
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface TrialSummary extends Assessment {
|
|
131
|
-
trial: number;
|
|
132
|
-
checks: CheckOutcome[];
|
|
133
|
-
domainOutcomes: NonNullable<EvaluationResult["domainOutcomes"]>;
|
|
134
|
-
artifactPath: string;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface CaseSummary extends Assessment {
|
|
138
|
-
caseId: string;
|
|
139
|
-
trials: TrialSummary[];
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface CliResult extends Assessment {
|
|
143
|
-
format: "sevro.cli-result.v1";
|
|
144
|
-
runId: string;
|
|
145
|
-
exitCode: number;
|
|
146
|
-
evidencePath: string;
|
|
147
|
-
cases: CaseSummary[];
|
|
148
|
-
diagnostic?: { code: string; message: string };
|
|
149
|
-
}
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { isRecord } from "../value-guards";
|
|
4
|
-
|
|
5
|
-
type Entry = Record<string, unknown>;
|
|
6
|
-
const MAX_BYTES = 8 * 1024 * 1024;
|
|
7
|
-
const MAX_AGENTS = 64;
|
|
8
|
-
const MAX_SKILLS = 128;
|
|
9
|
-
|
|
10
|
-
function label(value: unknown): value is string {
|
|
11
|
-
return typeof value === "string" && /^[A-Za-z0-9_.:-]{1,256}$/.test(value);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function identity(value: unknown): value is string {
|
|
15
|
-
return typeof value === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(value);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function blocks(entry: Entry): Entry[] {
|
|
19
|
-
const content = isRecord(entry.message) ? entry.message.content : null;
|
|
20
|
-
return Array.isArray(content) ? content.filter(isRecord) : [];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function entries(text: string): Entry[] {
|
|
24
|
-
return text
|
|
25
|
-
.split(/\r?\n/)
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.map((line) => {
|
|
28
|
-
const parsed: unknown = JSON.parse(line);
|
|
29
|
-
if (!isRecord(parsed)) throw new Error("invalid Claude session entry");
|
|
30
|
-
return parsed;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function readSession(path: string): Promise<string> {
|
|
35
|
-
const file = await lstat(path);
|
|
36
|
-
if (!file.isFile() || file.isSymbolicLink() || file.size > MAX_BYTES)
|
|
37
|
-
throw new Error("Claude session file is unavailable");
|
|
38
|
-
return readFile(path, "utf8");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
type NestedSkill = {
|
|
42
|
-
ancestorToolUseId: string;
|
|
43
|
-
skill: string;
|
|
44
|
-
invocation: string;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
type State = {
|
|
48
|
-
sessionId: string;
|
|
49
|
-
projectDir: string;
|
|
50
|
-
seen: Set<string>;
|
|
51
|
-
calls: NestedSkill[];
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
async function sidecarChild(
|
|
55
|
-
state: State,
|
|
56
|
-
toolUseId: string,
|
|
57
|
-
parentAgentId: string | null,
|
|
58
|
-
): Promise<string> {
|
|
59
|
-
const directory = join(state.projectDir, state.sessionId, "subagents");
|
|
60
|
-
const names = (await readdir(directory)).filter((name) =>
|
|
61
|
-
/^agent-[A-Za-z0-9_-]+\.meta\.json$/.test(name),
|
|
62
|
-
);
|
|
63
|
-
if (names.length > MAX_AGENTS) throw new Error("excessive Claude agents");
|
|
64
|
-
const matches: string[] = [];
|
|
65
|
-
for (const name of names) {
|
|
66
|
-
const metadata: unknown = JSON.parse(
|
|
67
|
-
await readSession(join(directory, name)),
|
|
68
|
-
);
|
|
69
|
-
if (metadataMatches(metadata, toolUseId, parentAgentId))
|
|
70
|
-
matches.push(name.slice(6, -10));
|
|
71
|
-
}
|
|
72
|
-
return uniqueChildIdentity(matches);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function metadataMatches(
|
|
76
|
-
metadata: unknown,
|
|
77
|
-
toolUseId: string,
|
|
78
|
-
parentAgentId: string | null,
|
|
79
|
-
): boolean {
|
|
80
|
-
return (
|
|
81
|
-
isRecord(metadata) &&
|
|
82
|
-
metadata.toolUseId === toolUseId &&
|
|
83
|
-
(metadata.parentAgentId ?? null) === parentAgentId
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
function uniqueChildIdentity(matches: string[]): string {
|
|
87
|
-
const [id] = matches;
|
|
88
|
-
if (matches.length !== 1 || !identity(id))
|
|
89
|
-
throw new Error("Claude child identity is unavailable");
|
|
90
|
-
return id;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function childId(
|
|
94
|
-
entry: Entry,
|
|
95
|
-
state: State,
|
|
96
|
-
toolUseId: string,
|
|
97
|
-
parentAgentId: string | null,
|
|
98
|
-
): Promise<string> {
|
|
99
|
-
const result = isRecord(entry.toolUseResult) ? entry.toolUseResult : null;
|
|
100
|
-
if (result) {
|
|
101
|
-
if (result.status !== "completed" || !identity(result.agentId))
|
|
102
|
-
throw new Error("Claude child did not complete");
|
|
103
|
-
return result.agentId;
|
|
104
|
-
}
|
|
105
|
-
return sidecarChild(state, toolUseId, parentAgentId);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function childSession(state: State, agentId: string) {
|
|
109
|
-
const directory = join(state.projectDir, state.sessionId, "subagents");
|
|
110
|
-
const path = join(directory, `agent-${agentId}.jsonl`);
|
|
111
|
-
if ((await realpath(path)) !== path)
|
|
112
|
-
throw new Error("Claude child session is redirected");
|
|
113
|
-
return readSession(path);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function skillCall(block: Entry, ancestorToolUseId: string | null) {
|
|
117
|
-
const invocation = isRecord(block.input) ? block.input.skill : null;
|
|
118
|
-
if (!label(invocation)) throw new Error("invalid Claude Skill call");
|
|
119
|
-
return ancestorToolUseId
|
|
120
|
-
? {
|
|
121
|
-
ancestorToolUseId,
|
|
122
|
-
skill: invocation.slice(invocation.lastIndexOf(":") + 1),
|
|
123
|
-
invocation,
|
|
124
|
-
}
|
|
125
|
-
: null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
type VisitContext = {
|
|
129
|
-
state: State;
|
|
130
|
-
agentId: string | null;
|
|
131
|
-
ancestorToolUseId: string | null;
|
|
132
|
-
pending: Set<string>;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
async function visit(
|
|
136
|
-
text: string,
|
|
137
|
-
state: State,
|
|
138
|
-
agentId: string | null,
|
|
139
|
-
ancestorToolUseId: string | null,
|
|
140
|
-
): Promise<void> {
|
|
141
|
-
const context: VisitContext = {
|
|
142
|
-
state,
|
|
143
|
-
agentId,
|
|
144
|
-
ancestorToolUseId,
|
|
145
|
-
pending: new Set(),
|
|
146
|
-
};
|
|
147
|
-
for (const entry of entries(text)) {
|
|
148
|
-
if (entry.type !== "assistant" && entry.type !== "user") continue;
|
|
149
|
-
await visitEntry(entry, context);
|
|
150
|
-
}
|
|
151
|
-
if (context.pending.size) throw new Error("incomplete Claude Agent graph");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async function visitEntry(entry: Entry, context: VisitContext): Promise<void> {
|
|
155
|
-
if (
|
|
156
|
-
entry.sessionId !== context.state.sessionId ||
|
|
157
|
-
(entry.agentId ?? null) !== context.agentId
|
|
158
|
-
)
|
|
159
|
-
throw new Error("Claude session identity mismatch");
|
|
160
|
-
for (const block of blocks(entry)) await visitBlock(entry, block, context);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function visitBlock(
|
|
164
|
-
entry: Entry,
|
|
165
|
-
block: Entry,
|
|
166
|
-
context: VisitContext,
|
|
167
|
-
): Promise<void> {
|
|
168
|
-
if (entry.type === "assistant" && block.type === "tool_use")
|
|
169
|
-
assistantBlock(block, context);
|
|
170
|
-
if (acceptedChildResult(block, entry, context.pending))
|
|
171
|
-
await visitChild(entry, block, context);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function acceptedChildResult(
|
|
175
|
-
block: Entry,
|
|
176
|
-
entry: Entry,
|
|
177
|
-
pending: Set<string>,
|
|
178
|
-
): block is Entry & { tool_use_id: string } {
|
|
179
|
-
return (
|
|
180
|
-
entry.type === "user" &&
|
|
181
|
-
block.type === "tool_result" &&
|
|
182
|
-
identity(block.tool_use_id) &&
|
|
183
|
-
pending.has(block.tool_use_id)
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function assistantBlock(block: Entry, context: VisitContext): void {
|
|
188
|
-
if (block.name === "Skill") retainSkill(block, context);
|
|
189
|
-
if (block.name === "Agent" || block.name === "Task")
|
|
190
|
-
registerAgent(block, context.pending);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function retainSkill(block: Entry, context: VisitContext): void {
|
|
194
|
-
const call = skillCall(block, context.ancestorToolUseId);
|
|
195
|
-
if (call) context.state.calls.push(call);
|
|
196
|
-
if (context.state.calls.length > MAX_SKILLS)
|
|
197
|
-
throw new Error("excessive Claude Skill calls");
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function registerAgent(block: Entry, pending: Set<string>): void {
|
|
201
|
-
if (!identity(block.id) || pending.has(block.id))
|
|
202
|
-
throw new Error("invalid Claude Agent call");
|
|
203
|
-
pending.add(block.id);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function visitChild(
|
|
207
|
-
entry: Entry,
|
|
208
|
-
block: Entry & { tool_use_id: string },
|
|
209
|
-
context: VisitContext,
|
|
210
|
-
): Promise<void> {
|
|
211
|
-
if (block.is_error === true) throw new Error("Claude child call failed");
|
|
212
|
-
const { state, agentId, ancestorToolUseId, pending } = context;
|
|
213
|
-
const child = await childId(entry, state, block.tool_use_id, agentId);
|
|
214
|
-
if (state.seen.has(child) || state.seen.size >= MAX_AGENTS)
|
|
215
|
-
throw new Error("repeated Claude child");
|
|
216
|
-
state.seen.add(child);
|
|
217
|
-
await visit(
|
|
218
|
-
await childSession(state, child),
|
|
219
|
-
state,
|
|
220
|
-
child,
|
|
221
|
-
ancestorToolUseId ?? block.tool_use_id,
|
|
222
|
-
);
|
|
223
|
-
pending.delete(block.tool_use_id);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export async function claudeNestedSkillsObservation(
|
|
227
|
-
stream: string,
|
|
228
|
-
configRoot: string,
|
|
229
|
-
workspace: string,
|
|
230
|
-
) {
|
|
231
|
-
const unavailable = {
|
|
232
|
-
id: "sevro.claude.nested-skills" as const,
|
|
233
|
-
completeness: "partial" as const,
|
|
234
|
-
data: { method: "native_session_graph", calls: [] as NestedSkill[] },
|
|
235
|
-
};
|
|
236
|
-
try {
|
|
237
|
-
const sessions = [
|
|
238
|
-
...new Set(
|
|
239
|
-
entries(stream)
|
|
240
|
-
.filter((entry) => entry.type === "result")
|
|
241
|
-
.map((entry) => entry.session_id),
|
|
242
|
-
),
|
|
243
|
-
];
|
|
244
|
-
if (sessions.length !== 1 || !identity(sessions[0])) return unavailable;
|
|
245
|
-
const canonicalWorkspace = await realpath(workspace);
|
|
246
|
-
const canonicalConfigRoot = await realpath(configRoot);
|
|
247
|
-
const projectDir = join(
|
|
248
|
-
canonicalConfigRoot,
|
|
249
|
-
"projects",
|
|
250
|
-
canonicalWorkspace.replace(/[^A-Za-z0-9]/g, "-"),
|
|
251
|
-
);
|
|
252
|
-
const state: State = {
|
|
253
|
-
sessionId: sessions[0],
|
|
254
|
-
projectDir,
|
|
255
|
-
seen: new Set(),
|
|
256
|
-
calls: [],
|
|
257
|
-
};
|
|
258
|
-
await visit(
|
|
259
|
-
await readSession(join(projectDir, `${state.sessionId}.jsonl`)),
|
|
260
|
-
state,
|
|
261
|
-
null,
|
|
262
|
-
null,
|
|
263
|
-
);
|
|
264
|
-
return {
|
|
265
|
-
id: "sevro.claude.nested-skills" as const,
|
|
266
|
-
completeness: "complete" as const,
|
|
267
|
-
data: { method: "native_session_graph", calls: state.calls },
|
|
268
|
-
};
|
|
269
|
-
} catch {
|
|
270
|
-
return unavailable;
|
|
271
|
-
}
|
|
272
|
-
}
|