bitfab-cli 0.2.259 → 0.2.261
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 +26 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28412,20 +28412,24 @@ var saveTemplate = {
|
|
|
28412
28412
|
description: external_exports.string().optional().describe("Optional description. Pass an empty string to clear an existing description.")
|
|
28413
28413
|
}
|
|
28414
28414
|
};
|
|
28415
|
-
var
|
|
28416
|
-
name: "
|
|
28417
|
-
title: "
|
|
28418
|
-
description: "
|
|
28415
|
+
var saveTracePlan = {
|
|
28416
|
+
name: "save_trace_plan",
|
|
28417
|
+
title: "Save Trace Plan",
|
|
28418
|
+
description: "Create or update a tracing instrumentation plan. CREATE: omit `planId` and send `language`, the full `tree`, `capturedNodeIds`, and usually `traceFunctionKey`; this creates a reviewable plan and returns its id, Studio URL, initial capture count, and expiry. UPDATE: pass `planId`; use targeted `capture` / `uncapture` and/or `mockOnReplayByNodeId` changes without restating the tree, or send `tree` plus `capturedNodeIds` together for a structural replacement. Targeted changes preserve status; a structural change to a confirmed plan reopens it to `awaiting`. Never create a second plan when one already exists for the work in hand: save it by `planId` instead. The captured set must stay one connected sub-tree with exactly one entry point, and that entry point always re-runs live and can never be mocked. Updates return the current plan summary, including the final captured set and replay/mock decisions.",
|
|
28419
28419
|
inputSchema: {
|
|
28420
|
-
|
|
28420
|
+
planId: external_exports.string().optional().describe("Existing trace plan id to update. Omit to create a new plan. Do not send creation-only fields `language`, `source`, or `agentRunId` when this is supplied."),
|
|
28421
|
+
language: external_exports.enum(["python", "typescript", "ruby", "go"]).optional().describe("CREATE mode. Source language of the user's code. Required when `planId` is omitted."),
|
|
28421
28422
|
// Structured (not z.unknown()) so the tool's JSON Schema shows the agent
|
|
28422
28423
|
// the per-node `analysis` field instead of burying it in prose, which is
|
|
28423
28424
|
// how plans arrived with every node unclassified.
|
|
28424
|
-
tree: external_exports.preprocess(parseJsonString, tracePlanTreeShape).describe("TracePlanTree: { rootId, nodes: { [id]: TraceNode } }. Each TraceNode has id, name, kind ('manual' | 'auto' | 'pure'), file, line, signature, parentId, childIds, plus optional framework, fields, sampleInput, sampleOutput. Every node, including uncaptured context nodes, must carry `analysis` describing WHAT THAT NODE DOES: { classification, sideEffectKind?, readKind?, innerCall?, mockable?, unmockableReason?, inputSerializable?, outputSerializable? }. Set `mockable` mechanically from the node's `kind`: `kind: 'manual'` (a hand-written `withSpan`/`@span`) is mockable, omit `mockable`; `kind: 'auto'` (captured by a framework handler/processor/stream/collector) gets `mockable: false` + `unmockableReason`, the ONE exception being Vercel AI SDK model spans (its `wrapLanguageModel` middleware routes the call through `withSpan`), which are mockable, omit; the root gets omit (never mockable). Rationale: mocking returns a span's recorded output instead of running the call, which only works when the call goes through a `withSpan` wrapper; `auto` framework spans are observed, not wrapped. Key hazard: an `auto` `external_read`/`side_effect` (a framework tool hitting a DB/HTTP) left mockable promises a mock replay can't deliver; its fix is a manual `withSpan` around that call or a db-snapshot, never a mock. Serializability is two raw facts, distinct from `mockable`: set `outputSerializable: false` when the recorded OUTPUT does not round-trip through serialization, and set `inputSerializable: false` when the recorded INPUT does not (its arguments hold a DB client, an open stream, a callback, or a class instance with no JSON form); omit either when it serializes (the default). Two rules follow. (1) Do NOT choose a root whose input is not serializable: replay re-runs the root against its recorded input, so a non-serializable-input root is not replayable, promote the root to a caller that takes a serializable input instead. (2) Do NOT mock a node whose output is not serializable (replay has no recorded value to return); the server enforces this by forcing any `outputSerializable: false` node unmockable, so you don't need to also set `mockable` for that reason. classification is 'pure' (deterministic local compute), 'model_call' (the span that IS the actual LLM/model call; a wrapper/orchestrator whose model call is represented by a child node, e.g. a LangChain chain.invoke or the root, is 'pure', not 'model_call'; never bubble a child's model_call up to its parent), 'external_read' (reads external mutable state: DB SELECT, outbound GET, vector search, cache read; set readKind), or 'side_effect' (mutates external state: DB write, outbound POST/PUT/DELETE, email, queue, payment, filesystem; set sideEffectKind). The server derives the replay disposition (`mockOnReplay`) and the whole validation summary from this classification and mockability, so do NOT send them. Include ~10 surrounding callees below each leaf as `pure` (uncaptured) context nodes so the user can see what's adjacent in the codebase when they edit in the UI. Every node MUST be a descendant of `rootId`: the plan tree renders downward from the root, so a node above the root or on a side branch off one of those ancestors is stored and counted but never drawn. Never send callers above the root."),
|
|
28425
|
-
capturedNodeIds: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).describe("
|
|
28425
|
+
tree: external_exports.preprocess(parseJsonString, tracePlanTreeShape).optional().describe("TracePlanTree: { rootId, nodes: { [id]: TraceNode } }. Each TraceNode has id, name, kind ('manual' | 'auto' | 'pure'), file, line, signature, parentId, childIds, plus optional framework, fields, sampleInput, sampleOutput. Every node, including uncaptured context nodes, must carry `analysis` describing WHAT THAT NODE DOES: { classification, sideEffectKind?, readKind?, innerCall?, mockable?, unmockableReason?, inputSerializable?, outputSerializable? }. Set `mockable` mechanically from the node's `kind`: `kind: 'manual'` (a hand-written `withSpan`/`@span`) is mockable, omit `mockable`; `kind: 'auto'` (captured by a framework handler/processor/stream/collector) gets `mockable: false` + `unmockableReason`, the ONE exception being Vercel AI SDK model spans (its `wrapLanguageModel` middleware routes the call through `withSpan`), which are mockable, omit; the root gets omit (never mockable). Rationale: mocking returns a span's recorded output instead of running the call, which only works when the call goes through a `withSpan` wrapper; `auto` framework spans are observed, not wrapped. Key hazard: an `auto` `external_read`/`side_effect` (a framework tool hitting a DB/HTTP) left mockable promises a mock replay can't deliver; its fix is a manual `withSpan` around that call or a db-snapshot, never a mock. Serializability is two raw facts, distinct from `mockable`: set `outputSerializable: false` when the recorded OUTPUT does not round-trip through serialization, and set `inputSerializable: false` when the recorded INPUT does not (its arguments hold a DB client, an open stream, a callback, or a class instance with no JSON form); omit either when it serializes (the default). Two rules follow. (1) Do NOT choose a root whose input is not serializable: replay re-runs the root against its recorded input, so a non-serializable-input root is not replayable, promote the root to a caller that takes a serializable input instead. (2) Do NOT mock a node whose output is not serializable (replay has no recorded value to return); the server enforces this by forcing any `outputSerializable: false` node unmockable, so you don't need to also set `mockable` for that reason. classification is 'pure' (deterministic local compute), 'model_call' (the span that IS the actual LLM/model call; a wrapper/orchestrator whose model call is represented by a child node, e.g. a LangChain chain.invoke or the root, is 'pure', not 'model_call'; never bubble a child's model_call up to its parent), 'external_read' (reads external mutable state: DB SELECT, outbound GET, vector search, cache read; set readKind), or 'side_effect' (mutates external state: DB write, outbound POST/PUT/DELETE, email, queue, payment, filesystem; set sideEffectKind). The server derives the replay disposition (`mockOnReplay`) and the whole validation summary from this classification and mockability, so do NOT send them. Include ~10 surrounding callees below each leaf as `pure` (uncaptured) context nodes so the user can see what's adjacent in the codebase when they edit in the UI. Every node MUST be a descendant of `rootId`: the plan tree renders downward from the root, so a node above the root or on a side branch off one of those ancestors is stored and counted but never drawn. Never send callers above the root."),
|
|
28426
|
+
capturedNodeIds: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).optional().describe("CREATE or STRUCTURAL update mode. Absolute captured set for `tree`. Required with `tree`; must reference ids in the tree and form one connected sub-tree."),
|
|
28427
|
+
capture: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).optional().describe("TARGETED update mode. Node ids to add to the existing captured set. Requires `planId`; cannot be combined with `tree` / `capturedNodeIds`."),
|
|
28428
|
+
uncapture: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).optional().describe("TARGETED update mode. Node ids to remove from the existing captured set. Requires `planId`; cannot be combined with `tree` / `capturedNodeIds`."),
|
|
28429
|
+
mockOnReplayByNodeId: external_exports.preprocess(parseJsonString, external_exports.record(external_exports.string(), external_exports.boolean())).optional().describe("UPDATE mode. Per-node replay/mock overrides. Only listed nodes change. The replay entry point and framework-observed spans cannot be mocked."),
|
|
28426
28430
|
stats: external_exports.preprocess(parseJsonString, external_exports.unknown()).optional().describe("Optional sample-run stats: { durationMs?, tokens?, cost? }"),
|
|
28427
|
-
traceFunctionKey: external_exports.string().min(1).optional().describe("
|
|
28428
|
-
source: external_exports.enum(["interactive", "analyze_repo"]).optional().describe("How this plan was produced. Omit
|
|
28431
|
+
traceFunctionKey: external_exports.string().min(1).optional().describe("Trace function key. Recommended when creating; when updating, pass only to rename a key that was wrong or changed."),
|
|
28432
|
+
source: external_exports.enum(["interactive", "analyze_repo"]).optional().describe("CREATE mode. How this plan was produced. Omit for a normal interactive plan; set 'analyze_repo' only from analyze-repo batch mode."),
|
|
28429
28433
|
agentRunId: external_exports.uuid().optional()
|
|
28430
28434
|
}
|
|
28431
28435
|
};
|
|
@@ -28434,32 +28438,17 @@ var confirmTracePlan = {
|
|
|
28434
28438
|
title: "Confirm Trace Plan",
|
|
28435
28439
|
description: "Confirm a trace plan WITHOUT the browser, for the continue path where the user accepted the ASCII plan in chat, or left the Studio plan page without saving. Persists the plan as the latest *confirmed* plan for its traceFunctionKey (so later get_trace_plan by key, setup view, and setup modify can find it) and returns the final captured set plus per-node replay/mock decisions, exactly like get_trace_plan. Studio's Close/Update button does this for the browser path; call this only when the user continues without Studio, or leaves the plan page without saving. Pass the recommended capturedNodeIds; omit mockOnReplayByNodeId to accept the analysis-derived replay decisions (only pass it to override specific nodes the way a Studio toggle would).",
|
|
28436
28440
|
inputSchema: {
|
|
28437
|
-
planId: external_exports.string().describe("The trace plan id returned by
|
|
28441
|
+
planId: external_exports.string().describe("The trace plan id returned by save_trace_plan."),
|
|
28438
28442
|
capturedNodeIds: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string()).min(1)).describe("The captured set to confirm. Must reference ids present in the plan's tree and form a single connected sub-tree with one entry point (its top-most captured span). The entry point does NOT have to be the plan's tree root: untracing the tree root and rooting at a captured descendant is honored and persisted as-is."),
|
|
28439
28443
|
mockOnReplayByNodeId: external_exports.preprocess(parseJsonString, external_exports.record(external_exports.string(), external_exports.boolean())).optional().describe("Optional per-node overrides of the replay/mock decision (the equivalent of a Studio toggle). Omit to accept the server's analysis-derived defaults. The replay entry point (the top-most captured span) always re-runs live and cannot be mocked.")
|
|
28440
28444
|
}
|
|
28441
28445
|
};
|
|
28442
|
-
var updateTracePlan = {
|
|
28443
|
-
name: "update_trace_plan",
|
|
28444
|
-
title: "Update Trace Plan",
|
|
28445
|
-
description: "Change an EXISTING trace plan in place. Use this instead of create_trace_plan whenever a plan already exists for the work in hand (a Modify cycle bootstrapped via get_trace_plan, or an adjustment to a plan you just created): re-uploading produces a second plan competing for the same traceFunctionKey. Two mutually exclusive modes. TARGETED: pass `capture` / `uncapture` (node ids to add to or remove from the captured set) and/or `mockOnReplayByNodeId` (per-node replay/mock overrides) to adjust dispositions without restating the tree; deltas apply to the plan's current state, so they do not clobber a capture the user just saved in Studio. STRUCTURAL: pass `tree` plus `capturedNodeIds` together to replace the plan's whole node tree, for when the code changed and nodes must be added or removed. Sending both modes at once is an error. A structural update on a CONFIRMED plan reopens it to `awaiting`, because replacing the tree invalidates the user's acceptance, so you must confirm_trace_plan again afterwards or `setup view`/`setup modify` will not find a confirmed plan for that key; a targeted update leaves the status alone. Any update refreshes the plan's expiry, so it also revives an expired plan. The captured set must stay one connected sub-tree with exactly one entry point, and that entry point always re-runs live and can never be mocked. Returns the resulting plan exactly as get_trace_plan does, including the final captured set and per-node replay/mock decisions.",
|
|
28446
|
-
inputSchema: {
|
|
28447
|
-
planId: external_exports.string().describe("The trace plan id returned by create_trace_plan."),
|
|
28448
|
-
capture: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).optional().describe("TARGETED mode. Node ids to add to the captured set. Must reference ids present in the plan's tree. Cannot be combined with `tree` / `capturedNodeIds`."),
|
|
28449
|
-
uncapture: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).optional().describe("TARGETED mode. Node ids to remove from the captured set. Must reference ids present in the plan's tree. Removing a node that joins two captured branches leaves a disconnected capture set and is rejected. Cannot be combined with `tree` / `capturedNodeIds`."),
|
|
28450
|
-
mockOnReplayByNodeId: external_exports.preprocess(parseJsonString, external_exports.record(external_exports.string(), external_exports.boolean())).optional().describe("Per-node overrides of the replay/mock decision (the equivalent of a Studio toggle): true serves the span's recorded output on replay, false re-runs it live. Only the listed nodes change. Valid in either mode. The replay entry point cannot be mocked, and neither can a framework-observed (`kind: 'auto'`) span, since replay cannot short-circuit one."),
|
|
28451
|
-
tree: external_exports.preprocess(parseJsonString, tracePlanTreeShape).optional().describe("STRUCTURAL mode. The full replacement TracePlanTree, same shape and the same per-node `analysis` requirements as create_trace_plan. Send this only when the plan's nodes themselves change (code moved, a call was added or removed); to merely capture or mock differently use `capture` / `uncapture` / `mockOnReplayByNodeId`. Must be sent together with `capturedNodeIds`. `rootId` cannot be changed; to move the replay entry point, change which nodes are captured instead, an uncaptured tree root is honored and the top-most captured span becomes the entry."),
|
|
28452
|
-
capturedNodeIds: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.string())).optional().describe("STRUCTURAL mode. The absolute captured set for the replacement `tree`, restated in full. Must reference ids present in that tree and form a single connected sub-tree with one entry point. Required with `tree`, and invalid without it."),
|
|
28453
|
-
traceFunctionKey: external_exports.string().min(1).optional().describe("Optional replacement trace function key, for when the key the plan was created with was wrong or is being renamed."),
|
|
28454
|
-
stats: external_exports.preprocess(parseJsonString, external_exports.unknown()).optional().describe("Optional sample-run stats: { durationMs?, tokens?, cost? }")
|
|
28455
|
-
}
|
|
28456
|
-
};
|
|
28457
28446
|
var getTracePlan = {
|
|
28458
28447
|
name: "get_trace_plan",
|
|
28459
28448
|
title: "Get Trace Plan",
|
|
28460
|
-
description: "Read a trace plan. Two modes: pass `planId` to read a specific plan (use this after
|
|
28449
|
+
description: "Read a trace plan. Two modes: pass `planId` to read a specific plan (use this after save_trace_plan + browser confirmation to learn whether the user confirmed, cancelled, or is still pending); or pass `traceFunctionKey` to fetch the latest *confirmed* plan for that key. At the start of a Modify cycle, treat that confirmed plan as historical intent and reconcile its tree and captured nodes with current code; never assume the stored snapshot proves current instrumentation or use it to skip rereading code. Returns the full plan including the tree as JSON.",
|
|
28461
28450
|
inputSchema: {
|
|
28462
|
-
planId: external_exports.string().optional().describe("The trace plan id returned by
|
|
28451
|
+
planId: external_exports.string().optional().describe("The trace plan id returned by save_trace_plan."),
|
|
28463
28452
|
traceFunctionKey: external_exports.string().optional().describe("Trace function key. Returns the latest confirmed plan for this key in the caller's organization, or a 'no prior plan' message if none exists.")
|
|
28464
28453
|
}
|
|
28465
28454
|
};
|
|
@@ -28536,9 +28525,8 @@ var ALL_TOOL_CONTRACTS = [
|
|
|
28536
28525
|
getTemplateReference,
|
|
28537
28526
|
getTemplate,
|
|
28538
28527
|
saveTemplate,
|
|
28539
|
-
|
|
28528
|
+
saveTracePlan,
|
|
28540
28529
|
confirmTracePlan,
|
|
28541
|
-
updateTracePlan,
|
|
28542
28530
|
getTracePlan,
|
|
28543
28531
|
listTracePlans
|
|
28544
28532
|
];
|
|
@@ -29630,7 +29618,7 @@ function activityLabelFromEvent(event) {
|
|
|
29630
29618
|
}
|
|
29631
29619
|
function friendlyToolLabel(block) {
|
|
29632
29620
|
const name = typeof block.name === "string" ? block.name : "";
|
|
29633
|
-
if (name.includes("create_trace_plan")) {
|
|
29621
|
+
if (name.includes("save_trace_plan") || name.includes("create_trace_plan")) {
|
|
29634
29622
|
return "Uploading a draft trace plan";
|
|
29635
29623
|
}
|
|
29636
29624
|
if (name.includes("get_bitfab_api_key")) {
|
|
@@ -29681,7 +29669,7 @@ function countUploadedTracePlans(logText) {
|
|
|
29681
29669
|
continue;
|
|
29682
29670
|
}
|
|
29683
29671
|
const b = block;
|
|
29684
|
-
if (b.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string" && b.name.includes("create_trace_plan")) {
|
|
29672
|
+
if (b.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string" && (b.name.includes("save_trace_plan") || b.name.includes("create_trace_plan"))) {
|
|
29685
29673
|
planToolUseIds.add(b.id);
|
|
29686
29674
|
}
|
|
29687
29675
|
if (b.type === "tool_result" && typeof b.tool_use_id === "string" && b.is_error !== true) {
|
|
@@ -29962,7 +29950,7 @@ function codexActivityLabelFromEvent(event) {
|
|
|
29962
29950
|
if (!item) {
|
|
29963
29951
|
return null;
|
|
29964
29952
|
}
|
|
29965
|
-
if (isMcpToolItem(item, "create_trace_plan")) {
|
|
29953
|
+
if (isMcpToolItem(item, "save_trace_plan") || isMcpToolItem(item, "create_trace_plan")) {
|
|
29966
29954
|
return "Uploading a draft trace plan";
|
|
29967
29955
|
}
|
|
29968
29956
|
if (isMcpToolItem(item, "get_bitfab_api_key")) {
|
|
@@ -29995,7 +29983,7 @@ function countUploadedTracePlans2(logText) {
|
|
|
29995
29983
|
continue;
|
|
29996
29984
|
}
|
|
29997
29985
|
const item = eventItem(event);
|
|
29998
|
-
if (item && isMcpToolItem(item, "create_trace_plan")) {
|
|
29986
|
+
if (item && (isMcpToolItem(item, "save_trace_plan") || isMcpToolItem(item, "create_trace_plan"))) {
|
|
29999
29987
|
const status = typeof item.status === "string" ? item.status : "";
|
|
30000
29988
|
if (status === "completed" || status === "" && eventType(event) === "item.completed") {
|
|
30001
29989
|
completedPlanCalls++;
|
|
@@ -30006,7 +29994,7 @@ function countUploadedTracePlans2(logText) {
|
|
|
30006
29994
|
continue;
|
|
30007
29995
|
}
|
|
30008
29996
|
const b = block;
|
|
30009
|
-
if (b.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string" && b.name.includes("create_trace_plan")) {
|
|
29997
|
+
if (b.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string" && (b.name.includes("save_trace_plan") || b.name.includes("create_trace_plan"))) {
|
|
30010
29998
|
planToolUseIds.add(b.id);
|
|
30011
29999
|
}
|
|
30012
30000
|
if (b.type === "tool_result" && typeof b.tool_use_id === "string" && b.is_error !== true) {
|
|
@@ -30343,7 +30331,7 @@ function cursorActivityLabelFromEvent(event) {
|
|
|
30343
30331
|
return null;
|
|
30344
30332
|
}
|
|
30345
30333
|
const mcpToolName = mcpToolCallName(toolCall);
|
|
30346
|
-
if (mcpToolName?.includes("create_trace_plan")) {
|
|
30334
|
+
if (mcpToolName?.includes("save_trace_plan") || mcpToolName?.includes("create_trace_plan")) {
|
|
30347
30335
|
return "Uploading a draft trace plan";
|
|
30348
30336
|
}
|
|
30349
30337
|
if (mcpToolName?.includes("get_bitfab_api_key")) {
|
|
@@ -30390,7 +30378,7 @@ function countUploadedTracePlans3(logText) {
|
|
|
30390
30378
|
} catch {
|
|
30391
30379
|
continue;
|
|
30392
30380
|
}
|
|
30393
|
-
if (
|
|
30381
|
+
if (isSuccessfulSaveTracePlanEvent(event)) {
|
|
30394
30382
|
completedPlanCalls++;
|
|
30395
30383
|
}
|
|
30396
30384
|
}
|
|
@@ -30399,7 +30387,7 @@ function countUploadedTracePlans3(logText) {
|
|
|
30399
30387
|
}
|
|
30400
30388
|
return 0;
|
|
30401
30389
|
}
|
|
30402
|
-
function
|
|
30390
|
+
function isSuccessfulSaveTracePlanEvent(event) {
|
|
30403
30391
|
if (typeof event !== "object" || event === null) {
|
|
30404
30392
|
return false;
|
|
30405
30393
|
}
|
|
@@ -30423,7 +30411,7 @@ function isSuccessfulCreateTracePlanEvent(event) {
|
|
|
30423
30411
|
typeof args === "object" && args !== null ? args.name : void 0
|
|
30424
30412
|
];
|
|
30425
30413
|
return names.some(
|
|
30426
|
-
(name) => typeof name === "string" && name.includes("create_trace_plan")
|
|
30414
|
+
(name) => typeof name === "string" && (name.includes("save_trace_plan") || name.includes("create_trace_plan"))
|
|
30427
30415
|
) && hasSuccessfulToolResult(call.result);
|
|
30428
30416
|
}
|
|
30429
30417
|
function hasSuccessfulToolResult(result) {
|