bluekiwi 0.2.5 → 0.3.1
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/assets/index.js +0 -1
- package/dist/assets/mcp/server.js +434 -7
- package/dist/assets/skills/bk-approve/SKILL.md +18 -6
- package/dist/assets/skills/bk-design/SKILL.md +220 -14
- package/dist/assets/skills/bk-improve/SKILL.md +106 -20
- package/dist/assets/skills/bk-instruction/SKILL.md +21 -0
- package/dist/assets/skills/bk-report/SKILL.md +18 -0
- package/dist/assets/skills/bk-rewind/SKILL.md +2 -2
- package/dist/assets/skills/bk-share/SKILL.md +5 -5
- package/dist/assets/skills/bk-start/SKILL.md +282 -19
- package/dist/assets/skills/bk-status/SKILL.md +35 -5
- package/dist/commands/dev-link.js +1 -1
- package/package.json +1 -1
- package/dist/assets/skills/bk-next/SKILL.md +0 -228
package/dist/assets/index.js
CHANGED
|
@@ -20908,6 +20908,50 @@ var BlueKiwiClient = class {
|
|
|
20908
20908
|
}
|
|
20909
20909
|
throw new BlueKiwiNetworkError("Unreachable", lastError);
|
|
20910
20910
|
}
|
|
20911
|
+
async requestFormData(method, path2, formData) {
|
|
20912
|
+
const url2 = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
|
|
20913
|
+
const init = {
|
|
20914
|
+
method,
|
|
20915
|
+
headers: {
|
|
20916
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
20917
|
+
},
|
|
20918
|
+
body: formData
|
|
20919
|
+
};
|
|
20920
|
+
let lastError;
|
|
20921
|
+
for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
|
|
20922
|
+
try {
|
|
20923
|
+
const res = await fetch(url2, init);
|
|
20924
|
+
if (res.status === 401) {
|
|
20925
|
+
throw new BlueKiwiAuthError();
|
|
20926
|
+
}
|
|
20927
|
+
if (!res.ok) {
|
|
20928
|
+
const text2 = await res.text().catch(() => "");
|
|
20929
|
+
if (res.status >= 500 && attempt < RETRY_DELAYS_MS.length) {
|
|
20930
|
+
lastError = new BlueKiwiApiError(res.status, text2);
|
|
20931
|
+
await sleep(RETRY_DELAYS_MS[attempt]);
|
|
20932
|
+
continue;
|
|
20933
|
+
}
|
|
20934
|
+
throw new BlueKiwiApiError(res.status, text2);
|
|
20935
|
+
}
|
|
20936
|
+
const text = await res.text();
|
|
20937
|
+
return text ? JSON.parse(text) : null;
|
|
20938
|
+
} catch (err) {
|
|
20939
|
+
if (err instanceof BlueKiwiAuthError || err instanceof BlueKiwiApiError) {
|
|
20940
|
+
throw err;
|
|
20941
|
+
}
|
|
20942
|
+
lastError = err;
|
|
20943
|
+
if (attempt < RETRY_DELAYS_MS.length) {
|
|
20944
|
+
await sleep(RETRY_DELAYS_MS[attempt]);
|
|
20945
|
+
continue;
|
|
20946
|
+
}
|
|
20947
|
+
throw new BlueKiwiNetworkError(
|
|
20948
|
+
`Failed to reach ${url2} after ${RETRY_DELAYS_MS.length + 1} attempts`,
|
|
20949
|
+
err
|
|
20950
|
+
);
|
|
20951
|
+
}
|
|
20952
|
+
}
|
|
20953
|
+
throw new BlueKiwiNetworkError("Unreachable", lastError);
|
|
20954
|
+
}
|
|
20911
20955
|
};
|
|
20912
20956
|
function sleep(ms) {
|
|
20913
20957
|
return new Promise((r) => setTimeout(r, ms));
|
|
@@ -21022,6 +21066,15 @@ var tools = [
|
|
|
21022
21066
|
folder_id: { type: "number" }
|
|
21023
21067
|
}
|
|
21024
21068
|
),
|
|
21069
|
+
tool(
|
|
21070
|
+
"list_tasks",
|
|
21071
|
+
"List tasks visible to the current user. Optionally filter by workflow_id, status (running/completed/failed), or search query. Returns task id, workflow_title, status, current_step, total_steps, and logs.",
|
|
21072
|
+
{
|
|
21073
|
+
workflow_id: { type: "number" },
|
|
21074
|
+
status: { type: "string" },
|
|
21075
|
+
q: { type: "string" }
|
|
21076
|
+
}
|
|
21077
|
+
),
|
|
21025
21078
|
tool(
|
|
21026
21079
|
"list_workflow_versions",
|
|
21027
21080
|
"List every version in the same family as the given workflow id, including active and archived ones. Returns the active_version_id and an ordered versions array.",
|
|
@@ -21135,9 +21188,10 @@ var tools = [
|
|
|
21135
21188
|
),
|
|
21136
21189
|
tool(
|
|
21137
21190
|
"get_web_response",
|
|
21138
|
-
"Fetch the
|
|
21191
|
+
"Fetch VS response for a task. Without node_id: returns the latest response. With node_id: returns the response history for that node across all loop iterations (web_response only, no visual_html). Useful for tracking how user preferences evolved across iterations.",
|
|
21139
21192
|
{
|
|
21140
|
-
task_id: { type: "number" }
|
|
21193
|
+
task_id: { type: "number" },
|
|
21194
|
+
node_id: { type: "number" }
|
|
21141
21195
|
},
|
|
21142
21196
|
["task_id"]
|
|
21143
21197
|
),
|
|
@@ -21151,6 +21205,48 @@ var tools = [
|
|
|
21151
21205
|
},
|
|
21152
21206
|
["task_id", "node_id", "visual_html"]
|
|
21153
21207
|
),
|
|
21208
|
+
tool(
|
|
21209
|
+
"set_visual_html",
|
|
21210
|
+
`Submit a VS content fragment for a visual_selection=true gate node. Write HTML fragments using bk-* component classes \u2014 the frame (CSS, JS, submit button) is added automatically. Do NOT include <html>, <head>, or <body> tags.
|
|
21211
|
+
|
|
21212
|
+
DIALOG SIZE DIRECTIVE (optional, add as first line):
|
|
21213
|
+
<!-- @bk size=sm --> default (448px) \u2014 simple options, checklist
|
|
21214
|
+
<!-- @bk size=md --> medium (672px) \u2014 cards, code-compare
|
|
21215
|
+
<!-- @bk size=lg --> large (896px) \u2014 pros-cons, ranking, timeline
|
|
21216
|
+
<!-- @bk size=xl --> wide (1152px) \u2014 mockups, matrix, side-by-side wireframes
|
|
21217
|
+
<!-- @bk size=full --> fullscreen (95vw) \u2014 dashboard previews, complex layouts
|
|
21218
|
+
If omitted, defaults to sm. Use xl or full for any content that benefits from horizontal space.
|
|
21219
|
+
|
|
21220
|
+
Built-in components (use class names directly):
|
|
21221
|
+
SELECTION (collect choices):
|
|
21222
|
+
- bk-options: A/B/C cards. Wrap in .bk-options, each .bk-option with data-value. Optional data-recommended badge. Contains .bk-option-letter + .bk-option-body with h3+p.
|
|
21223
|
+
- bk-cards: Visual cards. Wrap in .bk-cards, each .bk-card with data-value. Contains .bk-card-image + .bk-card-body.
|
|
21224
|
+
- bk-checklist: Multi-select. Wrap in .bk-checklist, each .bk-check-item with data-value. Optional data-checked for defaults.
|
|
21225
|
+
- bk-code-compare: Code selection. Wrap in .bk-code-compare, each .bk-code-option with data-value. Contains .bk-code-label + pre.bk-code.
|
|
21226
|
+
|
|
21227
|
+
INPUT (collect values):
|
|
21228
|
+
- bk-slider: Numeric. data-name, data-min, data-max, data-value, data-unit on .bk-slider. Contains label.
|
|
21229
|
+
- bk-ranking: Drag reorder. Wrap in .bk-ranking, each .bk-rank-item with data-value.
|
|
21230
|
+
- bk-matrix: 2x2 placement. .bk-matrix with data-x-label, data-y-label. Each .bk-matrix-item with data-value.
|
|
21231
|
+
|
|
21232
|
+
DISPLAY (no values):
|
|
21233
|
+
- bk-split: Side-by-side. Two .bk-split-panel inside .bk-split.
|
|
21234
|
+
- bk-pros-cons: .bk-pros + .bk-cons inside .bk-pros-cons.
|
|
21235
|
+
- bk-mockup: .bk-mockup-header + .bk-mockup-body inside .bk-mockup.
|
|
21236
|
+
- bk-timeline: .bk-timeline-item with data-status="done|current|pending".
|
|
21237
|
+
|
|
21238
|
+
Layout: h2 for title, .bk-subtitle, .bk-section for breaks, .bk-label for category.
|
|
21239
|
+
|
|
21240
|
+
Response format (JSON via get_web_response):
|
|
21241
|
+
{selections: ["a"], values: {budget: 70}, ranking: ["security","ux"], matrix: {auth: {x:0.8,y:0.9}}}
|
|
21242
|
+
Only populated fields are included.`,
|
|
21243
|
+
{
|
|
21244
|
+
task_id: { type: "number" },
|
|
21245
|
+
node_id: { type: "number" },
|
|
21246
|
+
html: { type: "string" }
|
|
21247
|
+
},
|
|
21248
|
+
["task_id", "node_id", "html"]
|
|
21249
|
+
),
|
|
21154
21250
|
tool(
|
|
21155
21251
|
"save_artifacts",
|
|
21156
21252
|
"Save artifacts for a task step",
|
|
@@ -21286,6 +21382,114 @@ var tools = [
|
|
|
21286
21382
|
},
|
|
21287
21383
|
["workflow_id"]
|
|
21288
21384
|
),
|
|
21385
|
+
tool(
|
|
21386
|
+
"append_node",
|
|
21387
|
+
"Append a new node at the end of a workflow. node_type determines auto_advance automatically (action=auto, gate/loop=manual). Use hitl=true for action nodes that require human approval. For loop nodes, set loop_back_to to the target step_order (usually self). For gate nodes, set visual_selection=true for click-based HTML selection UI.",
|
|
21388
|
+
{
|
|
21389
|
+
workflow_id: { type: "number" },
|
|
21390
|
+
title: { type: "string" },
|
|
21391
|
+
instruction: { type: "string" },
|
|
21392
|
+
node_type: { type: "string" },
|
|
21393
|
+
hitl: { type: "boolean" },
|
|
21394
|
+
visual_selection: { type: "boolean" },
|
|
21395
|
+
loop_back_to: { type: "number" },
|
|
21396
|
+
credential_id: { type: "number" },
|
|
21397
|
+
instruction_id: { type: "number" }
|
|
21398
|
+
},
|
|
21399
|
+
["workflow_id", "title", "node_type"]
|
|
21400
|
+
),
|
|
21401
|
+
tool(
|
|
21402
|
+
"insert_node",
|
|
21403
|
+
"Insert a new node after a specific step_order in a workflow. Nodes after the insertion point are shifted by +1. For loop nodes, set loop_back_to. For gate nodes, set visual_selection=true for click-based selection.",
|
|
21404
|
+
{
|
|
21405
|
+
workflow_id: { type: "number" },
|
|
21406
|
+
after_step: { type: "number" },
|
|
21407
|
+
title: { type: "string" },
|
|
21408
|
+
instruction: { type: "string" },
|
|
21409
|
+
node_type: { type: "string" },
|
|
21410
|
+
hitl: { type: "boolean" },
|
|
21411
|
+
visual_selection: { type: "boolean" },
|
|
21412
|
+
loop_back_to: { type: "number" },
|
|
21413
|
+
credential_id: { type: "number" },
|
|
21414
|
+
instruction_id: { type: "number" }
|
|
21415
|
+
},
|
|
21416
|
+
["workflow_id", "after_step", "title", "node_type"]
|
|
21417
|
+
),
|
|
21418
|
+
tool(
|
|
21419
|
+
"update_node",
|
|
21420
|
+
"Partially update a single node. Only provided fields are changed. If node_type changes, auto_advance is re-enforced automatically.",
|
|
21421
|
+
{
|
|
21422
|
+
workflow_id: { type: "number" },
|
|
21423
|
+
node_id: { type: "number" },
|
|
21424
|
+
title: { type: "string" },
|
|
21425
|
+
instruction: { type: "string" },
|
|
21426
|
+
node_type: { type: "string" },
|
|
21427
|
+
hitl: { type: "boolean" },
|
|
21428
|
+
credential_id: { type: "number" },
|
|
21429
|
+
instruction_id: { type: "number" },
|
|
21430
|
+
loop_back_to: { type: "number" }
|
|
21431
|
+
},
|
|
21432
|
+
["workflow_id", "node_id"]
|
|
21433
|
+
),
|
|
21434
|
+
tool(
|
|
21435
|
+
"remove_node",
|
|
21436
|
+
"Delete a single node and reindex step_order for subsequent nodes (all nodes after the deleted one shift by -1).",
|
|
21437
|
+
{
|
|
21438
|
+
workflow_id: { type: "number" },
|
|
21439
|
+
node_id: { type: "number" }
|
|
21440
|
+
},
|
|
21441
|
+
["workflow_id", "node_id"]
|
|
21442
|
+
),
|
|
21443
|
+
tool(
|
|
21444
|
+
"list_attachments",
|
|
21445
|
+
"List file attachments for a workflow node. Returns metadata only (id, filename, mime_type, size_bytes).",
|
|
21446
|
+
{
|
|
21447
|
+
workflow_id: { type: "number" },
|
|
21448
|
+
node_id: { type: "number" }
|
|
21449
|
+
},
|
|
21450
|
+
["workflow_id", "node_id"]
|
|
21451
|
+
),
|
|
21452
|
+
tool(
|
|
21453
|
+
"get_attachment",
|
|
21454
|
+
"Download attachment content. Returns text content directly for text files. For binary files, returns metadata with binary=true.",
|
|
21455
|
+
{
|
|
21456
|
+
workflow_id: { type: "number" },
|
|
21457
|
+
node_id: { type: "number" },
|
|
21458
|
+
attachment_id: { type: "number" }
|
|
21459
|
+
},
|
|
21460
|
+
["workflow_id", "node_id", "attachment_id"]
|
|
21461
|
+
),
|
|
21462
|
+
tool(
|
|
21463
|
+
"upload_attachment",
|
|
21464
|
+
"Upload a text file as an attachment to a workflow node. Text-only \u2014 binary uploads must use the web UI. Use this after creating a workflow to attach scripts, reference docs, or config files that agents can download during execution.",
|
|
21465
|
+
{
|
|
21466
|
+
workflow_id: { type: "number" },
|
|
21467
|
+
node_id: { type: "number" },
|
|
21468
|
+
filename: { type: "string" },
|
|
21469
|
+
content: { type: "string" },
|
|
21470
|
+
mime_type: { type: "string" }
|
|
21471
|
+
},
|
|
21472
|
+
["workflow_id", "node_id", "filename", "content"]
|
|
21473
|
+
),
|
|
21474
|
+
tool(
|
|
21475
|
+
"delete_attachment",
|
|
21476
|
+
"Delete an attachment from a workflow node.",
|
|
21477
|
+
{
|
|
21478
|
+
workflow_id: { type: "number" },
|
|
21479
|
+
node_id: { type: "number" },
|
|
21480
|
+
attachment_id: { type: "number" }
|
|
21481
|
+
},
|
|
21482
|
+
["workflow_id", "node_id", "attachment_id"]
|
|
21483
|
+
),
|
|
21484
|
+
tool(
|
|
21485
|
+
"save_feedback",
|
|
21486
|
+
"Save post-workflow feedback for a completed task. Call this after collecting user survey responses and before calling complete_task. feedback is an array of {question, answer} objects.",
|
|
21487
|
+
{
|
|
21488
|
+
task_id: { type: "number" },
|
|
21489
|
+
feedback: { type: "array" }
|
|
21490
|
+
},
|
|
21491
|
+
["task_id", "feedback"]
|
|
21492
|
+
),
|
|
21289
21493
|
tool(
|
|
21290
21494
|
"save_findings",
|
|
21291
21495
|
"Save one or more compliance findings for a task",
|
|
@@ -21323,7 +21527,7 @@ var tools = [
|
|
|
21323
21527
|
),
|
|
21324
21528
|
tool(
|
|
21325
21529
|
"share_folder",
|
|
21326
|
-
"Share a folder with a user group at the given access level (
|
|
21530
|
+
"Share a folder with a user group at the given access level (reader or contributor). Owner or admin only.",
|
|
21327
21531
|
{
|
|
21328
21532
|
folder_id: { type: "number" },
|
|
21329
21533
|
group_id: { type: "number" },
|
|
@@ -21340,6 +21544,24 @@ var tools = [
|
|
|
21340
21544
|
},
|
|
21341
21545
|
["folder_id", "group_id"]
|
|
21342
21546
|
),
|
|
21547
|
+
tool(
|
|
21548
|
+
"update_folder",
|
|
21549
|
+
"Rename or change the description of a folder. Cannot rename system folders. Only the owner or an admin can edit.",
|
|
21550
|
+
{
|
|
21551
|
+
folder_id: { type: "number" },
|
|
21552
|
+
name: { type: "string" },
|
|
21553
|
+
description: { type: "string" }
|
|
21554
|
+
},
|
|
21555
|
+
["folder_id"]
|
|
21556
|
+
),
|
|
21557
|
+
tool(
|
|
21558
|
+
"delete_folder",
|
|
21559
|
+
"Delete an empty folder. Returns an error if the folder contains any workflows, instructions, credentials, or child folders.",
|
|
21560
|
+
{
|
|
21561
|
+
folder_id: { type: "number" }
|
|
21562
|
+
},
|
|
21563
|
+
["folder_id"]
|
|
21564
|
+
),
|
|
21343
21565
|
tool(
|
|
21344
21566
|
"move_workflow",
|
|
21345
21567
|
"Move a workflow into a different folder. Caller must have edit permission on the destination folder.",
|
|
@@ -21405,6 +21627,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
21405
21627
|
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
21406
21628
|
return wrap(await client.request("GET", `/api/workflows${suffix}`));
|
|
21407
21629
|
}
|
|
21630
|
+
case "list_tasks": {
|
|
21631
|
+
const qs = new URLSearchParams();
|
|
21632
|
+
if (typeof args.workflow_id === "number")
|
|
21633
|
+
qs.set("workflow_id", String(args.workflow_id));
|
|
21634
|
+
if (typeof args.status === "string") qs.set("status", args.status);
|
|
21635
|
+
if (typeof args.q === "string") qs.set("q", args.q);
|
|
21636
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
21637
|
+
return wrap(await client.request("GET", `/api/tasks${suffix}`));
|
|
21638
|
+
}
|
|
21408
21639
|
case "list_workflow_versions": {
|
|
21409
21640
|
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21410
21641
|
return wrap(
|
|
@@ -21426,12 +21657,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
21426
21657
|
)
|
|
21427
21658
|
);
|
|
21428
21659
|
}
|
|
21429
|
-
case "start_workflow":
|
|
21660
|
+
case "start_workflow": {
|
|
21661
|
+
const MODEL_NAME_RE = /^(claude|gpt|gemini|o\d|llama|mistral|qwen|deepseek)[-/.]/i;
|
|
21662
|
+
const clientName = server.getClientVersion()?.name ?? null;
|
|
21663
|
+
let providerSlug = null;
|
|
21664
|
+
let modelSlug = null;
|
|
21665
|
+
try {
|
|
21666
|
+
const meta3 = JSON.parse(
|
|
21667
|
+
typeof args.session_meta === "string" ? args.session_meta : "{}"
|
|
21668
|
+
);
|
|
21669
|
+
providerSlug = meta3.agent ?? null;
|
|
21670
|
+
modelSlug = meta3.model_id ?? null;
|
|
21671
|
+
} catch {
|
|
21672
|
+
}
|
|
21673
|
+
if (clientName) {
|
|
21674
|
+
if (MODEL_NAME_RE.test(clientName)) {
|
|
21675
|
+
if (!modelSlug) modelSlug = clientName;
|
|
21676
|
+
} else if (!providerSlug) {
|
|
21677
|
+
providerSlug = clientName;
|
|
21678
|
+
}
|
|
21679
|
+
}
|
|
21680
|
+
args.provider_slug = providerSlug;
|
|
21681
|
+
args.model_slug = modelSlug;
|
|
21430
21682
|
return wrap(await client.request("POST", "/api/tasks/start", args));
|
|
21683
|
+
}
|
|
21431
21684
|
case "execute_step": {
|
|
21432
21685
|
const taskId = requireNumberArg(args, "task_id");
|
|
21433
21686
|
const body = { ...args };
|
|
21434
21687
|
delete body.task_id;
|
|
21688
|
+
if (body.model_id && !body.model_slug) {
|
|
21689
|
+
body.model_slug = body.model_id;
|
|
21690
|
+
}
|
|
21435
21691
|
return wrap(
|
|
21436
21692
|
await client.request("POST", `/api/tasks/${taskId}/execute`, body)
|
|
21437
21693
|
);
|
|
@@ -21488,9 +21744,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
21488
21744
|
}
|
|
21489
21745
|
case "get_web_response": {
|
|
21490
21746
|
const taskId = requireNumberArg(args, "task_id");
|
|
21491
|
-
|
|
21492
|
-
|
|
21493
|
-
);
|
|
21747
|
+
const nodeId = typeof args.node_id === "number" ? args.node_id : null;
|
|
21748
|
+
const url2 = nodeId ? `/api/tasks/${taskId}/respond?node_id=${nodeId}` : `/api/tasks/${taskId}/respond`;
|
|
21749
|
+
return wrap(await client.request("GET", url2));
|
|
21494
21750
|
}
|
|
21495
21751
|
case "submit_visual": {
|
|
21496
21752
|
const taskId = requireNumberArg(args, "task_id");
|
|
@@ -21500,6 +21756,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
21500
21756
|
await client.request("POST", `/api/tasks/${taskId}/visual`, body)
|
|
21501
21757
|
);
|
|
21502
21758
|
}
|
|
21759
|
+
case "set_visual_html": {
|
|
21760
|
+
const taskId = requireNumberArg(args, "task_id");
|
|
21761
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21762
|
+
return wrap(
|
|
21763
|
+
await client.request("POST", `/api/tasks/${taskId}/visual`, {
|
|
21764
|
+
node_id: nodeId,
|
|
21765
|
+
html: args.html
|
|
21766
|
+
})
|
|
21767
|
+
);
|
|
21768
|
+
}
|
|
21503
21769
|
case "save_artifacts": {
|
|
21504
21770
|
const taskId = requireNumberArg(args, "task_id");
|
|
21505
21771
|
const body = { ...args };
|
|
@@ -21581,6 +21847,146 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
21581
21847
|
await client.request("DELETE", `/api/workflows/${workflowId}`)
|
|
21582
21848
|
);
|
|
21583
21849
|
}
|
|
21850
|
+
case "append_node": {
|
|
21851
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21852
|
+
const body = { ...args };
|
|
21853
|
+
delete body.workflow_id;
|
|
21854
|
+
return wrap(
|
|
21855
|
+
await client.request(
|
|
21856
|
+
"POST",
|
|
21857
|
+
`/api/workflows/${workflowId}/nodes`,
|
|
21858
|
+
body
|
|
21859
|
+
)
|
|
21860
|
+
);
|
|
21861
|
+
}
|
|
21862
|
+
case "insert_node": {
|
|
21863
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21864
|
+
const afterStep = requireNumberArg(args, "after_step");
|
|
21865
|
+
const body = { ...args };
|
|
21866
|
+
delete body.workflow_id;
|
|
21867
|
+
delete body.after_step;
|
|
21868
|
+
return wrap(
|
|
21869
|
+
await client.request(
|
|
21870
|
+
"POST",
|
|
21871
|
+
`/api/workflows/${workflowId}/nodes?after=${afterStep}`,
|
|
21872
|
+
body
|
|
21873
|
+
)
|
|
21874
|
+
);
|
|
21875
|
+
}
|
|
21876
|
+
case "update_node": {
|
|
21877
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21878
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21879
|
+
const body = { ...args };
|
|
21880
|
+
delete body.workflow_id;
|
|
21881
|
+
delete body.node_id;
|
|
21882
|
+
return wrap(
|
|
21883
|
+
await client.request(
|
|
21884
|
+
"PATCH",
|
|
21885
|
+
`/api/workflows/${workflowId}/nodes/${nodeId}`,
|
|
21886
|
+
body
|
|
21887
|
+
)
|
|
21888
|
+
);
|
|
21889
|
+
}
|
|
21890
|
+
case "remove_node": {
|
|
21891
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21892
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21893
|
+
return wrap(
|
|
21894
|
+
await client.request(
|
|
21895
|
+
"DELETE",
|
|
21896
|
+
`/api/workflows/${workflowId}/nodes/${nodeId}`
|
|
21897
|
+
)
|
|
21898
|
+
);
|
|
21899
|
+
}
|
|
21900
|
+
case "list_attachments": {
|
|
21901
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21902
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21903
|
+
return wrap(
|
|
21904
|
+
await client.request(
|
|
21905
|
+
"GET",
|
|
21906
|
+
`/api/workflows/${workflowId}/nodes/${nodeId}/attachments`
|
|
21907
|
+
)
|
|
21908
|
+
);
|
|
21909
|
+
}
|
|
21910
|
+
case "get_attachment": {
|
|
21911
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21912
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21913
|
+
const attachmentId = requireNumberArg(args, "attachment_id");
|
|
21914
|
+
const url2 = `${apiUrl.replace(/\/$/, "")}/api/workflows/${workflowId}/nodes/${nodeId}/attachments/${attachmentId}`;
|
|
21915
|
+
const res = await fetch(url2, {
|
|
21916
|
+
method: "GET",
|
|
21917
|
+
headers: {
|
|
21918
|
+
Authorization: `Bearer ${apiKey}`
|
|
21919
|
+
}
|
|
21920
|
+
});
|
|
21921
|
+
if (res.status === 401) {
|
|
21922
|
+
throw new BlueKiwiAuthError();
|
|
21923
|
+
}
|
|
21924
|
+
if (!res.ok) {
|
|
21925
|
+
throw new BlueKiwiApiError(
|
|
21926
|
+
res.status,
|
|
21927
|
+
await res.text().catch(() => "")
|
|
21928
|
+
);
|
|
21929
|
+
}
|
|
21930
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
21931
|
+
if (contentType.includes("application/json")) {
|
|
21932
|
+
const text = await res.text();
|
|
21933
|
+
return wrap(text ? JSON.parse(text) : null);
|
|
21934
|
+
}
|
|
21935
|
+
const disposition = res.headers.get("content-disposition") ?? "";
|
|
21936
|
+
const filenameMatch = /filename\*=UTF-8''([^;]+)|filename=\"?([^\";]+)\"?/i.exec(
|
|
21937
|
+
disposition
|
|
21938
|
+
);
|
|
21939
|
+
const filename = decodeURIComponent(
|
|
21940
|
+
filenameMatch?.[1] ?? filenameMatch?.[2] ?? ""
|
|
21941
|
+
);
|
|
21942
|
+
const sizeHeader = res.headers.get("content-length");
|
|
21943
|
+
const sizeBytes = typeof sizeHeader === "string" ? Number(sizeHeader) : void 0;
|
|
21944
|
+
return wrap({
|
|
21945
|
+
data: {
|
|
21946
|
+
id: attachmentId,
|
|
21947
|
+
filename: filename || void 0,
|
|
21948
|
+
mime_type: contentType || void 0,
|
|
21949
|
+
size_bytes: typeof sizeBytes === "number" && Number.isFinite(sizeBytes) ? sizeBytes : void 0,
|
|
21950
|
+
binary: true
|
|
21951
|
+
}
|
|
21952
|
+
});
|
|
21953
|
+
}
|
|
21954
|
+
case "upload_attachment": {
|
|
21955
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21956
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21957
|
+
const filename = requireStringArg(args, "filename");
|
|
21958
|
+
const content = requireStringArg(args, "content");
|
|
21959
|
+
const mimeType = typeof args.mime_type === "string" && args.mime_type.length > 0 ? args.mime_type : "text/plain";
|
|
21960
|
+
const blob = new Blob([content], { type: mimeType });
|
|
21961
|
+
const formData = new FormData();
|
|
21962
|
+
formData.append("file", blob, filename);
|
|
21963
|
+
return wrap(
|
|
21964
|
+
await client.requestFormData(
|
|
21965
|
+
"POST",
|
|
21966
|
+
`/api/workflows/${workflowId}/nodes/${nodeId}/attachments`,
|
|
21967
|
+
formData
|
|
21968
|
+
)
|
|
21969
|
+
);
|
|
21970
|
+
}
|
|
21971
|
+
case "delete_attachment": {
|
|
21972
|
+
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21973
|
+
const nodeId = requireNumberArg(args, "node_id");
|
|
21974
|
+
const attachmentId = requireNumberArg(args, "attachment_id");
|
|
21975
|
+
return wrap(
|
|
21976
|
+
await client.request(
|
|
21977
|
+
"DELETE",
|
|
21978
|
+
`/api/workflows/${workflowId}/nodes/${nodeId}/attachments/${attachmentId}`
|
|
21979
|
+
)
|
|
21980
|
+
);
|
|
21981
|
+
}
|
|
21982
|
+
case "save_feedback": {
|
|
21983
|
+
const taskId = requireNumberArg(args, "task_id");
|
|
21984
|
+
return wrap(
|
|
21985
|
+
await client.request("POST", `/api/tasks/${taskId}/feedback`, {
|
|
21986
|
+
feedback: args.feedback
|
|
21987
|
+
})
|
|
21988
|
+
);
|
|
21989
|
+
}
|
|
21584
21990
|
case "save_findings": {
|
|
21585
21991
|
const taskId = requireNumberArg(args, "task_id");
|
|
21586
21992
|
const body = { findings: args.findings };
|
|
@@ -21629,6 +22035,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
21629
22035
|
)
|
|
21630
22036
|
);
|
|
21631
22037
|
}
|
|
22038
|
+
case "update_folder": {
|
|
22039
|
+
const folderId = requireNumberArg(args, "folder_id");
|
|
22040
|
+
const body = {};
|
|
22041
|
+
if (typeof args.name === "string") body.name = args.name;
|
|
22042
|
+
if (typeof args.description === "string")
|
|
22043
|
+
body.description = args.description;
|
|
22044
|
+
return wrap(
|
|
22045
|
+
await client.request("PUT", `/api/folders/${folderId}`, body)
|
|
22046
|
+
);
|
|
22047
|
+
}
|
|
22048
|
+
case "delete_folder": {
|
|
22049
|
+
const folderId = requireNumberArg(args, "folder_id");
|
|
22050
|
+
return wrap(await client.request("DELETE", `/api/folders/${folderId}`));
|
|
22051
|
+
}
|
|
21632
22052
|
case "move_workflow": {
|
|
21633
22053
|
const workflowId = requireNumberArg(args, "workflow_id");
|
|
21634
22054
|
const folderId = requireNumberArg(args, "folder_id");
|
|
@@ -21711,6 +22131,13 @@ function requireNumberArg(args, key) {
|
|
|
21711
22131
|
}
|
|
21712
22132
|
return value;
|
|
21713
22133
|
}
|
|
22134
|
+
function requireStringArg(args, key) {
|
|
22135
|
+
const value = args[key];
|
|
22136
|
+
if (typeof value !== "string") {
|
|
22137
|
+
throw new Error(`${key} must be a string`);
|
|
22138
|
+
}
|
|
22139
|
+
return value;
|
|
22140
|
+
}
|
|
21714
22141
|
function wrap(data) {
|
|
21715
22142
|
return {
|
|
21716
22143
|
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bk-approve
|
|
3
|
-
description: BlueKiwi approval skill. Handles pending approvals when resuming a session that was interrupted mid-HITL, or when the user explicitly wants to approve a paused step. During normal execution, HITL approval is handled inline by bk-start
|
|
3
|
+
description: BlueKiwi approval skill. Handles pending approvals when resuming a session that was interrupted mid-HITL, or when the user explicitly wants to approve a paused step. During normal execution, HITL approval is handled inline by bk-start. Use this skill when the user says "/bk-approve", "approve this", "approve step", or returns to a session where a HITL step is already waiting.
|
|
4
4
|
user_invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ user_invocable: true
|
|
|
9
9
|
Handle pending approvals in a running workflow. Covers two scenarios:
|
|
10
10
|
|
|
11
11
|
- **Gate step** (`node_type: gate`): Agent asked the human a question; collect the answer.
|
|
12
|
-
- **HITL step** (`node_type: action`, `
|
|
12
|
+
- **HITL step** (`node_type: action`, `hitl: true`): Agent completed work and called `request_approval`; human reviews and approves before the workflow continues.
|
|
13
13
|
|
|
14
14
|
## Argument Handling
|
|
15
15
|
|
|
@@ -36,11 +36,23 @@ Check `log_status` and `node_type` to determine scenario:
|
|
|
36
36
|
|
|
37
37
|
1. Show the gate question from `instruction`.
|
|
38
38
|
2. Check `get_web_response` for a pre-submitted web response. If found, show it and ask to confirm.
|
|
39
|
+
If the web response is a JSON object from bk-\* components, display a readable summary instead of raw JSON:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
VS Response:
|
|
43
|
+
- Selected: [option names from selections array]
|
|
44
|
+
- Values: budget = 70%, confidence = 85%
|
|
45
|
+
- Ranking: 1. Security, 2. Performance, 3. UX
|
|
46
|
+
- Matrix: auth -> high urgency / high importance
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Map `selections` values back to the option labels shown in the VS screen. Present `values` with their units, `ranking` as a numbered list, and `matrix` positions as quadrant descriptions using high/low language for each axis.
|
|
50
|
+
|
|
39
51
|
3. Collect decision via AskUserQuestion:
|
|
40
52
|
- header: "Gate decision"
|
|
41
53
|
- options: ["Approve (Recommended)", "Approve with edits", "Reject and revise", "Rewind to previous step"]
|
|
42
54
|
4. Call `execute_step` with the decision as `output`, status `"success"`.
|
|
43
|
-
5. Call `advance` to move to the next step and follow the
|
|
55
|
+
5. Call `advance` to move to the next step and follow the auto-advance loop (see bk-start auto-advance loop).
|
|
44
56
|
|
|
45
57
|
### Step 2b: HITL Step
|
|
46
58
|
|
|
@@ -61,12 +73,12 @@ Check `log_status` and `node_type` to determine scenario:
|
|
|
61
73
|
3. **If Approved**:
|
|
62
74
|
- Call `approve_step(task_id=<id>)` MCP tool.
|
|
63
75
|
- Call `advance` to move to the next step.
|
|
64
|
-
- Follow the
|
|
76
|
+
- Follow the auto-advance loop (see bk-start auto-advance loop).
|
|
65
77
|
|
|
66
78
|
4. **If Rejected**:
|
|
67
79
|
- Ask the user for the reason.
|
|
68
80
|
- Call `rewind` to return to this step so the agent can redo it.
|
|
69
|
-
- Tell the user: "Rewound to step {N}. Type `/bk-
|
|
81
|
+
- Tell the user: "Rewound to step {N}. Type `/bk-start` to retry."
|
|
70
82
|
|
|
71
83
|
5. **If Rewind to earlier step**:
|
|
72
84
|
- Switch to `/bk-rewind` flow.
|
|
@@ -74,5 +86,5 @@ Check `log_status` and `node_type` to determine scenario:
|
|
|
74
86
|
## Notes
|
|
75
87
|
|
|
76
88
|
- `approve_step` MCP tool handles authentication automatically using the configured API key.
|
|
77
|
-
- After approving a HITL step, always follow the
|
|
89
|
+
- After approving a HITL step, always follow the auto-advance loop — the next step may auto-proceed.
|
|
78
90
|
- If `advance` still returns 403 after approval, wait a moment and retry — the approval write may not have propagated yet.
|