@wiro-ai/wiro-mcp 1.4.0 → 1.5.0
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 +50 -17
- package/dist/client.d.ts +25 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +129 -29
- package/dist/client.js.map +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +24 -17
- package/dist/instructions.js.map +1 -1
- package/dist/playbooks.d.ts.map +1 -1
- package/dist/playbooks.js +61 -48
- package/dist/playbooks.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +4 -3
- package/dist/prompts.js.map +1 -1
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/apply-agent-skills.d.ts.map +1 -1
- package/dist/tools/apply-agent-skills.js +400 -88
- package/dist/tools/apply-agent-skills.js.map +1 -1
- package/dist/tools/bind-agent-project.d.ts +4 -0
- package/dist/tools/bind-agent-project.d.ts.map +1 -0
- package/dist/tools/bind-agent-project.js +403 -0
- package/dist/tools/bind-agent-project.js.map +1 -0
- package/dist/tools/cancel-agent-message.d.ts.map +1 -1
- package/dist/tools/cancel-agent-message.js +6 -3
- package/dist/tools/cancel-agent-message.js.map +1 -1
- package/dist/tools/cancel-task.js +3 -3
- package/dist/tools/cancel-task.js.map +1 -1
- package/dist/tools/clone-workflow.d.ts.map +1 -1
- package/dist/tools/clone-workflow.js +5 -4
- package/dist/tools/clone-workflow.js.map +1 -1
- package/dist/tools/connect-agent-oauth.d.ts.map +1 -1
- package/dist/tools/connect-agent-oauth.js +144 -44
- package/dist/tools/connect-agent-oauth.js.map +1 -1
- package/dist/tools/control-agent.d.ts.map +1 -1
- package/dist/tools/control-agent.js +133 -45
- package/dist/tools/control-agent.js.map +1 -1
- package/dist/tools/create-workflow.d.ts +9 -6
- package/dist/tools/create-workflow.d.ts.map +1 -1
- package/dist/tools/create-workflow.js +13 -13
- package/dist/tools/create-workflow.js.map +1 -1
- package/dist/tools/delete-agent-session.d.ts.map +1 -1
- package/dist/tools/delete-agent-session.js +20 -14
- package/dist/tools/delete-agent-session.js.map +1 -1
- package/dist/tools/delete-agent.d.ts.map +1 -1
- package/dist/tools/delete-agent.js +14 -10
- package/dist/tools/delete-agent.js.map +1 -1
- package/dist/tools/delete-workflow.d.ts.map +1 -1
- package/dist/tools/delete-workflow.js +6 -5
- package/dist/tools/delete-workflow.js.map +1 -1
- package/dist/tools/deploy-agent.d.ts.map +1 -1
- package/dist/tools/deploy-agent.js +215 -47
- package/dist/tools/deploy-agent.js.map +1 -1
- package/dist/tools/disconnect-agent-oauth.d.ts.map +1 -1
- package/dist/tools/disconnect-agent-oauth.js +32 -12
- package/dist/tools/disconnect-agent-oauth.js.map +1 -1
- package/dist/tools/get-account.d.ts.map +1 -1
- package/dist/tools/get-account.js +157 -49
- package/dist/tools/get-account.js.map +1 -1
- package/dist/tools/get-agent-activity.d.ts.map +1 -1
- package/dist/tools/get-agent-activity.js +186 -36
- package/dist/tools/get-agent-activity.js.map +1 -1
- package/dist/tools/get-agent-conversation.d.ts.map +1 -1
- package/dist/tools/get-agent-conversation.js +65 -39
- package/dist/tools/get-agent-conversation.js.map +1 -1
- package/dist/tools/get-agent-message.d.ts.map +1 -1
- package/dist/tools/get-agent-message.js +6 -4
- package/dist/tools/get-agent-message.js.map +1 -1
- package/dist/tools/get-agent-template.d.ts.map +1 -1
- package/dist/tools/get-agent-template.js +33 -17
- package/dist/tools/get-agent-template.js.map +1 -1
- package/dist/tools/get-model-schema.d.ts.map +1 -1
- package/dist/tools/get-model-schema.js +7 -5
- package/dist/tools/get-model-schema.js.map +1 -1
- package/dist/tools/get-my-agent.d.ts.map +1 -1
- package/dist/tools/get-my-agent.js +54 -11
- package/dist/tools/get-my-agent.js.map +1 -1
- package/dist/tools/get-playbook.d.ts.map +1 -1
- package/dist/tools/get-playbook.js +3 -2
- package/dist/tools/get-playbook.js.map +1 -1
- 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 +29 -8
- 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 +25 -9
- package/dist/tools/get-task.js.map +1 -1
- package/dist/tools/get-workflow.d.ts.map +1 -1
- package/dist/tools/get-workflow.js +46 -21
- package/dist/tools/get-workflow.js.map +1 -1
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +10 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kill-task.js +3 -3
- package/dist/tools/kill-task.js.map +1 -1
- package/dist/tools/list-agent-skills.d.ts.map +1 -1
- package/dist/tools/list-agent-skills.js +5 -4
- package/dist/tools/list-agent-skills.js.map +1 -1
- package/dist/tools/list-agent-templates.js +4 -4
- package/dist/tools/list-agent-templates.js.map +1 -1
- package/dist/tools/list-my-agents.d.ts.map +1 -1
- package/dist/tools/list-my-agents.js +14 -8
- package/dist/tools/list-my-agents.js.map +1 -1
- package/dist/tools/list-projects.d.ts +4 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +141 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-studio-results.js +3 -3
- package/dist/tools/list-studio-results.js.map +1 -1
- package/dist/tools/list-tasks.js +3 -3
- package/dist/tools/list-tasks.js.map +1 -1
- package/dist/tools/list-workflow-runs.d.ts.map +1 -1
- package/dist/tools/list-workflow-runs.js +6 -4
- package/dist/tools/list-workflow-runs.js.map +1 -1
- package/dist/tools/list-workflows.d.ts.map +1 -1
- package/dist/tools/list-workflows.js +7 -6
- package/dist/tools/list-workflows.js.map +1 -1
- package/dist/tools/manage-agent-billing.d.ts.map +1 -1
- package/dist/tools/manage-agent-billing.js +259 -87
- package/dist/tools/manage-agent-billing.js.map +1 -1
- package/dist/tools/manage-agent-custom-skill.d.ts.map +1 -1
- package/dist/tools/manage-agent-custom-skill.js +91 -21
- package/dist/tools/manage-agent-custom-skill.js.map +1 -1
- package/dist/tools/recommend-model.js +3 -3
- package/dist/tools/recommend-model.js.map +1 -1
- package/dist/tools/resume-workflow-run.d.ts.map +1 -1
- package/dist/tools/resume-workflow-run.js +3 -2
- package/dist/tools/resume-workflow-run.js.map +1 -1
- package/dist/tools/run-model.js +5 -5
- package/dist/tools/run-model.js.map +1 -1
- package/dist/tools/run-workflow.d.ts.map +1 -1
- package/dist/tools/run-workflow.js +3 -2
- package/dist/tools/run-workflow.js.map +1 -1
- package/dist/tools/save-workflow-from-agent.d.ts.map +1 -1
- package/dist/tools/save-workflow-from-agent.js +22 -20
- package/dist/tools/save-workflow-from-agent.js.map +1 -1
- package/dist/tools/schemas.d.ts +150 -127
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +76 -3
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/search-docs.js +3 -3
- package/dist/tools/search-docs.js.map +1 -1
- package/dist/tools/search-models.d.ts.map +1 -1
- package/dist/tools/search-models.js +7 -4
- package/dist/tools/search-models.js.map +1 -1
- package/dist/tools/send-agent-message.d.ts.map +1 -1
- package/dist/tools/send-agent-message.js +32 -11
- package/dist/tools/send-agent-message.js.map +1 -1
- package/dist/tools/set-agent-credentials.d.ts.map +1 -1
- package/dist/tools/set-agent-credentials.js +60 -59
- package/dist/tools/set-agent-credentials.js.map +1 -1
- package/dist/tools/update-agent.d.ts.map +1 -1
- package/dist/tools/update-agent.js +59 -21
- package/dist/tools/update-agent.js.map +1 -1
- package/dist/tools/update-workflow.d.ts +205 -6
- package/dist/tools/update-workflow.d.ts.map +1 -1
- package/dist/tools/update-workflow.js +20 -7
- package/dist/tools/update-workflow.js.map +1 -1
- package/dist/tools/upload-file.d.ts.map +1 -1
- package/dist/tools/upload-file.js +13 -9
- 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 +23 -5
- package/dist/tools/wait-for-task.js.map +1 -1
- package/dist/tools/wait-for-workflow-run.d.ts +18 -18
- package/dist/tools/wait-for-workflow-run.d.ts.map +1 -1
- package/dist/tools/wait-for-workflow-run.js +7 -6
- package/dist/tools/wait-for-workflow-run.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/agent-catalog.d.ts +168 -139
- package/dist/utils/agent-catalog.d.ts.map +1 -1
- package/dist/utils/agent-catalog.js +34 -14
- package/dist/utils/agent-catalog.js.map +1 -1
- package/dist/utils/agent-config.d.ts +111 -8
- package/dist/utils/agent-config.d.ts.map +1 -1
- package/dist/utils/agent-config.js +290 -40
- package/dist/utils/agent-config.js.map +1 -1
- package/dist/utils/agent-message.d.ts +41 -22
- package/dist/utils/agent-message.d.ts.map +1 -1
- package/dist/utils/agent-message.js +94 -31
- package/dist/utils/agent-message.js.map +1 -1
- package/dist/utils/agent-targets.d.ts +8 -4
- package/dist/utils/agent-targets.d.ts.map +1 -1
- package/dist/utils/agent-targets.js +51 -35
- package/dist/utils/agent-targets.js.map +1 -1
- package/dist/utils/agent-turns.d.ts +31 -24
- package/dist/utils/agent-turns.d.ts.map +1 -1
- package/dist/utils/agent-turns.js +154 -35
- package/dist/utils/agent-turns.js.map +1 -1
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +22 -1
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/format.d.ts +27 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +429 -82
- 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 +27 -1
- package/dist/utils/json-schema-dialect.js.map +1 -1
- package/dist/utils/media.d.ts +8 -0
- package/dist/utils/media.d.ts.map +1 -1
- package/dist/utils/media.js +29 -0
- package/dist/utils/media.js.map +1 -1
- package/dist/utils/poll.d.ts +5 -2
- package/dist/utils/poll.d.ts.map +1 -1
- package/dist/utils/poll.js +10 -9
- package/dist/utils/poll.js.map +1 -1
- package/dist/utils/quoted-text.d.ts +28 -0
- package/dist/utils/quoted-text.d.ts.map +1 -0
- package/dist/utils/quoted-text.js +64 -0
- package/dist/utils/quoted-text.js.map +1 -0
- package/dist/utils/structured.d.ts +2 -0
- package/dist/utils/structured.d.ts.map +1 -1
- package/dist/utils/structured.js +15 -2
- package/dist/utils/structured.js.map +1 -1
- package/dist/utils/studio-runs.d.ts +4 -4
- package/dist/utils/studio-runs.d.ts.map +1 -1
- package/dist/utils/studio-runs.js +11 -8
- package/dist/utils/studio-runs.js.map +1 -1
- package/dist/utils/user-agent.d.ts +376 -241
- package/dist/utils/user-agent.d.ts.map +1 -1
- package/dist/utils/user-agent.js +302 -89
- package/dist/utils/user-agent.js.map +1 -1
- package/dist/utils/wiro-project.d.ts +45 -0
- package/dist/utils/wiro-project.d.ts.map +1 -0
- package/dist/utils/wiro-project.js +66 -0
- package/dist/utils/wiro-project.js.map +1 -0
- package/dist/utils/workflow-agent.d.ts +4 -4
- package/dist/utils/workflow-save.d.ts +23 -5
- package/dist/utils/workflow-save.d.ts.map +1 -1
- package/dist/utils/workflow-save.js +130 -38
- package/dist/utils/workflow-save.js.map +1 -1
- package/dist/utils/workflow.d.ts +79 -61
- package/dist/utils/workflow.d.ts.map +1 -1
- package/dist/utils/workflow.js +149 -19
- package/dist/utils/workflow.js.map +1 -1
- package/dist/utils/workspace.d.ts +64 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +173 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +6 -4
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { asString, errorResult, errorText, requireApiSuccess, toNumber, unixToIso, } from '../utils/api.js';
|
|
3
|
-
import { agentStatusLabel, agentStatusOf, checkSkillSet, credentialsNeededFor, describeSkillsApplyError, diffSkills, enabledSkillNames,
|
|
4
|
-
import { outcomeUnknown } from '../utils/user-agent.js';
|
|
2
|
+
import { asString, errorResult, errorText, isPlainObject, requireApiSuccess, toNumber, unixToIso, } from '../utils/api.js';
|
|
3
|
+
import { agentStatusLabel, agentStatusOf, checkSkillSet, credentialsNeededFor, daysLeftText, describeSkillsApplyError, diffSkills, enabledSkillNames, formatUsd, isAgentRunning, loadSkillRegistry, newSkillChangeKey, parseSkillChangeKey, planBilling, planSkillChanges, readPlanChange, readUserAgent, underOneDayLeft, restartNote, skillChangeDigest, skillPriceTag, workspaceHint, } from '../utils/agent-config.js';
|
|
4
|
+
import { asTier, bindProjectStep, formatCredits, outcomeUnknown, toTierPricing, WIRO_PROJECT_CREDENTIAL } from '../utils/user-agent.js';
|
|
5
5
|
import { DEFAULT_WAIT_SECONDS } from '../utils/poll.js';
|
|
6
|
-
import {
|
|
6
|
+
import { isPlainTeamMember, teamAgentBillingRefused } from '../utils/workspace.js';
|
|
7
|
+
import { nextActionSchema, strictInput } from './schemas.js';
|
|
7
8
|
const pricingSchema = z.object({
|
|
8
9
|
currentMonthlyUsd: z.number().nullable(),
|
|
9
10
|
newMonthlyUsd: z.number().nullable(),
|
|
10
11
|
deltaMonthlyUsd: z.number().nullable(),
|
|
11
|
-
currentMonthlyCredits: z.number().nullable()
|
|
12
|
-
|
|
12
|
+
currentMonthlyCredits: z.number().nullable()
|
|
13
|
+
.describe('The monthly credits each renewal grants now.'),
|
|
14
|
+
newMonthlyCredits: z.number().nullable()
|
|
15
|
+
.describe('The monthly credits each renewal grants after the change.'),
|
|
13
16
|
deltaMonthlyCredits: z.number().nullable(),
|
|
17
|
+
creditsNow: z.number().optional()
|
|
18
|
+
.describe('The credits this period gains now, below 0 when it loses them: the change counts for the time left in the period.'),
|
|
19
|
+
creditsThisPeriod: z.number().optional()
|
|
20
|
+
.describe('The monthly credits this period grants in all after the change.'),
|
|
21
|
+
creditsFromRenewal: z.number().optional()
|
|
22
|
+
.describe('The monthly credits every renewal grants after the change.'),
|
|
23
|
+
periodEnd: z.string().optional()
|
|
24
|
+
.describe('When the current period ends.'),
|
|
25
|
+
note: z.string().optional(),
|
|
14
26
|
});
|
|
15
27
|
const outputSchema = {
|
|
16
28
|
agentGuid: z.string(),
|
|
@@ -32,7 +44,12 @@ const outputSchema = {
|
|
|
32
44
|
plan: z.enum(['prepaid', 'card', 'none']),
|
|
33
45
|
subscriptionStatus: z.string().nullable(),
|
|
34
46
|
wallet: z.enum(['team', 'personal']),
|
|
35
|
-
estimatedChargeNowUsd: z.number().nullable().optional()
|
|
47
|
+
estimatedChargeNowUsd: z.number().nullable().optional()
|
|
48
|
+
.describe('What a wallet plan pays now at list price, as Wiro works it out, before any discount on the wallet; 0 on a card plan, whose difference goes to the card subscription.'),
|
|
49
|
+
creditNowUsd: z.number().nullable().optional()
|
|
50
|
+
.describe('For a drop on a wallet plan, what goes back to the balances that paid; null when it is worked out when the change is applied.'),
|
|
51
|
+
usedCreditsUsd: z.number().nullable().optional()
|
|
52
|
+
.describe('The part of a credit that comes off for credits already used above what the plan keeps.'),
|
|
36
53
|
chargedNowUsd: z.number().nullable().optional(),
|
|
37
54
|
note: z.string(),
|
|
38
55
|
}),
|
|
@@ -54,12 +71,89 @@ const outputSchema = {
|
|
|
54
71
|
nextAction: nextActionSchema.optional(),
|
|
55
72
|
};
|
|
56
73
|
const UNCHANGED_RESTART = 'Nothing changed, so the agent does not restart.';
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
// The tier and the price live with Wiro, and the agent reads neither: only a skill that turns on or off restarts it.
|
|
75
|
+
const TIER_ONLY_RESTART = 'No skill turns on or off, so a change of the tier alone does not restart the agent.';
|
|
76
|
+
function roundCents(usd) {
|
|
77
|
+
return Math.round(usd * 100) / 100;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* How long an applied credit lasts, from the parts Wiro says it split into. Each
|
|
81
|
+
* part went back to the balance that paid it and keeps that balance's end date,
|
|
82
|
+
* except the part whose source has ended since (each ...UntilPeriodEndUsd): that
|
|
83
|
+
* part lasts until the plan's current period ends. The two amounts add up to the
|
|
84
|
+
* credit, and parts that come to nothing say nothing.
|
|
85
|
+
*/
|
|
86
|
+
function creditTerms(parts) {
|
|
87
|
+
if (!isPlainObject(parts))
|
|
88
|
+
return '';
|
|
89
|
+
const usd = (key) => toNumber(parts[key]) ?? 0;
|
|
90
|
+
const untilEnd = roundCents(usd('couponUntilPeriodEndUsd') + usd('storeUntilPeriodEndUsd') + usd('depositUntilPeriodEndUsd'));
|
|
91
|
+
const keepsEnd = roundCents(roundCents(usd('couponUsd') + usd('storeUsd') + usd('depositUsd')) - untilEnd);
|
|
92
|
+
if (keepsEnd > 0 && untilEnd > 0) {
|
|
93
|
+
return `${formatUsd(keepsEnd)} of it went back to the balances that paid and keeps their end dates, and ${formatUsd(untilEnd)} lasts until this period ends, because the credit that paid it has ended.`;
|
|
94
|
+
}
|
|
95
|
+
if (keepsEnd > 0)
|
|
96
|
+
return 'It went back to the balances that paid and keeps their end dates.';
|
|
97
|
+
return untilEnd > 0 ? 'It lasts until this period ends, because the credit that paid it has ended.' : '';
|
|
98
|
+
}
|
|
99
|
+
/** A count of credits with its sign, such as +97 or -690. */
|
|
100
|
+
function signedCredits(credits) {
|
|
101
|
+
return `${credits > 0 ? '+' : ''}${formatCredits(credits)}`;
|
|
102
|
+
}
|
|
103
|
+
/** The fields of Wiro's plan change that the pricing output carries. */
|
|
104
|
+
function periodFields(change) {
|
|
105
|
+
if (!change)
|
|
106
|
+
return {};
|
|
107
|
+
const periodEnd = unixToIso(change.periodEnd);
|
|
108
|
+
return {
|
|
109
|
+
...(change.creditsNow !== null ? { creditsNow: change.creditsNow } : {}),
|
|
110
|
+
...(change.creditsThisPeriod !== null ? { creditsThisPeriod: change.creditsThisPeriod } : {}),
|
|
111
|
+
...(change.creditsFromRenewal !== null ? { creditsFromRenewal: change.creditsFromRenewal } : {}),
|
|
112
|
+
...(periodEnd ? { periodEnd } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* What a change does to this period's credits when none come with it: those it takes away end for the
|
|
117
|
+
* time left, and the period keeps the rest. A plan priced again at today's rates can also keep its
|
|
118
|
+
* credits, or gain some, while its price drops.
|
|
119
|
+
*/
|
|
120
|
+
function creditsEndNote(skillsOff, creditsNow, creditsThisPeriod) {
|
|
121
|
+
if (creditsNow !== null && creditsNow > 0) {
|
|
122
|
+
return `This period gains ${formatCredits(creditsNow)} credits for the time left${creditsThisPeriod !== null ? `, ${formatCredits(creditsThisPeriod)} in all` : ''}.`;
|
|
123
|
+
}
|
|
124
|
+
if (creditsNow === 0) {
|
|
125
|
+
return creditsThisPeriod !== null ? `This period keeps its ${formatCredits(creditsThisPeriod)} credits.` : 'This period keeps its credits.';
|
|
126
|
+
}
|
|
127
|
+
const keeps = creditsThisPeriod !== null ? `, and this period keeps ${formatCredits(creditsThisPeriod)}` : '';
|
|
128
|
+
if (skillsOff === 0)
|
|
129
|
+
return `The credits the change takes away end now for the time left${keeps}.`;
|
|
130
|
+
return `${skillsOff === 1 ? 'Its' : 'Their'} credits for the time left end now${keeps}.`;
|
|
131
|
+
}
|
|
60
132
|
function difference(next, current) {
|
|
61
133
|
return next === null || current === null ? null : Math.round((next - current) * 100) / 100;
|
|
62
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* A skill change prices the plan at today's prices for all its skills, so a
|
|
137
|
+
* plan that started at other prices also changes for the skills it already
|
|
138
|
+
* has, and so do its monthly credits. The agent's tiers carry today's prices
|
|
139
|
+
* and credits for those skills. They are the base of the new price, not a price
|
|
140
|
+
* the plan reaches. An agent without an active plan cannot take the change yet,
|
|
141
|
+
* and a move to Pro prices the whole plan at today's Pro prices, which the quote
|
|
142
|
+
* already shows.
|
|
143
|
+
*/
|
|
144
|
+
function todayPriceNote(row, billing, currentPrice, currentCredits, tierChange) {
|
|
145
|
+
if (tierChange)
|
|
146
|
+
return null;
|
|
147
|
+
const tier = asTier(row.tier);
|
|
148
|
+
const tiers = toTierPricing(isPlainObject(row.agent) ? row.agent.tiers : undefined);
|
|
149
|
+
const today = tier && tiers ? tiers[tier] : null;
|
|
150
|
+
if (billing.status !== 'active' || currentPrice === null || today === null || !difference(today.priceUsd, currentPrice))
|
|
151
|
+
return null;
|
|
152
|
+
const versus = currentCredits !== null && today.monthlyCredits !== currentCredits
|
|
153
|
+
? ` with ${formatCredits(today.monthlyCredits)} monthly credits, not its current ${formatUsd(currentPrice)} and ${formatCredits(currentCredits)}`
|
|
154
|
+
: `, not its current ${formatUsd(currentPrice)}`;
|
|
155
|
+
return `Today's ${tier === 'pro' ? 'Pro' : 'Starter'} price for the skills this plan already has is ${formatUsd(today.priceUsd)} a month${versus}. A skill change counts from today's price, and the new price in this quote already includes that.`;
|
|
156
|
+
}
|
|
63
157
|
/**
|
|
64
158
|
* The wallet of the agent's workspace, which Wiro charges or credits for a
|
|
65
159
|
* skill change: the team's for a team agent, else its owner's personal wallet.
|
|
@@ -69,51 +163,175 @@ function difference(next, current) {
|
|
|
69
163
|
function walletName(billing) {
|
|
70
164
|
return billing.wallet === 'team' ? 'the team wallet' : 'your personal wallet';
|
|
71
165
|
}
|
|
72
|
-
/**
|
|
166
|
+
/** When the new monthly credits come: a plan set to end renews only if it is kept, and an agent without a plan gets them once it is subscribed. */
|
|
167
|
+
function creditsWhen(billing) {
|
|
168
|
+
if (billing.plan === 'none')
|
|
169
|
+
return 'a month once it is subscribed';
|
|
170
|
+
return billing.status === 'active' && billing.cancelAtPeriodEnd === true
|
|
171
|
+
? 'at each renewal after the change, if the plan is kept'
|
|
172
|
+
: 'at each renewal after the change';
|
|
173
|
+
}
|
|
174
|
+
/** When an active plan's next renewal gives the new monthly credits. A card plan whose renewal is not known names it as a condition. */
|
|
175
|
+
function renewalWhen(billing) {
|
|
176
|
+
if (billing.cancelAtPeriodEnd === true)
|
|
177
|
+
return 'from the next renewal, if the plan is kept';
|
|
178
|
+
return billing.cancelAtPeriodEnd === null ? 'from the next renewal, if the plan renews' : 'from the next renewal';
|
|
179
|
+
}
|
|
180
|
+
/** What renewals give after an applied change: a plan that may not renew names it as a condition, as its quote did. */
|
|
181
|
+
function renewalGives(billing, credits) {
|
|
182
|
+
return billing.cancelAtPeriodEnd === false ? `every renewal gives ${formatCredits(credits)}` : `${formatCredits(credits)} ${renewalWhen(billing)}`;
|
|
183
|
+
}
|
|
184
|
+
/** For a cancelled plan, a skill change only covers the time left: nothing renews at the new price. */
|
|
73
185
|
function cancelledNote(billing) {
|
|
74
186
|
const end = billing.periodEnd !== null ? unixToIso(billing.periodEnd) : undefined;
|
|
75
187
|
if (billing.plan === 'card') {
|
|
76
188
|
return `The plan is set to end${end ? ` after ${end}` : ' with this period'} and does not renew.`;
|
|
77
189
|
}
|
|
78
|
-
return `The plan is cancelled and ends about a day after ${end ?? 'the end of this period'}, so this covers only the
|
|
190
|
+
return `The plan is cancelled and ends about a day after ${end ?? 'the end of this period'}, so this covers only the time left and the new price is not renewed. Undo the cancellation with manage_agent_billing (renew) to keep the plan.`;
|
|
191
|
+
}
|
|
192
|
+
/** A card subscription that ends with this period: the plan is set to end, or it moves to wallet billing. */
|
|
193
|
+
function cardEndsWithPeriod(billing) {
|
|
194
|
+
return billing.plan === 'card' && (billing.cancelAtPeriodEnd === true || billing.switchingToWallet);
|
|
195
|
+
}
|
|
196
|
+
function walletSwitchNote(billing, price) {
|
|
197
|
+
return `When this period ends, the plan moves to wallet billing and renews from ${walletName(billing)}${price !== null ? ` at ${formatUsd(price)} a month` : ''}.`;
|
|
198
|
+
}
|
|
199
|
+
// A card subscription that ends with this period is not renewed by card, so its notes point to no next invoice.
|
|
200
|
+
const CARD_ENDS_QUOTE = 'Nothing is charged now. The card subscription is updated to the new price for the rest of this period.';
|
|
201
|
+
// A card plan whose renewal is not known may also end with this period, so its next invoice is named only for a plan that renews by card.
|
|
202
|
+
const CARD_UNKNOWN_QUOTE = 'Nothing is charged now. The card subscription is updated to the new price, and a plan that renews by card shows the prorated difference on its next invoice.';
|
|
203
|
+
/** Only a wallet plan that has ended comes back with renew; any other plan that is not active takes a new one (manage_agent_billing). */
|
|
204
|
+
function renewsBack(billing) {
|
|
205
|
+
return billing.plan === 'prepaid' && billing.status === 'expired';
|
|
206
|
+
}
|
|
207
|
+
/** What a price drop on a wallet plan gives back now, from Wiro's own plan change. */
|
|
208
|
+
function walletCreditNote(change) {
|
|
209
|
+
const used = change.usedCreditsUsd ?? 0;
|
|
210
|
+
// A credit is at most 1 cent for each credit the change takes away unused, so one that takes none away gives nothing back.
|
|
211
|
+
if (change.creditNowUsd === 0 && used > 0 && change.creditsNow !== null && change.creditsNow >= 0) {
|
|
212
|
+
return 'Nothing comes back: the change takes no credits away.';
|
|
213
|
+
}
|
|
214
|
+
if (change.creditNowUsd === null) {
|
|
215
|
+
return 'What comes back is worked out when the change is applied: what the time left is worth, less 1 cent for each credit already used above what the plan keeps.';
|
|
216
|
+
}
|
|
217
|
+
if (change.creditNowUsd > 0) {
|
|
218
|
+
return `${formatUsd(change.creditNowUsd)} goes back to the balances that paid, and each keeps its own end date.`
|
|
219
|
+
+ (used > 0 ? ` ${formatUsd(used)} comes off for credits already used above what the plan keeps.` : '');
|
|
220
|
+
}
|
|
221
|
+
return used > 0
|
|
222
|
+
? 'Nothing comes back: the credits already used are worth more than the time left.'
|
|
223
|
+
: 'Nothing comes back to the wallet now.';
|
|
79
224
|
}
|
|
80
|
-
|
|
81
|
-
|
|
225
|
+
/**
|
|
226
|
+
* The quote's billing line. An active plan's money and credits come from the
|
|
227
|
+
* plan change Wiro works out on the plan as it is now (change); without it,
|
|
228
|
+
* the line says what happens without a number.
|
|
229
|
+
*/
|
|
230
|
+
function quoteBillingNote(billing, delta, change, nextPrice, skillsOff) {
|
|
231
|
+
// A card plan whose renewal is not known gets the new price without a renewal claim.
|
|
232
|
+
const after = billing.cancelAtPeriodEnd === true
|
|
82
233
|
? ` ${cancelledNote(billing)}`
|
|
83
|
-
:
|
|
234
|
+
: billing.switchingToWallet
|
|
235
|
+
? ` ${walletSwitchNote(billing, nextPrice)}`
|
|
236
|
+
: nextPrice === null
|
|
237
|
+
? ''
|
|
238
|
+
: billing.cancelAtPeriodEnd === false
|
|
239
|
+
? ` The plan then renews at ${formatUsd(nextPrice)} a month.`
|
|
240
|
+
: ` The new monthly price is ${formatUsd(nextPrice)}.`;
|
|
84
241
|
if (billing.plan === 'none') {
|
|
85
242
|
return 'The agent has no subscription yet, so nothing is charged now. The new monthly price applies once it is subscribed.';
|
|
86
243
|
}
|
|
87
244
|
if (billing.status !== 'active') {
|
|
88
|
-
return
|
|
245
|
+
return renewsBack(billing)
|
|
246
|
+
? `The subscription is ${billing.status ?? 'not active'}. Skills can only change on an active subscription, so renew it with manage_agent_billing first.`
|
|
247
|
+
: `The subscription is ${billing.status ?? 'not active'}. Skills can only change on an active subscription, so start a new plan with manage_agent_billing (subscribe) first.`;
|
|
89
248
|
}
|
|
90
|
-
if (delta
|
|
249
|
+
if (delta !== null && Math.abs(delta) < 0.005)
|
|
250
|
+
return 'The monthly price does not change, so nothing is charged.';
|
|
251
|
+
if (delta === null || change === null) {
|
|
91
252
|
return billing.plan === 'prepaid'
|
|
92
|
-
? `The difference for the rest of this period is taken from, or credited to, ${walletName(billing)} when the change is applied. A credit
|
|
93
|
-
:
|
|
253
|
+
? `The difference for the rest of this period is taken from, or credited to, ${walletName(billing)} when the change is applied. A credit goes back to the balances that paid, and each keeps its own end date.${after}`
|
|
254
|
+
: cardEndsWithPeriod(billing)
|
|
255
|
+
? `${CARD_ENDS_QUOTE}${after}`
|
|
256
|
+
: billing.cancelAtPeriodEnd === null
|
|
257
|
+
? `${CARD_UNKNOWN_QUOTE}${after}`
|
|
258
|
+
: `Any prorated difference appears on the next invoice.${after}`;
|
|
94
259
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
260
|
+
const ends = creditsEndNote(skillsOff, change.creditsNow, change.creditsThisPeriod);
|
|
261
|
+
// Credits come with a rise when this period gains them. A plan priced again at today's rates can also
|
|
262
|
+
// lose credits, or keep them, while its price rises.
|
|
263
|
+
const gained = change.creditsNow !== null && change.creditsNow > 0 ? change.creditsNow : null;
|
|
264
|
+
const notGained = change.creditsNow !== null && change.creditsNow <= 0 ? ` ${ends}` : '';
|
|
265
|
+
if (!change.wallet) {
|
|
266
|
+
if (delta > 0) {
|
|
267
|
+
const added = gained !== null ? ` ${formatCredits(gained)} credits come now for the rest of this period.` : notGained;
|
|
268
|
+
if (cardEndsWithPeriod(billing))
|
|
269
|
+
return `${CARD_ENDS_QUOTE}${added}${after}`;
|
|
270
|
+
if (billing.cancelAtPeriodEnd === null)
|
|
271
|
+
return `${CARD_UNKNOWN_QUOTE}${added}${after}`;
|
|
272
|
+
return gained !== null
|
|
273
|
+
? `Nothing is charged now. The prorated difference appears on the next invoice, and ${formatCredits(gained)} credits come now for the rest of this period.${after}`
|
|
274
|
+
: `Nothing is charged now. The prorated difference appears on the next invoice.${notGained}${after}`;
|
|
275
|
+
}
|
|
276
|
+
if (cardEndsWithPeriod(billing))
|
|
277
|
+
return `${CARD_ENDS_QUOTE} ${ends}${after}`;
|
|
278
|
+
if (billing.cancelAtPeriodEnd === null)
|
|
279
|
+
return `${CARD_UNKNOWN_QUOTE} ${ends}${after}`;
|
|
280
|
+
return `The prorated difference comes off the next invoice. ${ends}${after}`;
|
|
99
281
|
}
|
|
100
|
-
const days = billing.daysRemaining !== null ? ` for the ${billing.daysRemaining} days left in this period` : ' for the rest of this period';
|
|
101
282
|
if (delta > 0) {
|
|
102
|
-
|
|
283
|
+
const charge = formatUsd(change.chargeNowUsd ?? 0);
|
|
284
|
+
const credits = gained !== null ? `, and ${formatCredits(gained)} credits come with it` : '';
|
|
285
|
+
const days = daysLeftText(change.secondsLeft);
|
|
286
|
+
return underOneDayLeft(change.secondsLeft)
|
|
287
|
+
? `${charge} is taken from ${walletName(billing)} now at the one-day minimum${credits}.${notGained}${after}`
|
|
288
|
+
: `${charge} is taken from ${walletName(billing)} now for the rest of this period${days ? ` (${days})` : ''}${credits}.${notGained}${after}`;
|
|
103
289
|
}
|
|
104
|
-
return
|
|
290
|
+
return `${walletCreditNote(change)} ${ends}${after}`;
|
|
105
291
|
}
|
|
106
292
|
function chargeNote(billing, body) {
|
|
107
293
|
const charged = toNumber(body.prepaidWalletDelta);
|
|
108
|
-
const
|
|
294
|
+
const used = toNumber(body.usedCreditsUsd) ?? 0;
|
|
295
|
+
const creditsNow = isPlainObject(body.pricing) ? toNumber(body.pricing.creditsNow) : null;
|
|
296
|
+
const cancelled = billing.cancelAtPeriodEnd === true && billing.status === 'active' ? ` ${cancelledNote(billing)}` : '';
|
|
109
297
|
if (charged !== null && charged > 0) {
|
|
110
|
-
|
|
298
|
+
const credits = creditsNow !== null && creditsNow > 0 ? `, and ${formatCredits(creditsNow)} credits came with it` : '';
|
|
299
|
+
return { charged, note: `${formatUsd(charged)} was taken from ${walletName(billing)} for the rest of this period${credits}.${cancelled}` };
|
|
111
300
|
}
|
|
112
301
|
if (charged !== null && charged < 0) {
|
|
113
|
-
|
|
302
|
+
const terms = creditTerms(body.prepaidCreditParts);
|
|
303
|
+
const usedNote = used > 0 ? ` ${formatUsd(used)} came off for credits already used above what the plan keeps.` : '';
|
|
304
|
+
return { charged, note: `${formatUsd(Math.abs(charged))} was credited to ${walletName(billing)}.${terms ? ` ${terms}` : ''}${usedNote}${cancelled}` };
|
|
305
|
+
}
|
|
306
|
+
// A change that took no credits away had nothing to credit, and nothing failed.
|
|
307
|
+
if (billing.plan === 'prepaid' && used > 0 && creditsNow !== null && creditsNow >= 0) {
|
|
308
|
+
return { charged: null, note: `Nothing came back: the change took no credits away.${cancelled}` };
|
|
309
|
+
}
|
|
310
|
+
// The credits already used came to more than the time left was worth: no credit, and nothing failed.
|
|
311
|
+
// A part Wiro could not add (prepaidCreditFailedUsd) was due all the same: its warning follows, and the
|
|
312
|
+
// credits used took only what Wiro names.
|
|
313
|
+
if (billing.plan === 'prepaid' && used > 0) {
|
|
314
|
+
return (toNumber(body.prepaidCreditFailedUsd) ?? 0) > 0
|
|
315
|
+
? { charged: null, note: `Nothing came back now. ${formatUsd(used)} came off for credits already used above what the plan keeps.${cancelled}` }
|
|
316
|
+
: { charged: null, note: `Nothing came back: the credits already used were worth more than the time left.${cancelled}` };
|
|
114
317
|
}
|
|
115
318
|
if (body.stripeProrationApplied === true) {
|
|
116
|
-
|
|
319
|
+
const moved = isPlainObject(body.pricing) ? toNumber(body.pricing.deltaUsd) : null;
|
|
320
|
+
if (moved !== null && Math.abs(moved) < 0.005) {
|
|
321
|
+
return { charged: null, note: `The monthly price did not change, so nothing was charged.${cancelled}` };
|
|
322
|
+
}
|
|
323
|
+
const added = moved !== null && moved > 0 && creditsNow !== null && creditsNow > 0
|
|
324
|
+
? ` ${formatCredits(creditsNow)} credits came now for the rest of this period.`
|
|
325
|
+
: '';
|
|
326
|
+
if (cardEndsWithPeriod(billing)) {
|
|
327
|
+
const price = isPlainObject(body.pricing) ? toNumber(body.pricing.newPriceUsd) : null;
|
|
328
|
+
const switching = billing.switchingToWallet ? ` ${walletSwitchNote(billing, price)}` : '';
|
|
329
|
+
return { charged: null, note: `Nothing was charged now. The card subscription was updated to the new price for the rest of this period.${added}${cancelled}${switching}` };
|
|
330
|
+
}
|
|
331
|
+
if (billing.cancelAtPeriodEnd === null) {
|
|
332
|
+
return { charged: null, note: `Nothing was charged now. The card subscription was updated to the new price, and a plan that renews by card shows the prorated difference on its next invoice.${added}` };
|
|
333
|
+
}
|
|
334
|
+
return { charged: null, note: `The card subscription was updated; any prorated difference appears on the next invoice.${added}${cancelled}` };
|
|
117
335
|
}
|
|
118
336
|
if (billing.plan === 'none') {
|
|
119
337
|
return { charged: null, note: 'Nothing was charged: the agent has no subscription yet.' };
|
|
@@ -128,6 +346,21 @@ function appliedBillingNote(billing, body) {
|
|
|
128
346
|
.filter((warning) => Boolean(warning));
|
|
129
347
|
return warnings.length > 0 ? { ...result, note: [result.note, ...warnings].join(' ') } : result;
|
|
130
348
|
}
|
|
349
|
+
/** The sentence for a change whose skills need a Wiro project. */
|
|
350
|
+
function bindProjectNote(needed) {
|
|
351
|
+
return needed.some(entry => entry.credentials.includes(WIRO_PROJECT_CREDENTIAL))
|
|
352
|
+
? ' bind_agent_project connects a Wiro project, with no key to paste.'
|
|
353
|
+
: '';
|
|
354
|
+
}
|
|
355
|
+
/** How to give the new skills what they still need. A Wiro project comes first, since it takes no key. */
|
|
356
|
+
function stillNeededSteps(needed) {
|
|
357
|
+
const keys = needed.flatMap(entry => entry.credentials);
|
|
358
|
+
const fields = 'set_agent_credentials or connect_agent_oauth.';
|
|
359
|
+
if (!keys.includes(WIRO_PROJECT_CREDENTIAL))
|
|
360
|
+
return `Set them with ${fields}`;
|
|
361
|
+
const others = keys.some(key => key !== WIRO_PROJECT_CREDENTIAL) ? ` Set the others with ${fields}` : '';
|
|
362
|
+
return `Connect the Wiro project with bind_agent_project, with no key to paste.${others}`;
|
|
363
|
+
}
|
|
131
364
|
function listLine(label, names) {
|
|
132
365
|
return names.length > 0 ? `**${label}:** ${names.join(', ')}` : null;
|
|
133
366
|
}
|
|
@@ -149,22 +382,29 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
149
382
|
title: 'Change an agent\'s skills',
|
|
150
383
|
description: 'Turn registry skills on or off for a deployed agent, and optionally move it '
|
|
151
384
|
+ 'from Starter to Pro, as one billed change. Skills set the agent\'s monthly price and monthly credits. '
|
|
385
|
+
+ 'On a team agent only a team admin or the person who deployed the agent can change its skills; other '
|
|
386
|
+
+ 'members are refused and nothing is charged. '
|
|
152
387
|
+ 'Steps: 1) call without confirm. The tool checks the names, adds the skills they '
|
|
153
|
-
+ 'depend on, and returns the current and new monthly price,
|
|
154
|
-
+ '
|
|
388
|
+
+ 'depend on, and returns the current and new monthly price, what Wiro charges or credits now, '
|
|
389
|
+
+ 'the credits this period gains or loses, and an idempotency_key. Nothing changes yet. 2) Show the price to the '
|
|
155
390
|
+ 'user. 3) After they approve, call again with confirm: true and that idempotency_key. '
|
|
156
|
-
+ 'The key is tied to the quoted prices
|
|
157
|
-
+ 'is applied and a new quote with a new key comes back. Reusing the key after a timeout '
|
|
391
|
+
+ 'The key is tied to the quoted prices and, on a prepaid plan, the billing period: if '
|
|
392
|
+
+ 'either changed since the quote, nothing is applied and a new quote with a new key comes back. Reusing the key after a timeout '
|
|
158
393
|
+ 'is safe, the change is applied once. Billing: on a prepaid plan the monthly difference '
|
|
159
|
-
+ 'for the
|
|
160
|
-
+ '
|
|
161
|
-
+ '
|
|
162
|
-
+ '
|
|
163
|
-
+ '
|
|
394
|
+
+ 'for the time left in the period is taken from the wallet at once, and the added credits come '
|
|
395
|
+
+ 'for that same time; every renewal gives all of them. When the price drops, what the time left '
|
|
396
|
+
+ 'is worth goes back to the balances that paid, less 1 cent for each credit already used above '
|
|
397
|
+
+ 'what the plan keeps, and each balance keeps its own end date. A skill that comes off loses its '
|
|
398
|
+
+ 'credits for the time left; the ones the time already passed paid for stay until the renewal. '
|
|
399
|
+
+ 'On a card plan nothing is charged at once: the card subscription is updated to the new price, '
|
|
400
|
+
+ 'a plan that renews by card shows the difference on its next invoice, and the added credits come '
|
|
401
|
+
+ 'for the time left. A running agent restarts once when a skill turns on or off; a change of the '
|
|
402
|
+
+ 'tier alone leaves it running. Newly enabled skills may still need credentials '
|
|
403
|
+
+ '(set_agent_credentials, connect_agent_oauth, bind_agent_project for a Wiro project) and do not create scheduled tasks '
|
|
164
404
|
+ '(manage_agent_custom_skill does). Find skill names with list_agent_skills and the '
|
|
165
405
|
+ 'agent\'s current skills with get_my_agent. The agents playbook (get_playbook) has the '
|
|
166
406
|
+ 'full setup walkthrough.',
|
|
167
|
-
inputSchema: {
|
|
407
|
+
inputSchema: strictInput({
|
|
168
408
|
agent_guid: z.string().min(1).describe('The deployed agent\'s guid, from list_my_agents.'),
|
|
169
409
|
skills: z.record(z.string(), z.boolean())
|
|
170
410
|
.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.'),
|
|
@@ -175,12 +415,12 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
175
415
|
confirm: z.boolean().default(false)
|
|
176
416
|
.describe('false (default) only returns the quote. true applies the change and charges it; it needs the idempotency_key from the quote.'),
|
|
177
417
|
idempotency_key: z.string().min(8).max(128).optional()
|
|
178
|
-
.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.'),
|
|
179
|
-
},
|
|
418
|
+
.describe('With confirm: true, the key from the quote the user approved. It ties the approval to the quoted prices and, on a prepaid plan, the billing period, and repeating the call with it after a timeout never applies the change twice. Omit it to get a quote.'),
|
|
419
|
+
}),
|
|
180
420
|
outputSchema,
|
|
181
421
|
annotations: {
|
|
182
422
|
readOnlyHint: false,
|
|
183
|
-
destructiveHint:
|
|
423
|
+
destructiveHint: true,
|
|
184
424
|
idempotentHint: true,
|
|
185
425
|
openWorldHint: true,
|
|
186
426
|
},
|
|
@@ -227,7 +467,9 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
227
467
|
alreadySet: diff.alreadySet.filter(name => !plan.addedDependencies.includes(name)),
|
|
228
468
|
};
|
|
229
469
|
const credentialsNeeded = credentialsNeededFor(diff.enable, registry, row);
|
|
230
|
-
|
|
470
|
+
// A running agent restarts only for a skill that turns on or off.
|
|
471
|
+
const skillsMove = Object.keys(diff.changes).length > 0;
|
|
472
|
+
const nothingToDo = !skillsMove && !tierChange;
|
|
231
473
|
const base = {
|
|
232
474
|
agentGuid: agent_guid,
|
|
233
475
|
changes,
|
|
@@ -237,8 +479,8 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
237
479
|
};
|
|
238
480
|
const billingBase = { plan: billing.plan, subscriptionStatus: billing.status, wallet: billing.wallet };
|
|
239
481
|
// An empty change cannot charge anything. With a key issued for this
|
|
240
|
-
// same change
|
|
241
|
-
//
|
|
482
|
+
// same change it is still sent, so a repeated confirm gets the result
|
|
483
|
+
// of the earlier call with that key.
|
|
242
484
|
if (nothingToDo && !(confirm && givenKey)) {
|
|
243
485
|
return {
|
|
244
486
|
content: [{ type: 'text', text: `## No change needed\n\nThe skills${tier ? ' and tier' : ''} are already as requested on agent \`${agent_guid}\`.` }],
|
|
@@ -251,8 +493,30 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
251
493
|
};
|
|
252
494
|
}
|
|
253
495
|
const currentPrice = toNumber(row.monthlypriceusd);
|
|
254
|
-
|
|
496
|
+
// The plan's monthly figure is what each renewal grants. This period's can differ after a change inside it.
|
|
497
|
+
const currentCredits = toNumber(row.renewalcredits) ?? toNumber(row.monthlycredits);
|
|
255
498
|
const key = idempotency_key?.trim() ?? '';
|
|
499
|
+
// Every team member can read a team agent and price a change, but only a
|
|
500
|
+
// team admin or the person who deployed it can apply one. The credit
|
|
501
|
+
// history check refuses the other members up front; the organization's
|
|
502
|
+
// owner passes it and gets Wiro's answer on confirm.
|
|
503
|
+
if (!nothingToDo && await isPlainTeamMember(client, row, extra.signal)
|
|
504
|
+
&& await teamAgentBillingRefused(client, agent_guid, extra.signal)) {
|
|
505
|
+
const message = 'Only a team admin or the person who deployed this agent can change its skills, and this '
|
|
506
|
+
+ 'connection acts as a team member without those rights. Nothing was quoted, changed or charged. Ask a '
|
|
507
|
+
+ 'team admin or the person who deployed the agent to make the change.';
|
|
508
|
+
return {
|
|
509
|
+
content: [{ type: 'text', text: `## Skill change not possible\n\n${message}` }],
|
|
510
|
+
structuredContent: {
|
|
511
|
+
...base,
|
|
512
|
+
status: 'refused',
|
|
513
|
+
billing: { ...billingBase, note: message },
|
|
514
|
+
restart: UNCHANGED_RESTART,
|
|
515
|
+
error: { code: 97, message },
|
|
516
|
+
},
|
|
517
|
+
isError: true,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
256
520
|
if (!nothingToDo) {
|
|
257
521
|
const preview = requireApiSuccess(await client.post('/UserAgent/PricingPreview', {
|
|
258
522
|
useragentguid: agent_guid,
|
|
@@ -263,19 +527,20 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
263
527
|
if (newPrice === null) {
|
|
264
528
|
throw new Error('Wiro did not return the new monthly price of this change, so it cannot be quoted. Try again later.');
|
|
265
529
|
}
|
|
266
|
-
const priceTag = skillPriceTag(currentPrice, newPrice);
|
|
530
|
+
const priceTag = skillPriceTag(currentPrice, newPrice, billing.plan === 'prepaid' ? billing.periodEnd : null);
|
|
267
531
|
const approved = confirm && givenKey !== null && givenKey.priceTag === priceTag;
|
|
268
532
|
if (!approved) {
|
|
269
533
|
const newCredits = toNumber(preview.totalMonthlyCredits);
|
|
270
534
|
const delta = difference(newPrice, currentPrice);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
535
|
+
// What Wiro works out for this change on the plan as it is now. Only an active plan has one.
|
|
536
|
+
const change = billing.plan !== 'none' && billing.status === 'active' ? readPlanChange(preview.planChange) : null;
|
|
537
|
+
const note = quoteBillingNote(billing, delta, change, newPrice, changes.disable.length);
|
|
538
|
+
const walletDrop = change !== null && change.wallet && delta !== null && delta < 0;
|
|
275
539
|
const quoteKey = givenKey?.priceTag === priceTag ? key : newSkillChangeKey(digest, priceTag);
|
|
276
540
|
const priceChanged = confirm && givenKey !== null;
|
|
277
541
|
const blocked = billing.plan !== 'none' && billing.status !== 'active';
|
|
278
|
-
const restart = restartNote(status, '
|
|
542
|
+
const restart = skillsMove ? restartNote(status, 'skill change') : TIER_ONLY_RESTART;
|
|
543
|
+
const priceNote = todayPriceNote(row, billing, currentPrice, currentCredits, tierChange);
|
|
279
544
|
const pricing = {
|
|
280
545
|
currentMonthlyUsd: currentPrice,
|
|
281
546
|
newMonthlyUsd: newPrice,
|
|
@@ -283,11 +548,13 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
283
548
|
currentMonthlyCredits: currentCredits,
|
|
284
549
|
newMonthlyCredits: newCredits,
|
|
285
550
|
deltaMonthlyCredits: difference(newCredits, currentCredits),
|
|
551
|
+
...periodFields(change),
|
|
552
|
+
...(priceNote ? { note: priceNote } : {}),
|
|
286
553
|
};
|
|
287
554
|
const nextAction = blocked
|
|
288
555
|
? {
|
|
289
556
|
tool: 'manage_agent_billing',
|
|
290
|
-
arguments: { agent_guid, action: 'renew' },
|
|
557
|
+
arguments: { agent_guid, action: renewsBack(billing) ? 'renew' : 'subscribe' },
|
|
291
558
|
reason: 'The subscription must be active before skills can change.',
|
|
292
559
|
}
|
|
293
560
|
: {
|
|
@@ -307,7 +574,7 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
307
574
|
const lead = !confirm
|
|
308
575
|
? null
|
|
309
576
|
: priceChanged
|
|
310
|
-
? '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.'
|
|
577
|
+
? 'The price or the billing period 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.'
|
|
311
578
|
: '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.';
|
|
312
579
|
const lines = [
|
|
313
580
|
'## Skill change quote',
|
|
@@ -320,15 +587,21 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
320
587
|
listLine('Already as requested', changes.alreadySet),
|
|
321
588
|
tierChange ? `**Plan:** ${currentTier ?? 'unknown'} to ${tierChange}` : null,
|
|
322
589
|
`**Monthly price:** ${currentPrice !== null ? formatUsd(currentPrice) : 'unknown'} now, ${formatUsd(newPrice)} after the change${delta !== null ? ` (${delta >= 0 ? '+' : ''}${formatUsd(delta)})` : ''}`,
|
|
323
|
-
|
|
590
|
+
priceNote ? `**Price update:** ${priceNote}` : null,
|
|
591
|
+
change?.creditsFromRenewal !== null && change?.creditsFromRenewal !== undefined
|
|
592
|
+
? `**Monthly credits:** ${currentCredits !== null ? formatCredits(currentCredits) : 'unknown'} now, ${formatCredits(change.creditsFromRenewal)} ${renewalWhen(billing)}`
|
|
593
|
+
: newCredits !== null ? `**Monthly credits:** ${currentCredits !== null ? formatCredits(currentCredits) : 'unknown'} now, ${formatCredits(newCredits)} ${creditsWhen(billing)}` : null,
|
|
594
|
+
change && change.creditsNow !== null && change.creditsThisPeriod !== null
|
|
595
|
+
? `**This period:** ${signedCredits(change.creditsNow)} credits now, ${formatCredits(change.creditsThisPeriod)} in all until ${unixToIso(change.periodEnd) ?? 'the period ends'}`
|
|
596
|
+
: null,
|
|
324
597
|
`**Billing:** ${note}`,
|
|
325
598
|
`**Restart:** ${restart}`,
|
|
326
599
|
credentialsNeeded.length > 0
|
|
327
|
-
? `**Credentials the new skills need:** ${credentialsNeeded.map(entry => `${entry.skill} (${entry.credentials.join(', ')})`).join('; ')}`
|
|
600
|
+
? `**Credentials the new skills need:** ${credentialsNeeded.map(entry => `${entry.skill} (${entry.credentials.join(', ')})`).join('; ')}.${bindProjectNote(credentialsNeeded)}`
|
|
328
601
|
: null,
|
|
329
602
|
'',
|
|
330
603
|
blocked
|
|
331
|
-
? 'Renew the subscription with manage_agent_billing before applying this change
|
|
604
|
+
? `${renewsBack(billing) ? 'Renew the subscription' : 'Start a new plan'} with manage_agent_billing before applying this change.`
|
|
332
605
|
: `Nothing has changed yet. After the user approves, call apply_agent_skills again with confirm: true and idempotency_key \`${quoteKey}\`.`,
|
|
333
606
|
].filter((line) => line !== null);
|
|
334
607
|
return {
|
|
@@ -339,7 +612,12 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
339
612
|
idempotencyKey: quoteKey,
|
|
340
613
|
...(priceChanged ? { priceChanged: true } : {}),
|
|
341
614
|
pricing,
|
|
342
|
-
billing: {
|
|
615
|
+
billing: {
|
|
616
|
+
...billingBase,
|
|
617
|
+
estimatedChargeNowUsd: change ? change.chargeNowUsd : null,
|
|
618
|
+
...(walletDrop ? { creditNowUsd: change.creditNowUsd, usedCreditsUsd: change.usedCreditsUsd } : {}),
|
|
619
|
+
note,
|
|
620
|
+
},
|
|
343
621
|
...(Array.isArray(preview.enabledSkills) ? { enabledSkills: preview.enabledSkills.map(String) } : {}),
|
|
344
622
|
restart,
|
|
345
623
|
nextAction,
|
|
@@ -362,8 +640,15 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
362
640
|
throw error;
|
|
363
641
|
if (!outcomeUnknown(error)) {
|
|
364
642
|
const failure = describeSkillsApplyError(error, billing.plan);
|
|
643
|
+
// Code 110 is the whole outcome: the next step reads the agent, and the call is not repeated.
|
|
644
|
+
const failedChange = failure.code === 110;
|
|
645
|
+
const nextAction = failedChange
|
|
646
|
+
? { tool: 'get_my_agent', arguments: { agent_guid }, reason: 'Check the agent\'s skills and monthly price. Do not repeat the change.' }
|
|
647
|
+
: failure.code === 107
|
|
648
|
+
? { tool: 'apply_agent_skills', arguments: { agent_guid, skills, ...(tier ? { tier } : {}), add_dependencies }, reason: 'Get a new quote for this change, with a new key.' }
|
|
649
|
+
: undefined;
|
|
365
650
|
const text = [
|
|
366
|
-
'## Skill change not applied',
|
|
651
|
+
failedChange ? '## Skill change failed' : '## Skill change not applied',
|
|
367
652
|
'',
|
|
368
653
|
failure.message,
|
|
369
654
|
failure.guidance ? `\n${failure.guidance}` : '',
|
|
@@ -376,7 +661,7 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
376
661
|
status: 'refused',
|
|
377
662
|
idempotencyKey: key,
|
|
378
663
|
billing: { ...billingBase, note: failure.guidance || failure.message },
|
|
379
|
-
restart: UNCHANGED_RESTART,
|
|
664
|
+
restart: failedChange ? 'The agent does not restart for this change.' : UNCHANGED_RESTART,
|
|
380
665
|
error: {
|
|
381
666
|
code: failure.code,
|
|
382
667
|
message: failure.message,
|
|
@@ -385,13 +670,17 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
385
670
|
...(failure.requiredUsd !== undefined ? { requiredUsd: failure.requiredUsd } : {}),
|
|
386
671
|
...(failure.availableUsd !== undefined ? { availableUsd: failure.availableUsd } : {}),
|
|
387
672
|
},
|
|
673
|
+
...(nextAction ? { nextAction } : {}),
|
|
388
674
|
},
|
|
389
675
|
isError: true,
|
|
390
676
|
};
|
|
391
677
|
}
|
|
392
678
|
const message = errorText(error);
|
|
393
679
|
if (sentEmpty) {
|
|
394
|
-
const note =
|
|
680
|
+
const note = 'The agent\'s skills already match the request, so the change is in place. Do not repeat it. '
|
|
681
|
+
+ (billing.plan === 'prepaid'
|
|
682
|
+
? `What the earlier call with this idempotency_key took from, or credited to, ${walletName(billing)} is not shown here; get_account with recent_transactions shows it.`
|
|
683
|
+
: 'Any price difference follows the plan\'s usual billing.');
|
|
395
684
|
return {
|
|
396
685
|
content: [{ type: 'text', text: `## No change needed\n\n${note}` }],
|
|
397
686
|
structuredContent: {
|
|
@@ -403,34 +692,34 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
403
692
|
},
|
|
404
693
|
};
|
|
405
694
|
}
|
|
406
|
-
// The
|
|
407
|
-
//
|
|
695
|
+
// The agent is read again to see whether the change is in place
|
|
696
|
+
// before anything is reported.
|
|
408
697
|
const holds = await changeHolds(client, agent_guid, plan.requested, tierChange, extra.signal);
|
|
409
698
|
if (holds === true) {
|
|
410
|
-
const note = `
|
|
699
|
+
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)}; a credit goes back to the balances that paid. get_account with recent_transactions shows the amount, and get_agent_activity with kind usage the credits it moved.` : 'Any price difference follows the plan\'s usual billing.'}`;
|
|
411
700
|
return {
|
|
412
701
|
content: [{ type: 'text', text: [
|
|
413
|
-
'## Skills updated
|
|
702
|
+
'## Skills updated',
|
|
414
703
|
'',
|
|
415
704
|
`**Agent:** \`${agent_guid}\``,
|
|
416
705
|
listLine('Turned on', changes.enable),
|
|
417
706
|
listLine('Turned off', changes.disable),
|
|
418
707
|
`**Billing:** ${note}`,
|
|
419
|
-
`**Restart:** ${restartNote(status, '
|
|
708
|
+
`**Restart:** ${skillsMove ? restartNote(status, 'skill change') : TIER_ONLY_RESTART}`,
|
|
420
709
|
].filter((line) => line !== null).join('\n') }],
|
|
421
710
|
structuredContent: {
|
|
422
711
|
...base,
|
|
423
712
|
status: 'applied',
|
|
424
713
|
idempotencyKey: key,
|
|
425
714
|
billing: { ...billingBase, chargedNowUsd: null, note },
|
|
426
|
-
restart: restartNote(status, '
|
|
715
|
+
restart: skillsMove ? restartNote(status, 'skill change') : TIER_ONLY_RESTART,
|
|
427
716
|
},
|
|
428
717
|
};
|
|
429
718
|
}
|
|
430
719
|
const unverified = holds === null;
|
|
431
720
|
const note = unverified
|
|
432
|
-
? `
|
|
433
|
-
: `
|
|
721
|
+
? `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.`
|
|
722
|
+
: `The agent's skills are unchanged, so the change was not applied (${message}). Repeating this call with the same idempotency_key is safe.`;
|
|
434
723
|
return {
|
|
435
724
|
content: [{ type: 'text', text: `## Skill change ${unverified ? 'not confirmed' : 'not applied'}\n\n${note}\n\nidempotency_key: \`${key}\`` }],
|
|
436
725
|
structuredContent: {
|
|
@@ -438,17 +727,18 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
438
727
|
status: 'refused',
|
|
439
728
|
idempotencyKey: key,
|
|
440
729
|
billing: { ...billingBase, note },
|
|
441
|
-
restart: unverified ? 'The agent restarts if the change was saved.' :
|
|
730
|
+
restart: !unverified ? UNCHANGED_RESTART : skillsMove ? 'The agent restarts if the change was saved.' : TIER_ONLY_RESTART,
|
|
442
731
|
error: { code: null, message },
|
|
443
732
|
},
|
|
444
733
|
isError: true,
|
|
445
734
|
};
|
|
446
735
|
}
|
|
447
|
-
//
|
|
448
|
-
//
|
|
736
|
+
// The live agent is checked against this change before it is reported
|
|
737
|
+
// as applied.
|
|
449
738
|
const holds = await changeHolds(client, agent_guid, plan.requested, tierChange, extra.signal);
|
|
450
739
|
if (holds === false) {
|
|
451
|
-
const note = '
|
|
740
|
+
const note = 'The agent\'s skills do not match this change, so it is not in place. Check get_my_agent; if they '
|
|
741
|
+
+ 'still do not match the request, call apply_agent_skills again without idempotency_key to get a new quote.';
|
|
452
742
|
return {
|
|
453
743
|
content: [{ type: 'text', text: `## Skill change not applied\n\n${note}` }],
|
|
454
744
|
structuredContent: {
|
|
@@ -471,6 +761,13 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
471
761
|
// result of the earlier call with this key, not a new charge.
|
|
472
762
|
const replayed = sentEmpty && !applied.noOp;
|
|
473
763
|
const serverPricing = applied.pricing ?? {};
|
|
764
|
+
// An agent with no plan has no period and no renewal yet, as its quote says.
|
|
765
|
+
const appliedChange = billing.plan === 'none' ? null : readPlanChange({
|
|
766
|
+
creditsNow: serverPricing.creditsNow,
|
|
767
|
+
creditsThisPeriod: serverPricing.creditsThisPeriod,
|
|
768
|
+
creditsFromRenewal: serverPricing.creditsFromRenewal,
|
|
769
|
+
periodEnd: serverPricing.periodEnd,
|
|
770
|
+
});
|
|
474
771
|
const pricing = applied.noOp
|
|
475
772
|
? undefined
|
|
476
773
|
: {
|
|
@@ -480,30 +777,36 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
480
777
|
currentMonthlyCredits: toNumber(serverPricing.previousMonthlyCredits),
|
|
481
778
|
newMonthlyCredits: toNumber(serverPricing.newMonthlyCredits),
|
|
482
779
|
deltaMonthlyCredits: toNumber(serverPricing.deltaCredits),
|
|
780
|
+
...periodFields(appliedChange),
|
|
483
781
|
};
|
|
484
782
|
const charge = appliedBillingNote(billing, applied);
|
|
783
|
+
const usedCreditsUsd = toNumber(applied.usedCreditsUsd);
|
|
485
784
|
const billingNote = replayed
|
|
486
785
|
? `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}`
|
|
487
786
|
: charge.note;
|
|
787
|
+
// Wiro says whether this change restarts a running agent; an answer without it goes by the skills that move.
|
|
788
|
+
const restarts = typeof applied.restartTriggered === 'boolean' ? applied.restartTriggered : skillsMove;
|
|
488
789
|
const restart = applied.noOp || replayed
|
|
489
790
|
? UNCHANGED_RESTART
|
|
490
|
-
: restartNote(status, '
|
|
791
|
+
: restarts ? restartNote(status, 'skill change') : TIER_ONLY_RESTART;
|
|
491
792
|
const firstCredential = credentialsNeeded[0]?.credentials[0];
|
|
492
793
|
const nextAction = applied.noOp || replayed
|
|
493
794
|
? undefined
|
|
494
|
-
: firstCredential
|
|
495
|
-
?
|
|
496
|
-
|
|
497
|
-
arguments: { agent_guid, credential: firstCredential },
|
|
498
|
-
reason: `See what ${firstCredential} needs before asking the user for it.`,
|
|
499
|
-
}
|
|
500
|
-
: running
|
|
795
|
+
: firstCredential === WIRO_PROJECT_CREDENTIAL
|
|
796
|
+
? bindProjectStep(agent_guid, 'Connect the Wiro project the new skills need.')
|
|
797
|
+
: firstCredential
|
|
501
798
|
? {
|
|
502
|
-
tool: '
|
|
503
|
-
arguments: { agent_guid,
|
|
504
|
-
reason:
|
|
799
|
+
tool: 'set_agent_credentials',
|
|
800
|
+
arguments: { agent_guid, credential: firstCredential },
|
|
801
|
+
reason: `See what ${firstCredential} needs before asking the user for it.`,
|
|
505
802
|
}
|
|
506
|
-
:
|
|
803
|
+
: running && restarts
|
|
804
|
+
? {
|
|
805
|
+
tool: 'get_my_agent',
|
|
806
|
+
arguments: { agent_guid, wait_seconds: DEFAULT_WAIT_SECONDS },
|
|
807
|
+
reason: 'Check that the agent is running again with the new skills.',
|
|
808
|
+
}
|
|
809
|
+
: undefined;
|
|
507
810
|
const lines = [
|
|
508
811
|
applied.noOp ? '## No change needed' : replayed ? '## Already applied' : '## Skills updated',
|
|
509
812
|
'',
|
|
@@ -515,11 +818,14 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
515
818
|
pricing?.newMonthlyUsd !== undefined && pricing.newMonthlyUsd !== null
|
|
516
819
|
? `**Monthly price:** ${formatUsd(pricing.newMonthlyUsd)}${pricing.currentMonthlyUsd !== null ? ` (was ${formatUsd(pricing.currentMonthlyUsd)})` : ''}`
|
|
517
820
|
: null,
|
|
821
|
+
pricing && appliedChange?.creditsNow !== null && appliedChange?.creditsNow !== undefined && appliedChange.creditsThisPeriod !== null
|
|
822
|
+
? `**This period:** ${signedCredits(appliedChange.creditsNow)} credits, ${formatCredits(appliedChange.creditsThisPeriod)} in all until ${unixToIso(appliedChange.periodEnd) ?? 'the period ends'}${appliedChange.creditsFromRenewal !== null ? `; ${renewalGives(billing, appliedChange.creditsFromRenewal)}` : ''}`
|
|
823
|
+
: null,
|
|
518
824
|
`**Billing:** ${billingNote}`,
|
|
519
825
|
`**Restart:** ${restart}`,
|
|
520
826
|
holds === null ? 'The agent could not be read afterwards to confirm the new skills; get_my_agent shows them.' : null,
|
|
521
827
|
credentialsNeeded.length > 0
|
|
522
|
-
? `**Still needed:** credentials for ${credentialsNeeded.map(entry => `${entry.skill} (${entry.credentials.join(', ')})`).join('; ')}.
|
|
828
|
+
? `**Still needed:** credentials for ${credentialsNeeded.map(entry => `${entry.skill} (${entry.credentials.join(', ')})`).join('; ')}. ${stillNeededSteps(credentialsNeeded)}`
|
|
523
829
|
: null,
|
|
524
830
|
].filter((line) => line !== null);
|
|
525
831
|
return {
|
|
@@ -529,7 +835,13 @@ export function registerApplyAgentSkills(server, client) {
|
|
|
529
835
|
status: applied.noOp ? 'no_change' : 'applied',
|
|
530
836
|
idempotencyKey: key,
|
|
531
837
|
...(pricing ? { pricing } : {}),
|
|
532
|
-
|
|
838
|
+
// The saved answer of an earlier call: its charge is in the note, and this call charged nothing.
|
|
839
|
+
billing: {
|
|
840
|
+
...billingBase,
|
|
841
|
+
chargedNowUsd: replayed ? 0 : charge.charged,
|
|
842
|
+
...(usedCreditsUsd !== null && usedCreditsUsd > 0 ? { usedCreditsUsd } : {}),
|
|
843
|
+
note: billingNote,
|
|
844
|
+
},
|
|
533
845
|
...(Array.isArray(serverPricing.enabledSkills) ? { enabledSkills: serverPricing.enabledSkills.map(String) } : {}),
|
|
534
846
|
restart,
|
|
535
847
|
...(nextAction ? { nextAction } : {}),
|