@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.
- package/CONTRIBUTING.md +170 -0
- package/LICENSE +63 -136
- package/README.md +83 -66
- package/docs/README.md +44 -0
- package/docs/acknowledgements.md +14 -0
- package/docs/architecture.md +146 -0
- package/docs/assets/README.md +27 -0
- package/docs/assets/sevro-logo.png +0 -0
- package/docs/assets/sevro-symbol.svg +1 -0
- package/docs/assets/sevro-wordmark.svg +1 -0
- package/docs/builtin-graders-v1.md +41 -13
- package/docs/codex-host.md +184 -52
- package/docs/contracts.md +19 -0
- package/docs/creating-cases.md +139 -0
- package/docs/development-cli.md +190 -30
- package/docs/documentation-quality.md +61 -0
- package/docs/evaluation-workflow.md +110 -0
- package/docs/evidence/code-mode-controls-71.md +64 -0
- package/docs/evidence/code-mode-programs-85.md +64 -0
- package/docs/evidence/codex-state-73/README.md +204 -0
- package/docs/evidence/codex-state-73/darrow-migration.patch +182 -0
- package/docs/evidence/codex-state-73/results.json +661 -0
- package/docs/evidence/local-http-74.md +104 -0
- package/docs/evidence/runtime-startup-30/README.md +185 -0
- package/docs/evidence/runtime-startup-30/after.json +319 -0
- package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +2 -0
- package/docs/evidence/runtime-startup-30/before.json +403 -0
- package/docs/evidence/runtime-startup-30/consumer-smoke.json +63 -0
- package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +3 -0
- package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +3 -0
- package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +20 -0
- package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +2 -0
- package/docs/evidence/skill-reads-70/results.json +823 -0
- package/docs/evidence.md +21 -0
- package/docs/extension-protocol-v1.md +50 -16
- package/docs/fixture-cleanup-validation.md +3 -0
- package/docs/getting-started.md +102 -0
- package/docs/guide-evaluation.md +89 -0
- package/docs/guide-verification.json +239 -0
- package/docs/identity-v1.md +35 -1
- package/docs/installing.md +88 -0
- package/docs/licenses/BUSL-1.1-0.1.0-rc.2.txt +159 -0
- package/docs/licensing.md +42 -0
- package/docs/native-controls.md +76 -5
- package/docs/native-hosts.md +228 -0
- package/docs/reading-results.md +141 -0
- package/docs/recipes.md +489 -0
- package/docs/releases.md +81 -40
- package/docs/report-v1.md +29 -1
- package/docs/results-v1.md +180 -0
- package/docs/run-settings-v1.md +147 -0
- package/docs/running-evaluations.md +31 -0
- package/docs/runtime-v1.md +614 -0
- package/docs/specs/repository-guide.md +58 -0
- package/docs/troubleshooting.md +30 -0
- package/docs/typescript-quality.md +234 -0
- package/docs/usability-study.md +114 -0
- package/examples/basic/host.ts +2 -2
- 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 +39 -6
- package/schemas/report-v1.schema.json +9 -0
- package/schemas/run-evidence-v1.schema.json +2 -1
- package/schemas/runtime-v1.schema.json +193 -0
- package/src/advisory.ts +10 -5
- package/src/candidate-artifacts.ts +208 -0
- package/src/candidate-codex-state.ts +185 -0
- package/src/case-validation.ts +105 -0
- package/src/cli/cancellation.ts +20 -0
- package/src/cli/command.ts +81 -0
- package/src/cli/completion.ts +15 -0
- package/src/cli/demo.ts +59 -0
- package/src/cli/evaluation.ts +55 -0
- package/src/cli/extension.ts +79 -0
- package/src/cli/help.ts +222 -0
- package/src/cli/hosts.ts +59 -0
- package/src/cli/init-settings.ts +30 -0
- package/src/cli/init.ts +111 -0
- package/src/cli/invocation.ts +627 -0
- package/src/cli/loading.ts +151 -0
- package/src/cli/preparation.ts +163 -0
- package/src/cli/presentation.ts +91 -0
- package/src/cli/progress.ts +6 -0
- package/src/cli/run-manifest.ts +133 -0
- package/src/cli/run-options.ts +101 -0
- package/src/cli/run-overrides.ts +83 -0
- package/src/cli/settings.ts +160 -0
- package/src/cli/summary.ts +43 -0
- package/src/cli.ts +13 -843
- package/src/contract-types.ts +94 -0
- package/src/engine.ts +19 -2373
- package/src/evaluation/advisory.ts +150 -0
- package/src/evaluation/check-plan.ts +510 -0
- package/src/evaluation/collection.ts +88 -0
- package/src/evaluation/context.ts +10 -0
- package/src/evaluation/grading-host.ts +39 -0
- package/src/evaluation/grading.ts +258 -0
- package/src/evaluation/invocation-plan.ts +548 -0
- package/src/evaluation/plan.ts +736 -0
- package/src/evaluation/progress.ts +19 -0
- package/src/evaluation/resources.ts +241 -0
- package/src/evaluation/run.ts +206 -0
- package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +3 -1
- package/src/evaluation/semantic.ts +312 -0
- package/src/evaluation/trial.ts +866 -0
- package/src/evaluation/types.ts +207 -0
- package/src/evidence/host-results.ts +92 -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 +228 -148
- package/src/extension-session.ts +326 -195
- package/src/fixtures/advisory.ts +338 -0
- package/src/fixtures/bin.ts +57 -0
- package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
- package/src/fixtures/create.ts +187 -0
- package/src/fixtures/generated.ts +300 -0
- package/src/fixtures/git-hooks.ts +24 -0
- package/src/fixtures/preparation.ts +270 -0
- package/src/{repository-fixture.ts → fixtures/repository.ts} +207 -85
- package/src/fixtures/setup.ts +224 -0
- package/src/fixtures/tools.ts +88 -0
- package/src/fixtures/workspace-state.ts +212 -0
- package/src/generated/cli-result.cjs +2 -0
- package/src/generated/cli-result.d.cts +3 -0
- package/src/generated/extension.cjs +2 -0
- package/src/generated/extension.d.cts +3 -0
- package/src/generated/report.cjs +2 -0
- package/src/generated/report.d.cts +3 -0
- package/src/generated/run-evidence.cjs +2 -0
- package/src/generated/run-evidence.d.cts +3 -0
- package/src/generated/runtime.cjs +2 -0
- package/src/generated/runtime.d.cts +3 -0
- package/src/graders/artifact.ts +56 -23
- package/src/graders/git-head.ts +79 -75
- package/src/graders/output.ts +222 -140
- package/src/graders/semantic.ts +102 -43
- package/src/graders/shell.ts +379 -207
- package/src/host-diagnostic-redaction.ts +52 -0
- package/src/host-diagnostics.ts +319 -0
- package/src/hosts/claude-continuation.ts +63 -18
- package/src/hosts/claude-credential.ts +43 -62
- package/src/hosts/claude-events.ts +86 -51
- package/src/hosts/claude-keychain.ts +59 -0
- package/src/hosts/claude-native-goal.ts +57 -33
- package/src/hosts/claude-process.ts +111 -0
- package/src/hosts/claude-repository-invocation.ts +208 -117
- package/src/hosts/claude-settings.ts +240 -21
- package/src/hosts/claude-skill-activations.ts +166 -0
- package/src/hosts/claude-state.ts +403 -0
- package/src/hosts/claude-tool-calls.ts +171 -106
- package/src/hosts/claude.ts +561 -451
- package/src/hosts/codex-app-server.ts +326 -178
- package/src/hosts/codex-configuration.ts +44 -18
- package/src/hosts/codex-events.ts +97 -60
- package/src/hosts/codex-exec-controls.ts +516 -0
- package/src/hosts/codex-invocation.ts +70 -0
- package/src/hosts/codex-marketplace.ts +283 -0
- package/src/hosts/codex-native-calls.ts +546 -500
- package/src/hosts/codex-process.ts +202 -0
- package/src/hosts/codex-profile.ts +257 -28
- package/src/hosts/codex-session-files.ts +106 -0
- package/src/hosts/codex-state.ts +492 -0
- package/src/hosts/codex.ts +783 -832
- package/src/hosts/isolation-common.ts +94 -0
- package/src/hosts/isolation-roots.ts +54 -27
- package/src/hosts/isolation.ts +10 -0
- package/src/hosts/linux-sandbox.ts +159 -0
- package/src/hosts/mac-sandbox.ts +187 -61
- package/src/hosts/native-controls.ts +152 -62
- package/src/hosts/process-lifecycle.ts +37 -0
- package/src/hosts/protected-worktrees.ts +51 -19
- package/src/hosts/skill-hook-catalog.ts +131 -0
- package/src/hosts/skill-hook-handler.ts +374 -0
- package/src/hosts/skill-hooks.ts +580 -0
- package/src/hosts/workspace-fingerprint.ts +55 -35
- package/src/identity.ts +102 -48
- package/src/instrumentation.ts +109 -48
- package/src/native-transcript-state.ts +131 -0
- package/src/native-transcript-view.ts +90 -0
- package/src/native-transcripts.ts +286 -0
- package/src/process-lifecycle.ts +150 -0
- package/src/provenance.ts +161 -81
- package/src/prune.ts +215 -0
- package/src/report.ts +172 -140
- package/src/resolved-case.ts +33 -0
- package/src/results-lock.ts +29 -0
- package/src/results.ts +133 -86
- package/src/run-owner.ts +154 -63
- package/src/runtime/config.ts +456 -0
- package/src/runtime/discovery-apple.ts +90 -0
- package/src/runtime/discovery-providers.ts +63 -0
- package/src/runtime/discovery.ts +70 -0
- package/src/runtime/hook-processes.ts +97 -0
- package/src/runtime/hooks.ts +476 -0
- package/src/runtime/paths.ts +313 -0
- package/src/runtime/query.ts +85 -0
- package/src/runtime/seeds.ts +134 -0
- package/src/runtime/shared-cache.ts +143 -0
- package/src/runtime/state.ts +143 -0
- package/src/schema-types.ts +198 -0
- package/src/schema.ts +15 -19
- package/src/transcript-retention.ts +146 -0
- package/src/value-guards.ts +20 -0
- package/src/advisory-fixture.ts +0 -282
- package/src/fixture-bin.ts +0 -87
- package/src/fixture-setup.ts +0 -163
- package/src/generated-fixture.ts +0 -220
- package/src/git-hooks.ts +0 -63
- package/src/hosts/claude-nested-skills.ts +0 -218
- package/src/hosts/codex-command-output.ts +0 -399
- package/src/hosts/codex-executor-command.ts +0 -195
- package/src/hosts/codex-skill-reads.ts +0 -391
- package/src/preparation.ts +0 -211
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
import { isRecord } from "../value-guards";
|
|
1
3
|
import { lstat, readFile, realpath, stat } from "node:fs/promises";
|
|
2
4
|
import { dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
3
5
|
import { parse } from "smol-toml";
|
|
@@ -9,8 +11,8 @@ export async function configurationRoot(root: string): Promise<string> {
|
|
|
9
11
|
if (!(await stat(canonical)).isDirectory())
|
|
10
12
|
throw new Error("not a directory");
|
|
11
13
|
return canonical;
|
|
12
|
-
} catch {
|
|
13
|
-
throw new Error(`configuration root is unreadable: ${root}
|
|
14
|
+
} catch (cause) {
|
|
15
|
+
throw new Error(`configuration root is unreadable: ${root}`, { cause });
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -34,6 +36,15 @@ export async function codexAgentConcurrency(
|
|
|
34
36
|
root: string,
|
|
35
37
|
): Promise<number | null> {
|
|
36
38
|
const configPath = join(root, ".codex", "config.toml");
|
|
39
|
+
const source = await readConfiguration(root, configPath);
|
|
40
|
+
if (source === null) return null;
|
|
41
|
+
return parseAgentConcurrency(source, configPath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function readConfiguration(
|
|
45
|
+
root: string,
|
|
46
|
+
configPath: string,
|
|
47
|
+
): Promise<string | null> {
|
|
37
48
|
let source: string;
|
|
38
49
|
let declared = false;
|
|
39
50
|
try {
|
|
@@ -41,17 +52,7 @@ export async function codexAgentConcurrency(
|
|
|
41
52
|
const info = await lstat(configPath);
|
|
42
53
|
declared = true;
|
|
43
54
|
const canonical = await realpath(configPath);
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
!info.isFile() ||
|
|
47
|
-
info.size > 64 * 1024 ||
|
|
48
|
-
location === ".." ||
|
|
49
|
-
location.startsWith(`..${sep}`) ||
|
|
50
|
-
isAbsolute(location)
|
|
51
|
-
)
|
|
52
|
-
throw new Error(
|
|
53
|
-
"configuration must be a contained regular file up to 64 KiB",
|
|
54
|
-
);
|
|
55
|
+
validateConfigurationFile(info, relative(root, canonical));
|
|
55
56
|
source = await readFile(configPath, "utf8");
|
|
56
57
|
} catch (error) {
|
|
57
58
|
if (!declared && (error as NodeJS.ErrnoException).code === "ENOENT")
|
|
@@ -60,18 +61,30 @@ export async function codexAgentConcurrency(
|
|
|
60
61
|
cause: error,
|
|
61
62
|
});
|
|
62
63
|
}
|
|
64
|
+
return source;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseAgentConcurrency(
|
|
68
|
+
source: string,
|
|
69
|
+
configPath: string,
|
|
70
|
+
): number | null {
|
|
63
71
|
let config: Record<string, unknown>;
|
|
64
72
|
try {
|
|
65
73
|
config = parse(source, { integersAsBigInt: true });
|
|
66
|
-
} catch {
|
|
67
|
-
throw new Error(`Invalid Codex configuration: ${configPath}
|
|
74
|
+
} catch (cause) {
|
|
75
|
+
throw new Error(`Invalid Codex configuration: ${configPath}`, { cause });
|
|
68
76
|
}
|
|
69
77
|
const agents = config.agents;
|
|
70
78
|
if (agents === undefined) return null;
|
|
71
|
-
if (!
|
|
79
|
+
if (!isRecord(agents))
|
|
72
80
|
throw new Error(`Expected an agents table in ${configPath}`);
|
|
73
|
-
|
|
74
|
-
.max_concurrent_threads_per_session
|
|
81
|
+
return concurrencyLimit(
|
|
82
|
+
agents.max_concurrent_threads_per_session,
|
|
83
|
+
configPath,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function concurrencyLimit(limit: unknown, configPath: string): number | null {
|
|
75
88
|
if (limit === undefined) return null;
|
|
76
89
|
if (
|
|
77
90
|
typeof limit !== "bigint" ||
|
|
@@ -83,3 +96,16 @@ export async function codexAgentConcurrency(
|
|
|
83
96
|
);
|
|
84
97
|
return Number(limit);
|
|
85
98
|
}
|
|
99
|
+
|
|
100
|
+
function validateConfigurationFile(info: Stats, location: string): void {
|
|
101
|
+
if (!info.isFile() || info.size > 64 * 1024 || escapedRoot(location))
|
|
102
|
+
throw new Error(
|
|
103
|
+
"configuration must be a contained regular file up to 64 KiB",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function escapedRoot(location: string): boolean {
|
|
108
|
+
return (
|
|
109
|
+
location === ".." || location.startsWith(`..${sep}`) || isAbsolute(location)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -1,32 +1,89 @@
|
|
|
1
|
+
import { isRecord } from "../value-guards";
|
|
1
2
|
const MAX_EVENT_STREAM_BYTES = 8 * 1024 * 1024;
|
|
2
3
|
|
|
3
4
|
export class CodexEventError extends Error {
|
|
4
|
-
constructor(message: string) {
|
|
5
|
-
super(message);
|
|
5
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
6
|
+
super(message, options);
|
|
6
7
|
this.name = "CodexEventError";
|
|
7
8
|
}
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
function record(value: unknown): value is Record<string, unknown> {
|
|
11
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
11
|
function usage(
|
|
15
12
|
value: unknown,
|
|
16
13
|
): { inputTokens: number; outputTokens: number } | null {
|
|
17
|
-
if (!
|
|
14
|
+
if (!isRecord(value)) return null;
|
|
18
15
|
const input = value.input_tokens;
|
|
19
16
|
const output = value.output_tokens;
|
|
17
|
+
if (!nonnegativeInteger(input) || !nonnegativeInteger(output)) return null;
|
|
18
|
+
return { inputTokens: input, outputTokens: output };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function nonnegativeInteger(value: unknown): value is number {
|
|
22
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface TurnState {
|
|
26
|
+
threadId: string | null;
|
|
27
|
+
completed: boolean;
|
|
28
|
+
failed: boolean;
|
|
29
|
+
finalMessage: string | null;
|
|
30
|
+
tokenUsage: ReturnType<typeof usage>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseEvent(line: string): Record<string, unknown> {
|
|
34
|
+
let parsed: unknown;
|
|
35
|
+
try {
|
|
36
|
+
parsed = JSON.parse(line);
|
|
37
|
+
} catch (cause) {
|
|
38
|
+
throw new CodexEventError("Codex event stream contains invalid JSONL", {
|
|
39
|
+
cause,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (!isRecord(parsed) || typeof parsed.type !== "string")
|
|
43
|
+
throw new CodexEventError("Codex event stream contains an invalid event");
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function startThread(state: TurnState, event: Record<string, unknown>): void {
|
|
20
48
|
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
typeof output !== "number" ||
|
|
25
|
-
!Number.isSafeInteger(output) ||
|
|
26
|
-
output < 0
|
|
49
|
+
state.threadId !== null ||
|
|
50
|
+
typeof event.thread_id !== "string" ||
|
|
51
|
+
!event.thread_id
|
|
27
52
|
)
|
|
28
|
-
|
|
29
|
-
|
|
53
|
+
throw new CodexEventError("Codex event stream has an invalid thread start");
|
|
54
|
+
state.threadId = event.thread_id;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function completeTurn(state: TurnState, event: Record<string, unknown>): void {
|
|
58
|
+
if (!state.threadId || state.completed || state.failed)
|
|
59
|
+
throw new CodexEventError(
|
|
60
|
+
"Codex event stream has an invalid turn completion",
|
|
61
|
+
);
|
|
62
|
+
state.completed = true;
|
|
63
|
+
state.tokenUsage = usage(event.usage);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function completeItem(state: TurnState, item: unknown): void {
|
|
67
|
+
if (!isRecord(item) || item.type !== "agent_message" || state.completed)
|
|
68
|
+
return;
|
|
69
|
+
state.finalMessage = typeof item.text === "string" ? item.text : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function applyEvent(state: TurnState, event: Record<string, unknown>): void {
|
|
73
|
+
switch (event.type) {
|
|
74
|
+
case "thread.started":
|
|
75
|
+
startThread(state, event);
|
|
76
|
+
break;
|
|
77
|
+
case "item.completed":
|
|
78
|
+
completeItem(state, event.item);
|
|
79
|
+
break;
|
|
80
|
+
case "turn.completed":
|
|
81
|
+
completeTurn(state, event);
|
|
82
|
+
break;
|
|
83
|
+
case "turn.failed":
|
|
84
|
+
state.failed = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
30
87
|
}
|
|
31
88
|
|
|
32
89
|
export interface CodexEventSummary {
|
|
@@ -45,58 +102,38 @@ export function summarizeCodexEvents(
|
|
|
45
102
|
): CodexEventSummary {
|
|
46
103
|
if (Buffer.byteLength(stream, "utf8") > MAX_EVENT_STREAM_BYTES)
|
|
47
104
|
throw new CodexEventError("Codex event stream exceeds 8 MiB");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
105
|
+
const state: TurnState = {
|
|
106
|
+
threadId: null,
|
|
107
|
+
completed: false,
|
|
108
|
+
failed: false,
|
|
109
|
+
finalMessage: null,
|
|
110
|
+
tokenUsage: null,
|
|
111
|
+
};
|
|
54
112
|
for (const line of stream.split("\n")) {
|
|
55
113
|
if (!line.trim()) continue;
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
parsed = JSON.parse(line);
|
|
59
|
-
} catch {
|
|
60
|
-
throw new CodexEventError("Codex event stream contains invalid JSONL");
|
|
61
|
-
}
|
|
62
|
-
if (!record(parsed) || typeof parsed.type !== "string")
|
|
63
|
-
throw new CodexEventError("Codex event stream contains an invalid event");
|
|
64
|
-
if (parsed.type === "thread.started") {
|
|
65
|
-
if (
|
|
66
|
-
threadId !== null ||
|
|
67
|
-
typeof parsed.thread_id !== "string" ||
|
|
68
|
-
!parsed.thread_id
|
|
69
|
-
)
|
|
70
|
-
throw new CodexEventError(
|
|
71
|
-
"Codex event stream has an invalid thread start",
|
|
72
|
-
);
|
|
73
|
-
threadId = parsed.thread_id;
|
|
74
|
-
}
|
|
75
|
-
if (parsed.type === "item.completed" && record(parsed.item)) {
|
|
76
|
-
if (parsed.item.type === "agent_message" && !completed) {
|
|
77
|
-
finalMessage =
|
|
78
|
-
typeof parsed.item.text === "string" ? parsed.item.text : null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (parsed.type === "turn.completed") {
|
|
82
|
-
if (!threadId || completed || failed)
|
|
83
|
-
throw new CodexEventError(
|
|
84
|
-
"Codex event stream has an invalid turn completion",
|
|
85
|
-
);
|
|
86
|
-
completed = true;
|
|
87
|
-
usageComplete = usage(parsed.usage) !== null;
|
|
88
|
-
tokenUsage = usage(parsed.usage);
|
|
89
|
-
}
|
|
90
|
-
if (parsed.type === "turn.failed") failed = true;
|
|
114
|
+
applyEvent(state, parseEvent(line));
|
|
91
115
|
}
|
|
116
|
+
return summarizeTurn(state, exitCode);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function summarizeTurn(state: TurnState, exitCode: number): CodexEventSummary {
|
|
120
|
+
const { threadId, completed, failed, finalMessage, tokenUsage } = state;
|
|
92
121
|
if (!threadId)
|
|
93
122
|
throw new CodexEventError("Codex event stream has no thread start");
|
|
123
|
+
const complete = exitCode === 0 && completed && !failed;
|
|
94
124
|
return {
|
|
95
125
|
threadId,
|
|
96
|
-
complete
|
|
126
|
+
complete,
|
|
97
127
|
finalMessage,
|
|
98
|
-
|
|
99
|
-
outputTokens: tokenUsage?.outputTokens ?? null,
|
|
100
|
-
usageComplete: exitCode === 0 && completed && !failed && usageComplete,
|
|
128
|
+
...summarizeUsage(tokenUsage, complete),
|
|
101
129
|
};
|
|
102
130
|
}
|
|
131
|
+
|
|
132
|
+
function summarizeUsage(
|
|
133
|
+
tokenUsage: ReturnType<typeof usage>,
|
|
134
|
+
complete: boolean,
|
|
135
|
+
) {
|
|
136
|
+
if (tokenUsage === null)
|
|
137
|
+
return { inputTokens: null, outputTokens: null, usageComplete: false };
|
|
138
|
+
return { ...tokenUsage, usageComplete: complete };
|
|
139
|
+
}
|