@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,399 +0,0 @@
|
|
|
1
|
-
import { isAbsolute, normalize } from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import {
|
|
4
|
-
literalExecutorCommand,
|
|
5
|
-
type LiteralExecutorCommand,
|
|
6
|
-
} from "./codex-executor-command";
|
|
7
|
-
|
|
8
|
-
interface NativeEntry {
|
|
9
|
-
ordinal: number;
|
|
10
|
-
payload: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface YieldedChunk {
|
|
14
|
-
process: string;
|
|
15
|
-
id: string;
|
|
16
|
-
output: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface RecoveredCommandOutput {
|
|
20
|
-
output: string;
|
|
21
|
-
chunks: number;
|
|
22
|
-
completedCall?: boolean;
|
|
23
|
-
literalCommandCall?: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function record(value: unknown): value is Record<string, unknown> {
|
|
27
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function processId(value: unknown): string | undefined {
|
|
31
|
-
if (typeof value !== "string" && typeof value !== "number") return undefined;
|
|
32
|
-
return /^(?:[1-9][0-9]*)$/.test(String(value)) ? String(value) : undefined;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function yieldedChunk(
|
|
36
|
-
value: Record<string, unknown>,
|
|
37
|
-
): YieldedChunk | undefined {
|
|
38
|
-
const process = processId(value.session_id);
|
|
39
|
-
if (!process || value.exit_code != null || typeof value.output !== "string")
|
|
40
|
-
return undefined;
|
|
41
|
-
if (
|
|
42
|
-
typeof value.chunk_id !== "string" ||
|
|
43
|
-
!/^[\w-]{1,64}$/.test(value.chunk_id)
|
|
44
|
-
)
|
|
45
|
-
return undefined;
|
|
46
|
-
if (
|
|
47
|
-
typeof value.wall_time_seconds !== "number" ||
|
|
48
|
-
!Number.isFinite(value.wall_time_seconds) ||
|
|
49
|
-
value.wall_time_seconds < 0
|
|
50
|
-
)
|
|
51
|
-
return undefined;
|
|
52
|
-
return { process, id: value.chunk_id, output: value.output };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Decode only host result/content envelopes, never arbitrary object properties,
|
|
56
|
-
// prose containing JSON, or JSON printed inside a command's output.
|
|
57
|
-
function resultChunks(value: unknown, depth = 0): YieldedChunk[] {
|
|
58
|
-
if (depth > 6) return [];
|
|
59
|
-
if (typeof value === "string") {
|
|
60
|
-
try {
|
|
61
|
-
return resultChunks(JSON.parse(value), depth + 1);
|
|
62
|
-
} catch {
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (Array.isArray(value))
|
|
67
|
-
return value.flatMap((part) => resultChunks(part, depth + 1));
|
|
68
|
-
if (!record(value)) return [];
|
|
69
|
-
if (value.type === "input_text" || value.type === "text")
|
|
70
|
-
return resultChunks(value.text, depth + 1);
|
|
71
|
-
const chunk = yieldedChunk(value);
|
|
72
|
-
return chunk ? [chunk] : [];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function execCall(payload: Record<string, unknown>): boolean {
|
|
76
|
-
if (payload.type !== "custom_tool_call" && payload.type !== "function_call")
|
|
77
|
-
return false;
|
|
78
|
-
const namespace = payload.namespace;
|
|
79
|
-
if (namespace !== undefined && namespace !== "functions") return false;
|
|
80
|
-
const names = [
|
|
81
|
-
"exec",
|
|
82
|
-
"exec_command",
|
|
83
|
-
"write_stdin",
|
|
84
|
-
"functions.exec",
|
|
85
|
-
"functions.exec_command",
|
|
86
|
-
"functions.write_stdin",
|
|
87
|
-
];
|
|
88
|
-
return typeof payload.name === "string" && names.includes(payload.name);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function chunksAtEntry(
|
|
92
|
-
entry: NativeEntry,
|
|
93
|
-
entries: NativeEntry[],
|
|
94
|
-
): YieldedChunk[] {
|
|
95
|
-
const { payload, ordinal } = entry;
|
|
96
|
-
if (
|
|
97
|
-
payload.type !== "custom_tool_call_output" &&
|
|
98
|
-
payload.type !== "function_call_output"
|
|
99
|
-
)
|
|
100
|
-
return [];
|
|
101
|
-
if (typeof payload.call_id !== "string") return [];
|
|
102
|
-
const calls = entries.filter(
|
|
103
|
-
(other) =>
|
|
104
|
-
execCall(other.payload) && other.payload.call_id === payload.call_id,
|
|
105
|
-
);
|
|
106
|
-
if (calls.length !== 1 || calls[0]!.ordinal >= ordinal) return [];
|
|
107
|
-
return resultChunks(payload.output);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function commandProcess(entry: NativeEntry): string | undefined {
|
|
111
|
-
const { payload } = entry;
|
|
112
|
-
if (payload.type !== "item_completed" || !record(payload.item))
|
|
113
|
-
return undefined;
|
|
114
|
-
if (
|
|
115
|
-
payload.item.type !== "CommandExecution" ||
|
|
116
|
-
payload.item.source !== "unified_exec_startup"
|
|
117
|
-
)
|
|
118
|
-
return undefined;
|
|
119
|
-
return processId(payload.item.process_id);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function executorTextBlocks(value: unknown): string[] | undefined {
|
|
123
|
-
if (typeof value === "string") {
|
|
124
|
-
try {
|
|
125
|
-
value = JSON.parse(value);
|
|
126
|
-
} catch {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (!Array.isArray(value) || value.length < 2) return undefined;
|
|
131
|
-
const text: string[] = [];
|
|
132
|
-
for (const block of value) {
|
|
133
|
-
if (
|
|
134
|
-
!record(block) ||
|
|
135
|
-
!["input_text", "text"].includes(String(block.type)) ||
|
|
136
|
-
typeof block.text !== "string"
|
|
137
|
-
)
|
|
138
|
-
return undefined;
|
|
139
|
-
text.push(block.text);
|
|
140
|
-
}
|
|
141
|
-
return text;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function structuredExecResult(text: string): boolean {
|
|
145
|
-
try {
|
|
146
|
-
const value: unknown = JSON.parse(text);
|
|
147
|
-
return record(value) && ("output" in value || "session_id" in value);
|
|
148
|
-
} catch {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function completedText(value: unknown): string | undefined {
|
|
154
|
-
const text = executorTextBlocks(value);
|
|
155
|
-
if (!text) return undefined;
|
|
156
|
-
if (
|
|
157
|
-
!/^Script completed\nWall time [0-9]+(?:\.[0-9]+)? seconds\nOutput:\n$/.test(
|
|
158
|
-
text[0]!,
|
|
159
|
-
)
|
|
160
|
-
)
|
|
161
|
-
return undefined;
|
|
162
|
-
const content = text.slice(1);
|
|
163
|
-
// Structured exec chunks keep their process-identity rules above. Do not
|
|
164
|
-
// reinterpret a late yielded chunk as a plain completed-call result.
|
|
165
|
-
return content.some(structuredExecResult) ? undefined : content.join("\n");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function uniqueExecutorCall(
|
|
169
|
-
result: NativeEntry,
|
|
170
|
-
results: NativeEntry[],
|
|
171
|
-
entries: NativeEntry[],
|
|
172
|
-
): NativeEntry | undefined {
|
|
173
|
-
const id = result.payload.call_id;
|
|
174
|
-
if (
|
|
175
|
-
typeof id !== "string" ||
|
|
176
|
-
results.filter((other) => other.payload.call_id === id).length !== 1
|
|
177
|
-
)
|
|
178
|
-
return undefined;
|
|
179
|
-
const calls = entries.filter(
|
|
180
|
-
(entry) => execCall(entry.payload) && entry.payload.call_id === id,
|
|
181
|
-
);
|
|
182
|
-
return calls.length === 1 && calls[0]!.ordinal < result.ordinal
|
|
183
|
-
? calls[0]
|
|
184
|
-
: undefined;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function uniqueCommandIdentity(
|
|
188
|
-
command: NativeEntry,
|
|
189
|
-
entries: NativeEntry[],
|
|
190
|
-
): boolean {
|
|
191
|
-
const process = commandProcess(command);
|
|
192
|
-
if (
|
|
193
|
-
!process ||
|
|
194
|
-
entries.filter((entry) => commandProcess(entry) === process).length !== 1
|
|
195
|
-
)
|
|
196
|
-
return false;
|
|
197
|
-
const item = command.payload.item as Record<string, unknown>;
|
|
198
|
-
return (
|
|
199
|
-
typeof item.id === "string" &&
|
|
200
|
-
entries.filter(
|
|
201
|
-
({ payload }) =>
|
|
202
|
-
payload.type === "item_completed" &&
|
|
203
|
-
record(payload.item) &&
|
|
204
|
-
payload.item.id === item.id,
|
|
205
|
-
).length === 1
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function soleCompletedCommand(
|
|
210
|
-
call: NativeEntry,
|
|
211
|
-
result: NativeEntry,
|
|
212
|
-
entries: NativeEntry[],
|
|
213
|
-
): NativeEntry | undefined {
|
|
214
|
-
const between = entries.filter(
|
|
215
|
-
(entry) => entry.ordinal > call.ordinal && entry.ordinal < result.ordinal,
|
|
216
|
-
);
|
|
217
|
-
if (
|
|
218
|
-
between.some(({ payload }) =>
|
|
219
|
-
["custom_tool_call", "function_call"].includes(String(payload.type)),
|
|
220
|
-
)
|
|
221
|
-
)
|
|
222
|
-
return undefined;
|
|
223
|
-
const commands = between.filter(
|
|
224
|
-
({ payload }) =>
|
|
225
|
-
payload.type === "item_completed" &&
|
|
226
|
-
record(payload.item) &&
|
|
227
|
-
payload.item.type === "CommandExecution",
|
|
228
|
-
);
|
|
229
|
-
return commands.length === 1 && uniqueCommandIdentity(commands[0]!, entries)
|
|
230
|
-
? commands[0]
|
|
231
|
-
: undefined;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function completedCallOutputs(entries: NativeEntry[]): Map<number, string> {
|
|
235
|
-
const outputs = new Map<number, string>();
|
|
236
|
-
const results = entries.filter(({ payload }) =>
|
|
237
|
-
["custom_tool_call_output", "function_call_output"].includes(
|
|
238
|
-
String(payload.type),
|
|
239
|
-
),
|
|
240
|
-
);
|
|
241
|
-
for (const result of results) {
|
|
242
|
-
const call = uniqueExecutorCall(result, results, entries);
|
|
243
|
-
if (!call) continue;
|
|
244
|
-
const command = soleCompletedCommand(call, result, entries);
|
|
245
|
-
if (!command) continue;
|
|
246
|
-
const item = command.payload.item as Record<string, unknown>;
|
|
247
|
-
const literal = literalCallCommand(call.payload);
|
|
248
|
-
if (!literal || !literalCommandMatches(item, literal)) continue;
|
|
249
|
-
const output = completedText(result.payload.output);
|
|
250
|
-
if (
|
|
251
|
-
output !== undefined &&
|
|
252
|
-
!(
|
|
253
|
-
typeof item.aggregated_output === "string" &&
|
|
254
|
-
item.aggregated_output.includes(output)
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
outputs.set(command.ordinal, output);
|
|
258
|
-
}
|
|
259
|
-
return outputs;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function literalCommandMatches(
|
|
263
|
-
item: Record<string, unknown>,
|
|
264
|
-
command: LiteralExecutorCommand,
|
|
265
|
-
): boolean {
|
|
266
|
-
const argv = item.command;
|
|
267
|
-
if (
|
|
268
|
-
!Array.isArray(argv) ||
|
|
269
|
-
argv.length !== 3 ||
|
|
270
|
-
!["-c", "-lc"].includes(argv[1]) ||
|
|
271
|
-
argv[2] !== command.command ||
|
|
272
|
-
(command.shell !== undefined && argv[0] !== command.shell)
|
|
273
|
-
)
|
|
274
|
-
return false;
|
|
275
|
-
if (command.cwd === undefined) return true;
|
|
276
|
-
return matchingCwd(item.cwd, command.cwd);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function matchingCwd(observed: unknown, requested: string): boolean {
|
|
280
|
-
if (!isAbsolute(requested) || typeof observed !== "string") return false;
|
|
281
|
-
try {
|
|
282
|
-
const cwd = observed.startsWith("file:")
|
|
283
|
-
? fileURLToPath(observed)
|
|
284
|
-
: observed;
|
|
285
|
-
return isAbsolute(cwd) && normalize(cwd) === normalize(requested);
|
|
286
|
-
} catch {
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function literalCallOutputs(entries: NativeEntry[]): Map<number, string> {
|
|
292
|
-
const outputs = new Map<number, string>();
|
|
293
|
-
const results = entries.filter(({ payload }) =>
|
|
294
|
-
["custom_tool_call_output", "function_call_output"].includes(
|
|
295
|
-
String(payload.type),
|
|
296
|
-
),
|
|
297
|
-
);
|
|
298
|
-
for (const result of results) {
|
|
299
|
-
const output = completedText(result.payload.output);
|
|
300
|
-
if (output === undefined) continue;
|
|
301
|
-
const call = uniqueExecutorCall(result, results, entries);
|
|
302
|
-
const command = call && literalCallCommand(call.payload);
|
|
303
|
-
if (!call || !command) continue;
|
|
304
|
-
const matches = entries.filter(
|
|
305
|
-
(entry) =>
|
|
306
|
-
commandProcess(entry) &&
|
|
307
|
-
literalCommandMatches(
|
|
308
|
-
entry.payload.item as Record<string, unknown>,
|
|
309
|
-
command,
|
|
310
|
-
),
|
|
311
|
-
);
|
|
312
|
-
if (
|
|
313
|
-
matches.length !== 1 ||
|
|
314
|
-
matches[0]!.ordinal <= result.ordinal ||
|
|
315
|
-
!uniqueCommandIdentity(matches[0]!, entries)
|
|
316
|
-
)
|
|
317
|
-
continue;
|
|
318
|
-
const sameCalls = entries.filter(
|
|
319
|
-
(entry) =>
|
|
320
|
-
execCall(entry.payload) &&
|
|
321
|
-
literalCallCommand(entry.payload)?.command === command.command,
|
|
322
|
-
);
|
|
323
|
-
if (sameCalls.length !== 1) continue;
|
|
324
|
-
outputs.set(matches[0]!.ordinal, output);
|
|
325
|
-
}
|
|
326
|
-
return outputs;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function literalCallCommand(
|
|
330
|
-
payload: Record<string, unknown>,
|
|
331
|
-
): LiteralExecutorCommand | undefined {
|
|
332
|
-
if (
|
|
333
|
-
payload.type !== "custom_tool_call" ||
|
|
334
|
-
!["exec", "functions.exec"].includes(String(payload.name))
|
|
335
|
-
)
|
|
336
|
-
return undefined;
|
|
337
|
-
return literalExecutorCommand(payload.input);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function mergeLiteralOutputs(
|
|
341
|
-
outputs: Map<number, RecoveredCommandOutput>,
|
|
342
|
-
entries: NativeEntry[],
|
|
343
|
-
): void {
|
|
344
|
-
for (const [ordinal, output] of literalCallOutputs(entries)) {
|
|
345
|
-
const earlier = outputs.get(ordinal);
|
|
346
|
-
outputs.set(ordinal, {
|
|
347
|
-
...earlier,
|
|
348
|
-
output: output + (earlier?.output ?? ""),
|
|
349
|
-
chunks: earlier?.chunks ?? 0,
|
|
350
|
-
literalCommandCall: true,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/** Actor-local tool output bound to one native command completion. */
|
|
356
|
-
export function nativeCommandOutputs(
|
|
357
|
-
entries: NativeEntry[],
|
|
358
|
-
): Map<number, RecoveredCommandOutput> {
|
|
359
|
-
const completions = entries.flatMap((entry) => {
|
|
360
|
-
const process = commandProcess(entry);
|
|
361
|
-
return process ? [{ process, entry }] : [];
|
|
362
|
-
});
|
|
363
|
-
const chunks = entries.flatMap((entry) =>
|
|
364
|
-
chunksAtEntry(entry, entries).map((chunk) => ({
|
|
365
|
-
...chunk,
|
|
366
|
-
ordinal: entry.ordinal,
|
|
367
|
-
})),
|
|
368
|
-
);
|
|
369
|
-
const outputs = new Map<number, RecoveredCommandOutput>();
|
|
370
|
-
for (const { process, entry } of completions) {
|
|
371
|
-
// A reused or duplicated process identifier cannot bind a result uniquely.
|
|
372
|
-
if (completions.filter((other) => other.process === process).length !== 1)
|
|
373
|
-
continue;
|
|
374
|
-
const earlier = chunks
|
|
375
|
-
.filter(
|
|
376
|
-
(chunk) => chunk.process === process && chunk.ordinal < entry.ordinal,
|
|
377
|
-
)
|
|
378
|
-
.sort((a, b) => a.ordinal - b.ordinal);
|
|
379
|
-
if (
|
|
380
|
-
!earlier.length ||
|
|
381
|
-
new Set(earlier.map((chunk) => chunk.id)).size !== earlier.length
|
|
382
|
-
)
|
|
383
|
-
continue;
|
|
384
|
-
outputs.set(entry.ordinal, {
|
|
385
|
-
output: earlier.map((chunk) => chunk.output).join(""),
|
|
386
|
-
chunks: earlier.length,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
for (const [ordinal, output] of completedCallOutputs(entries)) {
|
|
390
|
-
const earlier = outputs.get(ordinal);
|
|
391
|
-
outputs.set(ordinal, {
|
|
392
|
-
output: (earlier?.output ?? "") + output,
|
|
393
|
-
chunks: earlier?.chunks ?? 0,
|
|
394
|
-
completedCall: true,
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
mergeLiteralOutputs(outputs, entries);
|
|
398
|
-
return outputs;
|
|
399
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
|
|
3
|
-
export interface LiteralExecutorCommand {
|
|
4
|
-
command: string;
|
|
5
|
-
cwd?: string;
|
|
6
|
-
shell?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function primitive(node: ts.Expression): string | number | boolean | undefined {
|
|
10
|
-
if (ts.isStringLiteralLike(node)) return node.text;
|
|
11
|
-
if (ts.isNumericLiteral(node)) return Number(node.text);
|
|
12
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
13
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function literalOptions(
|
|
18
|
-
node: ts.Expression,
|
|
19
|
-
): Map<string, string | number | boolean> | undefined {
|
|
20
|
-
if (!ts.isObjectLiteralExpression(node)) return undefined;
|
|
21
|
-
const values = new Map<string, string | number | boolean>();
|
|
22
|
-
const allowed = new Set([
|
|
23
|
-
"cmd",
|
|
24
|
-
"workdir",
|
|
25
|
-
"shell",
|
|
26
|
-
"yield_time_ms",
|
|
27
|
-
"max_output_tokens",
|
|
28
|
-
"login",
|
|
29
|
-
"tty",
|
|
30
|
-
]);
|
|
31
|
-
for (const property of node.properties) {
|
|
32
|
-
if (!ts.isPropertyAssignment(property)) return undefined;
|
|
33
|
-
const name = propertyName(property.name);
|
|
34
|
-
const value = primitive(property.initializer);
|
|
35
|
-
if (!name || !allowed.has(name) || values.has(name) || value === undefined)
|
|
36
|
-
return undefined;
|
|
37
|
-
values.set(name, value);
|
|
38
|
-
}
|
|
39
|
-
return values;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function propertyName(name: ts.PropertyName): string | undefined {
|
|
43
|
-
return ts.isIdentifier(name) || ts.isStringLiteral(name)
|
|
44
|
-
? name.text
|
|
45
|
-
: undefined;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function optionalString(value: unknown): value is string | undefined {
|
|
49
|
-
return value === undefined || typeof value === "string";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function commandOptions(
|
|
53
|
-
node: ts.Expression,
|
|
54
|
-
): LiteralExecutorCommand | undefined {
|
|
55
|
-
const values = literalOptions(node);
|
|
56
|
-
if (!values) return undefined;
|
|
57
|
-
const command = values.get("cmd");
|
|
58
|
-
const cwd = values.get("workdir");
|
|
59
|
-
const shell = values.get("shell");
|
|
60
|
-
if (
|
|
61
|
-
typeof command !== "string" ||
|
|
62
|
-
!command ||
|
|
63
|
-
!optionalString(cwd) ||
|
|
64
|
-
!optionalString(shell)
|
|
65
|
-
)
|
|
66
|
-
return undefined;
|
|
67
|
-
return {
|
|
68
|
-
command,
|
|
69
|
-
...(cwd === undefined ? {} : { cwd }),
|
|
70
|
-
...(shell === undefined ? {} : { shell }),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function commandDeclaration(statement: ts.Statement):
|
|
75
|
-
| {
|
|
76
|
-
name: string;
|
|
77
|
-
command: LiteralExecutorCommand;
|
|
78
|
-
}
|
|
79
|
-
| undefined {
|
|
80
|
-
if (
|
|
81
|
-
!ts.isVariableStatement(statement) ||
|
|
82
|
-
!(statement.declarationList.flags & ts.NodeFlags.Const) ||
|
|
83
|
-
statement.declarationList.declarations.length !== 1
|
|
84
|
-
)
|
|
85
|
-
return undefined;
|
|
86
|
-
const declaration = statement.declarationList.declarations[0]!;
|
|
87
|
-
if (
|
|
88
|
-
!ts.isIdentifier(declaration.name) ||
|
|
89
|
-
["tools", "text", "store"].includes(declaration.name.text) ||
|
|
90
|
-
!declaration.initializer
|
|
91
|
-
)
|
|
92
|
-
return undefined;
|
|
93
|
-
const command = awaitedCommand(declaration.initializer);
|
|
94
|
-
return command ? { name: declaration.name.text, command } : undefined;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function awaitedCommand(
|
|
98
|
-
expression: ts.Expression,
|
|
99
|
-
): LiteralExecutorCommand | undefined {
|
|
100
|
-
if (!ts.isAwaitExpression(expression)) return undefined;
|
|
101
|
-
const call = expression.expression;
|
|
102
|
-
if (
|
|
103
|
-
!ts.isCallExpression(call) ||
|
|
104
|
-
call.arguments.length !== 1 ||
|
|
105
|
-
!ts.isPropertyAccessExpression(call.expression) ||
|
|
106
|
-
!ts.isIdentifier(call.expression.expression) ||
|
|
107
|
-
call.expression.expression.text !== "tools" ||
|
|
108
|
-
call.expression.name.text !== "exec_command"
|
|
109
|
-
)
|
|
110
|
-
return undefined;
|
|
111
|
-
return commandOptions(call.arguments[0]!);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function resultProperty(
|
|
115
|
-
node: ts.Expression,
|
|
116
|
-
name: string,
|
|
117
|
-
field: string,
|
|
118
|
-
): boolean {
|
|
119
|
-
return (
|
|
120
|
-
ts.isPropertyAccessExpression(node) &&
|
|
121
|
-
ts.isIdentifier(node.expression) &&
|
|
122
|
-
node.expression.text === name &&
|
|
123
|
-
node.name.text === field
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function outputStatement(
|
|
128
|
-
statement: ts.Statement,
|
|
129
|
-
name: string,
|
|
130
|
-
): string | undefined {
|
|
131
|
-
if (
|
|
132
|
-
!ts.isExpressionStatement(statement) ||
|
|
133
|
-
!ts.isCallExpression(statement.expression) ||
|
|
134
|
-
!ts.isIdentifier(statement.expression.expression)
|
|
135
|
-
)
|
|
136
|
-
return undefined;
|
|
137
|
-
return directOutput(statement.expression, name);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function directOutput(
|
|
141
|
-
call: ts.CallExpression,
|
|
142
|
-
name: string,
|
|
143
|
-
): string | undefined {
|
|
144
|
-
const operation = (call.expression as ts.Identifier).text;
|
|
145
|
-
if (
|
|
146
|
-
operation === "text" &&
|
|
147
|
-
call.arguments.length === 1 &&
|
|
148
|
-
resultProperty(call.arguments[0]!, name, "output")
|
|
149
|
-
)
|
|
150
|
-
return "text";
|
|
151
|
-
if (
|
|
152
|
-
operation === "store" &&
|
|
153
|
-
call.arguments.length === 2 &&
|
|
154
|
-
ts.isStringLiteralLike(call.arguments[0]!) &&
|
|
155
|
-
resultProperty(call.arguments[1]!, name, "session_id")
|
|
156
|
-
)
|
|
157
|
-
return "store";
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Recognize only one unconditional awaited command whose output is returned directly. */
|
|
162
|
-
export function literalExecutorCommand(
|
|
163
|
-
code: unknown,
|
|
164
|
-
): LiteralExecutorCommand | undefined {
|
|
165
|
-
if (typeof code !== "string") return undefined;
|
|
166
|
-
const source = ts.createSourceFile(
|
|
167
|
-
"native-exec.js",
|
|
168
|
-
code,
|
|
169
|
-
ts.ScriptTarget.Latest,
|
|
170
|
-
false,
|
|
171
|
-
ts.ScriptKind.JS,
|
|
172
|
-
);
|
|
173
|
-
const diagnostics = (
|
|
174
|
-
source as ts.SourceFile & { parseDiagnostics?: readonly ts.Diagnostic[] }
|
|
175
|
-
).parseDiagnostics;
|
|
176
|
-
if (
|
|
177
|
-
!diagnostics ||
|
|
178
|
-
diagnostics.length ||
|
|
179
|
-
source.statements.length < 2 ||
|
|
180
|
-
source.statements.length > 3
|
|
181
|
-
)
|
|
182
|
-
return undefined;
|
|
183
|
-
const declaration = commandDeclaration(source.statements[0]!);
|
|
184
|
-
if (!declaration) return undefined;
|
|
185
|
-
const outputs = source.statements
|
|
186
|
-
.slice(1)
|
|
187
|
-
.map((statement) => outputStatement(statement, declaration.name));
|
|
188
|
-
if (
|
|
189
|
-
outputs.some((output) => output === undefined) ||
|
|
190
|
-
outputs.filter((output) => output === "text").length !== 1 ||
|
|
191
|
-
outputs.filter((output) => output === "store").length > 1
|
|
192
|
-
)
|
|
193
|
-
return undefined;
|
|
194
|
-
return declaration.command;
|
|
195
|
-
}
|