@wiro-ai/wiro-mcp 1.3.4 → 1.4.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/README.md +183 -16
- package/dist/client.d.ts +16 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +126 -24
- package/dist/client.js.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +29 -0
- package/dist/instructions.js.map +1 -0
- package/dist/playbooks.d.ts +8 -0
- package/dist/playbooks.d.ts.map +1 -0
- package/dist/playbooks.js +667 -0
- package/dist/playbooks.js.map +1 -0
- package/dist/prompts.d.ts +4 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +133 -0
- package/dist/prompts.js.map +1 -0
- package/dist/server.d.ts +15 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +12 -7
- package/dist/server.js.map +1 -1
- package/dist/tools/apply-agent-skills.d.ts +4 -0
- package/dist/tools/apply-agent-skills.d.ts.map +1 -0
- package/dist/tools/apply-agent-skills.js +601 -0
- package/dist/tools/apply-agent-skills.js.map +1 -0
- package/dist/tools/cancel-agent-message.d.ts +4 -0
- package/dist/tools/cancel-agent-message.d.ts.map +1 -0
- package/dist/tools/cancel-agent-message.js +126 -0
- package/dist/tools/cancel-agent-message.js.map +1 -0
- package/dist/tools/cancel-task.js +3 -3
- package/dist/tools/cancel-task.js.map +1 -1
- package/dist/tools/clone-workflow.d.ts +4 -0
- package/dist/tools/clone-workflow.d.ts.map +1 -0
- package/dist/tools/clone-workflow.js +83 -0
- package/dist/tools/clone-workflow.js.map +1 -0
- package/dist/tools/connect-agent-oauth.d.ts +4 -0
- package/dist/tools/connect-agent-oauth.d.ts.map +1 -0
- package/dist/tools/connect-agent-oauth.js +508 -0
- package/dist/tools/connect-agent-oauth.js.map +1 -0
- package/dist/tools/control-agent.d.ts +4 -0
- package/dist/tools/control-agent.d.ts.map +1 -0
- package/dist/tools/control-agent.js +289 -0
- package/dist/tools/control-agent.js.map +1 -0
- package/dist/tools/create-workflow.d.ts +100 -0
- package/dist/tools/create-workflow.d.ts.map +1 -0
- package/dist/tools/create-workflow.js +102 -0
- package/dist/tools/create-workflow.js.map +1 -0
- package/dist/tools/delete-agent-session.d.ts +6 -0
- package/dist/tools/delete-agent-session.d.ts.map +1 -0
- package/dist/tools/delete-agent-session.js +134 -0
- package/dist/tools/delete-agent-session.js.map +1 -0
- package/dist/tools/delete-agent.d.ts +4 -0
- package/dist/tools/delete-agent.d.ts.map +1 -0
- package/dist/tools/delete-agent.js +145 -0
- package/dist/tools/delete-agent.js.map +1 -0
- package/dist/tools/delete-workflow.d.ts +4 -0
- package/dist/tools/delete-workflow.d.ts.map +1 -0
- package/dist/tools/delete-workflow.js +77 -0
- package/dist/tools/delete-workflow.js.map +1 -0
- package/dist/tools/deploy-agent.d.ts +4 -0
- package/dist/tools/deploy-agent.d.ts.map +1 -0
- package/dist/tools/deploy-agent.js +463 -0
- package/dist/tools/deploy-agent.js.map +1 -0
- package/dist/tools/disconnect-agent-oauth.d.ts +4 -0
- package/dist/tools/disconnect-agent-oauth.d.ts.map +1 -0
- package/dist/tools/disconnect-agent-oauth.js +118 -0
- package/dist/tools/disconnect-agent-oauth.js.map +1 -0
- package/dist/tools/get-account.d.ts +4 -0
- package/dist/tools/get-account.d.ts.map +1 -0
- package/dist/tools/get-account.js +112 -0
- package/dist/tools/get-account.js.map +1 -0
- package/dist/tools/get-agent-activity.d.ts +4 -0
- package/dist/tools/get-agent-activity.d.ts.map +1 -0
- package/dist/tools/get-agent-activity.js +383 -0
- package/dist/tools/get-agent-activity.js.map +1 -0
- package/dist/tools/get-agent-conversation.d.ts +4 -0
- package/dist/tools/get-agent-conversation.d.ts.map +1 -0
- package/dist/tools/get-agent-conversation.js +228 -0
- package/dist/tools/get-agent-conversation.js.map +1 -0
- package/dist/tools/get-agent-message.d.ts +4 -0
- package/dist/tools/get-agent-message.d.ts.map +1 -0
- package/dist/tools/get-agent-message.js +68 -0
- package/dist/tools/get-agent-message.js.map +1 -0
- package/dist/tools/get-agent-template.d.ts +4 -0
- package/dist/tools/get-agent-template.d.ts.map +1 -0
- package/dist/tools/get-agent-template.js +128 -0
- package/dist/tools/get-agent-template.js.map +1 -0
- package/dist/tools/get-model-schema.d.ts +5 -1
- package/dist/tools/get-model-schema.d.ts.map +1 -1
- package/dist/tools/get-model-schema.js +138 -8
- package/dist/tools/get-model-schema.js.map +1 -1
- package/dist/tools/get-my-agent.d.ts +4 -0
- package/dist/tools/get-my-agent.d.ts.map +1 -0
- package/dist/tools/get-my-agent.js +117 -0
- package/dist/tools/get-my-agent.js.map +1 -0
- package/dist/tools/get-playbook.d.ts +3 -0
- package/dist/tools/get-playbook.d.ts.map +1 -0
- package/dist/tools/get-playbook.js +50 -0
- package/dist/tools/get-playbook.js.map +1 -0
- package/dist/tools/get-task-price.d.ts +1 -1
- package/dist/tools/get-task-price.d.ts.map +1 -1
- package/dist/tools/get-task-price.js +64 -16
- package/dist/tools/get-task-price.js.map +1 -1
- package/dist/tools/get-task.d.ts.map +1 -1
- package/dist/tools/get-task.js +26 -9
- package/dist/tools/get-task.js.map +1 -1
- package/dist/tools/get-workflow.d.ts +9 -0
- package/dist/tools/get-workflow.d.ts.map +1 -0
- package/dist/tools/get-workflow.js +105 -0
- package/dist/tools/get-workflow.js.map +1 -0
- package/dist/tools/index.d.ts +63 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +131 -14
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kill-task.d.ts.map +1 -1
- package/dist/tools/kill-task.js +10 -6
- package/dist/tools/kill-task.js.map +1 -1
- package/dist/tools/list-agent-skills.d.ts +4 -0
- package/dist/tools/list-agent-skills.d.ts.map +1 -0
- package/dist/tools/list-agent-skills.js +242 -0
- package/dist/tools/list-agent-skills.js.map +1 -0
- package/dist/tools/list-agent-templates.d.ts +4 -0
- package/dist/tools/list-agent-templates.d.ts.map +1 -0
- package/dist/tools/list-agent-templates.js +110 -0
- package/dist/tools/list-agent-templates.js.map +1 -0
- package/dist/tools/list-my-agents.d.ts +4 -0
- package/dist/tools/list-my-agents.d.ts.map +1 -0
- package/dist/tools/list-my-agents.js +93 -0
- package/dist/tools/list-my-agents.js.map +1 -0
- package/dist/tools/list-studio-results.d.ts +4 -0
- package/dist/tools/list-studio-results.d.ts.map +1 -0
- package/dist/tools/list-studio-results.js +95 -0
- package/dist/tools/list-studio-results.js.map +1 -0
- package/dist/tools/list-tasks.d.ts.map +1 -1
- package/dist/tools/list-tasks.js +7 -5
- package/dist/tools/list-tasks.js.map +1 -1
- package/dist/tools/list-workflow-runs.d.ts +4 -0
- package/dist/tools/list-workflow-runs.d.ts.map +1 -0
- package/dist/tools/list-workflow-runs.js +110 -0
- package/dist/tools/list-workflow-runs.js.map +1 -0
- package/dist/tools/list-workflows.d.ts +4 -0
- package/dist/tools/list-workflows.d.ts.map +1 -0
- package/dist/tools/list-workflows.js +143 -0
- package/dist/tools/list-workflows.js.map +1 -0
- package/dist/tools/manage-agent-billing.d.ts +4 -0
- package/dist/tools/manage-agent-billing.d.ts.map +1 -0
- package/dist/tools/manage-agent-billing.js +788 -0
- package/dist/tools/manage-agent-billing.js.map +1 -0
- package/dist/tools/manage-agent-custom-skill.d.ts +4 -0
- package/dist/tools/manage-agent-custom-skill.d.ts.map +1 -0
- package/dist/tools/manage-agent-custom-skill.js +531 -0
- package/dist/tools/manage-agent-custom-skill.js.map +1 -0
- package/dist/tools/recommend-model.d.ts.map +1 -1
- package/dist/tools/recommend-model.js +6 -5
- package/dist/tools/recommend-model.js.map +1 -1
- package/dist/tools/resume-workflow-run.d.ts +4 -0
- package/dist/tools/resume-workflow-run.d.ts.map +1 -0
- package/dist/tools/resume-workflow-run.js +69 -0
- package/dist/tools/resume-workflow-run.js.map +1 -0
- package/dist/tools/run-model.d.ts.map +1 -1
- package/dist/tools/run-model.js +40 -4
- package/dist/tools/run-model.js.map +1 -1
- package/dist/tools/run-workflow.d.ts +6 -0
- package/dist/tools/run-workflow.d.ts.map +1 -0
- package/dist/tools/run-workflow.js +132 -0
- package/dist/tools/run-workflow.js.map +1 -0
- package/dist/tools/save-workflow-from-agent.d.ts +4 -0
- package/dist/tools/save-workflow-from-agent.d.ts.map +1 -0
- package/dist/tools/save-workflow-from-agent.js +280 -0
- package/dist/tools/save-workflow-from-agent.js.map +1 -0
- package/dist/tools/schemas.d.ts +182 -115
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +26 -6
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/search-models.d.ts.map +1 -1
- package/dist/tools/search-models.js +9 -6
- package/dist/tools/search-models.js.map +1 -1
- package/dist/tools/send-agent-message.d.ts +4 -0
- package/dist/tools/send-agent-message.d.ts.map +1 -0
- package/dist/tools/send-agent-message.js +156 -0
- package/dist/tools/send-agent-message.js.map +1 -0
- package/dist/tools/set-agent-credentials.d.ts +4 -0
- package/dist/tools/set-agent-credentials.d.ts.map +1 -0
- package/dist/tools/set-agent-credentials.js +540 -0
- package/dist/tools/set-agent-credentials.js.map +1 -0
- package/dist/tools/update-agent.d.ts +4 -0
- package/dist/tools/update-agent.d.ts.map +1 -0
- package/dist/tools/update-agent.js +179 -0
- package/dist/tools/update-agent.js.map +1 -0
- package/dist/tools/update-workflow.d.ts +201 -0
- package/dist/tools/update-workflow.d.ts.map +1 -0
- package/dist/tools/update-workflow.js +168 -0
- package/dist/tools/update-workflow.js.map +1 -0
- package/dist/tools/upload-file.d.ts.map +1 -1
- package/dist/tools/upload-file.js +12 -7
- package/dist/tools/upload-file.js.map +1 -1
- package/dist/tools/wait-for-task.d.ts.map +1 -1
- package/dist/tools/wait-for-task.js +20 -2
- package/dist/tools/wait-for-task.js.map +1 -1
- package/dist/tools/wait-for-workflow-run.d.ts +259 -0
- package/dist/tools/wait-for-workflow-run.d.ts.map +1 -0
- package/dist/tools/wait-for-workflow-run.js +190 -0
- package/dist/tools/wait-for-workflow-run.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/agent-catalog.d.ts +1256 -0
- package/dist/utils/agent-catalog.d.ts.map +1 -0
- package/dist/utils/agent-catalog.js +387 -0
- package/dist/utils/agent-catalog.js.map +1 -0
- package/dist/utils/agent-config.d.ts +443 -0
- package/dist/utils/agent-config.d.ts.map +1 -0
- package/dist/utils/agent-config.js +1676 -0
- package/dist/utils/agent-config.js.map +1 -0
- package/dist/utils/agent-message.d.ts +112 -0
- package/dist/utils/agent-message.d.ts.map +1 -0
- package/dist/utils/agent-message.js +243 -0
- package/dist/utils/agent-message.js.map +1 -0
- package/dist/utils/agent-targets.d.ts +76 -0
- package/dist/utils/agent-targets.d.ts.map +1 -0
- package/dist/utils/agent-targets.js +412 -0
- package/dist/utils/agent-targets.js.map +1 -0
- package/dist/utils/agent-turns.d.ts +354 -0
- package/dist/utils/agent-turns.d.ts.map +1 -0
- package/dist/utils/agent-turns.js +660 -0
- package/dist/utils/agent-turns.js.map +1 -0
- package/dist/utils/api.d.ts +40 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +111 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/format.d.ts +65 -2
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +583 -81
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/json-schema-dialect.d.ts +8 -1
- package/dist/utils/json-schema-dialect.d.ts.map +1 -1
- package/dist/utils/json-schema-dialect.js +23 -1
- package/dist/utils/json-schema-dialect.js.map +1 -1
- package/dist/utils/media.d.ts +28 -0
- package/dist/utils/media.d.ts.map +1 -0
- package/dist/utils/media.js +65 -0
- package/dist/utils/media.js.map +1 -0
- package/dist/utils/poll.d.ts +39 -0
- package/dist/utils/poll.d.ts.map +1 -0
- package/dist/utils/poll.js +111 -0
- package/dist/utils/poll.js.map +1 -0
- package/dist/utils/remote-file.js +2 -2
- package/dist/utils/remote-file.js.map +1 -1
- package/dist/utils/structured.d.ts +4 -0
- package/dist/utils/structured.d.ts.map +1 -1
- package/dist/utils/structured.js +23 -7
- package/dist/utils/structured.js.map +1 -1
- package/dist/utils/studio-runs.d.ts +87 -0
- package/dist/utils/studio-runs.d.ts.map +1 -0
- package/dist/utils/studio-runs.js +167 -0
- package/dist/utils/studio-runs.js.map +1 -0
- package/dist/utils/user-agent.d.ts +1199 -0
- package/dist/utils/user-agent.d.ts.map +1 -0
- package/dist/utils/user-agent.js +897 -0
- package/dist/utils/user-agent.js.map +1 -0
- package/dist/utils/workflow-agent.d.ts +151 -0
- package/dist/utils/workflow-agent.d.ts.map +1 -0
- package/dist/utils/workflow-agent.js +292 -0
- package/dist/utils/workflow-agent.js.map +1 -0
- package/dist/utils/workflow-artifacts.d.ts +160 -0
- package/dist/utils/workflow-artifacts.d.ts.map +1 -0
- package/dist/utils/workflow-artifacts.js +801 -0
- package/dist/utils/workflow-artifacts.js.map +1 -0
- package/dist/utils/workflow-save.d.ts +120 -0
- package/dist/utils/workflow-save.d.ts.map +1 -0
- package/dist/utils/workflow-save.js +728 -0
- package/dist/utils/workflow-save.js.map +1 -0
- package/dist/utils/workflow.d.ts +564 -0
- package/dist/utils/workflow.d.ts.map +1 -0
- package/dist/utils/workflow.js +712 -0
- package/dist/utils/workflow.js.map +1 -0
- package/dist/utils/workspace.d.ts +38 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +90 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { asString, errorResult, errorText, isPlainObject, requireApiSuccess, toNumber, unixToIso, } from '../utils/api.js';
|
|
3
|
+
import { agentStatusLabel, agentStatusOf, checkSkillSet, credentialsNeededFor, describeSkillsApplyError, diffSkills, enabledSkillNames, estimateProratedUsd, formatUsd, isAgentRunning, loadSkillRegistry, newSkillChangeKey, parseSkillChangeKey, planBilling, planSkillChanges, readUserAgent, restartNote, skillChangeDigest, skillPriceTag, workspaceHint, } from '../utils/agent-config.js';
|
|
4
|
+
import { asTier, outcomeUnknown, toTierPricing } from '../utils/user-agent.js';
|
|
5
|
+
import { DEFAULT_WAIT_SECONDS } from '../utils/poll.js';
|
|
6
|
+
import { isPlainTeamMember, teamAgentBillingRefused } from '../utils/workspace.js';
|
|
7
|
+
import { nextActionSchema } from './schemas.js';
|
|
8
|
+
const pricingSchema = z.object({
|
|
9
|
+
currentMonthlyUsd: z.number().nullable(),
|
|
10
|
+
newMonthlyUsd: z.number().nullable(),
|
|
11
|
+
deltaMonthlyUsd: z.number().nullable(),
|
|
12
|
+
currentMonthlyCredits: z.number().nullable(),
|
|
13
|
+
newMonthlyCredits: z.number().nullable(),
|
|
14
|
+
deltaMonthlyCredits: z.number().nullable(),
|
|
15
|
+
note: z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
const outputSchema = {
|
|
18
|
+
agentGuid: z.string(),
|
|
19
|
+
status: z.enum(['quote', 'applied', 'no_change', 'refused']),
|
|
20
|
+
idempotencyKey: z.string().optional(),
|
|
21
|
+
priceChanged: z.boolean().optional(),
|
|
22
|
+
changes: z.object({
|
|
23
|
+
enable: z.array(z.string()),
|
|
24
|
+
disable: z.array(z.string()),
|
|
25
|
+
addedDependencies: z.array(z.string()),
|
|
26
|
+
alreadySet: z.array(z.string()),
|
|
27
|
+
}),
|
|
28
|
+
tier: z.object({
|
|
29
|
+
current: z.string().nullable(),
|
|
30
|
+
next: z.string().nullable(),
|
|
31
|
+
}),
|
|
32
|
+
pricing: pricingSchema.optional(),
|
|
33
|
+
billing: z.object({
|
|
34
|
+
plan: z.enum(['prepaid', 'card', 'none']),
|
|
35
|
+
subscriptionStatus: z.string().nullable(),
|
|
36
|
+
wallet: z.enum(['team', 'personal']),
|
|
37
|
+
estimatedChargeNowUsd: z.number().nullable().optional(),
|
|
38
|
+
chargedNowUsd: z.number().nullable().optional(),
|
|
39
|
+
note: z.string(),
|
|
40
|
+
}),
|
|
41
|
+
enabledSkills: z.array(z.string()).optional(),
|
|
42
|
+
credentialsNeeded: z.array(z.object({
|
|
43
|
+
skill: z.string(),
|
|
44
|
+
credentials: z.array(z.string()),
|
|
45
|
+
})),
|
|
46
|
+
agentStatus: z.string(),
|
|
47
|
+
restart: z.string(),
|
|
48
|
+
error: z.object({
|
|
49
|
+
code: z.number().nullable(),
|
|
50
|
+
message: z.string(),
|
|
51
|
+
deps: z.array(z.object({ skill: z.string(), requires: z.string() })).optional(),
|
|
52
|
+
conflicts: z.array(z.object({ a: z.string(), b: z.string() })).optional(),
|
|
53
|
+
requiredUsd: z.number().nullable().optional(),
|
|
54
|
+
availableUsd: z.number().nullable().optional(),
|
|
55
|
+
}).optional(),
|
|
56
|
+
nextAction: nextActionSchema.optional(),
|
|
57
|
+
};
|
|
58
|
+
const UNCHANGED_RESTART = 'Nothing changed, so the agent does not restart.';
|
|
59
|
+
// A credit for skills turned off goes to the wallet with an end date: it can be
|
|
60
|
+
// spent until the plan's current period ends.
|
|
61
|
+
const CREDIT_SPEND_BY = 'It can be spent until this period ends.';
|
|
62
|
+
function difference(next, current) {
|
|
63
|
+
return next === null || current === null ? null : Math.round((next - current) * 100) / 100;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A skill change prices the plan at today's prices for all its skills, so a
|
|
67
|
+
* plan that started at other prices also changes for the skills it already
|
|
68
|
+
* has, and so do its monthly credits. The agent's tiers carry today's prices
|
|
69
|
+
* and credits for those skills. An agent without a plan has no price of its
|
|
70
|
+
* own to move from, and a move to Pro prices the whole plan at today's Pro
|
|
71
|
+
* prices, which the quote already shows.
|
|
72
|
+
*/
|
|
73
|
+
function todayPriceNote(row, billing, currentPrice, currentCredits, tierChange) {
|
|
74
|
+
if (tierChange)
|
|
75
|
+
return null;
|
|
76
|
+
const tier = asTier(row.tier);
|
|
77
|
+
const tiers = toTierPricing(isPlainObject(row.agent) ? row.agent.tiers : undefined);
|
|
78
|
+
const today = tier && tiers ? tiers[tier] : null;
|
|
79
|
+
if (billing.plan === 'none' || currentPrice === null || today === null || !difference(today.priceUsd, currentPrice))
|
|
80
|
+
return null;
|
|
81
|
+
const credits = currentCredits !== null && today.monthlyCredits !== currentCredits
|
|
82
|
+
? `, and its monthly credits from ${currentCredits} to ${today.monthlyCredits}`
|
|
83
|
+
: '';
|
|
84
|
+
return `The change also moves the plan from its current ${formatUsd(currentPrice)} a month to ${formatUsd(today.priceUsd)} a month, today's ${tier === 'pro' ? 'Pro' : 'Starter'} price for the skills it already has${credits}.`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The wallet of the agent's workspace, which Wiro charges or credits for a
|
|
88
|
+
* skill change: the team's for a team agent, else its owner's personal wallet.
|
|
89
|
+
* Only the owner can change a personal agent's skills, so that wallet is the
|
|
90
|
+
* caller's own.
|
|
91
|
+
*/
|
|
92
|
+
function walletName(billing) {
|
|
93
|
+
return billing.wallet === 'team' ? 'the team wallet' : 'your personal wallet';
|
|
94
|
+
}
|
|
95
|
+
/** For a cancelled plan, a skill change only covers the days left: nothing renews at the new price. */
|
|
96
|
+
function cancelledNote(billing) {
|
|
97
|
+
const end = billing.periodEnd !== null ? unixToIso(billing.periodEnd) : undefined;
|
|
98
|
+
if (billing.plan === 'card') {
|
|
99
|
+
return `The plan is set to end${end ? ` after ${end}` : ' with this period'} and does not renew.`;
|
|
100
|
+
}
|
|
101
|
+
return `The plan is cancelled and ends about a day after ${end ?? 'the end of this period'}, so this covers only the days left and the new price is not renewed. Undo the cancellation with manage_agent_billing (renew) to keep the plan.`;
|
|
102
|
+
}
|
|
103
|
+
function quoteBillingNote(billing, delta, estimate, nextPrice) {
|
|
104
|
+
// A card plan whose renewal is not known gets the new price without a renewal claim.
|
|
105
|
+
const after = billing.cancelAtPeriodEnd === true
|
|
106
|
+
? ` ${cancelledNote(billing)}`
|
|
107
|
+
: nextPrice === null
|
|
108
|
+
? ''
|
|
109
|
+
: billing.cancelAtPeriodEnd === false
|
|
110
|
+
? ` The plan then renews at ${formatUsd(nextPrice)} a month.`
|
|
111
|
+
: ` The new monthly price is ${formatUsd(nextPrice)}.`;
|
|
112
|
+
if (billing.plan === 'none') {
|
|
113
|
+
return 'The agent has no subscription yet, so nothing is charged now. The new monthly price applies once it is subscribed.';
|
|
114
|
+
}
|
|
115
|
+
if (billing.status !== 'active') {
|
|
116
|
+
return `The subscription is ${billing.status ?? 'not active'}. Skills can only change on an active subscription, so renew it with manage_agent_billing first.`;
|
|
117
|
+
}
|
|
118
|
+
if (delta === null) {
|
|
119
|
+
return billing.plan === 'prepaid'
|
|
120
|
+
? `The difference for the rest of this period is taken from, or credited to, ${walletName(billing)} when the change is applied. A credit can be spent until this period ends.${after}`
|
|
121
|
+
: `Any prorated difference appears on the next invoice.${after}`;
|
|
122
|
+
}
|
|
123
|
+
if (Math.abs(delta) < 0.005)
|
|
124
|
+
return 'The monthly price does not change, so nothing is charged.';
|
|
125
|
+
if (billing.plan === 'card') {
|
|
126
|
+
return `Nothing is charged now. The card subscription is updated and the prorated difference appears on the next invoice.${after}`;
|
|
127
|
+
}
|
|
128
|
+
const days = billing.daysRemaining !== null ? ` for the ${billing.daysRemaining} days left in this period` : ' for the rest of this period';
|
|
129
|
+
if (delta > 0) {
|
|
130
|
+
return `About ${formatUsd(estimate ?? 0)} is taken from ${walletName(billing)} now${days}.${after}`;
|
|
131
|
+
}
|
|
132
|
+
return `About ${formatUsd(Math.abs(estimate ?? 0))} is credited back to ${walletName(billing)} now${days}. ${CREDIT_SPEND_BY}${after}`;
|
|
133
|
+
}
|
|
134
|
+
function chargeNote(billing, body) {
|
|
135
|
+
const charged = toNumber(body.prepaidWalletDelta);
|
|
136
|
+
const cancelled = billing.cancelAtPeriodEnd === true && billing.status === 'active' ? ` ${cancelledNote(billing)}` : '';
|
|
137
|
+
if (charged !== null && charged > 0) {
|
|
138
|
+
return { charged, note: `${formatUsd(charged)} was taken from ${walletName(billing)} for the rest of this period.${cancelled}` };
|
|
139
|
+
}
|
|
140
|
+
if (charged !== null && charged < 0) {
|
|
141
|
+
return { charged, note: `${formatUsd(Math.abs(charged))} was credited to ${walletName(billing)}. ${CREDIT_SPEND_BY}${cancelled}` };
|
|
142
|
+
}
|
|
143
|
+
if (body.stripeProrationApplied === true) {
|
|
144
|
+
return { charged: null, note: `The card subscription was updated; any prorated difference appears on the next invoice.${cancelled}` };
|
|
145
|
+
}
|
|
146
|
+
if (billing.plan === 'none') {
|
|
147
|
+
return { charged: null, note: 'Nothing was charged: the agent has no subscription yet.' };
|
|
148
|
+
}
|
|
149
|
+
return { charged: null, note: `Nothing was charged now.${cancelled}` };
|
|
150
|
+
}
|
|
151
|
+
/** What the change charged, then any notes Wiro adds about its billing, as Wiro wrote them. */
|
|
152
|
+
function appliedBillingNote(billing, body) {
|
|
153
|
+
const result = chargeNote(billing, body);
|
|
154
|
+
const warnings = (Array.isArray(body.warnings) ? body.warnings : [])
|
|
155
|
+
.map(warning => asString(warning)?.trim())
|
|
156
|
+
.filter((warning) => Boolean(warning));
|
|
157
|
+
return warnings.length > 0 ? { ...result, note: [result.note, ...warnings].join(' ') } : result;
|
|
158
|
+
}
|
|
159
|
+
function listLine(label, names) {
|
|
160
|
+
return names.length > 0 ? `**${label}:** ${names.join(', ')}` : null;
|
|
161
|
+
}
|
|
162
|
+
/** Whether the agent's live skills and tier are what this change asked for; null when that cannot be read. */
|
|
163
|
+
async function changeHolds(client, agentGuid, requested, tier, signal) {
|
|
164
|
+
let live;
|
|
165
|
+
try {
|
|
166
|
+
live = await readUserAgent(client, agentGuid, signal);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const enabled = new Set(enabledSkillNames(live));
|
|
172
|
+
const skillsHold = Object.entries(requested).every(([name, on]) => enabled.has(name) === on);
|
|
173
|
+
return skillsHold && (!tier || asString(live.tier) === tier);
|
|
174
|
+
}
|
|
175
|
+
export function registerApplyAgentSkills(server, client) {
|
|
176
|
+
server.registerTool('apply_agent_skills', {
|
|
177
|
+
title: 'Change an agent\'s skills',
|
|
178
|
+
description: 'Turn registry skills on or off for a deployed agent, and optionally move it '
|
|
179
|
+
+ 'from Starter to Pro, as one billed change. Skills set the agent\'s monthly price and monthly credits. '
|
|
180
|
+
+ 'On a team agent only a team admin or the person who deployed the agent can change its skills; other '
|
|
181
|
+
+ 'members are refused and nothing is charged. '
|
|
182
|
+
+ 'Steps: 1) call without confirm. The tool checks the names, adds the skills they '
|
|
183
|
+
+ 'depend on, and returns the current and new monthly price, an estimate of what is '
|
|
184
|
+
+ 'charged now and an idempotency_key. Nothing changes yet. 2) Show the price to the '
|
|
185
|
+
+ 'user. 3) After they approve, call again with confirm: true and that idempotency_key. '
|
|
186
|
+
+ 'The key is tied to the quoted prices: if the price changed since the quote, nothing '
|
|
187
|
+
+ 'is applied and a new quote with a new key comes back. Reusing the key after a timeout '
|
|
188
|
+
+ 'is safe, the change is applied once. Billing: on a prepaid plan the monthly difference '
|
|
189
|
+
+ 'for the days left in the period is taken from the wallet at once, or credited to it '
|
|
190
|
+
+ 'when the price drops, and that credit can be spent until the period ends; on a card '
|
|
191
|
+
+ 'plan it is added to the next invoice. A running agent '
|
|
192
|
+
+ 'restarts once to load the skills. Newly enabled skills may still need credentials '
|
|
193
|
+
+ '(set_agent_credentials, connect_agent_oauth) and do not create scheduled tasks '
|
|
194
|
+
+ '(manage_agent_custom_skill does). Find skill names with list_agent_skills and the '
|
|
195
|
+
+ 'agent\'s current skills with get_my_agent. The agents playbook (get_playbook) has the '
|
|
196
|
+
+ 'full setup walkthrough.',
|
|
197
|
+
inputSchema: {
|
|
198
|
+
agent_guid: z.string().min(1).describe('The deployed agent\'s guid, from list_my_agents.'),
|
|
199
|
+
skills: z.record(z.string(), z.boolean())
|
|
200
|
+
.describe('Skills to change, as name: true to turn on or name: false to turn off, for example {"int-instagram-post": true}. Registry names only (list_agent_skills). Custom skills and scheduled tasks (cs-...) are managed with manage_agent_custom_skill; channels such as Telegram turn on through set_agent_credentials.'),
|
|
201
|
+
tier: z.enum(['starter', 'pro']).optional()
|
|
202
|
+
.describe('Optional: also move from Starter to Pro in this billed change. To upgrade without a skill change, use manage_agent_billing (upgrade_to_pro).'),
|
|
203
|
+
add_dependencies: z.boolean().default(true)
|
|
204
|
+
.describe('Also turn on the skills that the enabled skills depend on (default true). Without them the change is refused.'),
|
|
205
|
+
confirm: z.boolean().default(false)
|
|
206
|
+
.describe('false (default) only returns the quote. true applies the change and charges it; it needs the idempotency_key from the quote.'),
|
|
207
|
+
idempotency_key: z.string().min(8).max(128).optional()
|
|
208
|
+
.describe('With confirm: true, the key from the quote the user approved. It ties the approval to the quoted prices, and repeating the call with it after a timeout never applies the change twice. Omit it to get a quote.'),
|
|
209
|
+
},
|
|
210
|
+
outputSchema,
|
|
211
|
+
annotations: {
|
|
212
|
+
readOnlyHint: false,
|
|
213
|
+
destructiveHint: false,
|
|
214
|
+
idempotentHint: true,
|
|
215
|
+
openWorldHint: true,
|
|
216
|
+
},
|
|
217
|
+
}, async ({ agent_guid, skills, tier, add_dependencies, confirm, idempotency_key }, extra) => {
|
|
218
|
+
try {
|
|
219
|
+
if (Object.keys(skills).length === 0 && !tier) {
|
|
220
|
+
return errorResult(new Error('Nothing to change: pass at least one skill or a tier.'));
|
|
221
|
+
}
|
|
222
|
+
const registry = await loadSkillRegistry(client, extra.signal);
|
|
223
|
+
const plan = planSkillChanges(skills, registry, add_dependencies);
|
|
224
|
+
if (plan.errors.length > 0) {
|
|
225
|
+
return errorResult(new Error(`The change was not sent:\n\n- ${plan.errors.join('\n- ')}`));
|
|
226
|
+
}
|
|
227
|
+
const row = await readUserAgent(client, agent_guid, extra.signal);
|
|
228
|
+
const status = agentStatusOf(row);
|
|
229
|
+
const currentTier = asString(row.tier) ?? null;
|
|
230
|
+
if (tier === 'starter' && currentTier === 'pro') {
|
|
231
|
+
return errorResult(new Error('A Pro plan cannot be moved back to Starter here. Cancel the plan with manage_agent_billing and subscribe again at Starter once it ends.'));
|
|
232
|
+
}
|
|
233
|
+
const tierChange = tier && tier !== currentTier ? tier : undefined;
|
|
234
|
+
const digest = skillChangeDigest(agent_guid, plan.requested, tier);
|
|
235
|
+
const givenKey = idempotency_key ? parseSkillChangeKey(idempotency_key) : null;
|
|
236
|
+
if (givenKey && givenKey.digest !== digest) {
|
|
237
|
+
return errorResult(new Error('This idempotency_key belongs to a different skill change. Omit idempotency_key to start a new change, or repeat the exact change it was issued for.'));
|
|
238
|
+
}
|
|
239
|
+
const enabledNow = enabledSkillNames(row);
|
|
240
|
+
const diff = diffSkills(plan.requested, enabledNow);
|
|
241
|
+
const check = checkSkillSet(enabledNow, diff.changes, registry);
|
|
242
|
+
if (check.deps.length > 0 || check.conflicts.length > 0) {
|
|
243
|
+
const problems = [
|
|
244
|
+
...check.deps.map(dep => (diff.disable.includes(dep.requires)
|
|
245
|
+
? `${dep.skill} needs ${dep.requires}. Keep ${dep.requires} on, or also turn off ${dep.skill}.`
|
|
246
|
+
: `${dep.skill} needs ${dep.requires}, which is off. Turn ${dep.requires} on in the same change.`)),
|
|
247
|
+
...check.conflicts.map(pair => `${pair.a} and ${pair.b} cannot be on together. Keep only one.`),
|
|
248
|
+
];
|
|
249
|
+
return errorResult(new Error(`The change was not sent:\n\n- ${[...new Set(problems)].join('\n- ')}`));
|
|
250
|
+
}
|
|
251
|
+
const billing = planBilling(row);
|
|
252
|
+
const running = isAgentRunning(status);
|
|
253
|
+
const changes = {
|
|
254
|
+
enable: diff.enable,
|
|
255
|
+
disable: diff.disable,
|
|
256
|
+
addedDependencies: plan.addedDependencies.filter(name => diff.enable.includes(name)),
|
|
257
|
+
alreadySet: diff.alreadySet.filter(name => !plan.addedDependencies.includes(name)),
|
|
258
|
+
};
|
|
259
|
+
const credentialsNeeded = credentialsNeededFor(diff.enable, registry, row);
|
|
260
|
+
const nothingToDo = Object.keys(diff.changes).length === 0 && !tierChange;
|
|
261
|
+
const base = {
|
|
262
|
+
agentGuid: agent_guid,
|
|
263
|
+
changes,
|
|
264
|
+
tier: { current: currentTier, next: tierChange ?? currentTier },
|
|
265
|
+
credentialsNeeded,
|
|
266
|
+
agentStatus: agentStatusLabel(status),
|
|
267
|
+
};
|
|
268
|
+
const billingBase = { plan: billing.plan, subscriptionStatus: billing.status, wallet: billing.wallet };
|
|
269
|
+
// An empty change cannot charge anything. With a key issued for this
|
|
270
|
+
// same change it is still sent, so a repeated confirm gets the result
|
|
271
|
+
// of the earlier call with that key.
|
|
272
|
+
if (nothingToDo && !(confirm && givenKey)) {
|
|
273
|
+
return {
|
|
274
|
+
content: [{ type: 'text', text: `## No change needed\n\nThe skills${tier ? ' and tier' : ''} are already as requested on agent \`${agent_guid}\`.` }],
|
|
275
|
+
structuredContent: {
|
|
276
|
+
...base,
|
|
277
|
+
status: 'no_change',
|
|
278
|
+
billing: { ...billingBase, note: 'Nothing is charged.' },
|
|
279
|
+
restart: UNCHANGED_RESTART,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
const currentPrice = toNumber(row.monthlypriceusd);
|
|
284
|
+
const currentCredits = toNumber(row.monthlycredits);
|
|
285
|
+
const key = idempotency_key?.trim() ?? '';
|
|
286
|
+
// Every team member can read a team agent and price a change, but only a
|
|
287
|
+
// team admin or the person who deployed it can apply one. The credit
|
|
288
|
+
// history check refuses the other members up front; the organization's
|
|
289
|
+
// owner passes it and gets Wiro's answer on confirm.
|
|
290
|
+
if (!nothingToDo && await isPlainTeamMember(client, row, extra.signal)
|
|
291
|
+
&& await teamAgentBillingRefused(client, agent_guid, extra.signal)) {
|
|
292
|
+
const message = 'Only a team admin or the person who deployed this agent can change its skills, and this '
|
|
293
|
+
+ 'connection acts as a team member without those rights. Nothing was quoted, changed or charged. Ask a '
|
|
294
|
+
+ 'team admin or the person who deployed the agent to make the change.';
|
|
295
|
+
return {
|
|
296
|
+
content: [{ type: 'text', text: `## Skill change not possible\n\n${message}` }],
|
|
297
|
+
structuredContent: {
|
|
298
|
+
...base,
|
|
299
|
+
status: 'refused',
|
|
300
|
+
billing: { ...billingBase, note: message },
|
|
301
|
+
restart: UNCHANGED_RESTART,
|
|
302
|
+
error: { code: 97, message },
|
|
303
|
+
},
|
|
304
|
+
isError: true,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
if (!nothingToDo) {
|
|
308
|
+
const preview = requireApiSuccess(await client.post('/UserAgent/PricingPreview', {
|
|
309
|
+
useragentguid: agent_guid,
|
|
310
|
+
skillOverrides: diff.changes,
|
|
311
|
+
...(tierChange ? { tier: tierChange } : {}),
|
|
312
|
+
}, extra.signal), 'Could not price this change.');
|
|
313
|
+
const newPrice = toNumber(preview.totalPriceUsd);
|
|
314
|
+
if (newPrice === null) {
|
|
315
|
+
throw new Error('Wiro did not return the new monthly price of this change, so it cannot be quoted. Try again later.');
|
|
316
|
+
}
|
|
317
|
+
const priceTag = skillPriceTag(currentPrice, newPrice);
|
|
318
|
+
const approved = confirm && givenKey !== null && givenKey.priceTag === priceTag;
|
|
319
|
+
if (!approved) {
|
|
320
|
+
const newCredits = toNumber(preview.totalMonthlyCredits);
|
|
321
|
+
const delta = difference(newPrice, currentPrice);
|
|
322
|
+
const estimate = billing.plan === 'prepaid' && billing.status === 'active' && delta !== null
|
|
323
|
+
? estimateProratedUsd(delta, billing.periodEnd)
|
|
324
|
+
: null;
|
|
325
|
+
const note = quoteBillingNote(billing, delta, estimate, newPrice);
|
|
326
|
+
const quoteKey = givenKey?.priceTag === priceTag ? key : newSkillChangeKey(digest, priceTag);
|
|
327
|
+
const priceChanged = confirm && givenKey !== null;
|
|
328
|
+
const blocked = billing.plan !== 'none' && billing.status !== 'active';
|
|
329
|
+
const restart = restartNote(status, 'skill change');
|
|
330
|
+
const priceNote = todayPriceNote(row, billing, currentPrice, currentCredits, tierChange);
|
|
331
|
+
const pricing = {
|
|
332
|
+
currentMonthlyUsd: currentPrice,
|
|
333
|
+
newMonthlyUsd: newPrice,
|
|
334
|
+
deltaMonthlyUsd: delta,
|
|
335
|
+
currentMonthlyCredits: currentCredits,
|
|
336
|
+
newMonthlyCredits: newCredits,
|
|
337
|
+
deltaMonthlyCredits: difference(newCredits, currentCredits),
|
|
338
|
+
...(priceNote ? { note: priceNote } : {}),
|
|
339
|
+
};
|
|
340
|
+
const nextAction = blocked
|
|
341
|
+
? {
|
|
342
|
+
tool: 'manage_agent_billing',
|
|
343
|
+
arguments: { agent_guid, action: 'renew' },
|
|
344
|
+
reason: 'The subscription must be active before skills can change.',
|
|
345
|
+
}
|
|
346
|
+
: {
|
|
347
|
+
tool: 'apply_agent_skills',
|
|
348
|
+
arguments: {
|
|
349
|
+
agent_guid,
|
|
350
|
+
skills,
|
|
351
|
+
...(tier ? { tier } : {}),
|
|
352
|
+
add_dependencies,
|
|
353
|
+
confirm: true,
|
|
354
|
+
idempotency_key: quoteKey,
|
|
355
|
+
},
|
|
356
|
+
reason: delta === null || delta > 0
|
|
357
|
+
? 'Ask the user to approve this charge first.'
|
|
358
|
+
: 'Ask the user to confirm this change first.',
|
|
359
|
+
};
|
|
360
|
+
const lead = !confirm
|
|
361
|
+
? null
|
|
362
|
+
: priceChanged
|
|
363
|
+
? 'The price changed since the quote, so nothing was applied or charged. This is the new quote with a new idempotency_key; show it to the user and ask again.'
|
|
364
|
+
: 'Nothing was applied: confirming needs the idempotency_key of a quote, which ties the approval to the price. This is the quote; show it to the user first.';
|
|
365
|
+
const lines = [
|
|
366
|
+
'## Skill change quote',
|
|
367
|
+
'',
|
|
368
|
+
...(lead ? [lead, ''] : []),
|
|
369
|
+
`**Agent:** \`${agent_guid}\` (${agentStatusLabel(status)})`,
|
|
370
|
+
listLine('Turn on', changes.enable),
|
|
371
|
+
listLine('Added because other skills need them', changes.addedDependencies),
|
|
372
|
+
listLine('Turn off', changes.disable),
|
|
373
|
+
listLine('Already as requested', changes.alreadySet),
|
|
374
|
+
tierChange ? `**Plan:** ${currentTier ?? 'unknown'} to ${tierChange}` : null,
|
|
375
|
+
`**Monthly price:** ${currentPrice !== null ? formatUsd(currentPrice) : 'unknown'} now, ${formatUsd(newPrice)} after the change${delta !== null ? ` (${delta >= 0 ? '+' : ''}${formatUsd(delta)})` : ''}`,
|
|
376
|
+
priceNote ? `**Price update:** ${priceNote}` : null,
|
|
377
|
+
newCredits !== null ? `**Monthly credits:** ${currentCredits ?? 'unknown'} now, ${newCredits} after the change` : null,
|
|
378
|
+
`**Billing:** ${note}`,
|
|
379
|
+
`**Restart:** ${restart}`,
|
|
380
|
+
credentialsNeeded.length > 0
|
|
381
|
+
? `**Credentials the new skills need:** ${credentialsNeeded.map(entry => `${entry.skill} (${entry.credentials.join(', ')})`).join('; ')}`
|
|
382
|
+
: null,
|
|
383
|
+
'',
|
|
384
|
+
blocked
|
|
385
|
+
? 'Renew the subscription with manage_agent_billing before applying this change.'
|
|
386
|
+
: `Nothing has changed yet. After the user approves, call apply_agent_skills again with confirm: true and idempotency_key \`${quoteKey}\`.`,
|
|
387
|
+
].filter((line) => line !== null);
|
|
388
|
+
return {
|
|
389
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
390
|
+
structuredContent: {
|
|
391
|
+
...base,
|
|
392
|
+
status: 'quote',
|
|
393
|
+
idempotencyKey: quoteKey,
|
|
394
|
+
...(priceChanged ? { priceChanged: true } : {}),
|
|
395
|
+
pricing,
|
|
396
|
+
billing: { ...billingBase, estimatedChargeNowUsd: estimate, note },
|
|
397
|
+
...(Array.isArray(preview.enabledSkills) ? { enabledSkills: preview.enabledSkills.map(String) } : {}),
|
|
398
|
+
restart,
|
|
399
|
+
nextAction,
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const sentEmpty = nothingToDo;
|
|
405
|
+
let applied;
|
|
406
|
+
try {
|
|
407
|
+
applied = requireApiSuccess(await client.post('/UserAgent/SkillsApply', {
|
|
408
|
+
useragentguid: agent_guid,
|
|
409
|
+
skills: diff.changes,
|
|
410
|
+
idempotencyKey: key,
|
|
411
|
+
...(tierChange ? { tier: tierChange } : {}),
|
|
412
|
+
}, extra.signal), 'The skill change was refused.');
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
if (extra.signal.aborted)
|
|
416
|
+
throw error;
|
|
417
|
+
if (!outcomeUnknown(error)) {
|
|
418
|
+
const failure = describeSkillsApplyError(error, billing.plan);
|
|
419
|
+
const text = [
|
|
420
|
+
'## Skill change not applied',
|
|
421
|
+
'',
|
|
422
|
+
failure.message,
|
|
423
|
+
failure.guidance ? `\n${failure.guidance}` : '',
|
|
424
|
+
failure.retrySameKey ? `\nidempotency_key: \`${key}\`` : '',
|
|
425
|
+
].join('\n').trim();
|
|
426
|
+
return {
|
|
427
|
+
content: [{ type: 'text', text }],
|
|
428
|
+
structuredContent: {
|
|
429
|
+
...base,
|
|
430
|
+
status: 'refused',
|
|
431
|
+
idempotencyKey: key,
|
|
432
|
+
billing: { ...billingBase, note: failure.guidance || failure.message },
|
|
433
|
+
restart: UNCHANGED_RESTART,
|
|
434
|
+
error: {
|
|
435
|
+
code: failure.code,
|
|
436
|
+
message: failure.message,
|
|
437
|
+
...(failure.deps ? { deps: failure.deps } : {}),
|
|
438
|
+
...(failure.conflicts ? { conflicts: failure.conflicts } : {}),
|
|
439
|
+
...(failure.requiredUsd !== undefined ? { requiredUsd: failure.requiredUsd } : {}),
|
|
440
|
+
...(failure.availableUsd !== undefined ? { availableUsd: failure.availableUsd } : {}),
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
isError: true,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const message = errorText(error);
|
|
447
|
+
if (sentEmpty) {
|
|
448
|
+
const note = 'The agent\'s skills already match the request, so the change is in place. Do not repeat it. '
|
|
449
|
+
+ 'What the earlier call with this idempotency_key charged is not shown here; get_agent_activity with kind usage shows it.';
|
|
450
|
+
return {
|
|
451
|
+
content: [{ type: 'text', text: `## No change needed\n\n${note}` }],
|
|
452
|
+
structuredContent: {
|
|
453
|
+
...base,
|
|
454
|
+
status: 'no_change',
|
|
455
|
+
idempotencyKey: key,
|
|
456
|
+
billing: { ...billingBase, note },
|
|
457
|
+
restart: UNCHANGED_RESTART,
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
// The agent is read again to see whether the change is in place
|
|
462
|
+
// before anything is reported.
|
|
463
|
+
const holds = await changeHolds(client, agent_guid, plan.requested, tierChange, extra.signal);
|
|
464
|
+
if (holds === true) {
|
|
465
|
+
const note = `The agent's skills now match the request, so the change is in place. Do not repeat it. ${billing.plan === 'prepaid' && billing.status === 'active' ? `The difference for the rest of this period was probably taken from, or credited to, ${walletName(billing)}, and a credit can be spent until this period ends; get_agent_activity with kind usage and get_account show it.` : 'Any price difference follows the plan\'s usual billing.'}`;
|
|
466
|
+
return {
|
|
467
|
+
content: [{ type: 'text', text: [
|
|
468
|
+
'## Skills updated',
|
|
469
|
+
'',
|
|
470
|
+
`**Agent:** \`${agent_guid}\``,
|
|
471
|
+
listLine('Turned on', changes.enable),
|
|
472
|
+
listLine('Turned off', changes.disable),
|
|
473
|
+
`**Billing:** ${note}`,
|
|
474
|
+
`**Restart:** ${restartNote(status, 'skill change')}`,
|
|
475
|
+
].filter((line) => line !== null).join('\n') }],
|
|
476
|
+
structuredContent: {
|
|
477
|
+
...base,
|
|
478
|
+
status: 'applied',
|
|
479
|
+
idempotencyKey: key,
|
|
480
|
+
billing: { ...billingBase, chargedNowUsd: null, note },
|
|
481
|
+
restart: restartNote(status, 'skill change'),
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const unverified = holds === null;
|
|
486
|
+
const note = unverified
|
|
487
|
+
? `The change could not be confirmed (${message}). Check get_my_agent to see whether the skills match the request before trying again; repeating this call with the same idempotency_key never applies the change twice.`
|
|
488
|
+
: `The agent's skills are unchanged, so the change was not applied (${message}). Repeating this call with the same idempotency_key is safe.`;
|
|
489
|
+
return {
|
|
490
|
+
content: [{ type: 'text', text: `## Skill change ${unverified ? 'not confirmed' : 'not applied'}\n\n${note}\n\nidempotency_key: \`${key}\`` }],
|
|
491
|
+
structuredContent: {
|
|
492
|
+
...base,
|
|
493
|
+
status: 'refused',
|
|
494
|
+
idempotencyKey: key,
|
|
495
|
+
billing: { ...billingBase, note },
|
|
496
|
+
restart: unverified ? 'The agent restarts if the change was saved.' : UNCHANGED_RESTART,
|
|
497
|
+
error: { code: null, message },
|
|
498
|
+
},
|
|
499
|
+
isError: true,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
// The live agent is checked against this change before it is reported
|
|
503
|
+
// as applied.
|
|
504
|
+
const holds = await changeHolds(client, agent_guid, plan.requested, tierChange, extra.signal);
|
|
505
|
+
if (holds === false) {
|
|
506
|
+
const note = 'The agent\'s skills do not match this change, so it is not in place. Check get_my_agent; if they '
|
|
507
|
+
+ 'still do not match the request, call apply_agent_skills again without idempotency_key to get a new quote.';
|
|
508
|
+
return {
|
|
509
|
+
content: [{ type: 'text', text: `## Skill change not applied\n\n${note}` }],
|
|
510
|
+
structuredContent: {
|
|
511
|
+
...base,
|
|
512
|
+
status: 'refused',
|
|
513
|
+
idempotencyKey: key,
|
|
514
|
+
billing: { ...billingBase, note },
|
|
515
|
+
restart: UNCHANGED_RESTART,
|
|
516
|
+
error: { code: null, message: note },
|
|
517
|
+
nextAction: {
|
|
518
|
+
tool: 'apply_agent_skills',
|
|
519
|
+
arguments: { agent_guid, skills, ...(tier ? { tier } : {}), add_dependencies },
|
|
520
|
+
reason: 'Get a new quote for this change.',
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
isError: true,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
// An empty request that comes back as a real change is the saved
|
|
527
|
+
// result of the earlier call with this key, not a new charge.
|
|
528
|
+
const replayed = sentEmpty && !applied.noOp;
|
|
529
|
+
const serverPricing = applied.pricing ?? {};
|
|
530
|
+
const pricing = applied.noOp
|
|
531
|
+
? undefined
|
|
532
|
+
: {
|
|
533
|
+
currentMonthlyUsd: toNumber(serverPricing.previousPriceUsd),
|
|
534
|
+
newMonthlyUsd: toNumber(serverPricing.newPriceUsd),
|
|
535
|
+
deltaMonthlyUsd: toNumber(serverPricing.deltaUsd),
|
|
536
|
+
currentMonthlyCredits: toNumber(serverPricing.previousMonthlyCredits),
|
|
537
|
+
newMonthlyCredits: toNumber(serverPricing.newMonthlyCredits),
|
|
538
|
+
deltaMonthlyCredits: toNumber(serverPricing.deltaCredits),
|
|
539
|
+
};
|
|
540
|
+
const charge = appliedBillingNote(billing, applied);
|
|
541
|
+
const billingNote = replayed
|
|
542
|
+
? `This change was already applied by an earlier call with this idempotency_key, and nothing new was charged now. That call's billing: ${charge.note}`
|
|
543
|
+
: charge.note;
|
|
544
|
+
const restart = applied.noOp || replayed
|
|
545
|
+
? UNCHANGED_RESTART
|
|
546
|
+
: restartNote(status, 'skill change');
|
|
547
|
+
const firstCredential = credentialsNeeded[0]?.credentials[0];
|
|
548
|
+
const nextAction = applied.noOp || replayed
|
|
549
|
+
? undefined
|
|
550
|
+
: firstCredential
|
|
551
|
+
? {
|
|
552
|
+
tool: 'set_agent_credentials',
|
|
553
|
+
arguments: { agent_guid, credential: firstCredential },
|
|
554
|
+
reason: `See what ${firstCredential} needs before asking the user for it.`,
|
|
555
|
+
}
|
|
556
|
+
: running
|
|
557
|
+
? {
|
|
558
|
+
tool: 'get_my_agent',
|
|
559
|
+
arguments: { agent_guid, wait_seconds: DEFAULT_WAIT_SECONDS },
|
|
560
|
+
reason: 'Check that the agent is running again with the new skills.',
|
|
561
|
+
}
|
|
562
|
+
: undefined;
|
|
563
|
+
const lines = [
|
|
564
|
+
applied.noOp ? '## No change needed' : replayed ? '## Already applied' : '## Skills updated',
|
|
565
|
+
'',
|
|
566
|
+
`**Agent:** \`${agent_guid}\``,
|
|
567
|
+
listLine('Turned on', changes.enable),
|
|
568
|
+
listLine('Turned off', changes.disable),
|
|
569
|
+
applied.noOp || replayed ? listLine('Already as requested', changes.alreadySet) : null,
|
|
570
|
+
tierChange && !applied.noOp ? `**Plan:** now ${asString(applied.tier) ?? tierChange}` : null,
|
|
571
|
+
pricing?.newMonthlyUsd !== undefined && pricing.newMonthlyUsd !== null
|
|
572
|
+
? `**Monthly price:** ${formatUsd(pricing.newMonthlyUsd)}${pricing.currentMonthlyUsd !== null ? ` (was ${formatUsd(pricing.currentMonthlyUsd)})` : ''}`
|
|
573
|
+
: null,
|
|
574
|
+
`**Billing:** ${billingNote}`,
|
|
575
|
+
`**Restart:** ${restart}`,
|
|
576
|
+
holds === null ? 'The agent could not be read afterwards to confirm the new skills; get_my_agent shows them.' : null,
|
|
577
|
+
credentialsNeeded.length > 0
|
|
578
|
+
? `**Still needed:** credentials for ${credentialsNeeded.map(entry => `${entry.skill} (${entry.credentials.join(', ')})`).join('; ')}. Set them with set_agent_credentials or connect_agent_oauth.`
|
|
579
|
+
: null,
|
|
580
|
+
].filter((line) => line !== null);
|
|
581
|
+
return {
|
|
582
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
583
|
+
structuredContent: {
|
|
584
|
+
...base,
|
|
585
|
+
status: applied.noOp ? 'no_change' : 'applied',
|
|
586
|
+
idempotencyKey: key,
|
|
587
|
+
...(pricing ? { pricing } : {}),
|
|
588
|
+
billing: { ...billingBase, chargedNowUsd: charge.charged, note: billingNote },
|
|
589
|
+
...(Array.isArray(serverPricing.enabledSkills) ? { enabledSkills: serverPricing.enabledSkills.map(String) } : {}),
|
|
590
|
+
restart,
|
|
591
|
+
...(nextAction ? { nextAction } : {}),
|
|
592
|
+
},
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
const hint = error instanceof Error ? workspaceHint(error.message) : undefined;
|
|
597
|
+
return errorResult(hint && error instanceof Error ? new Error(`${error.message} ${hint}`) : error);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
//# sourceMappingURL=apply-agent-skills.js.map
|