@visual-z/xeno 0.2.0 → 0.3.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/CHANGELOG.md +15 -0
- package/README.md +106 -26
- package/docs/install.md +52 -9
- package/docs/providers.md +11 -0
- package/package.json +5 -4
- package/src/apps/approve.ts +3 -3
- package/src/apps/cli.ts +10 -11
- package/src/apps/commands.ts +38 -5
- package/src/apps/completions.ts +73 -19
- package/src/apps/image-draft.ts +46 -0
- package/src/apps/project-trust.ts +2 -42
- package/src/apps/provider-connect.ts +346 -0
- package/src/apps/render.ts +22 -47
- package/src/apps/resources.ts +5 -2
- package/src/apps/shell.ts +298 -1526
- package/src/apps/startup-input.ts +130 -0
- package/src/apps/startup.ts +143 -115
- package/src/apps/ui/app.ts +1383 -0
- package/src/apps/ui/extension-host.ts +194 -0
- package/src/apps/ui/image-cache.ts +78 -0
- package/src/apps/ui/palette.ts +88 -0
- package/src/apps/ui/project.ts +127 -0
- package/src/apps/ui/standalone.ts +146 -0
- package/src/apps/ui/status-model.ts +68 -0
- package/src/apps/ui/surface.ts +84 -0
- package/src/apps/ui/theme.ts +86 -0
- package/src/core/acceptance.ts +107 -0
- package/src/core/action-defaults.ts +47 -295
- package/src/core/benchmark.ts +2 -2
- package/src/core/branch-receipt.ts +24 -0
- package/src/core/checkpoints.ts +64 -0
- package/src/core/churn.ts +2 -0
- package/src/core/command-artifacts.ts +94 -0
- package/src/core/command-contract.ts +35 -0
- package/src/core/completion-review.ts +21 -5
- package/src/core/context-detail.ts +37 -0
- package/src/core/context-policy.ts +74 -1
- package/src/core/continuation.ts +17 -2
- package/src/core/derive.ts +29 -3
- package/src/core/draft-images.ts +56 -0
- package/src/core/environment.ts +145 -0
- package/src/core/evaluation-coverage.ts +74 -0
- package/src/core/event-validation.ts +80 -5
- package/src/core/events.ts +97 -3
- package/src/core/evidence-links.ts +7 -0
- package/src/core/evidence.ts +13 -3
- package/src/core/execution.ts +8 -2
- package/src/core/experiment.ts +133 -0
- package/src/core/export.ts +9 -0
- package/src/core/failure-recovery.ts +173 -0
- package/src/core/file-precondition.ts +72 -0
- package/src/core/handoff.ts +65 -4
- package/src/core/history-query.ts +38 -2
- package/src/core/history.ts +38 -1
- package/src/core/integration-health.ts +34 -0
- package/src/core/keymap.ts +8 -20
- package/src/core/model-picker.ts +64 -0
- package/src/core/native-eval.ts +25 -0
- package/src/core/note-guidance.ts +34 -0
- package/src/core/observation-view.ts +49 -8
- package/src/core/permissions.ts +24 -2
- package/src/core/process-receipt.ts +105 -0
- package/src/core/process-state.ts +37 -0
- package/src/core/project.ts +45 -18
- package/src/core/prompt-policy.ts +37 -0
- package/src/core/query-eval-contract.ts +14 -0
- package/src/core/read-observation.ts +82 -0
- package/src/core/recovery-corpus.ts +17 -0
- package/src/core/recovery-evaluation.ts +60 -0
- package/src/core/recovery-sources.ts +74 -0
- package/src/core/runtime-target.ts +94 -0
- package/src/core/search-observation.ts +97 -0
- package/src/core/session-tree.ts +147 -0
- package/src/core/settings.ts +26 -10
- package/src/core/skills.ts +44 -0
- package/src/core/sql-query.ts +211 -0
- package/src/core/theme.ts +36 -58
- package/src/core/tool-contract.ts +51 -2
- package/src/core/tool-discovery.ts +87 -0
- package/src/core/tool-failure.ts +60 -0
- package/src/core/tool-mode-protocol.ts +25 -0
- package/src/core/transcript.ts +19 -4
- package/src/core/trial-lifecycle.ts +14 -0
- package/src/core/tui-mode.ts +29 -0
- package/src/core/update-check.ts +97 -0
- package/src/core/version.ts +55 -0
- package/src/core/work-state.ts +49 -3
- package/src/core/working-set.ts +91 -0
- package/src/runtime/auth.ts +2 -2
- package/src/runtime/branch-jobs.ts +36 -0
- package/src/runtime/clipboard.ts +15 -4
- package/src/runtime/command-artifacts.ts +64 -0
- package/src/runtime/environment-tools.ts +60 -0
- package/src/runtime/evidence-notes.ts +4 -2
- package/src/runtime/exec-sessions.ts +206 -61
- package/src/runtime/execution-control.ts +1 -0
- package/src/runtime/extension-ui.ts +22 -30
- package/src/runtime/extensions.ts +95 -16
- package/src/runtime/harness-comparison.ts +27 -7
- package/src/runtime/harness-eval.ts +460 -0
- package/src/runtime/history-cache.ts +57 -0
- package/src/runtime/history-tools.ts +62 -12
- package/src/runtime/integrations.ts +210 -0
- package/src/runtime/loop.ts +1021 -268
- package/src/runtime/model-discovery.ts +208 -0
- package/src/runtime/native-eval-fixtures.ts +33 -0
- package/src/runtime/native-eval.ts +143 -0
- package/src/runtime/oauth.ts +1 -1
- package/src/runtime/provider-connect.ts +109 -0
- package/src/runtime/provider-presets.ts +87 -0
- package/src/runtime/query-eval-verifier.ts +28 -0
- package/src/runtime/recovery-coverage.ts +71 -0
- package/src/runtime/recovery-evidence.ts +76 -0
- package/src/runtime/recovery-fixtures.ts +116 -0
- package/src/runtime/runtime-target.ts +149 -0
- package/src/runtime/tool-broker.ts +137 -0
- package/src/runtime/tool-discovery.ts +57 -0
- package/src/runtime/tool-mode-eval.ts +146 -0
- package/src/runtime/tool-queue.ts +77 -0
- package/src/runtime/tools.ts +628 -183
- package/src/runtime/trial-control.ts +45 -0
- package/src/runtime/update-check.ts +47 -0
- package/src/runtime/window-trial.ts +2 -1
- package/src/runtime/working-notes.ts +33 -3
- package/src/runtime/workspace-lifecycle.ts +97 -0
- package/src/store/acceptance.ts +15 -0
- package/src/store/checkpoint-history.ts +43 -0
- package/src/store/command-artifacts.ts +96 -0
- package/src/store/command.ts +65 -37
- package/src/store/file-checkpoints.ts +104 -0
- package/src/store/file-index.ts +13 -1
- package/src/store/git-checkpoints.ts +67 -0
- package/src/store/observations.ts +61 -20
- package/src/store/process-receipts.ts +34 -0
- package/src/store/query-source.ts +13 -8
- package/src/store/recovery.ts +2 -3
- package/src/store/replay.ts +16 -4
- package/src/store/runtime-target.ts +64 -0
- package/src/store/search.ts +171 -132
- package/src/store/session-archive.ts +4 -0
- package/src/store/source-cancellation.ts +6 -0
- package/src/store/update-cache.ts +36 -0
- package/src/store/workspace-observation.ts +113 -0
- package/src/tui/ansi-spans.ts +126 -0
- package/src/tui/style.ts +19 -16
- package/src/tui/ui/atomic-draft.ts +121 -0
- package/src/tui/ui/canvas.ts +96 -0
- package/src/tui/ui/colors.ts +70 -0
- package/src/tui/ui/composer.ts +199 -0
- package/src/tui/ui/dialog.ts +379 -0
- package/src/tui/ui/draft.ts +23 -0
- package/src/tui/ui/edge.ts +186 -0
- package/src/tui/ui/image-size.ts +40 -0
- package/src/tui/ui/ink.ts +210 -0
- package/src/tui/ui/items.ts +40 -0
- package/src/tui/ui/keys.ts +40 -0
- package/src/tui/ui/layout.ts +74 -0
- package/src/tui/ui/list-model.ts +86 -0
- package/src/tui/ui/list-view.ts +172 -0
- package/src/tui/ui/mascot.ts +67 -0
- package/src/tui/ui/messages.ts +518 -0
- package/src/tui/ui/motion.ts +28 -0
- package/src/tui/ui/paths.ts +10 -0
- package/src/tui/ui/pictures.ts +78 -0
- package/src/tui/ui/screen.ts +421 -0
- package/src/tui/ui/status.ts +271 -0
- package/src/tui/ui/styled.ts +85 -0
- package/src/tui/ui/testing.ts +5 -0
- package/src/tui/ui/tool-card.ts +150 -0
- package/src/apps/extension-ui.ts +0 -330
- package/src/apps/resource-browser.ts +0 -75
- package/src/apps/session-browser.ts +0 -113
- package/src/apps/session-picker.ts +0 -55
- package/src/tui/ansi.ts +0 -67
- package/src/tui/autocomplete.ts +0 -29
- package/src/tui/chat-viewport.ts +0 -47
- package/src/tui/complete.ts +0 -87
- package/src/tui/editor.ts +0 -940
- package/src/tui/host.ts +0 -264
- package/src/tui/input.ts +0 -75
- package/src/tui/keys.ts +0 -378
- package/src/tui/markdown.ts +0 -17
- package/src/tui/render.ts +0 -153
- package/src/tui/select.ts +0 -258
- package/src/tui/settings.ts +0 -44
- package/src/tui/statusline.ts +0 -162
- package/src/tui/system-theme.ts +0 -700
- package/src/tui/terminal.ts +0 -30
- package/src/tui/text.ts +0 -52
- package/src/tui/toolview.ts +0 -386
- package/src/tui/transcript.ts +0 -123
- package/src/tui/tree.ts +0 -471
- package/src/vendor/pi-tui/LICENSE.upstream +0 -21
- package/src/vendor/pi-tui/UPSTREAM.json +0 -50
- package/src/vendor/pi-tui/VENDOR.md +0 -25
- package/src/vendor/pi-tui/alt-screen-search.ts +0 -327
- package/src/vendor/pi-tui/autocomplete.ts +0 -860
- package/src/vendor/pi-tui/colors.ts +0 -367
- package/src/vendor/pi-tui/components/alt-screen-flash.ts +0 -51
- package/src/vendor/pi-tui/components/box.ts +0 -166
- package/src/vendor/pi-tui/components/cancellable-loader.ts +0 -40
- package/src/vendor/pi-tui/components/editor.ts +0 -2472
- package/src/vendor/pi-tui/components/h-stack.ts +0 -44
- package/src/vendor/pi-tui/components/image.ts +0 -127
- package/src/vendor/pi-tui/components/input.ts +0 -494
- package/src/vendor/pi-tui/components/loader.ts +0 -101
- package/src/vendor/pi-tui/components/markdown.ts +0 -1021
- package/src/vendor/pi-tui/components/mouse-region.ts +0 -33
- package/src/vendor/pi-tui/components/scroll-view.ts +0 -224
- package/src/vendor/pi-tui/components/select-list.ts +0 -273
- package/src/vendor/pi-tui/components/settings-list.ts +0 -328
- package/src/vendor/pi-tui/components/spacer.ts +0 -28
- package/src/vendor/pi-tui/components/stack.ts +0 -154
- package/src/vendor/pi-tui/components/text.ts +0 -107
- package/src/vendor/pi-tui/components/truncated-text.ts +0 -65
- package/src/vendor/pi-tui/components/v-stack.ts +0 -33
- package/src/vendor/pi-tui/editor-component.ts +0 -74
- package/src/vendor/pi-tui/fuzzy.ts +0 -138
- package/src/vendor/pi-tui/index.ts +0 -186
- package/src/vendor/pi-tui/keybindings.ts +0 -320
- package/src/vendor/pi-tui/keys.ts +0 -1401
- package/src/vendor/pi-tui/kill-ring.ts +0 -46
- package/src/vendor/pi-tui/latex.ts +0 -1506
- package/src/vendor/pi-tui/layout-node.ts +0 -51
- package/src/vendor/pi-tui/layout.ts +0 -449
- package/src/vendor/pi-tui/native/UPSTREAM.json +0 -23
- package/src/vendor/pi-tui/native/clipboard.h +0 -208
- package/src/vendor/pi-tui/native/darwin/README.md +0 -22
- package/src/vendor/pi-tui/native/darwin/build.sh +0 -69
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +0 -132
- package/src/vendor/pi-tui/native/linux/README.md +0 -23
- package/src/vendor/pi-tui/native/linux/build.sh +0 -65
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +0 -70
- package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +0 -274
- package/src/vendor/pi-tui/native/napi.h +0 -91
- package/src/vendor/pi-tui/native/win32/README.md +0 -31
- package/src/vendor/pi-tui/native/win32/build.mjs +0 -232
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/src/win32-platform.c +0 -241
- package/src/vendor/pi-tui/native-modifiers.ts +0 -13
- package/src/vendor/pi-tui/native-module-path.ts +0 -31
- package/src/vendor/pi-tui/native-platform.ts +0 -65
- package/src/vendor/pi-tui/oklab.ts +0 -233
- package/src/vendor/pi-tui/stdin-buffer.ts +0 -444
- package/src/vendor/pi-tui/terminal-colors.ts +0 -91
- package/src/vendor/pi-tui/terminal-image.ts +0 -731
- package/src/vendor/pi-tui/terminal.ts +0 -554
- package/src/vendor/pi-tui/tui-alt-screen.ts +0 -1750
- package/src/vendor/pi-tui/tui-main-screen.ts +0 -655
- package/src/vendor/pi-tui/tui.ts +0 -1493
- package/src/vendor/pi-tui/undo-stack.ts +0 -28
- package/src/vendor/pi-tui/utils.ts +0 -1385
- package/src/vendor/pi-tui/wheel-scroll.ts +0 -82
- package/src/vendor/pi-tui/word-navigation.ts +0 -117
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/** Q.19: fixed workloads and admission overlays share production tools and the v2 auditor. */
|
|
2
|
+
import { queryEvalVerifier } from "./query-eval-verifier.ts";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { seededRandom, type EvalJob } from "../core/experiment.ts";
|
|
6
|
+
import { TOOL_MODE_TASKS, type ToolModeTask, type ToolModeMetrics } from "../core/tool-mode-protocol.ts";
|
|
7
|
+
import { queryEvalContract } from "../core/query-eval-contract.ts";
|
|
8
|
+
import { inputError } from "../core/tool-contract.ts";
|
|
9
|
+
import type { LogEvent, ArtifactRef } from "../core/events.ts";
|
|
10
|
+
import { readObservation } from "../store/observations.ts";
|
|
11
|
+
import { TOOL_DEFINITIONS, modelToolSchema, type ToolContext } from "./tools.ts";
|
|
12
|
+
import type { ToolSchema } from "./provider.ts";
|
|
13
|
+
import type { ComparisonBackend } from "./harness-comparison.ts";
|
|
14
|
+
import type { EvalFixture } from "./harness-eval.ts";
|
|
15
|
+
|
|
16
|
+
const digest = (text: string) => createHash("sha256").update(text).digest("hex");
|
|
17
|
+
export const TOOL_MODE_SYSTEM = "Solve the supplied data task using query over the complete source files. Preserve inputs and verifier. " +
|
|
18
|
+
"Use the declared query syntax. Inspect all failures and original evidence. " +
|
|
19
|
+
"Write only answer.txt, run bun run gate, and report the observed outcome. Do not delegate.";
|
|
20
|
+
export interface ToolModeFixture {
|
|
21
|
+
answer: string;
|
|
22
|
+
queries: { path: string; json: Record<string, unknown>; sql: Record<string, unknown> }[];
|
|
23
|
+
}
|
|
24
|
+
export function toolModeFixture(seed: number, task: ToolModeTask, repeat: number): EvalFixture {
|
|
25
|
+
const random = seededRandom(seed + TOOL_MODE_TASKS.indexOf(task) * 1000 + repeat);
|
|
26
|
+
const count = task === "tool-batch" ? 8 : 1, rows = task === "tool-small" ? 1 : task === "tool-batch" ? 25 : 9000;
|
|
27
|
+
const files: Record<string, string> = {}, queries: ToolModeFixture["queries"] = [], expected: { path: string; rows: unknown[] }[] = [];
|
|
28
|
+
for (let i = 0; i < count; i++) {
|
|
29
|
+
const path = "data-" + i + ".jsonl", data = Array.from({ length: rows }, (_, row) => ({
|
|
30
|
+
team: "team" + row % 4, enabled: row % 3 !== 0, amount: 1 + Math.floor(random() * 97),
|
|
31
|
+
}));
|
|
32
|
+
files[path] = data.map(row => JSON.stringify(row)).join("\n") + "\n";
|
|
33
|
+
const base = { paths: [path], format: "jsonl" };
|
|
34
|
+
if (task === "tool-small") {
|
|
35
|
+
queries.push({ path, json: { ...base, select: ["amount"] }, sql: { ...base, sql: "SELECT amount FROM source" } });
|
|
36
|
+
expected.push({ path, rows: [{ amount: data[0]!.amount }] });
|
|
37
|
+
} else if (task === "tool-batch") {
|
|
38
|
+
queries.push({ path, json: { ...base, aggregate: [{ op: "sum", field: "amount", as: "total" }] }, sql: { ...base, sql: "SELECT SUM(amount) AS total FROM source" } });
|
|
39
|
+
expected.push({ path, rows: [{ total: data.reduce((n, row) => n + row.amount, 0) }] });
|
|
40
|
+
} else {
|
|
41
|
+
queries.push({ path, json: { ...base, where: [{ field: "enabled", op: "eq", value: true }], group_by: ["team"],
|
|
42
|
+
aggregate: [{ op: "sum", field: "amount", as: "total" }], order_by: [{ field: "team" }] },
|
|
43
|
+
sql: { ...base, sql: "SELECT team, SUM(amount) AS total FROM source WHERE enabled = TRUE GROUP BY team ORDER BY team" } });
|
|
44
|
+
// Independent fixture arithmetic; do not ask the query kernel to grade itself.
|
|
45
|
+
expected.push({ path, rows: Array.from({ length: 4 }, (_, n) => {
|
|
46
|
+
const team = "team" + n;
|
|
47
|
+
return { team, total: data.filter(row => row.enabled && row.team === team).reduce((sum, row) => sum + row.amount, 0) };
|
|
48
|
+
}) });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const answer = JSON.stringify(expected) + "\n", answerSha256 = digest(answer);
|
|
52
|
+
files["package.json"] = JSON.stringify({ scripts: { gate: "bun verify.ts" } }) + "\n";
|
|
53
|
+
const contract = queryEvalContract(task, queries.map(query => query.path), answerSha256);
|
|
54
|
+
files["verify.ts"] = queryEvalVerifier(contract);
|
|
55
|
+
return { task, repeat, files, digests: Object.fromEntries(Object.entries(files).map(([path, body]) => [path, digest(body)])),
|
|
56
|
+
answerSha256, prompts: [contract.instruction], toolMode: { answer, queries } };
|
|
57
|
+
}
|
|
58
|
+
export type ToolModeSchemas = Record<"json" | "sql", ToolSchema[]>;
|
|
59
|
+
export function toolModeSchemas(): ToolModeSchemas {
|
|
60
|
+
const build = (syntax: "json" | "sql"): ToolSchema[] => {
|
|
61
|
+
const names = ["read", "query", "history", "write", "exec_command", "write_stdin", "note"];
|
|
62
|
+
return names.map(name => {
|
|
63
|
+
const definition = TOOL_DEFINITIONS.find(tool => tool.name === name)!;
|
|
64
|
+
if (name !== "query") return modelToolSchema(definition, names);
|
|
65
|
+
const schema = structuredClone(definition.input_schema) as ToolSchema["input_schema"] & { additionalProperties?: boolean };
|
|
66
|
+
const jsonKeys = ["where", "select", "group_by", "aggregate", "order_by"];
|
|
67
|
+
for (const key of syntax === "json" ? ["sql"] : jsonKeys) delete schema.properties[key];
|
|
68
|
+
if (syntax === "sql") schema.required.push("sql");
|
|
69
|
+
schema.additionalProperties = false;
|
|
70
|
+
return { name, description: definition.description + " This trial admits only " + syntax.toUpperCase() + " query syntax.", input_schema: schema };
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
return { json: build("json"), sql: build("sql") };
|
|
74
|
+
}
|
|
75
|
+
export const toolModeSchemaKey = (job: EvalJob): keyof ToolModeSchemas => job.policy.querySyntax === "sql" ? "sql" : "json";
|
|
76
|
+
/** Schema visibility is not authority: the same frozen restriction runs before every admitted query. */
|
|
77
|
+
export function toolModeHooks(schemas: readonly ToolSchema[]): NonNullable<ToolContext["extensionHooks"]> {
|
|
78
|
+
const query = schemas.find(schema => schema.name === "query")!;
|
|
79
|
+
return { async before(name, input) {
|
|
80
|
+
if (name !== "query") return undefined;
|
|
81
|
+
const error = inputError(query.input_schema, input);
|
|
82
|
+
const properties = (query.input_schema as { properties: Record<string, unknown> }).properties;
|
|
83
|
+
const extra = input && typeof input === "object" ? Object.keys(input).find(key => !Object.hasOwn(properties, key)) : undefined;
|
|
84
|
+
return error || extra ? "Frozen query syntax restriction: " + (error ?? "unsupported field " + extra) : undefined;
|
|
85
|
+
}, async event() {} };
|
|
86
|
+
}
|
|
87
|
+
/** Oracle driver tests the machinery; neither its answers nor call choices are model evidence. */
|
|
88
|
+
export function toolModeBackend(input: EvalFixture): ComparisonBackend {
|
|
89
|
+
const fixture = input.toolMode!;
|
|
90
|
+
let step = 0;
|
|
91
|
+
return async payload => {
|
|
92
|
+
const declarations = payload["tools"] as { function: { name: string; parameters: { properties: Record<string, unknown> } } }[];
|
|
93
|
+
const syntax = declarations.find(tool => tool.function.name === "query")!.function.parameters.properties["sql"] ? "sql" : "json";
|
|
94
|
+
const calls: { name: string; input: unknown }[] = [], index = step++;
|
|
95
|
+
if (index === 0) {
|
|
96
|
+
for (const query of fixture.queries) calls.push({ name: "query", input: query[syntax] });
|
|
97
|
+
}
|
|
98
|
+
if (index === 1) {
|
|
99
|
+
const messages = payload["messages"] as { role: string; content?: unknown }[];
|
|
100
|
+
const observed = messages.filter(message => message.role === "tool").flatMap(message => {
|
|
101
|
+
const value = JSON.parse(String(message.content));
|
|
102
|
+
return [value];
|
|
103
|
+
}) as { sources: { path: string }[]; rows: unknown[] }[];
|
|
104
|
+
const answer = fixture.queries.map(query => {
|
|
105
|
+
const value = observed.find(value => value.sources?.length === 1 && value.sources[0]!.path === query.path);
|
|
106
|
+
if (!value) throw Error("mechanical driver lacks a successful source query receipt");
|
|
107
|
+
return { path: query.path, rows: value.rows };
|
|
108
|
+
});
|
|
109
|
+
calls.push({ name: "write", input: { path: "answer.txt", content: JSON.stringify(answer) + "\n" } });
|
|
110
|
+
}
|
|
111
|
+
if (index === 2) calls.push({ name: "exec_command", input: { cmd: "bun run gate", yield_time_ms: 1000 } });
|
|
112
|
+
return { id: "tool-mode-" + index, model: "eval-mechanical", choices: [{ index: 0, message: {
|
|
113
|
+
role: "assistant", content: calls.length ? null : "Mechanical protocol fixture finished.",
|
|
114
|
+
...(calls.length ? { tool_calls: calls.map((call, i) => ({ id: "mode-" + index + "-" + i, type: "function",
|
|
115
|
+
function: { name: call.name, arguments: JSON.stringify(call.input) } })) } : {}),
|
|
116
|
+
}, finish_reason: calls.length ? "tool_calls" : "stop" }], usage: { prompt_tokens: 200, completion_tokens: 32, prompt_cache_hit_tokens: 0 } };
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export async function toolModeMetrics(directory: string, input: EvalFixture, events: readonly LogEvent[], elapsedMs: number): Promise<ToolModeMetrics> {
|
|
120
|
+
if (!Number.isFinite(elapsedMs) || elapsedMs < 0) throw Error("missing tool mode elapsed observation");
|
|
121
|
+
const fixture = input.toolMode!, invocations = new Map(events.filter(event => event.t === "tool_invocation").map(event => [event.id, event]));
|
|
122
|
+
const sources = new Set<string>();
|
|
123
|
+
const original = async (ref: ArtifactRef): Promise<unknown> => {
|
|
124
|
+
const parts: Uint8Array[] = [];
|
|
125
|
+
for (let offset = 0;;) {
|
|
126
|
+
const page = await readObservation(join(directory, "observations"), ref.sha256, offset, 1024 * 1024);
|
|
127
|
+
parts.push(Buffer.from(page.base64, "base64"));
|
|
128
|
+
if (page.next === null) break;
|
|
129
|
+
offset = page.next;
|
|
130
|
+
}
|
|
131
|
+
return JSON.parse(Buffer.concat(parts).toString("utf8"));
|
|
132
|
+
};
|
|
133
|
+
for (const event of events) if (event.t === "tool_result" && event.ok && event.invocation_ref) {
|
|
134
|
+
const invocation = invocations.get(event.invocation_ref.event);
|
|
135
|
+
if (!invocation || invocation.call_id !== event.call_id || invocation.name !== "query") continue;
|
|
136
|
+
const value = event.data_ref ? await original(event.data_ref) : event.content_ref ? await original(event.content_ref) : event.data;
|
|
137
|
+
if (!value || typeof value !== "object" || !("sources" in value) || !Array.isArray(value.sources)) continue;
|
|
138
|
+
for (const source of value.sources as { path?: string; sha256?: string }[]) {
|
|
139
|
+
if (typeof source.path === "string" && fixture.queries.some(query => query.path === source.path) && input.digests[source.path] === source.sha256) sources.add(source.path);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const calls = [...invocations.values()], sourcePaths = [...sources].sort(), requiredSourcePaths = fixture.queries.map(query => query.path).sort();
|
|
143
|
+
return { elapsedMs, queryCalls: calls.filter(call => call.name === "query").length,
|
|
144
|
+
primitiveCalls: calls.length, sourcePaths, requiredSourcePaths,
|
|
145
|
+
sourceComplete: requiredSourcePaths.every(path => sources.has(path)) };
|
|
146
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Pi A / O.5: ordered admission, bounded readers and an exclusive write barrier. */
|
|
2
|
+
import { schedulingWindow } from "../core/scheduling.ts";
|
|
3
|
+
|
|
4
|
+
type Mode = "parallel" | "exclusive";
|
|
5
|
+
type Job = { mode: Mode; prepare(): Promise<() => Promise<unknown>>; resolve(value: unknown): void; reject(error: unknown): void };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A composing call owns an exclusive slot in its parent queue and gives its
|
|
9
|
+
* children a fresh queue. Re-entering the held parent slot would deadlock.
|
|
10
|
+
* Only exclusive tools may compose; concurrent readers therefore cannot each
|
|
11
|
+
* multiply the session's parallel limit with another queue.
|
|
12
|
+
*/
|
|
13
|
+
export class ToolQueue {
|
|
14
|
+
private readonly limit: number;
|
|
15
|
+
private readonly pending: Job[] = [];
|
|
16
|
+
private active = 0;
|
|
17
|
+
private exclusive = false;
|
|
18
|
+
private pumping = false;
|
|
19
|
+
private readonly idle = new Set<() => void>();
|
|
20
|
+
private closed = false;
|
|
21
|
+
|
|
22
|
+
constructor(limit: number) {
|
|
23
|
+
schedulingWindow([], 0, limit);
|
|
24
|
+
this.limit = limit;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
submit<T>(mode: Mode, prepare: () => Promise<() => Promise<T>>): Promise<T> {
|
|
28
|
+
if (this.closed) return Promise.reject(new Error("tool scope is closed; no invocation started"));
|
|
29
|
+
const result = new Promise<T>((resolve, reject) => {
|
|
30
|
+
this.pending.push({ mode, prepare, resolve: value => resolve(value as T), reject });
|
|
31
|
+
});
|
|
32
|
+
this.kick();
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private kick(): void { if (!this.pumping) { this.pumping = true; void this.pump(); } }
|
|
37
|
+
|
|
38
|
+
private async pump(): Promise<void> {
|
|
39
|
+
try {
|
|
40
|
+
while (this.pending.length > 0) {
|
|
41
|
+
const next = this.pending[0]!;
|
|
42
|
+
const occupied: Mode[] = Array.from({ length: this.active }, () => this.exclusive ? "exclusive" : "parallel");
|
|
43
|
+
if (schedulingWindow([...occupied, next.mode], 0, this.limit) <= this.active) break;
|
|
44
|
+
const job = this.pending.shift()!;
|
|
45
|
+
this.active++; this.exclusive = job.mode === "exclusive";
|
|
46
|
+
const finished = () => { this.active--; if (job.mode === "exclusive") this.exclusive = false; this.kick(); };
|
|
47
|
+
try {
|
|
48
|
+
const execute = await job.prepare();
|
|
49
|
+
void Promise.resolve().then(execute).then(job.resolve, job.reject).finally(finished);
|
|
50
|
+
} catch (error) { job.reject(error); this.active--; this.exclusive = false; }
|
|
51
|
+
}
|
|
52
|
+
} finally {
|
|
53
|
+
this.pumping = false;
|
|
54
|
+
if (this.active === 0 && this.pending.length === 0) { for (const resolve of this.idle) resolve(); this.idle.clear(); }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A generator can retain its slot across admission/display yields without executing early. */
|
|
59
|
+
acquire(mode: Mode): Promise<() => void> {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
let release!: () => void;
|
|
62
|
+
const done = new Promise<void>(finish => { release = finish; });
|
|
63
|
+
void this.submit(mode, async () => async () => { resolve(release); await done; }).catch(reject);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
whenIdle(): Promise<void> {
|
|
68
|
+
if (!this.pumping && this.active === 0 && this.pending.length === 0) return Promise.resolve();
|
|
69
|
+
return new Promise(resolve => this.idle.add(resolve));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Accepted jobs drain even when their caller forgot to await their promises. */
|
|
73
|
+
async close(): Promise<void> {
|
|
74
|
+
this.closed = true;
|
|
75
|
+
await this.whenIdle();
|
|
76
|
+
}
|
|
77
|
+
}
|