@agentwonderland/mcp 0.1.54 → 0.1.55
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/core/__tests__/api-client.test.js +25 -1
- package/dist/core/__tests__/link-cli.test.js +95 -0
- package/dist/core/api-client.d.ts +3 -1
- package/dist/core/api-client.js +21 -2
- package/dist/core/link-cli.d.ts +17 -0
- package/dist/core/link-cli.js +165 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/__tests__/playbooks.test.js +114 -34
- package/dist/tools/playbooks.js +146 -42
- package/package.json +1 -1
- package/src/core/__tests__/api-client.test.ts +33 -0
- package/src/core/__tests__/link-cli.test.ts +110 -0
- package/src/core/api-client.ts +25 -1
- package/src/core/link-cli.ts +189 -0
- package/src/core/version.ts +1 -1
- package/src/index.ts +1 -1
- package/src/tools/__tests__/playbooks.test.ts +125 -46
- package/src/tools/playbooks.ts +177 -56
package/src/tools/playbooks.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
|
|
3
|
+
import { apiGet, apiPost, apiPostWithApprovedLinkSpendRequest, apiPostWithPayment } from "../core/api-client.js";
|
|
4
4
|
import { uploadLocalFiles } from "../core/file-upload.js";
|
|
5
5
|
import { hasWalletConfigured, getConfiguredMethods, getWalletAddress } from "../core/payments.js";
|
|
6
|
-
import { requiresSpendConfirmation } from "../core/config.js";
|
|
6
|
+
import { getLinkConfig, setPendingLinkSpendRequest, requiresSpendConfirmation } from "../core/config.js";
|
|
7
7
|
import { canSpend, recordSpend, requiresPolicyConfirmation } from "../core/spend-policy.js";
|
|
8
8
|
import { formatRunResult } from "../core/formatters.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
decodeMppNetworkId,
|
|
11
|
+
ensureApprovedLinkSpendRequest,
|
|
12
|
+
LinkApprovalRequiredError,
|
|
13
|
+
} from "../core/link-cli.js";
|
|
10
14
|
import {
|
|
11
15
|
formatPaymentLabel,
|
|
12
16
|
resolveConfirmationMethod,
|
|
@@ -33,6 +37,13 @@ type PlaybookStepQuote = {
|
|
|
33
37
|
support_status: string;
|
|
34
38
|
};
|
|
35
39
|
|
|
40
|
+
type PlaybookFanoutControl = {
|
|
41
|
+
key: string;
|
|
42
|
+
default: number;
|
|
43
|
+
max: number;
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
36
47
|
type PlaybookRecord = {
|
|
37
48
|
id: string;
|
|
38
49
|
slug: string;
|
|
@@ -44,6 +55,9 @@ type PlaybookRecord = {
|
|
|
44
55
|
outcome: string;
|
|
45
56
|
support_status: "supported" | "catalog_only";
|
|
46
57
|
support_note?: string;
|
|
58
|
+
default_limits?: Record<string, number>;
|
|
59
|
+
applied_limits?: Record<string, number>;
|
|
60
|
+
fanout_controls?: PlaybookFanoutControl[];
|
|
47
61
|
input_schema: Record<string, unknown>;
|
|
48
62
|
sample_input: Record<string, unknown>;
|
|
49
63
|
budget_notes: string[];
|
|
@@ -57,6 +71,9 @@ type PlaybookRecord = {
|
|
|
57
71
|
estimated_cost_usd: number;
|
|
58
72
|
step_count: number;
|
|
59
73
|
blocking_issues: string[];
|
|
74
|
+
default_limits?: Record<string, number>;
|
|
75
|
+
applied_limits?: Record<string, number>;
|
|
76
|
+
fanout_controls?: PlaybookFanoutControl[];
|
|
60
77
|
steps: PlaybookStepQuote[];
|
|
61
78
|
};
|
|
62
79
|
};
|
|
@@ -128,6 +145,35 @@ function money(value: number): string {
|
|
|
128
145
|
return `$${value.toFixed(value < 1 ? 4 : 2)}`;
|
|
129
146
|
}
|
|
130
147
|
|
|
148
|
+
function cents(valueUsd: number): string {
|
|
149
|
+
return String(Math.max(1, Math.ceil(valueUsd * 100)));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function nonEmptyLimits(limits?: Record<string, number>): Record<string, number> | undefined {
|
|
153
|
+
return limits && Object.keys(limits).length > 0 ? limits : undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function appliedPlaybookLimits(playbook: PlaybookRecord): Record<string, number> | undefined {
|
|
157
|
+
return nonEmptyLimits(playbook.current_quote.applied_limits ?? playbook.applied_limits ?? playbook.default_limits);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function fanoutControls(playbook: PlaybookRecord): PlaybookFanoutControl[] {
|
|
161
|
+
return playbook.current_quote.fanout_controls ?? playbook.fanout_controls ?? [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildPlaybookLinkApprovalContext(params: {
|
|
165
|
+
playbook: PlaybookRecord;
|
|
166
|
+
budget: number;
|
|
167
|
+
stepCount: number;
|
|
168
|
+
runId: string;
|
|
169
|
+
}): string {
|
|
170
|
+
return [
|
|
171
|
+
`Approve up to ${money(params.budget)} for the Agent Wonderland "${params.playbook.name}" playbook.`,
|
|
172
|
+
`This playbook run ${params.runId} has ${params.stepCount} child-agent steps and will stop before exceeding the approved budget.`,
|
|
173
|
+
"Each child agent is still charged at its exact quoted price, unused budget is not spent, and failed child runs keep the existing Agent Wonderland refund behavior.",
|
|
174
|
+
].join(" ");
|
|
175
|
+
}
|
|
176
|
+
|
|
131
177
|
async function collectWalletAddresses(paymentMethod?: string): Promise<string[]> {
|
|
132
178
|
const addresses = new Set<string>();
|
|
133
179
|
const primary = await getWalletAddress(paymentMethod);
|
|
@@ -180,11 +226,13 @@ function formatPlaybookList(playbooks: PlaybookRecord[], query?: string) {
|
|
|
180
226
|
}
|
|
181
227
|
|
|
182
228
|
const lines = playbooks.map((playbook) => {
|
|
183
|
-
|
|
229
|
+
const support = playbook.support_status === "supported" ? "supported" : `catalog-only: ${playbook.support_note ?? "not launch executable"}`;
|
|
230
|
+
const limits = appliedPlaybookLimits(playbook);
|
|
231
|
+
const fanout = limits ? ` · default limits ${JSON.stringify(limits)}` : "";
|
|
184
232
|
return [
|
|
185
233
|
` ${playbook.name} (${playbook.slug})`,
|
|
186
234
|
` ${playbook.outcome}`,
|
|
187
|
-
` ${playbook.current_quote.step_count} step${playbook.current_quote.step_count === 1 ? "" : "s"} · ${money(playbook.current_quote.estimated_cost_usd)} est. · ${support}`,
|
|
235
|
+
` ${playbook.current_quote.step_count} step${playbook.current_quote.step_count === 1 ? "" : "s"} · ${money(playbook.current_quote.estimated_cost_usd)} est. · ${support}${fanout}`,
|
|
188
236
|
` Favorites: ${playbook.stats?.favorite_count ?? 0} · Rating: ${playbook.stats?.average_rating ? `${playbook.stats.average_rating}/5` : "unrated"} (${playbook.stats?.rating_count ?? 0})`,
|
|
189
237
|
` Inspect: get_playbook({ slug: "${playbook.slug}" })`,
|
|
190
238
|
].join("\n");
|
|
@@ -228,14 +276,25 @@ function formatPlaybook(playbook: PlaybookRecord) {
|
|
|
228
276
|
}
|
|
229
277
|
|
|
230
278
|
if (playbook.budget_notes.length) lines.push("", "Budget notes:", ...playbook.budget_notes.map((note) => ` - ${note}`));
|
|
279
|
+
const limits = appliedPlaybookLimits(playbook);
|
|
280
|
+
const controls = fanoutControls(playbook);
|
|
281
|
+
if (controls.length) {
|
|
282
|
+
lines.push("", "Fanout controls:");
|
|
283
|
+
for (const control of controls) {
|
|
284
|
+
const current = limits?.[control.key] ?? control.default;
|
|
285
|
+
lines.push(` - ${control.key}: default ${control.default}, current ${current}, max ${control.max} — ${control.description}`);
|
|
286
|
+
}
|
|
287
|
+
if (limits) lines.push(` Override by passing limits, for example limits: ${JSON.stringify(limits)}`);
|
|
288
|
+
}
|
|
231
289
|
if (playbook.risks.length) lines.push("", "Risks:", ...playbook.risks.map((risk) => ` - ${risk}`));
|
|
232
290
|
|
|
291
|
+
const runLimits = formatLimitsField(limits);
|
|
233
292
|
lines.push(
|
|
234
293
|
"",
|
|
235
294
|
"Sample input:",
|
|
236
295
|
JSON.stringify(playbook.sample_input, null, 2),
|
|
237
296
|
"",
|
|
238
|
-
`Run quote: run_playbook({ slug: "${playbook.slug}", input: <input>, budget: ${Math.max(1, Math.ceil(q.estimated_cost_usd + 1))} })`,
|
|
297
|
+
`Run quote: run_playbook({ slug: "${playbook.slug}", input: <input>, budget: ${Math.max(1, Math.ceil(q.estimated_cost_usd + 1))}${runLimits ? `, ${runLimits}` : ""} })`,
|
|
239
298
|
);
|
|
240
299
|
return lines.join("\n");
|
|
241
300
|
}
|
|
@@ -260,6 +319,18 @@ function formatRunConfirmationCommand(slug: string, method: string | undefined,
|
|
|
260
319
|
|
|
261
320
|
function formatQuoteNotes(playbook: PlaybookRecord): string[] {
|
|
262
321
|
const lines: string[] = [];
|
|
322
|
+
const controls = fanoutControls(playbook);
|
|
323
|
+
if (controls.length > 0) {
|
|
324
|
+
const limits = appliedPlaybookLimits(playbook) ?? {};
|
|
325
|
+
lines.push("", "Fanout controls:");
|
|
326
|
+
for (const control of controls) {
|
|
327
|
+
const current = limits[control.key] ?? control.default;
|
|
328
|
+
lines.push(` - ${control.key}: current ${current}, default ${control.default}, max ${control.max}`);
|
|
329
|
+
}
|
|
330
|
+
if (Object.keys(limits).length > 0) {
|
|
331
|
+
lines.push(` Override by editing limits in the confirmation call: ${JSON.stringify(limits)}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
263
334
|
if (playbook.budget_notes.length > 0) {
|
|
264
335
|
lines.push("", "Budget and fanout notes:", ...playbook.budget_notes.map((note) => ` - ${note}`));
|
|
265
336
|
}
|
|
@@ -356,6 +427,39 @@ function stepInput(baseInput: Record<string, unknown>, step: PlaybookStepQuote,
|
|
|
356
427
|
if (step.agent_slug === "scan-contract-for-risks" && !("file" in input) && input.contract) {
|
|
357
428
|
input.file = input.contract;
|
|
358
429
|
}
|
|
430
|
+
if (step.agent_slug === "marketing-copy-council" && !("brief" in input)) {
|
|
431
|
+
input.brief = input.product ?? input.context ?? input.text;
|
|
432
|
+
}
|
|
433
|
+
if (step.agent_slug === "write-landing-page-copy") {
|
|
434
|
+
const previousOutputs = Array.isArray(input.previous_outputs) ? input.previous_outputs : [];
|
|
435
|
+
const councilOutput = previousOutputs.find((item) => {
|
|
436
|
+
const record = item && typeof item === "object" ? item as Record<string, unknown> : null;
|
|
437
|
+
return record?.step === "council" || record?.step === "marketing-copy-council";
|
|
438
|
+
}) ?? previousOutputs[previousOutputs.length - 1];
|
|
439
|
+
const context = {
|
|
440
|
+
brief: input.brief,
|
|
441
|
+
product_slug: input.product_slug,
|
|
442
|
+
council_output: councilOutput,
|
|
443
|
+
previous_outputs: previousOutputs,
|
|
444
|
+
};
|
|
445
|
+
input.product = input.product ?? input.product_name ?? input.product_slug ?? "Landing page";
|
|
446
|
+
input.context = input.context ?? JSON.stringify(context);
|
|
447
|
+
}
|
|
448
|
+
if (step.agent_slug === "publish-html-to-a-public-url") {
|
|
449
|
+
const previousOutputs = Array.isArray(input.previous_outputs) ? input.previous_outputs : [];
|
|
450
|
+
const writerOutput = previousOutputs[previousOutputs.length - 1];
|
|
451
|
+
const outputRecord = writerOutput && typeof writerOutput === "object"
|
|
452
|
+
? writerOutput as Record<string, unknown>
|
|
453
|
+
: null;
|
|
454
|
+
const output = outputRecord?.output && typeof outputRecord.output === "object"
|
|
455
|
+
? outputRecord.output as Record<string, unknown>
|
|
456
|
+
: outputRecord?.output;
|
|
457
|
+
input.html = input.html
|
|
458
|
+
?? (output && typeof output === "object" ? (output as Record<string, unknown>).html : undefined)
|
|
459
|
+
?? (typeof output === "string" ? output : undefined)
|
|
460
|
+
?? JSON.stringify(output ?? writerOutput ?? {});
|
|
461
|
+
input.slug = input.slug ?? input.product_slug;
|
|
462
|
+
}
|
|
359
463
|
if (step.agent_slug === "place-search") {
|
|
360
464
|
input.location = input.location ?? input.zip;
|
|
361
465
|
}
|
|
@@ -660,7 +764,9 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
660
764
|
budget: effectiveBudget,
|
|
661
765
|
limits: effectiveLimits,
|
|
662
766
|
});
|
|
663
|
-
|
|
767
|
+
if (quoted?.playbook) {
|
|
768
|
+
playbook = quoted.playbook;
|
|
769
|
+
}
|
|
664
770
|
} catch {
|
|
665
771
|
// Quote analytics should not block the user-facing quote.
|
|
666
772
|
}
|
|
@@ -673,12 +779,13 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
673
779
|
return text(`Cannot run ${playbook.slug}; child-agent issues must be fixed first:\n${playbook.current_quote.blocking_issues.map((issue) => `- ${issue}`).join("\n")}`);
|
|
674
780
|
}
|
|
675
781
|
|
|
782
|
+
const runLimits = appliedPlaybookLimits(playbook) ?? effectiveLimits;
|
|
676
783
|
const estimatedCost = playbook.current_quote.estimated_cost_usd;
|
|
677
784
|
const spendCheck = canSpend({ method: spendMethod, amountUsd: Math.min(effectiveBudget, estimatedCost) });
|
|
678
785
|
if (!spendCheck.ok) return text(spendCheck.message);
|
|
679
786
|
|
|
680
787
|
if (estimatedCost > effectiveBudget) {
|
|
681
|
-
pendingPlaybookRuns.set(playbook.slug, { playbook, input: effectiveInput, budget: effectiveBudget, method, limits:
|
|
788
|
+
pendingPlaybookRuns.set(playbook.slug, { playbook, input: effectiveInput, budget: effectiveBudget, method, limits: runLimits });
|
|
682
789
|
const cheaperAlternatives = await formatCheaperAlternatives(playbook.slug, effectiveBudget);
|
|
683
790
|
return text([
|
|
684
791
|
`${playbook.name} quote exceeds the budget.`,
|
|
@@ -690,12 +797,12 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
690
797
|
...(cheaperAlternatives ? ["", cheaperAlternatives] : []),
|
|
691
798
|
"",
|
|
692
799
|
`Try a higher budget or reduced limits, then call:`,
|
|
693
|
-
formatRunConfirmationCommand(playbook.slug, method, Math.ceil(estimatedCost + 1),
|
|
800
|
+
formatRunConfirmationCommand(playbook.slug, method, Math.ceil(estimatedCost + 1), runLimits),
|
|
694
801
|
].join("\n"));
|
|
695
802
|
}
|
|
696
803
|
|
|
697
804
|
if ((requiresSpendConfirmation() || requiresPolicyConfirmation(spendMethod, estimatedCost)) && !confirmed) {
|
|
698
|
-
pendingPlaybookRuns.set(playbook.slug, { playbook, input: effectiveInput, budget: effectiveBudget, method, limits:
|
|
805
|
+
pendingPlaybookRuns.set(playbook.slug, { playbook, input: effectiveInput, budget: effectiveBudget, method, limits: runLimits });
|
|
699
806
|
return text([
|
|
700
807
|
`Ready to run ${playbook.name}`,
|
|
701
808
|
"",
|
|
@@ -711,7 +818,7 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
711
818
|
...formatQuoteNotes(playbook),
|
|
712
819
|
"",
|
|
713
820
|
"To proceed, call:",
|
|
714
|
-
formatRunConfirmationCommand(playbook.slug, method, effectiveBudget,
|
|
821
|
+
formatRunConfirmationCommand(playbook.slug, method, effectiveBudget, runLimits),
|
|
715
822
|
"",
|
|
716
823
|
"To cancel, do nothing.",
|
|
717
824
|
].join("\n"));
|
|
@@ -762,7 +869,7 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
762
869
|
slug: playbook.slug,
|
|
763
870
|
input: processedInput,
|
|
764
871
|
budget: effectiveBudget,
|
|
765
|
-
limits:
|
|
872
|
+
limits: runLimits,
|
|
766
873
|
}),
|
|
767
874
|
completedStepIds: new Set<string>(),
|
|
768
875
|
existingOutputs: [] as Array<{ step: string; job_id?: string; output?: unknown }>,
|
|
@@ -773,6 +880,14 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
773
880
|
|
|
774
881
|
let charged = runState.chargedUsd;
|
|
775
882
|
const childOutputs: Array<{ step: string; job_id?: string; output?: unknown }> = [...runState.existingOutputs];
|
|
883
|
+
let linkSpendRequestId: string | undefined;
|
|
884
|
+
let linkNetworkId: string | undefined;
|
|
885
|
+
const linkApprovalContext = buildPlaybookLinkApprovalContext({
|
|
886
|
+
playbook,
|
|
887
|
+
budget: effectiveBudget,
|
|
888
|
+
stepCount: playbook.current_quote.step_count,
|
|
889
|
+
runId: runState.run_id,
|
|
890
|
+
});
|
|
776
891
|
|
|
777
892
|
for (const [iteration, step] of runState.steps.entries()) {
|
|
778
893
|
if (runState.completedStepIds.has(step.id)) {
|
|
@@ -824,19 +939,44 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
824
939
|
let schemaError: { status?: number; message?: string } | undefined;
|
|
825
940
|
let usedPaidMethod = false;
|
|
826
941
|
const executeChild = async (childInput: Record<string, unknown>) => {
|
|
942
|
+
const body = { input: childInput, playbook_context: playbookContext };
|
|
827
943
|
try {
|
|
828
944
|
return await apiPost<Record<string, unknown>>(
|
|
829
945
|
`/agents/${step.agent_id}/run`,
|
|
830
|
-
|
|
946
|
+
body,
|
|
831
947
|
{ ensureConsumerPrincipal: true },
|
|
832
948
|
);
|
|
833
949
|
} catch (err) {
|
|
834
950
|
const status = (err as { status?: number })?.status;
|
|
835
951
|
if (status !== 402) throw err;
|
|
836
952
|
usedPaidMethod = true;
|
|
953
|
+
if ((method ?? spendMethod) === "link") {
|
|
954
|
+
const linkConfig = getLinkConfig();
|
|
955
|
+
if (!linkConfig) {
|
|
956
|
+
throw new Error('Payment method "link" is not configured. Run wallet_setup({ action: "add-link" }).');
|
|
957
|
+
}
|
|
958
|
+
const challenge = (err as { headers?: Headers })?.headers?.get("www-authenticate");
|
|
959
|
+
if (!challenge && !linkNetworkId) {
|
|
960
|
+
throw new Error("Link payment challenge did not include a WWW-Authenticate header.");
|
|
961
|
+
}
|
|
962
|
+
linkNetworkId = linkNetworkId ?? await decodeMppNetworkId(challenge!);
|
|
963
|
+
linkSpendRequestId = linkSpendRequestId ?? await ensureApprovedLinkSpendRequest({
|
|
964
|
+
amount: cents(effectiveBudget),
|
|
965
|
+
currency: "usd",
|
|
966
|
+
context: linkApprovalContext,
|
|
967
|
+
expiresAt: Math.floor(Date.now() / 1000) + 3600,
|
|
968
|
+
networkId: linkNetworkId,
|
|
969
|
+
paymentMethodId: linkConfig.paymentMethodId,
|
|
970
|
+
});
|
|
971
|
+
return await apiPostWithApprovedLinkSpendRequest<Record<string, unknown>>(
|
|
972
|
+
`/agents/${step.agent_id}/run`,
|
|
973
|
+
body,
|
|
974
|
+
linkSpendRequestId,
|
|
975
|
+
);
|
|
976
|
+
}
|
|
837
977
|
return await apiPostWithPayment<Record<string, unknown>>(
|
|
838
978
|
`/agents/${step.agent_id}/run`,
|
|
839
|
-
|
|
979
|
+
body,
|
|
840
980
|
method,
|
|
841
981
|
);
|
|
842
982
|
}
|
|
@@ -844,9 +984,26 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
844
984
|
try {
|
|
845
985
|
result = await executeChild(payload);
|
|
846
986
|
} catch (err) {
|
|
987
|
+
if (err instanceof LinkApprovalRequiredError) {
|
|
988
|
+
await recordStep(runState.run_id, step.id, {
|
|
989
|
+
status: "pending",
|
|
990
|
+
agent_id: step.agent_id,
|
|
991
|
+
provider_id: step.provider_id,
|
|
992
|
+
consumption_mode: "not_charged",
|
|
993
|
+
error_code: null,
|
|
994
|
+
failure_message: null,
|
|
995
|
+
});
|
|
996
|
+
await updateRun(runState.run_id, {
|
|
997
|
+
status: "paused",
|
|
998
|
+
error_code: "LINK_APPROVAL_REQUIRED",
|
|
999
|
+
failure_message: "Approve the Link spend request, then resume the playbook run.",
|
|
1000
|
+
});
|
|
1001
|
+
const receipt = await apiGet<PlaybookRunReceipt>(`/playbook-runs/${runState.run_id}`);
|
|
1002
|
+
return text(`${formatReceipt(receipt)}\n\n${err.message}`);
|
|
1003
|
+
}
|
|
847
1004
|
const apiErr = err as { status?: number; message?: string };
|
|
848
|
-
const validationFailed = apiErr?.status === 400
|
|
849
|
-
&& /missing required field|validation failed|invalid input|schema/i.test(apiErr.message ?? "");
|
|
1005
|
+
const validationFailed = (apiErr?.status === 400 || apiErr?.status === 422)
|
|
1006
|
+
&& /missing required field|validation failed|invalid input|schema|provide at least one/i.test(apiErr.message ?? "");
|
|
850
1007
|
if (validationFailed) {
|
|
851
1008
|
try {
|
|
852
1009
|
const agent = await apiGet<AgentSchemaDetails>(`/agents/${step.agent_id}`);
|
|
@@ -906,15 +1063,10 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
906
1063
|
}
|
|
907
1064
|
|
|
908
1065
|
const jobId = (result.job_id as string) ?? "";
|
|
909
|
-
const resultAgentId = (result.agent_id as string) ?? step.agent_id;
|
|
910
1066
|
const status = result.status as string;
|
|
911
1067
|
let output = result.output;
|
|
912
1068
|
let errorCode = result.error_code as string | undefined;
|
|
913
1069
|
|
|
914
|
-
if (result.feedback_token && jobId) {
|
|
915
|
-
storeFeedbackToken(jobId, result.feedback_token as string, resultAgentId);
|
|
916
|
-
}
|
|
917
|
-
|
|
918
1070
|
if (status === "processing" && jobId) {
|
|
919
1071
|
const pollResult = await pollJobUntilDone(jobId, method);
|
|
920
1072
|
output = pollResult.output;
|
|
@@ -1020,14 +1172,16 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
1020
1172
|
});
|
|
1021
1173
|
const receipt = await apiGet<PlaybookRunReceipt>(`/playbook-runs/${runState.run_id}`);
|
|
1022
1174
|
pendingPlaybookRuns.delete(playbook.slug);
|
|
1175
|
+
if ((method ?? spendMethod) === "link" && linkSpendRequestId) {
|
|
1176
|
+
setPendingLinkSpendRequest(null);
|
|
1177
|
+
}
|
|
1023
1178
|
|
|
1024
1179
|
const runBlocks = childOutputs.length > 0
|
|
1025
1180
|
? childOutputs.map((item) => item.output ? formatRunResult({ status: "success", job_id: item.job_id, output: item.output }, { paymentMethod: method }) : "").filter(Boolean)
|
|
1026
1181
|
: [];
|
|
1027
1182
|
return multiText(
|
|
1028
1183
|
uploadSummary ? `${uploadSummary}\n\n${formatReceipt(receipt)}` : formatReceipt(receipt),
|
|
1029
|
-
...runBlocks
|
|
1030
|
-
`IMPORTANT: After presenting the result above, you MUST ask the user: "Paid ${money(receipt.charged_usd)} across ${receipt.steps.filter((s) => s.status === "succeeded").length} steps. Was this playbook useful? I can save it with favorite_playbook({ slug: "${playbook.slug}" }) or record feedback with rate_playbook({ slug: "${playbook.slug}", run_id: "${receipt.run_id}", rating: <1-5>, useful: true, comment: "..." })."`,
|
|
1184
|
+
...runBlocks
|
|
1031
1185
|
);
|
|
1032
1186
|
},
|
|
1033
1187
|
);
|
|
@@ -1053,39 +1207,6 @@ export function registerPlaybookTools(server: McpServer): void {
|
|
|
1053
1207
|
},
|
|
1054
1208
|
);
|
|
1055
1209
|
|
|
1056
|
-
server.tool(
|
|
1057
|
-
"rate_playbook",
|
|
1058
|
-
"Record feedback on an Agent Wonderland Playbook after a run. Provide a rating, useful flag, comment, or any combination.",
|
|
1059
|
-
{
|
|
1060
|
-
playbook_id: z.string().optional(),
|
|
1061
|
-
slug: z.string().optional(),
|
|
1062
|
-
run_id: z.string().optional(),
|
|
1063
|
-
rating: z.number().int().min(1).max(5).optional(),
|
|
1064
|
-
useful: z.boolean().optional(),
|
|
1065
|
-
comment: z.string().max(2000).optional(),
|
|
1066
|
-
},
|
|
1067
|
-
async ({ playbook_id, slug, run_id, rating, useful, comment }) => {
|
|
1068
|
-
const id = slug ?? playbook_id;
|
|
1069
|
-
if (!id) return text("Provide slug or playbook_id.");
|
|
1070
|
-
if (rating == null && useful == null && !comment?.trim()) {
|
|
1071
|
-
return text("Provide rating, useful, or comment.");
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
const result = await apiPost<{ ok: boolean; feedback_id: string; playbook_slug: string }>(
|
|
1075
|
-
`/playbooks/${encodeURIComponent(id)}/feedback`,
|
|
1076
|
-
{
|
|
1077
|
-
run_id,
|
|
1078
|
-
rating,
|
|
1079
|
-
useful,
|
|
1080
|
-
comment,
|
|
1081
|
-
},
|
|
1082
|
-
{ ensureConsumerPrincipal: true },
|
|
1083
|
-
);
|
|
1084
|
-
|
|
1085
|
-
return text(`Recorded feedback for ${result.playbook_slug}.`);
|
|
1086
|
-
},
|
|
1087
|
-
);
|
|
1088
|
-
|
|
1089
1210
|
server.tool(
|
|
1090
1211
|
"get_playbook_run",
|
|
1091
1212
|
"Inspect current or historical Agent Wonderland Playbook run state, including completed steps, charged/refunded amount, partial output, and resume command when paused.",
|