bitfab-cli 0.2.417 → 0.2.419
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/dist/index.js +36 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28875,7 +28875,7 @@ var deleteAssertionCategory = {
|
|
|
28875
28875
|
var generateTraceAssertions = {
|
|
28876
28876
|
name: "generate_trace_assertions",
|
|
28877
28877
|
title: "Generate Trace Assertions",
|
|
28878
|
-
description:
|
|
28878
|
+
description: 'Generate review-ready assertion drafts from what one original trace actually did. Call this when the user asks Bitfab to generate assertions, infer expected behavior from a trace, or turn a known root cause into regression checks. Bitfab reads the organization\'s existing assertion categories, selects the categories supported by trace evidence, and generates assertions one category at a time. The generated assertions are saved awaiting human review, so they are not checked on replays until a person approves them. Read them back with get_trace_assertions and `approvalStates: ["pending"]`. Generation runs on Bitfab and this call waits up to four minutes for it. If it is still running after that, the result says so, and calling again with the same traceId and without regenerate checks on it. A trace has one generation at a time, so a repeat call joins the running or finished generation instead of spending model calls again, and regenerate while one is running joins that one too. Set regenerate to true after it finishes to archive the prior generated drafts and replace them. A failed generation is returned as an error with its reason. Human-authored assertions are never replaced. Pass guidance when the user knows what the assertions should be about, most often a root cause analysis of why the trace was wrong; it narrows the evidence and produces smaller assertions. Pass categoryIds to limit generation to those assertion categories. A replay trace id is refused because assertions belong to the original trace.',
|
|
28879
28879
|
inputSchema: {
|
|
28880
28880
|
traceId: external_exports.uuid().describe("The ORIGINAL trace to generate assertions for. A replay trace id is refused, and the error names the original to retry with."),
|
|
28881
28881
|
guidance: external_exports.string().trim().min(1).max(1e4).optional().describe("Optional assertion guidance, such as a root cause analysis of why the trace went wrong. Use it to focus generation on the smallest assertions that would catch this failure."),
|
|
@@ -28886,7 +28886,7 @@ var generateTraceAssertions = {
|
|
|
28886
28886
|
var saveTraceAssertions = {
|
|
28887
28887
|
name: "save_trace_assertions",
|
|
28888
28888
|
title: "Save Trace Assertions",
|
|
28889
|
-
description: `Record what SHOULD happen when this trace is replayed. Bitfab stores two kinds of label on a trace. A VERDICT says how a run that already happened turned out, and that is save_agent_labels / save_human_labels. An ASSERTION, which is what this tool writes, says what a correct run looks like for THIS specific input, carries no pass/fail of its own, and is checked against a later replay once a reviewer approves it. A trace holds at most one verdict per author and any number of assertions. Call it when the user describes the right answer for a particular case, for example "this booking should have picked the 6am flight, not the 9am". This is not a verdict on a run that already happened, which is save_agent_labels, and it is not a check that applies to every trace of a function, which is save_grader. Pass \`assertion\` plus optional \`passCriteria\` / \`failCriteria\`, the same trio save_grader takes, so an assertion that proves out across many traces can later be promoted into a grader with no rewriting. \`humanNote\` stores people-only context on the assertion; preserve and return it, but never use it to judge whether a replay passed. \`assigneeEmail\` names the member who should review it, which routes the assertion to that person without deciding anything, so it never changes the approval state. Pass an entry's \`id\` to edit an existing assertion, or omit it to add a new one, so two callers adding different assertions to one trace never overwrite each other. What this tool writes is saved awaiting review, so nothing checks it yet; a reviewer approves it, in Bitfab or with \`approvalState\` on a later call here, and from then on every replay of the trace is judged against it. \`approvalState\` decides an assertion that already exists, so deciding one is \`{ id, approvalState }\` with nothing else: an omitted \`assertion\` keeps the current wording. It is refused on a new assertion, so the call that invents a claim is never the call that puts it into force. One decision covers the assertion and the category it is filed under, so set \`category_assertion_id\` and \`approvalState\` in the same entry when you are deciding both; recategorizing on a later call without deciding returns it to not reviewed, since the approval covered the old category. What it gates is whether replays check the assertion at all, so decide from the assertion and the spans it cites rather than from memory of having written it. Rewording an assertion, or changing its criteria, target, or category, returns it to not reviewed unless the same call says otherwise. Call get_trace_assertions first to find the assertion to edit and to avoid creating a duplicate, and read them back before judging a replay, where it lists the approved ones
|
|
28889
|
+
description: `Record what SHOULD happen when this trace is replayed. Bitfab stores two kinds of label on a trace. A VERDICT says how a run that already happened turned out, and that is save_agent_labels / save_human_labels. An ASSERTION, which is what this tool writes, says what a correct run looks like for THIS specific input, carries no pass/fail of its own, and is checked against a later replay once a reviewer approves it. A trace holds at most one verdict per author and any number of assertions. Call it when the user describes the right answer for a particular case, for example "this booking should have picked the 6am flight, not the 9am". This is not a verdict on a run that already happened, which is save_agent_labels, and it is not a check that applies to every trace of a function, which is save_grader. Pass \`assertion\` plus optional \`passCriteria\` / \`failCriteria\`, the same trio save_grader takes, so an assertion that proves out across many traces can later be promoted into a grader with no rewriting. \`humanNote\` stores people-only context on the assertion; preserve and return it, but never use it to judge whether a replay passed. \`assigneeEmail\` names the member who should review it, which routes the assertion to that person without deciding anything, so it never changes the approval state. Pass an entry's \`id\` to edit an existing assertion, or omit it to add a new one, so two callers adding different assertions to one trace never overwrite each other. What this tool writes is saved awaiting review, so nothing checks it yet; a reviewer approves it, in Bitfab or with \`approvalState\` on a later call here, and from then on every replay of the trace is judged against it. \`approvalState\` decides an assertion that already exists, so deciding one is \`{ id, approvalState }\` with nothing else: an omitted \`assertion\` keeps the current wording. It is refused on a new assertion, so the call that invents a claim is never the call that puts it into force. One decision covers the assertion and the category it is filed under, so set \`category_assertion_id\` and \`approvalState\` in the same entry when you are deciding both; recategorizing on a later call without deciding returns it to not reviewed, since the approval covered the old category. What it gates is whether replays check the assertion at all, so decide from the assertion and the spans it cites rather than from memory of having written it. Rewording an assertion, or changing its criteria, target, or category, returns it to not reviewed unless the same call says otherwise. Call get_trace_assertions first to find the assertion to edit and to avoid creating a duplicate, and read them back before judging a replay, where it lists the approved ones by default. Pass it \`approvalStates: ["pending"]\` to read back drafts awaiting review, including the ones you just wrote. Up to ${MAX_ASSERTIONS_PER_REQUEST} per call.`,
|
|
28890
28890
|
inputSchema: {
|
|
28891
28891
|
traceId: external_exports.uuid().describe("The ORIGINAL trace to attach assertions to. A replay trace id is refused, and the error names the original to retry with, because a replay reads its original's assertions automatically."),
|
|
28892
28892
|
assertions: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.object({
|
|
@@ -28907,9 +28907,22 @@ var saveTraceAssertions = {
|
|
|
28907
28907
|
var getTraceAssertions = {
|
|
28908
28908
|
name: "get_trace_assertions",
|
|
28909
28909
|
title: "Get Trace Assertions",
|
|
28910
|
-
description: `Read what SHOULD happen when one or more traces are replayed: each trace's
|
|
28910
|
+
description: `Read what SHOULD happen when one or more traces are replayed: each trace's assertions, their review state, their pass/fail criteria, people-only human note, who is assigned to review each one, and what part of the evaluated trace each one checks. By default only assertions a person has approved are returned, because only those are checked on a replay; a trace whose assertions are all still awaiting review reads the same as one with none. Pass \`approvalStates\` to read other review states: \`["pending"]\` for the assertions still awaiting review, including the drafts generate_trace_assertions saved, so a batch of them can be reviewed and then decided with save_trace_assertions \`approvalState\`; add \`"rejected"\` to see the ones turned down. Judge a replay only against the approved ones. To find assertions without knowing which traces carry them, such as every draft awaiting review on a function, use list_trace_assertions. Each trace also reports its assertion generation, if one ran: running, completed with how many drafts it saved for review, or failed with the reason. Categorized assertions include category_assertion_id and the category's id, title, and description. A human note is returned so it can be read and edited, but it is never assessment evidence: judge only from the assertion, pass/fail criteria, and evaluated trace. Call this before judging a replay so the verdict is measured against what the user actually asked for rather than a guess, and before save_trace_assertions to find the assertion to edit or to avoid creating a duplicate. No span content is loaded, so one call accepts up to ${GET_TRACE_ASSERTIONS_MAX_IDS} ids. Accepts original trace ids and replay trace ids alike: a replay with no assertions of its own reads its original's, and the response says which original they came from. An assertion whose target cannot be found on the trace being evaluated is errored, never passed, so record it with save_agent_labels \`skip\` rather than a FAIL.`,
|
|
28911
28911
|
inputSchema: {
|
|
28912
|
-
traceIds: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.uuid()).min(1).max(GET_TRACE_ASSERTIONS_MAX_IDS)).describe(`Original trace IDs to read assertions for (1-${GET_TRACE_ASSERTIONS_MAX_IDS})`)
|
|
28912
|
+
traceIds: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.uuid()).min(1).max(GET_TRACE_ASSERTIONS_MAX_IDS)).describe(`Original trace IDs to read assertions for (1-${GET_TRACE_ASSERTIONS_MAX_IDS})`),
|
|
28913
|
+
approvalStates: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.enum(["pending", "approved", "rejected"])).min(1).max(3)).optional().describe('Review states to include: "approved" (checked on every replay), "pending" (awaiting review, including generated drafts), "rejected" (turned down, never checked). Defaults to ["approved"], which is what a replay is judged against. Pass ["pending"] to review drafts, or all three to see everything.')
|
|
28914
|
+
}
|
|
28915
|
+
};
|
|
28916
|
+
var listTraceAssertions = {
|
|
28917
|
+
name: "list_trace_assertions",
|
|
28918
|
+
title: "List Trace Assertions",
|
|
28919
|
+
description: `Find assertions across traces by review state, without knowing which traces carry them: for example every assertion still awaiting review on one traced function, which is the review queue. Each result names the trace it belongs to and carries the same fields get_trace_assertions returns, including its \`[ID: ...]\`, criteria, category, assignee, and approval. Newest first, one page at a time; pass the cursor a page returns to read the next. The cursor holds its place, so approving or rejecting the assertions on a page with save_trace_assertions \`approvalState\` before reading the next page never skips any. The first page also reports how many assertions are in each review state under the same filters. Only an assertion a person has approved is checked on a replay, so never score a replay against a pending or rejected one listed here. To read the assertions of traces you already have, use get_trace_assertions.`,
|
|
28920
|
+
inputSchema: {
|
|
28921
|
+
approvalStates: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.enum(["pending", "approved", "rejected"])).min(1).max(3)).describe('Review states to list: "pending" (awaiting review, including generated drafts), "approved" (checked on every replay), "rejected" (turned down, never checked). Pass ["pending"] for the review queue.'),
|
|
28922
|
+
traceFunctionKey: external_exports.string().min(1).optional().describe("Only assertions on traces of this traced function, from list_trace_functions. Omit to list across the whole organization."),
|
|
28923
|
+
categoryId: external_exports.union([external_exports.literal("unfiled"), external_exports.uuid()]).optional().describe('Only assertions filed under this category, from list_assertion_categories, or "unfiled" for assertions with no category. Omit for every category.'),
|
|
28924
|
+
limit: external_exports.preprocess(parseJsonString, external_exports.number().int().min(1).max(100)).optional().describe("Max assertions to return (default 25, max 100)"),
|
|
28925
|
+
cursor: external_exports.string().min(1).optional().describe("Opaque cursor from the previous page; reuse the same filters")
|
|
28913
28926
|
}
|
|
28914
28927
|
};
|
|
28915
28928
|
var archiveTraceAssertions = {
|
|
@@ -29235,6 +29248,7 @@ var ALL_TOOL_CONTRACTS = [
|
|
|
29235
29248
|
generateTraceAssertions,
|
|
29236
29249
|
saveTraceAssertions,
|
|
29237
29250
|
getTraceAssertions,
|
|
29251
|
+
listTraceAssertions,
|
|
29238
29252
|
archiveTraceAssertions,
|
|
29239
29253
|
listOrganizationMembers,
|
|
29240
29254
|
saveGrader,
|
|
@@ -29868,10 +29882,10 @@ var semver3 = __toESM(require_semver2(), 1);
|
|
|
29868
29882
|
|
|
29869
29883
|
// ../bitfab-plugin-lib/dist/bakedSdkVersions.js
|
|
29870
29884
|
var BAKED_SDK_VERSIONS = {
|
|
29871
|
-
typescript: "0.64.
|
|
29872
|
-
python: "0.64.
|
|
29873
|
-
ruby: "0.64.
|
|
29874
|
-
go: "0.64.
|
|
29885
|
+
typescript: "0.64.4",
|
|
29886
|
+
python: "0.64.4",
|
|
29887
|
+
ruby: "0.64.4",
|
|
29888
|
+
go: "0.64.3"
|
|
29875
29889
|
};
|
|
29876
29890
|
|
|
29877
29891
|
// ../bitfab-plugin-lib/dist/installedSdk.js
|
|
@@ -30978,6 +30992,16 @@ import { createWriteStream, mkdirSync as mkdirSync4, rmSync as rmSync2 } from "f
|
|
|
30978
30992
|
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
30979
30993
|
import { createInterface } from "readline";
|
|
30980
30994
|
|
|
30995
|
+
// ../bitfab-plugin-lib/dist/replayCloudResult.js
|
|
30996
|
+
var cloudSummarySchema = external_exports.object({
|
|
30997
|
+
cloudSummary: external_exports.literal(true),
|
|
30998
|
+
experimentId: external_exports.string().optional(),
|
|
30999
|
+
experimentUrl: external_exports.string().optional(),
|
|
31000
|
+
testRunId: external_exports.string().optional(),
|
|
31001
|
+
testRunUrl: external_exports.string().optional(),
|
|
31002
|
+
counts: external_exports.record(external_exports.string(), external_exports.number()).optional()
|
|
31003
|
+
}).passthrough();
|
|
31004
|
+
|
|
30981
31005
|
// ../bitfab-plugin-lib/dist/replayItemWriter.js
|
|
30982
31006
|
import { existsSync as existsSync2, mkdirSync as mkdirSync3, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
30983
31007
|
import { join as join3 } from "path";
|
|
@@ -35415,7 +35439,7 @@ cd <project-dir> && {{command:replayProgress}} --label <pipeline-name> --run-dir
|
|
|
35415
35439
|
|
|
35416
35440
|
For every item you can judge during the run, key the verdict by its **original trace id** (the \`originalTraceId\` field in each item file, the same \`item.originalTraceId\` on progress rows), the original trace the item was replayed from, which the server resolves to this run's replay trace, no local-to-server id mapping step. You need the run's \`experimentId\` to persist (enriched progress rows may carry \`event.experimentId\`; older scripts may only reveal it in the final \`ReplayResult\`). As soon as a small batch of judged items has its original trace ids and you know the \`experimentId\`, persist that batch with \`{{command:persistReplayLabels}}\`. Keep a set of original trace IDs already persisted so the final reconciliation never double-writes a verdict.
|
|
35417
35441
|
|
|
35418
|
-
4. **When the background command finishes, read the final \`type: "complete"\` row from this run's \`events.jsonl\`**. Its \`result\` carries run metadata (\`experimentId\`, \`experimentUrl\`, \`itemCount\`) and its \`items\` array carries item refs. Read each needed \`items[].itemPath\` for the full replay item (trace ID, duration, tokens, model, and full original/new outputs). Read from the **files**, not from the captured command output, which the harness truncates in the middle.
|
|
35442
|
+
4. **When the background command finishes, read the final \`type: "complete"\` row from this run's \`events.jsonl\`**. Its \`result\` carries run metadata (\`experimentId\`, \`experimentUrl\`, \`itemCount\`) and its \`items\` array carries item refs. Read each needed \`items[].itemPath\` for the full replay item (trace ID, duration, tokens, model, and full original/new outputs). Read from the **files**, not from the captured command output, which the harness truncates in the middle. A \`--cloud\` replay leaves \`items\` empty and sets \`result.cloudSummary: true\`, with the counts and each errored trace in \`result.erroredItems\`: read its items from Bitfab instead, {{tool:listExperimentTraces}} with the \`experimentId\` for each replay trace ID and its original, then \`{{command:readTracesBatched}} <replay and original trace IDs...> --scope full\` for their inputs and outputs.
|
|
35419
35443
|
|
|
35420
35444
|
**Before running: verify the installed replay command returns the full original and new output values AND at least one verdict persist key (\`item.originalTraceId\` or \`item.traceId\`) for every item** (not just lengths, counts, hashes, or truncated previews) so the run's \`items/*.json\` files carry them. Modern items may carry both: prefer lineage persistence by \`item.originalTraceId\` (older SDKs may print it under the deprecated \`sourceTraceId\` alias), and fall back to the server replay \`item.traceId\` only when original lineage is absent. After an item's replay trace is flushed, its \`item.traceId\` is available to lifecycle callbacks and in the final result. The oldest SDKs have no \`item.originalTraceId\` and persist by \`item.traceId\`. If the installed command returns neither, upgrade the SDK first; the Replay Output Contract and registry examples live in the SDK reference at \`https://docs.bitfab.ai/<language>-sdk.md\`. Subagents can't evaluate an improvement from \`5 \u2192 7 (+2)\`, and an item that carries no persist key blocks verdict persistence for that item.
|
|
35421
35445
|
|
|
@@ -36537,7 +36561,7 @@ If the block prints \`ERROR: Bitfab plugin not installed\`, the user hasn't inst
|
|
|
36537
36561
|
inspect: "Diagnose (and offer to fix) your tracing setup: auth, what's instrumented, plugin/SDK freshness, replay coverage, trace arrival.",
|
|
36538
36562
|
"switch-org": "Switch which Bitfab org the plugin reads and writes (replaces the local API key).",
|
|
36539
36563
|
replay: "Create or update replay registry modules for instrumented workflows.",
|
|
36540
|
-
cloud: "Run your normal replay on your own GitHub Actions by adding --cloud: same options
|
|
36564
|
+
cloud: "Run your normal replay on your own GitHub Actions by adding --cloud: same options and exit code, with a short summary of the result. One workflow file in the repository; no Bitfab repository connection or GitHub CLI required.",
|
|
36541
36565
|
"db-snapshot": "Set up per-trace database snapshots so replay runs against the DB state at trace time (TypeScript, Python, Ruby).",
|
|
36542
36566
|
templates: "Iterate on the span-rendering templates for one trace function.",
|
|
36543
36567
|
"analyze-repo": "Read-only discovery: scan source, rank the top workflows to instrument, and report recommendations without creating artifacts or changing code."
|
|
@@ -36602,7 +36626,7 @@ Never modify existing code on a refactor path without completing this three-step
|
|
|
36602
36626
|
kind: "action",
|
|
36603
36627
|
title: "Inspect the replay environment",
|
|
36604
36628
|
toolCalls: ["bash", "read", "glob", "grep", "ask"],
|
|
36605
|
-
body: `Cloud replay is the user's normal replay plus --cloud: every replay option works the same, the runner replays a snapshot of their working tree (changed files, plus new files git does not ignore) from the same directory, and the command prints the replay's
|
|
36629
|
+
body: `Cloud replay is the user's normal replay plus --cloud: every replay option works the same, the runner replays a snapshot of their working tree (changed files, plus new files git does not ignore) from the same directory, and the command prints the replay's progress and a short summary of its result (experiment ID and URL, replayed, same, changed, and errored counts, and each errored trace with its error), then exits with its exit code. Every trace's input and output are in the experiment in Bitfab, readable through the Bitfab MCP tools; --cloud-full-output prints the whole result instead. The code ships inside the SDK, and the repository keeps one file: .github/workflows/bitfab-replay.yml, which holds only settings.
|
|
36606
36630
|
|
|
36607
36631
|
Run the installed SDK's bitfab-replay --cloud --help; if it does not list --cloud-preview, upgrade the SDK through bitfab:update first. Check that the git origin is github.com and that Python 3.10+ exists (macOS or Linux). GitHub access needs no GitHub CLI: the SDK uses gh when it is logged in, otherwise GH_TOKEN or GITHUB_TOKEN, otherwise the github.com credential git stores. When the repository still has .bitfab/cloud.json from an older SDK, the SDK says to delete it and the old workflow and run --cloud-init again; show the user the old file's settings first.
|
|
36608
36632
|
|
|
@@ -36636,7 +36660,7 @@ Each replay pushes a temporary bitfab-replay/<UUID> branch. For each push-trigge
|
|
|
36636
36660
|
|
|
36637
36661
|
Verify with the replay command the user already runs. First add --cloud --cloud-preview, which pushes nothing and lists the changed and new files the snapshot would carry; review them for anything that should not leave the machine, since filename screening is not a secret scanner, and have the user gitignore it. Once the workflow is merged and secrets are set, and remote runs are authorized, run the same command with --cloud --dry-run: a real run that replays nothing but receives the secrets, stops on any empty secret, runs the --check command, and resolves the traces. Then, only when a replay is authorized, run with --cloud alone.
|
|
36638
36662
|
|
|
36639
|
-
From an agent session always add --cloud-detach, since a replay can run for hours, and follow it with --cloud-watch <UUID> (prints the replay's
|
|
36663
|
+
From an agent session always add --cloud-detach, since a replay can run for hours, and follow it with --cloud-watch <UUID> (prints the replay's progress and result summary and exits with its code), --cloud-status, --cloud-cancel, or --cloud-cleanup. --cloud-timeout <minutes> stops one replay early and keeps finished traces. Report separately whether the workflow is written, secrets are set, the workflow is merged, the preview was reviewed, the dry run passed, and a real replay completed.`,
|
|
36640
36664
|
next: "cleanup/finish"
|
|
36641
36665
|
}
|
|
36642
36666
|
]
|
package/package.json
CHANGED