@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,10 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ApiResultError, asString, errorResult, errorText, isPlainObject, requireApiSuccess, toNumber, unixToIso, } from '../utils/api.js';
|
|
4
|
-
import { workspaceHint } from '../utils/agent-config.js';
|
|
4
|
+
import { daysLeftText, readPlanChange, underOneDayLeft, workspaceHint } from '../utils/agent-config.js';
|
|
5
5
|
import { DEFAULT_WAIT_SECONDS } from '../utils/poll.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { isPlainTeamMember, teamAgentBillingRefused } from '../utils/workspace.js';
|
|
7
|
+
import { AMOUNT_TOLERANCE_USD, WALLET_NOT_READ_NOTE, agentDashboardUrl, agentSummarySchema, asTier, chargeRoute, formatAgentSummaryLines, formatCredits, formatUsd, isTransitionalStatus, isWalletNotRead, outcomeUnknown, readAgentDetail, roundUsd, sameAmount, toAgentDetail, toAgentSummary, } from '../utils/user-agent.js';
|
|
8
|
+
import { nextActionSchema, strictInput } from './schemas.js';
|
|
8
9
|
const ACTIONS = ['subscribe', 'upgrade_to_pro', 'cancel', 'renew', 'buy_credits'];
|
|
9
10
|
const quoteSchema = z.object({
|
|
10
11
|
chargeNowUsd: z.number().nullable(),
|
|
@@ -12,6 +13,8 @@ const quoteSchema = z.object({
|
|
|
12
13
|
maxChargeUsd: z.number().optional(),
|
|
13
14
|
recurringUsd: z.number().nullable().optional(),
|
|
14
15
|
monthlyCredits: z.number().nullable().optional(),
|
|
16
|
+
creditsNow: z.number().optional()
|
|
17
|
+
.describe('upgrade_to_pro: the credits this period gains now, below 0 when it loses them. monthlyCredits is what every renewal gives.'),
|
|
15
18
|
creditsAdded: z.number().optional(),
|
|
16
19
|
paidFrom: z.enum(['personal_wallet', 'team_wallet', 'card']),
|
|
17
20
|
tier: z.enum(['starter', 'pro']).optional(),
|
|
@@ -21,6 +24,8 @@ const resultSchema = z.object({
|
|
|
21
24
|
chargedUsd: z.number().nullable().optional(),
|
|
22
25
|
monthlyPriceUsd: z.number().optional(),
|
|
23
26
|
monthlyCredits: z.number().optional(),
|
|
27
|
+
creditsNow: z.number().optional()
|
|
28
|
+
.describe('upgrade_to_pro: the credits this period gained with it, below 0 when it lost them. monthlyCredits is what every renewal gives.'),
|
|
24
29
|
creditsAdded: z.number().optional(),
|
|
25
30
|
tier: z.string().optional(),
|
|
26
31
|
previousTier: z.string().optional(),
|
|
@@ -45,20 +50,114 @@ function waitAction(guid, reason) {
|
|
|
45
50
|
reason,
|
|
46
51
|
};
|
|
47
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The wait after an upgrade, a pack or a renewal, which keep a running agent up:
|
|
55
|
+
* only an agent on its way, one that was stopping (it starts again) or one queued
|
|
56
|
+
* or starting, is worth waiting for.
|
|
57
|
+
*/
|
|
58
|
+
function waitIfStarting(agent) {
|
|
59
|
+
return isTransitionalStatus(agent.status)
|
|
60
|
+
? waitAction(agent.guid, 'Wait until the agent is running.')
|
|
61
|
+
: undefined;
|
|
62
|
+
}
|
|
63
|
+
/** After a renewal, Wiro queues a stopped or failed agent; a running one keeps running. */
|
|
64
|
+
function renewWait(agent) {
|
|
65
|
+
if (agent.status === 0 || agent.status === 5)
|
|
66
|
+
return waitAction(agent.guid, 'Wait until the agent is running again.');
|
|
67
|
+
return waitIfStarting(agent);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A new plan queues a stopped or failed agent, and one waiting for setup; a
|
|
71
|
+
* running one keeps running, and one that was stopping starts again. An agent
|
|
72
|
+
* without a plan runs only while extra credits last.
|
|
73
|
+
*/
|
|
74
|
+
function subscribeStarts(status) {
|
|
75
|
+
return status !== 4;
|
|
76
|
+
}
|
|
77
|
+
function subscribeQuoteNote(status) {
|
|
78
|
+
switch (status) {
|
|
79
|
+
case 4:
|
|
80
|
+
return 'The agent keeps running: a new plan does not restart it.';
|
|
81
|
+
case 3:
|
|
82
|
+
return 'The agent is starting, and a new plan does not restart it.';
|
|
83
|
+
case 1:
|
|
84
|
+
return 'The agent is stopping, and it starts again right after the payment.';
|
|
85
|
+
case 2:
|
|
86
|
+
return 'The agent is already queued to start.';
|
|
87
|
+
default:
|
|
88
|
+
return 'The agent is queued to start right after the payment.';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function subscribeWait(agent) {
|
|
92
|
+
return subscribeStarts(agent.status) ? waitAction(agent.guid, 'Wait until the agent is running.') : undefined;
|
|
93
|
+
}
|
|
94
|
+
/** What an upgrade does to this period's credits. An allowance above Pro's is cut to Pro's for the time left. */
|
|
95
|
+
function upgradeCreditsText(creditsNow, renewal, done) {
|
|
96
|
+
if (creditsNow > 0) {
|
|
97
|
+
return `${formatCredits(creditsNow)} credits ${done ? 'came with it' : 'come now'} for the rest of this period${renewal}. `;
|
|
98
|
+
}
|
|
99
|
+
if (creditsNow < 0)
|
|
100
|
+
return `This period ${done ? 'lost' : 'loses'} ${formatCredits(-creditsNow)} credits for the time left${renewal}. `;
|
|
101
|
+
return `This period ${done ? 'kept' : 'keeps'} the credits it ${done ? 'had' : 'has'}${renewal}. `;
|
|
102
|
+
}
|
|
103
|
+
/** What a change to the plan does to the agent: a running one keeps running, and one that was stopping starts again. */
|
|
104
|
+
function planChangeAgentNote(status, what, quote = false) {
|
|
105
|
+
switch (status) {
|
|
106
|
+
case 4:
|
|
107
|
+
return `The agent keeps running: ${what} does not restart it.`;
|
|
108
|
+
case 3:
|
|
109
|
+
return `The agent is starting, and ${what} does not restart it.`;
|
|
110
|
+
case 1:
|
|
111
|
+
return quote
|
|
112
|
+
? 'The agent is stopping; if it is still stopping when the payment goes through, it starts again.'
|
|
113
|
+
: 'The agent was stopping, and it starts again.';
|
|
114
|
+
case 2:
|
|
115
|
+
return 'The agent is queued to start.';
|
|
116
|
+
case 6:
|
|
117
|
+
return 'The agent still needs setup; the credits are there once it starts.';
|
|
118
|
+
case 0:
|
|
119
|
+
return 'The agent is stopped; the credits are there when it starts.';
|
|
120
|
+
case 5:
|
|
121
|
+
return 'The agent stopped with an error; the credits are there when it starts.';
|
|
122
|
+
default:
|
|
123
|
+
return 'The credits are there when the agent starts.';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The Pro price of this agent and what moving to it does today, as Wiro works it
|
|
128
|
+
* out on the plan the agent has now. null when it cannot be read: the quote then
|
|
129
|
+
* falls back to the Pro price the agent shows and to the most the upgrade can charge.
|
|
130
|
+
*/
|
|
131
|
+
async function readProPreview(client, guid, signal) {
|
|
132
|
+
try {
|
|
133
|
+
const body = requireApiSuccess(await client.post('/UserAgent/PricingPreview', { useragentguid: guid, tier: 'pro' }, signal), 'The Pro price could not be read.');
|
|
134
|
+
const priceUsd = toNumber(body.totalPriceUsd);
|
|
135
|
+
const monthlyCredits = toNumber(body.totalMonthlyCredits);
|
|
136
|
+
return {
|
|
137
|
+
price: priceUsd !== null && monthlyCredits !== null ? { priceUsd, monthlyCredits } : null,
|
|
138
|
+
change: readPlanChange(body.planChange),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (signal.aborted)
|
|
143
|
+
throw error;
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
48
147
|
function tierName(tier) {
|
|
49
148
|
return tier === 'pro' ? 'Pro' : 'Starter';
|
|
50
149
|
}
|
|
51
|
-
/** The amount a confirmed call charges now, or null when it charges nothing. */
|
|
150
|
+
/** The amount a confirmed call charges now, or at most when that is worked out only then; null when it charges nothing. */
|
|
52
151
|
function chargeOf(quote) {
|
|
53
|
-
const amount = quote?.chargeNowUsd ?? null;
|
|
152
|
+
const amount = quote?.chargeNowUsd ?? quote?.maxChargeUsd ?? null;
|
|
54
153
|
return amount !== null && amount > 0 ? amount : null;
|
|
55
154
|
}
|
|
56
155
|
/**
|
|
57
156
|
* The amount the user approves with confirm_charge_usd, or null when the
|
|
58
|
-
* call charges nothing.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
157
|
+
* call charges nothing. An upgrade is approved at the most it can charge: the
|
|
158
|
+
* amount Wiro works out for a wallet plan, which only shrinks as the period
|
|
159
|
+
* runs on, or the full price difference when Wiro works the amount out only
|
|
160
|
+
* at the upgrade.
|
|
62
161
|
*/
|
|
63
162
|
function approvalAmountOf(action, quote) {
|
|
64
163
|
if (action !== 'upgrade_to_pro')
|
|
@@ -91,11 +190,11 @@ function changedLead(action, amount, approved, callerRequestId) {
|
|
|
91
190
|
if (action === 'buy_credits' && callerRequestId) {
|
|
92
191
|
// Wiro binds a purchase request id to the price of its first attempt.
|
|
93
192
|
return `${changed} Nothing was charged by this call. Show the new amount to the user and ask again. `
|
|
94
|
-
+ `If an earlier call with request_id "${callerRequestId}"
|
|
193
|
+
+ `If an earlier call with request_id "${callerRequestId}" ended without a clear result, do not approve the new amount for it: this request id works only at its original price. Check get_my_agent and the wallet balance with get_account, and if the balance changed, contact Wiro support.`;
|
|
95
194
|
}
|
|
96
195
|
return `${changed} Nothing was charged. Show the new amount to the user and ask again.`;
|
|
97
196
|
}
|
|
98
|
-
function planFor(client, action, agent, raw, input) {
|
|
197
|
+
async function planFor(client, action, agent, raw, input, signal) {
|
|
99
198
|
const guid = agent.guid;
|
|
100
199
|
const active = raw?.status === 'active';
|
|
101
200
|
const prepaid = raw?.provider === 'prepaid';
|
|
@@ -129,7 +228,7 @@ function planFor(client, action, agent, raw, input) {
|
|
|
129
228
|
tier,
|
|
130
229
|
},
|
|
131
230
|
message: `Subscribe on ${tierName(tier)}: ${formatUsd(price.priceUsd)} now from ${wallet}, then every 30 days until the plan is cancelled, with ${formatCredits(price.monthlyCredits)} credits per month. `
|
|
132
|
-
+
|
|
231
|
+
+ subscribeQuoteNote(agent.status)
|
|
133
232
|
+ (missing.length > 0
|
|
134
233
|
? ` Missing credentials (${missing.map(item => item.title).join(', ')}) can be added afterwards; until then the skills that need them fail.`
|
|
135
234
|
: ''),
|
|
@@ -143,9 +242,10 @@ function planFor(client, action, agent, raw, input) {
|
|
|
143
242
|
const credits = toNumber(body.monthlycredits) ?? price.monthlyCredits;
|
|
144
243
|
return {
|
|
145
244
|
outcome: 'done',
|
|
146
|
-
message: `Subscribed on ${tierName(tier)} and charged ${formatUsd(charged)} from ${wallet}. The plan renews every 30 days until it is cancelled.
|
|
245
|
+
message: `Subscribed on ${tierName(tier)} and charged ${formatUsd(charged)} from ${wallet}. The plan renews every 30 days until it is cancelled. `
|
|
246
|
+
+ (subscribeStarts(agent.status) ? 'The agent is starting.' : 'The agent keeps running: a new plan does not restart it.'),
|
|
147
247
|
result: { chargedUsd: charged, monthlyPriceUsd: charged, monthlyCredits: credits, tier },
|
|
148
|
-
nextAction:
|
|
248
|
+
nextAction: subscribeWait(agent),
|
|
149
249
|
};
|
|
150
250
|
},
|
|
151
251
|
};
|
|
@@ -160,23 +260,26 @@ function planFor(client, action, agent, raw, input) {
|
|
|
160
260
|
}
|
|
161
261
|
if (active) {
|
|
162
262
|
if (!prepaid) {
|
|
163
|
-
// A card plan can be set to stop at its period end
|
|
164
|
-
|
|
263
|
+
// A card plan can be set to stop at its period end.
|
|
264
|
+
const atPeriodEnd = agent.subscription?.atPeriodEnd;
|
|
165
265
|
return {
|
|
166
266
|
outcome: 'refused',
|
|
167
|
-
message:
|
|
267
|
+
message: atPeriodEnd === 'ends'
|
|
168
268
|
? `This plan is billed by card and is set to stop renewing after ${periodEnd}. Card plans are managed `
|
|
169
269
|
+ `from the dashboard, where it can be kept: ${agent.dashboardUrl}`
|
|
170
|
-
:
|
|
270
|
+
: atPeriodEnd === 'renews'
|
|
271
|
+
? `This plan is billed by card and renews by itself. It is managed from the dashboard: ${agent.dashboardUrl}`
|
|
272
|
+
: `This plan is billed by card and is paid through ${periodEnd}. Card plans are renewed or cancelled `
|
|
273
|
+
+ `from the dashboard: ${agent.dashboardUrl}`,
|
|
171
274
|
};
|
|
172
275
|
}
|
|
173
276
|
if (!cancelled) {
|
|
174
277
|
return { outcome: 'not_needed', message: 'The plan is active and renews by itself. Nothing to do.' };
|
|
175
278
|
}
|
|
176
|
-
const amount = agent.
|
|
279
|
+
const amount = agent.monthlyPriceUsd || agent.subscription?.amountUsd;
|
|
177
280
|
return {
|
|
178
281
|
outcome: 'quote',
|
|
179
|
-
quote: { chargeNowUsd: 0, estimated: false, recurringUsd: amount, monthlyCredits: agent.credits.monthly, paidFrom },
|
|
282
|
+
quote: { chargeNowUsd: 0, estimated: false, recurringUsd: amount, monthlyCredits: agent.credits.renewal ?? agent.credits.monthly, paidFrom },
|
|
180
283
|
message: `Undo the cancellation: nothing is charged now. The plan then renews from ${wallet} for ${formatUsd(amount)} after ${periodEnd} and every 30 days after that until it is cancelled.`,
|
|
181
284
|
confirmReason: 'Ask the user to confirm first: the plan will charge the wallet again at each renewal.',
|
|
182
285
|
perform: async (signal) => {
|
|
@@ -236,7 +339,7 @@ function planFor(client, action, agent, raw, input) {
|
|
|
236
339
|
...(credits !== null ? { monthlyCredits: credits } : {}),
|
|
237
340
|
renewal: 'renewed',
|
|
238
341
|
},
|
|
239
|
-
nextAction:
|
|
342
|
+
nextAction: renewWait(agent),
|
|
240
343
|
};
|
|
241
344
|
},
|
|
242
345
|
};
|
|
@@ -254,16 +357,17 @@ function planFor(client, action, agent, raw, input) {
|
|
|
254
357
|
};
|
|
255
358
|
};
|
|
256
359
|
if (!prepaid) {
|
|
257
|
-
|
|
360
|
+
// A plan moving to wallet billing has no cancel in the dashboard either: it can be cancelled once the wallet bills it.
|
|
361
|
+
if (raw?.nextprovider === 'prepaid') {
|
|
258
362
|
return {
|
|
259
363
|
outcome: 'refused',
|
|
260
|
-
message: `
|
|
364
|
+
message: `A switch to wallet billing is already scheduled for this plan, so it cannot be cancelled now. After ${periodEnd} it is billed from the wallet, and action cancel then stops its renewals.`,
|
|
261
365
|
};
|
|
262
366
|
}
|
|
263
|
-
if (
|
|
367
|
+
if (agent.workspace === 'personal') {
|
|
264
368
|
return {
|
|
265
369
|
outcome: 'refused',
|
|
266
|
-
message:
|
|
370
|
+
message: `This plan is billed by card. Card plans are cancelled from the dashboard: ${agent.dashboardUrl}`,
|
|
267
371
|
};
|
|
268
372
|
}
|
|
269
373
|
if (agent.subscription?.cancelAtPeriodEnd) {
|
|
@@ -290,6 +394,14 @@ function planFor(client, action, agent, raw, input) {
|
|
|
290
394
|
}
|
|
291
395
|
case 'upgrade_to_pro': {
|
|
292
396
|
if (!active) {
|
|
397
|
+
// A Pro plan that ended comes back at its own price with renew. Subscribe takes today's price.
|
|
398
|
+
if (agent.tier === 'pro' && raw?.status === 'expired' && prepaid) {
|
|
399
|
+
return {
|
|
400
|
+
outcome: 'refused',
|
|
401
|
+
message: 'This agent is already on Pro, and its plan has ended. Action renew brings the plan back at its own price; subscribe starts a new plan at today\'s price.',
|
|
402
|
+
nextAction: billingCall(guid, 'renew'),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
293
405
|
return {
|
|
294
406
|
outcome: 'refused',
|
|
295
407
|
message: 'Upgrading needs an active plan. Use action subscribe with tier pro instead.',
|
|
@@ -301,57 +413,72 @@ function planFor(client, action, agent, raw, input) {
|
|
|
301
413
|
// No checkout pages are opened for card plans: the user pays them in the dashboard.
|
|
302
414
|
if (route === 'card')
|
|
303
415
|
return { outcome: 'refused', message: cardPlan('upgrade it to Pro') };
|
|
304
|
-
const
|
|
416
|
+
const preview = await readProPreview(client, guid, signal);
|
|
417
|
+
const pro = preview?.price ?? agent.pricing?.pro;
|
|
305
418
|
if (!pro)
|
|
306
419
|
throw new Error('The Pro price of this agent is not available right now. Try again later.');
|
|
307
|
-
const ending = `The plan is cancelled, so it ends about a day after ${periodEnd} and does not renew at the Pro price; this charge covers only the
|
|
308
|
-
const current = agent.monthlyPriceUsd
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
const
|
|
420
|
+
const ending = `The plan is cancelled, so it ends about a day after ${periodEnd} and does not renew at the Pro price; this charge covers only the time left. To keep the plan, undo the cancellation first with action renew.`;
|
|
421
|
+
const current = agent.monthlyPriceUsd || agent.subscription?.amountUsd || 0;
|
|
422
|
+
const fullDifference = roundUsd(Math.max(0, pro.priceUsd - current));
|
|
423
|
+
// A wallet plan pays the amount Wiro works out on it now. A card plan moving to wallet billing is
|
|
424
|
+
// charged from the wallet for the time left of its card period, which Wiro works out at the upgrade.
|
|
425
|
+
const change = preview?.change?.wallet ? preview.change : null;
|
|
426
|
+
const exact = change?.chargeNowUsd ?? null;
|
|
427
|
+
const renewalCredits = change?.creditsFromRenewal ?? pro.monthlyCredits;
|
|
428
|
+
const creditsNow = change?.creditsNow ?? null;
|
|
429
|
+
const days = daysLeftText(change?.secondsLeft ?? null);
|
|
430
|
+
const money = exact !== null
|
|
431
|
+
? `Upgrade to Pro: ${formatUsd(exact)} now from ${wallet} for the rest of this period`
|
|
432
|
+
+ `${exact > 0 && underOneDayLeft(change?.secondsLeft ?? null) ? ', at the one-day minimum' : days ? ` (${days})` : ''}. `
|
|
433
|
+
: `Upgrade to Pro: the difference for the rest of this period is taken from ${wallet} now. Wiro works it out from the time left when the upgrade is applied, and it is never more than ${formatUsd(fullDifference)}, so that is the amount to approve. `;
|
|
434
|
+
const credits = creditsNow !== null
|
|
435
|
+
? upgradeCreditsText(creditsNow, cancelled ? '' : `, and every renewal gives ${formatCredits(renewalCredits)}`, false)
|
|
436
|
+
: `The added credits come for the time left in this period${cancelled ? '' : `, and every renewal gives ${formatCredits(renewalCredits)}`}. `;
|
|
315
437
|
return {
|
|
316
438
|
outcome: 'quote',
|
|
317
439
|
quote: {
|
|
318
|
-
chargeNowUsd:
|
|
319
|
-
estimated:
|
|
320
|
-
maxChargeUsd:
|
|
440
|
+
chargeNowUsd: exact,
|
|
441
|
+
estimated: exact === null,
|
|
442
|
+
maxChargeUsd: exact ?? fullDifference,
|
|
321
443
|
recurringUsd: cancelled ? null : pro.priceUsd,
|
|
322
|
-
monthlyCredits:
|
|
444
|
+
monthlyCredits: renewalCredits,
|
|
445
|
+
...(creditsNow !== null ? { creditsNow } : {}),
|
|
323
446
|
paidFrom,
|
|
324
447
|
tier: 'pro',
|
|
325
448
|
},
|
|
326
|
-
message:
|
|
327
|
-
+ `Wiro works out the exact amount from the real length of the billing period, and it is never more than ${formatUsd(maxCharge)}, so that is the amount to approve. `
|
|
449
|
+
message: money + credits
|
|
328
450
|
+ (cancelled ? `${ending} ` : `From the next renewal the plan costs ${formatUsd(pro.priceUsd)} every 30 days. `)
|
|
329
|
-
+
|
|
451
|
+
+ `${planChangeAgentNote(agent.status, 'the upgrade', true)} To go back to Starter later, cancel, let the plan end, then subscribe to Starter.`,
|
|
330
452
|
perform: async (signal) => {
|
|
331
453
|
const body = requireApiSuccess(await client.post('/UserAgent/UpgradeTier', { guid, targetTier: 'pro' }, signal), 'The plan could not be upgraded.');
|
|
332
454
|
if (asString(body.url)) {
|
|
333
|
-
// A checkout link means the plan is paid by card
|
|
455
|
+
// A checkout link means the plan is paid by card, and card payments are made in the dashboard.
|
|
334
456
|
return { outcome: 'refused', message: cardPlan('upgrade it to Pro') };
|
|
335
457
|
}
|
|
336
458
|
const charged = toNumber(body.proratedCharge);
|
|
337
459
|
const newPrice = toNumber(body.newPriceUsd) ?? pro.priceUsd;
|
|
338
|
-
const
|
|
460
|
+
const fromRenewal = toNumber(body.creditsFromRenewal) ?? toNumber(body.newMonthlyCredits) ?? renewalCredits;
|
|
461
|
+
const came = toNumber(body.creditsNow);
|
|
339
462
|
const previousTier = asString(body.previousTier);
|
|
340
463
|
return {
|
|
341
464
|
outcome: 'done',
|
|
342
465
|
message: `Upgraded to Pro and charged ${formatUsd(charged ?? 0)} from ${wallet}. `
|
|
466
|
+
+ (came !== null
|
|
467
|
+
? upgradeCreditsText(came, cancelled ? '' : `, and every renewal gives ${formatCredits(fromRenewal)}`, true)
|
|
468
|
+
: '')
|
|
343
469
|
+ (cancelled
|
|
344
470
|
? `The plan is still cancelled and ends about a day after ${periodEnd}; action renew undoes the cancellation. `
|
|
345
471
|
: `The plan now costs ${formatUsd(newPrice)} every 30 days. `)
|
|
346
|
-
+
|
|
472
|
+
+ planChangeAgentNote(agent.status, 'the upgrade'),
|
|
347
473
|
result: {
|
|
348
474
|
chargedUsd: charged,
|
|
349
475
|
monthlyPriceUsd: newPrice,
|
|
350
|
-
monthlyCredits:
|
|
476
|
+
monthlyCredits: fromRenewal,
|
|
477
|
+
...(came !== null ? { creditsNow: came } : {}),
|
|
351
478
|
tier: 'pro',
|
|
352
479
|
...(previousTier ? { previousTier } : {}),
|
|
353
480
|
},
|
|
354
|
-
nextAction:
|
|
481
|
+
nextAction: waitIfStarting(agent),
|
|
355
482
|
};
|
|
356
483
|
},
|
|
357
484
|
};
|
|
@@ -374,6 +501,7 @@ function planFor(client, action, agent, raw, input) {
|
|
|
374
501
|
if (!input.pack) {
|
|
375
502
|
return {
|
|
376
503
|
outcome: 'refused',
|
|
504
|
+
...(packs.length > 0 ? { heading: 'Available packs' } : {}),
|
|
377
505
|
message: packs.length > 0 ? `Choose a pack: ${packList}.` : 'No extra credit packs are available for this agent yet.',
|
|
378
506
|
...(packs[0] ? { nextAction: billingCall(guid, 'buy_credits', { pack: packs[0].pack }) } : {}),
|
|
379
507
|
};
|
|
@@ -388,7 +516,7 @@ function planFor(client, action, agent, raw, input) {
|
|
|
388
516
|
return {
|
|
389
517
|
outcome: 'quote',
|
|
390
518
|
quote: { chargeNowUsd: chosen.priceUsd, estimated: false, creditsAdded: chosen.credits, paidFrom, pack: chosen.pack },
|
|
391
|
-
message: `Buy the ${chosen.pack} pack: ${formatUsd(chosen.priceUsd)} now from ${wallet} for ${formatCredits(chosen.credits)} extra credits, added at once. Extra credits are used after the monthly credits
|
|
519
|
+
message: `Buy the ${chosen.pack} pack: ${formatUsd(chosen.priceUsd)} now from ${wallet} for ${formatCredits(chosen.credits)} extra credits, added at once. Extra credits are used after the monthly credits, do not reset each month and end 6 months (180 days) after purchase. ${planChangeAgentNote(agent.status, 'a credit pack', true)} This is a one-time charge.`,
|
|
392
520
|
perform: async (signal) => {
|
|
393
521
|
const body = requireApiSuccess(await client.post('/UserAgent/CreateExtraCreditCheckout', {
|
|
394
522
|
useragentguid: guid,
|
|
@@ -399,9 +527,9 @@ function planFor(client, action, agent, raw, input) {
|
|
|
399
527
|
return { outcome: 'refused', message: cardPlan('buy the pack') };
|
|
400
528
|
return {
|
|
401
529
|
outcome: 'done',
|
|
402
|
-
message: `Bought the ${chosen.pack} pack: ${formatCredits(chosen.credits)} credits for ${formatUsd(chosen.priceUsd)} from ${wallet}. The credits are added
|
|
530
|
+
message: `Bought the ${chosen.pack} pack: ${formatCredits(chosen.credits)} credits for ${formatUsd(chosen.priceUsd)} from ${wallet}. The credits are added. ${planChangeAgentNote(agent.status, 'a credit pack')}`,
|
|
403
531
|
result: { chargedUsd: chosen.priceUsd, creditsAdded: chosen.credits },
|
|
404
|
-
nextAction:
|
|
532
|
+
nextAction: waitIfStarting(agent),
|
|
405
533
|
};
|
|
406
534
|
},
|
|
407
535
|
};
|
|
@@ -423,8 +551,8 @@ function confirmArguments(guid, action, quote, requestId) {
|
|
|
423
551
|
}
|
|
424
552
|
function retryPurchaseText(guid, quote, requestId) {
|
|
425
553
|
const amount = chargeOf(quote);
|
|
426
|
-
return `To retry, call manage_agent_billing again with agent_guid "${guid}", action buy_credits, pack "${quote?.pack ?? ''}", request_id "${requestId}"${amount !== null ? `, confirm_charge_usd ${amount}` : ''} and confirm: true. That request id never charges twice, and it
|
|
427
|
-
+ (amount !== null ? ` This works only while the pack
|
|
554
|
+
return `To retry, call manage_agent_billing again with agent_guid "${guid}", action buy_credits, pack "${quote?.pack ?? ''}", request_id "${requestId}"${amount !== null ? `, confirm_charge_usd ${amount}` : ''} and confirm: true. That request id never charges twice, and repeating it completes this same purchase. Do not get a new quote for this purchase: a new request id is a new purchase.`
|
|
555
|
+
+ (amount !== null ? ` This request id works only while the pack costs ${formatUsd(amount)}. If the price changes, do not buy again: check get_my_agent and the wallet balance with get_account, and if the balance changed, contact Wiro support.` : '');
|
|
428
556
|
}
|
|
429
557
|
/**
|
|
430
558
|
* Whether the agent's packs show an active one of this size bought since
|
|
@@ -447,9 +575,8 @@ async function freshPackShows(client, guid, credits, sentAtMs, signal) {
|
|
|
447
575
|
}
|
|
448
576
|
}
|
|
449
577
|
/**
|
|
450
|
-
* After a
|
|
451
|
-
* says what it shows
|
|
452
|
-
* detail is where its result shows.
|
|
578
|
+
* After a paid request whose answer did not confirm it, reads the agent again
|
|
579
|
+
* and says what it shows.
|
|
453
580
|
*/
|
|
454
581
|
async function observeOpenOutcome(client, action, before, quote, requestId, sentAtMs, signal) {
|
|
455
582
|
let row;
|
|
@@ -457,11 +584,10 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
457
584
|
row = await readAgentDetail(client, before.guid, signal);
|
|
458
585
|
}
|
|
459
586
|
catch {
|
|
460
|
-
return { happened: false, text: 'The agent could not be read afterwards to check it.' };
|
|
587
|
+
return { happened: false, text: 'The agent could not be read afterwards to check it. Check get_my_agent and the wallet balance with get_account first.' };
|
|
461
588
|
}
|
|
462
589
|
const after = toAgentDetail(row);
|
|
463
590
|
const charged = chargeOf(quote);
|
|
464
|
-
const restart = waitAction(before.guid, 'Wait until the agent is running again.');
|
|
465
591
|
switch (action) {
|
|
466
592
|
case 'subscribe':
|
|
467
593
|
return after.subscription?.status === 'active'
|
|
@@ -470,9 +596,9 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
470
596
|
row,
|
|
471
597
|
text: 'The agent now has an active plan, so the payment went through. Do not subscribe again.',
|
|
472
598
|
result: { chargedUsd: charged, ...(quote?.tier ? { tier: quote.tier } : {}) },
|
|
473
|
-
nextAction:
|
|
599
|
+
nextAction: subscribeWait(before),
|
|
474
600
|
}
|
|
475
|
-
: { happened: false, text: 'The agent shows no active plan yet. Check get_my_agent again in a minute before trying again.' };
|
|
601
|
+
: { happened: false, text: 'The agent shows no active plan yet. Check get_my_agent again in a minute and the wallet balance with get_account before trying again; if the plan is still not active but the balance changed, contact Wiro support.' };
|
|
476
602
|
case 'renew':
|
|
477
603
|
return after.subscription?.status === 'active'
|
|
478
604
|
? {
|
|
@@ -480,9 +606,9 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
480
606
|
row,
|
|
481
607
|
text: 'The plan is active again, so the renewal went through. Do not renew it again.',
|
|
482
608
|
result: { chargedUsd: charged, renewal: 'renewed' },
|
|
483
|
-
nextAction:
|
|
609
|
+
nextAction: renewWait(before),
|
|
484
610
|
}
|
|
485
|
-
: { happened: false, text: 'The plan still shows as expired
|
|
611
|
+
: { happened: false, text: 'The plan still shows as expired. Check get_my_agent again in a minute and the wallet balance with get_account before renewing again; if the plan is still expired but the balance changed, contact Wiro support.' };
|
|
486
612
|
case 'upgrade_to_pro': {
|
|
487
613
|
if (after.tier === 'pro') {
|
|
488
614
|
return {
|
|
@@ -490,7 +616,7 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
490
616
|
row,
|
|
491
617
|
text: `The agent is on Pro now, so the upgrade went through. Do not upgrade again. The prorated charge is not shown, and it is at most ${formatUsd(quote?.maxChargeUsd)}.`,
|
|
492
618
|
result: { chargedUsd: null, tier: 'pro' },
|
|
493
|
-
nextAction:
|
|
619
|
+
nextAction: waitIfStarting(before),
|
|
494
620
|
};
|
|
495
621
|
}
|
|
496
622
|
const proPrice = before.pricing?.pro.priceUsd;
|
|
@@ -499,8 +625,8 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
499
625
|
return {
|
|
500
626
|
happened: false,
|
|
501
627
|
text: priced
|
|
502
|
-
? 'The
|
|
503
|
-
: 'The agent still shows Starter
|
|
628
|
+
? 'The agent does not show Pro yet. Do not upgrade again: check get_my_agent and the wallet balance with get_account, and if the balance changed, contact Wiro support.'
|
|
629
|
+
: 'The agent still shows Starter. Check get_my_agent and the wallet balance with get_account before trying again; if the balance changed, contact Wiro support.',
|
|
504
630
|
};
|
|
505
631
|
}
|
|
506
632
|
case 'buy_credits': {
|
|
@@ -513,18 +639,18 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
513
639
|
row,
|
|
514
640
|
text: `The agent's extra credits rose by ${formatCredits(rose)}, so the purchase went through. Do not buy it again.`,
|
|
515
641
|
result: bought,
|
|
516
|
-
nextAction:
|
|
642
|
+
nextAction: waitIfStarting(before),
|
|
517
643
|
};
|
|
518
644
|
}
|
|
519
645
|
const fresh = await freshPackShows(client, before.guid, credits, sentAtMs, signal);
|
|
520
646
|
if (fresh) {
|
|
521
|
-
// The pack
|
|
522
|
-
//
|
|
647
|
+
// The pack shows on the agent. If its credits do not show yet, the same
|
|
648
|
+
// request id completes the purchase without a second charge.
|
|
523
649
|
return {
|
|
524
650
|
happened: true,
|
|
525
651
|
row,
|
|
526
|
-
text: 'A new pack of that size shows on the agent, so the purchase went through
|
|
527
|
-
+ `
|
|
652
|
+
text: 'A new pack of that size shows on the agent, so the purchase went through. Do not buy it again. '
|
|
653
|
+
+ `If get_my_agent does not show its credits in a few minutes, repeat this purchase with the same request_id "${requestId ?? ''}" and confirm_charge_usd ${charged ?? ''}; that request id never charges twice.`,
|
|
528
654
|
result: bought,
|
|
529
655
|
nextAction: waitAction(before.guid, 'Check that the pack\'s credits show on the agent.'),
|
|
530
656
|
};
|
|
@@ -538,13 +664,20 @@ async function observeOpenOutcome(client, action, before, quote, requestId, sent
|
|
|
538
664
|
return { happened: false, text: '' };
|
|
539
665
|
}
|
|
540
666
|
}
|
|
541
|
-
|
|
667
|
+
// Code 104: another payment from the same wallet, or another change to the agent's plan, was going through.
|
|
668
|
+
const BUSY_NOTE = 'Nothing was charged or changed, and it is not a low balance. Offer to try again in a minute.';
|
|
669
|
+
function annotateKnownRefusal(error, requestId) {
|
|
542
670
|
const text = error.message;
|
|
671
|
+
if (error.codes.some(code => Number(code) === 104)) {
|
|
672
|
+
return new Error(`${text}\n\n${BUSY_NOTE}${requestId ? ` To try again, repeat the call with request_id "${requestId}"; that request id never charges twice.` : ''}`);
|
|
673
|
+
}
|
|
674
|
+
if (isWalletNotRead(error))
|
|
675
|
+
return new Error(`${text}\n\n${WALLET_NOT_READ_NOTE}`);
|
|
543
676
|
if (/insufficient wallet balance/i.test(text)) {
|
|
544
677
|
return new Error(`${text}\n\nNothing was charged. Funds are added at https://wiro.ai/panel/wallet/deposit/credit-card (for a team project, select that team in the dashboard first); once the user has added them and says yes, try again. get_account shows the balance.`);
|
|
545
678
|
}
|
|
546
679
|
if (/purchase request id was already used/i.test(text)) {
|
|
547
|
-
return new Error(`${text}\n\nNothing was charged now. This request id belongs to an earlier purchase of another pack or at another price. For a new purchase, get a new quote without request_id. If
|
|
680
|
+
return new Error(`${text}\n\nNothing was charged now. This request id belongs to an earlier purchase of another pack or at another price. For a new purchase, get a new quote without request_id. If the earlier purchase with this id ended without a clear result, do not buy again: check get_my_agent and the wallet balance with get_account, and if the balance changed, contact Wiro support.`);
|
|
548
681
|
}
|
|
549
682
|
const hint = workspaceHint(text);
|
|
550
683
|
if (hint)
|
|
@@ -557,17 +690,23 @@ export function registerManageAgentBilling(server, client) {
|
|
|
557
690
|
description: 'Pay for, renew, upgrade or cancel a deployed agent\'s plan, or buy extra credits. Charges '
|
|
558
691
|
+ 'come from the Wiro wallet of the agent\'s workspace (the team wallet for a team agent); the connected '
|
|
559
692
|
+ 'project must belong to that same workspace. Every call without confirm: true only returns what will happen '
|
|
560
|
-
+ 'and the amount (for
|
|
693
|
+
+ 'and the amount (for an upgrade, the prorated amount Wiro works out, or the most it can charge when '
|
|
694
|
+
+ 'that is worked out only at the upgrade). Show it to the user, get '
|
|
561
695
|
+ 'their approval, then repeat the call with confirm: true and confirm_charge_usd set to the quoted '
|
|
562
|
-
+ 'amount (for an upgrade,
|
|
696
|
+
+ 'amount (for an upgrade, quote.maxChargeUsd); if the price changed in between, nothing is charged and a '
|
|
563
697
|
+ 'new quote comes back. Never repeat a confirmed charge that failed or timed out without checking '
|
|
564
698
|
+ 'get_my_agent first. '
|
|
565
699
|
+ 'subscribe starts a plan for an agent without one; renew undoes a cancellation or renews an expired '
|
|
566
700
|
+ 'wallet plan; upgrade_to_pro charges the prorated difference now; buy_credits adds a credit pack '
|
|
567
|
-
+ '(Pro only); cancel stops renewals with no refund. Plans renew every 30 days until cancelled.
|
|
701
|
+
+ '(Pro only); cancel stops renewals with no refund. Plans renew every 30 days until cancelled. Renewals, '
|
|
702
|
+
+ 'upgrades and packs leave a running agent running. An '
|
|
568
703
|
+ 'agent whose plan is billed by card is upgraded and topped up in the Wiro dashboard, and a personal '
|
|
569
|
-
+ 'card plan is cancelled there; the tool returns the link and charges nothing.'
|
|
570
|
-
|
|
704
|
+
+ 'card plan is cancelled there; the tool returns the link and charges nothing. A card plan that is moving to '
|
|
705
|
+
+ 'wallet billing pays upgrade_to_pro and buy_credits from the wallet, and cancel works once the wallet bills it. '
|
|
706
|
+
+ 'On a team agent only a team '
|
|
707
|
+
+ 'admin, the organization\'s owner or the person who deployed the agent can pay for it or change its plan; '
|
|
708
|
+
+ 'other members are refused and nothing is charged.',
|
|
709
|
+
inputSchema: strictInput({
|
|
571
710
|
agent_guid: z.string().min(1).max(100)
|
|
572
711
|
.describe('The deployed agent\'s guid, from list_my_agents or deploy_agent.'),
|
|
573
712
|
action: z.enum(ACTIONS)
|
|
@@ -582,7 +721,7 @@ export function registerManageAgentBilling(server, client) {
|
|
|
582
721
|
.describe('With confirm: true, the amount the user approved, from the quote\'s nextAction (quote.chargeNowUsd, or quote.maxChargeUsd for upgrade_to_pro). Required for every action that charges: subscribe, upgrade_to_pro, buy_credits and renewing an expired plan. If a fresh quote no longer matches it, nothing is charged and the new quote is returned. For upgrade_to_pro it is the most the user agreed to pay now.'),
|
|
583
722
|
confirm: z.boolean().default(false)
|
|
584
723
|
.describe('false returns only the quote. true performs the action. Set it only after the user approved it.'),
|
|
585
|
-
},
|
|
724
|
+
}),
|
|
586
725
|
outputSchema: {
|
|
587
726
|
action: z.enum(ACTIONS),
|
|
588
727
|
agentGuid: z.string(),
|
|
@@ -608,10 +747,12 @@ export function registerManageAgentBilling(server, client) {
|
|
|
608
747
|
let sentAtMs = 0;
|
|
609
748
|
let before;
|
|
610
749
|
let plan;
|
|
750
|
+
let plainMember = false;
|
|
611
751
|
const respond = (payload) => {
|
|
612
752
|
const summary = toAgentSummary(payload.agentRow);
|
|
753
|
+
const summaryLines = formatAgentSummaryLines(summary, plainMember);
|
|
613
754
|
const dashboardUrl = before?.dashboardUrl ?? agentDashboardUrl(guid);
|
|
614
|
-
const heading = {
|
|
755
|
+
const heading = payload.heading ?? {
|
|
615
756
|
quote: 'Quote (nothing done yet)',
|
|
616
757
|
done: 'Done',
|
|
617
758
|
not_needed: 'Nothing to do',
|
|
@@ -625,7 +766,7 @@ export function registerManageAgentBilling(server, client) {
|
|
|
625
766
|
'',
|
|
626
767
|
payload.message,
|
|
627
768
|
'',
|
|
628
|
-
...
|
|
769
|
+
...(payload.agentStale ? summaryLines.slice(0, 1) : summaryLines),
|
|
629
770
|
`Dashboard: ${dashboardUrl}`,
|
|
630
771
|
].join('\n'),
|
|
631
772
|
}],
|
|
@@ -648,10 +789,22 @@ export function registerManageAgentBilling(server, client) {
|
|
|
648
789
|
const row = await readAgentDetail(client, guid, extra.signal);
|
|
649
790
|
const agent = toAgentDetail(row);
|
|
650
791
|
before = agent;
|
|
792
|
+
plainMember = await isPlainTeamMember(client, row, extra.signal);
|
|
651
793
|
const raw = isPlainObject(row.subscription) ? row.subscription : null;
|
|
652
|
-
plan = planFor(client, action, agent, raw, { tier, pack, requestId: requestId ?? '' });
|
|
794
|
+
plan = await planFor(client, action, agent, raw, { tier, pack, requestId: requestId ?? '' }, extra.signal);
|
|
653
795
|
if (plan.outcome !== 'quote' || !plan.perform) {
|
|
654
|
-
return respond({ outcome: plan.outcome, message: plan.message, nextAction: plan.nextAction, agentRow: row });
|
|
796
|
+
return respond({ outcome: plan.outcome, heading: plan.heading, message: plan.message, nextAction: plan.nextAction, agentRow: row });
|
|
797
|
+
}
|
|
798
|
+
// A team member without admin rights can still be the person who
|
|
799
|
+
// deployed the agent, who may pay for it, so Wiro is asked before a quote.
|
|
800
|
+
if (plainMember && await teamAgentBillingRefused(client, guid, extra.signal)) {
|
|
801
|
+
return respond({
|
|
802
|
+
outcome: 'refused',
|
|
803
|
+
message: 'Only a team admin, the organization\'s owner or the person who deployed this agent can pay for '
|
|
804
|
+
+ 'or change its plan, and this connection acts as a team member without those rights. Nothing was '
|
|
805
|
+
+ 'quoted or charged. Ask a team admin or the person who deployed the agent to do it.',
|
|
806
|
+
agentRow: row,
|
|
807
|
+
});
|
|
655
808
|
}
|
|
656
809
|
const problem = confirm ? approvalProblem(action, plan.quote, confirm_charge_usd) : null;
|
|
657
810
|
if (!confirm || problem) {
|
|
@@ -692,9 +845,9 @@ export function registerManageAgentBilling(server, client) {
|
|
|
692
845
|
// The action went through; the earlier snapshot still identifies the agent.
|
|
693
846
|
}
|
|
694
847
|
// A request id that already completed a purchase is answered with
|
|
695
|
-
// success again, without a charge
|
|
696
|
-
//
|
|
697
|
-
//
|
|
848
|
+
// success again, without a new charge or new credits. An older pack can
|
|
849
|
+
// end at the same moment, so the extra credits can grow by less than
|
|
850
|
+
// the pack, and the agent's packs decide.
|
|
698
851
|
const credits = plan.quote?.creditsAdded ?? 0;
|
|
699
852
|
const extraBefore = toNumber(row.extracredits);
|
|
700
853
|
const extraAfter = afterRead ? toNumber(after.extracredits) : null;
|
|
@@ -709,6 +862,20 @@ export function registerManageAgentBilling(server, client) {
|
|
|
709
862
|
agentRow: after,
|
|
710
863
|
});
|
|
711
864
|
}
|
|
865
|
+
// A repeat adds no credits. A new pack can add none only when an older
|
|
866
|
+
// pack ends at the same moment with at least as many left, so an answer
|
|
867
|
+
// with no growth says only what holds in both cases.
|
|
868
|
+
if (fresh && extraAfter <= extraBefore) {
|
|
869
|
+
return respond({
|
|
870
|
+
outcome: 'done',
|
|
871
|
+
heading: 'Pack in place',
|
|
872
|
+
message: `The ${plan.quote?.pack} pack is in place: ${formatCredits(credits)} credits for ${formatUsd(plan.quote?.chargeNowUsd)} from ${agent.workspace === 'team' ? 'the team wallet' : 'the personal wallet'}. This request id is charged only once. The agent's extra credits did not grow in this call, so if the id had already completed in an earlier call, nothing new was bought or charged now. ${planChangeAgentNote(agent.status, 'a credit pack')} For another pack, get a new quote without request_id.`,
|
|
873
|
+
quote: plan.quote,
|
|
874
|
+
result: { chargedUsd: null },
|
|
875
|
+
nextAction: performed.nextAction,
|
|
876
|
+
agentRow: after,
|
|
877
|
+
});
|
|
878
|
+
}
|
|
712
879
|
return respond({
|
|
713
880
|
outcome: 'done',
|
|
714
881
|
message: fresh
|
|
@@ -720,13 +887,18 @@ export function registerManageAgentBilling(server, client) {
|
|
|
720
887
|
agentRow: after,
|
|
721
888
|
});
|
|
722
889
|
}
|
|
890
|
+
// Without the read-back, the row shows the agent as it was before the change.
|
|
891
|
+
const stale = !afterRead && performed.outcome === 'done';
|
|
723
892
|
return respond({
|
|
724
893
|
outcome: performed.outcome,
|
|
725
|
-
message:
|
|
894
|
+
message: stale
|
|
895
|
+
? `${performed.message} The agent could not be read afterwards; get_my_agent shows its new status, plan and credits.`
|
|
896
|
+
: performed.message,
|
|
726
897
|
quote: plan.quote,
|
|
727
898
|
result: performed.result,
|
|
728
899
|
nextAction: performed.nextAction,
|
|
729
900
|
agentRow: after,
|
|
901
|
+
agentStale: stale,
|
|
730
902
|
});
|
|
731
903
|
}
|
|
732
904
|
catch (error) {
|
|
@@ -743,7 +915,7 @@ export function registerManageAgentBilling(server, client) {
|
|
|
743
915
|
if (observed.happened && observed.row) {
|
|
744
916
|
return respond({
|
|
745
917
|
outcome: 'done',
|
|
746
|
-
message: `
|
|
918
|
+
message: `The answer did not confirm this request, so the agent was read again. ${observed.text}`,
|
|
747
919
|
quote,
|
|
748
920
|
result: observed.result,
|
|
749
921
|
nextAction: observed.nextAction,
|
|
@@ -753,10 +925,10 @@ export function registerManageAgentBilling(server, client) {
|
|
|
753
925
|
const retry = action === 'buy_credits' && requestId
|
|
754
926
|
? retryPurchaseText(guid, quote, requestId)
|
|
755
927
|
: 'Do not repeat the call until this is checked.';
|
|
756
|
-
return errorResult(new Error(`${text}\n\nThe request was sent and the answer does not show whether it went through
|
|
928
|
+
return errorResult(new Error(`${text}\n\nThe request was sent and the answer does not show whether it went through. ${observed.text} ${retry}`));
|
|
757
929
|
}
|
|
758
930
|
if (error instanceof ApiResultError)
|
|
759
|
-
return errorResult(annotateKnownRefusal(error));
|
|
931
|
+
return errorResult(annotateKnownRefusal(error, sentAtMs > 0 ? requestId : undefined));
|
|
760
932
|
return errorResult(error);
|
|
761
933
|
}
|
|
762
934
|
});
|