@wiro-ai/wiro-mcp 1.3.3 → 1.4.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 +173 -16
- package/dist/client.d.ts +10 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +39 -20
- package/dist/client.js.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +25 -0
- package/dist/instructions.js.map +1 -0
- package/dist/playbooks.d.ts +8 -0
- package/dist/playbooks.d.ts.map +1 -0
- package/dist/playbooks.js +665 -0
- package/dist/playbooks.js.map +1 -0
- package/dist/prompts.d.ts +4 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +133 -0
- package/dist/prompts.js.map +1 -0
- package/dist/server.d.ts +9 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +9 -3
- package/dist/server.js.map +1 -1
- package/dist/tools/apply-agent-skills.d.ts +4 -0
- package/dist/tools/apply-agent-skills.d.ts.map +1 -0
- package/dist/tools/apply-agent-skills.js +545 -0
- package/dist/tools/apply-agent-skills.js.map +1 -0
- package/dist/tools/cancel-agent-message.d.ts +4 -0
- package/dist/tools/cancel-agent-message.d.ts.map +1 -0
- package/dist/tools/cancel-agent-message.js +126 -0
- package/dist/tools/cancel-agent-message.js.map +1 -0
- package/dist/tools/cancel-task.js +3 -3
- package/dist/tools/cancel-task.js.map +1 -1
- package/dist/tools/clone-workflow.d.ts +4 -0
- package/dist/tools/clone-workflow.d.ts.map +1 -0
- package/dist/tools/clone-workflow.js +83 -0
- package/dist/tools/clone-workflow.js.map +1 -0
- package/dist/tools/connect-agent-oauth.d.ts +4 -0
- package/dist/tools/connect-agent-oauth.d.ts.map +1 -0
- package/dist/tools/connect-agent-oauth.js +508 -0
- package/dist/tools/connect-agent-oauth.js.map +1 -0
- package/dist/tools/control-agent.d.ts +4 -0
- package/dist/tools/control-agent.d.ts.map +1 -0
- package/dist/tools/control-agent.js +274 -0
- package/dist/tools/control-agent.js.map +1 -0
- package/dist/tools/create-workflow.d.ts +100 -0
- package/dist/tools/create-workflow.d.ts.map +1 -0
- package/dist/tools/create-workflow.js +103 -0
- package/dist/tools/create-workflow.js.map +1 -0
- package/dist/tools/delete-agent-session.d.ts +6 -0
- package/dist/tools/delete-agent-session.d.ts.map +1 -0
- package/dist/tools/delete-agent-session.js +134 -0
- package/dist/tools/delete-agent-session.js.map +1 -0
- package/dist/tools/delete-agent.d.ts +4 -0
- package/dist/tools/delete-agent.d.ts.map +1 -0
- package/dist/tools/delete-agent.js +144 -0
- package/dist/tools/delete-agent.js.map +1 -0
- package/dist/tools/delete-workflow.d.ts +4 -0
- package/dist/tools/delete-workflow.d.ts.map +1 -0
- package/dist/tools/delete-workflow.js +77 -0
- package/dist/tools/delete-workflow.js.map +1 -0
- package/dist/tools/deploy-agent.d.ts +4 -0
- package/dist/tools/deploy-agent.d.ts.map +1 -0
- package/dist/tools/deploy-agent.js +455 -0
- package/dist/tools/deploy-agent.js.map +1 -0
- package/dist/tools/disconnect-agent-oauth.d.ts +4 -0
- package/dist/tools/disconnect-agent-oauth.d.ts.map +1 -0
- package/dist/tools/disconnect-agent-oauth.js +118 -0
- package/dist/tools/disconnect-agent-oauth.js.map +1 -0
- package/dist/tools/get-account.d.ts +4 -0
- package/dist/tools/get-account.d.ts.map +1 -0
- package/dist/tools/get-account.js +110 -0
- package/dist/tools/get-account.js.map +1 -0
- package/dist/tools/get-agent-activity.d.ts +4 -0
- package/dist/tools/get-agent-activity.d.ts.map +1 -0
- package/dist/tools/get-agent-activity.js +383 -0
- package/dist/tools/get-agent-activity.js.map +1 -0
- package/dist/tools/get-agent-conversation.d.ts +4 -0
- package/dist/tools/get-agent-conversation.d.ts.map +1 -0
- package/dist/tools/get-agent-conversation.js +224 -0
- package/dist/tools/get-agent-conversation.js.map +1 -0
- package/dist/tools/get-agent-message.d.ts +4 -0
- package/dist/tools/get-agent-message.d.ts.map +1 -0
- package/dist/tools/get-agent-message.js +68 -0
- package/dist/tools/get-agent-message.js.map +1 -0
- package/dist/tools/get-agent-template.d.ts +4 -0
- package/dist/tools/get-agent-template.d.ts.map +1 -0
- package/dist/tools/get-agent-template.js +107 -0
- package/dist/tools/get-agent-template.js.map +1 -0
- package/dist/tools/get-model-schema.d.ts +5 -1
- package/dist/tools/get-model-schema.d.ts.map +1 -1
- package/dist/tools/get-model-schema.js +136 -8
- package/dist/tools/get-model-schema.js.map +1 -1
- package/dist/tools/get-my-agent.d.ts +4 -0
- package/dist/tools/get-my-agent.d.ts.map +1 -0
- package/dist/tools/get-my-agent.js +78 -0
- package/dist/tools/get-my-agent.js.map +1 -0
- package/dist/tools/get-playbook.d.ts +3 -0
- package/dist/tools/get-playbook.d.ts.map +1 -0
- package/dist/tools/get-playbook.js +50 -0
- package/dist/tools/get-playbook.js.map +1 -0
- package/dist/tools/get-task-price.d.ts.map +1 -1
- package/dist/tools/get-task-price.js +42 -15
- 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 +4 -3
- package/dist/tools/get-task.js.map +1 -1
- package/dist/tools/get-workflow.d.ts +9 -0
- package/dist/tools/get-workflow.d.ts.map +1 -0
- package/dist/tools/get-workflow.js +99 -0
- package/dist/tools/get-workflow.js.map +1 -0
- package/dist/tools/index.d.ts +62 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +127 -14
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kill-task.d.ts.map +1 -1
- package/dist/tools/kill-task.js +10 -6
- package/dist/tools/kill-task.js.map +1 -1
- package/dist/tools/list-agent-skills.d.ts +4 -0
- package/dist/tools/list-agent-skills.d.ts.map +1 -0
- package/dist/tools/list-agent-skills.js +242 -0
- package/dist/tools/list-agent-skills.js.map +1 -0
- package/dist/tools/list-agent-templates.d.ts +4 -0
- package/dist/tools/list-agent-templates.d.ts.map +1 -0
- package/dist/tools/list-agent-templates.js +110 -0
- package/dist/tools/list-agent-templates.js.map +1 -0
- package/dist/tools/list-my-agents.d.ts +4 -0
- package/dist/tools/list-my-agents.d.ts.map +1 -0
- package/dist/tools/list-my-agents.js +90 -0
- package/dist/tools/list-my-agents.js.map +1 -0
- package/dist/tools/list-studio-results.d.ts +4 -0
- package/dist/tools/list-studio-results.d.ts.map +1 -0
- package/dist/tools/list-studio-results.js +95 -0
- package/dist/tools/list-studio-results.js.map +1 -0
- package/dist/tools/list-tasks.d.ts.map +1 -1
- package/dist/tools/list-tasks.js +7 -5
- package/dist/tools/list-tasks.js.map +1 -1
- package/dist/tools/list-workflow-runs.d.ts +4 -0
- package/dist/tools/list-workflow-runs.d.ts.map +1 -0
- package/dist/tools/list-workflow-runs.js +110 -0
- package/dist/tools/list-workflow-runs.js.map +1 -0
- package/dist/tools/list-workflows.d.ts +4 -0
- package/dist/tools/list-workflows.d.ts.map +1 -0
- package/dist/tools/list-workflows.js +143 -0
- package/dist/tools/list-workflows.js.map +1 -0
- package/dist/tools/manage-agent-billing.d.ts +4 -0
- package/dist/tools/manage-agent-billing.d.ts.map +1 -0
- package/dist/tools/manage-agent-billing.js +764 -0
- package/dist/tools/manage-agent-billing.js.map +1 -0
- package/dist/tools/manage-agent-custom-skill.d.ts +4 -0
- package/dist/tools/manage-agent-custom-skill.d.ts.map +1 -0
- package/dist/tools/manage-agent-custom-skill.js +537 -0
- package/dist/tools/manage-agent-custom-skill.js.map +1 -0
- package/dist/tools/recommend-model.d.ts.map +1 -1
- package/dist/tools/recommend-model.js +6 -5
- package/dist/tools/recommend-model.js.map +1 -1
- package/dist/tools/resume-workflow-run.d.ts +4 -0
- package/dist/tools/resume-workflow-run.d.ts.map +1 -0
- package/dist/tools/resume-workflow-run.js +69 -0
- package/dist/tools/resume-workflow-run.js.map +1 -0
- package/dist/tools/run-model.d.ts.map +1 -1
- package/dist/tools/run-model.js +39 -3
- package/dist/tools/run-model.js.map +1 -1
- package/dist/tools/run-workflow.d.ts +6 -0
- package/dist/tools/run-workflow.d.ts.map +1 -0
- package/dist/tools/run-workflow.js +132 -0
- package/dist/tools/run-workflow.js.map +1 -0
- package/dist/tools/save-workflow-from-agent.d.ts +4 -0
- package/dist/tools/save-workflow-from-agent.d.ts.map +1 -0
- package/dist/tools/save-workflow-from-agent.js +283 -0
- package/dist/tools/save-workflow-from-agent.js.map +1 -0
- package/dist/tools/schemas.d.ts +62 -12
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +17 -3
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/search-models.js +6 -6
- package/dist/tools/search-models.js.map +1 -1
- package/dist/tools/send-agent-message.d.ts +4 -0
- package/dist/tools/send-agent-message.d.ts.map +1 -0
- package/dist/tools/send-agent-message.js +156 -0
- package/dist/tools/send-agent-message.js.map +1 -0
- package/dist/tools/set-agent-credentials.d.ts +4 -0
- package/dist/tools/set-agent-credentials.d.ts.map +1 -0
- package/dist/tools/set-agent-credentials.js +529 -0
- package/dist/tools/set-agent-credentials.js.map +1 -0
- package/dist/tools/update-agent.d.ts +4 -0
- package/dist/tools/update-agent.d.ts.map +1 -0
- package/dist/tools/update-agent.js +178 -0
- package/dist/tools/update-agent.js.map +1 -0
- package/dist/tools/update-workflow.d.ts +201 -0
- package/dist/tools/update-workflow.d.ts.map +1 -0
- package/dist/tools/update-workflow.js +168 -0
- package/dist/tools/update-workflow.js.map +1 -0
- package/dist/tools/upload-file.d.ts.map +1 -1
- package/dist/tools/upload-file.js +4 -3
- package/dist/tools/upload-file.js.map +1 -1
- package/dist/tools/wait-for-workflow-run.d.ts +259 -0
- package/dist/tools/wait-for-workflow-run.d.ts.map +1 -0
- package/dist/tools/wait-for-workflow-run.js +190 -0
- package/dist/tools/wait-for-workflow-run.js.map +1 -0
- package/dist/types.d.ts +22 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/agent-catalog.d.ts +1256 -0
- package/dist/utils/agent-catalog.d.ts.map +1 -0
- package/dist/utils/agent-catalog.js +388 -0
- package/dist/utils/agent-catalog.js.map +1 -0
- package/dist/utils/agent-config.d.ts +441 -0
- package/dist/utils/agent-config.d.ts.map +1 -0
- package/dist/utils/agent-config.js +1667 -0
- package/dist/utils/agent-config.js.map +1 -0
- package/dist/utils/agent-message.d.ts +115 -0
- package/dist/utils/agent-message.d.ts.map +1 -0
- package/dist/utils/agent-message.js +246 -0
- package/dist/utils/agent-message.js.map +1 -0
- package/dist/utils/agent-targets.d.ts +76 -0
- package/dist/utils/agent-targets.d.ts.map +1 -0
- package/dist/utils/agent-targets.js +408 -0
- package/dist/utils/agent-targets.js.map +1 -0
- package/dist/utils/agent-turns.d.ts +354 -0
- package/dist/utils/agent-turns.d.ts.map +1 -0
- package/dist/utils/agent-turns.js +623 -0
- package/dist/utils/agent-turns.js.map +1 -0
- package/dist/utils/api.d.ts +40 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +111 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/format.d.ts +42 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +173 -14
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/media.d.ts +28 -0
- package/dist/utils/media.d.ts.map +1 -0
- package/dist/utils/media.js +65 -0
- package/dist/utils/media.js.map +1 -0
- package/dist/utils/poll.d.ts +39 -0
- package/dist/utils/poll.d.ts.map +1 -0
- package/dist/utils/poll.js +111 -0
- package/dist/utils/poll.js.map +1 -0
- package/dist/utils/remote-file.js +2 -2
- package/dist/utils/remote-file.js.map +1 -1
- package/dist/utils/structured.d.ts +2 -0
- package/dist/utils/structured.d.ts.map +1 -1
- package/dist/utils/structured.js +10 -10
- package/dist/utils/structured.js.map +1 -1
- package/dist/utils/studio-runs.d.ts +87 -0
- package/dist/utils/studio-runs.d.ts.map +1 -0
- package/dist/utils/studio-runs.js +167 -0
- package/dist/utils/studio-runs.js.map +1 -0
- package/dist/utils/user-agent.d.ts +1140 -0
- package/dist/utils/user-agent.d.ts.map +1 -0
- package/dist/utils/user-agent.js +802 -0
- package/dist/utils/user-agent.js.map +1 -0
- package/dist/utils/workflow-agent.d.ts +151 -0
- package/dist/utils/workflow-agent.d.ts.map +1 -0
- package/dist/utils/workflow-agent.js +292 -0
- package/dist/utils/workflow-agent.js.map +1 -0
- package/dist/utils/workflow-artifacts.d.ts +160 -0
- package/dist/utils/workflow-artifacts.d.ts.map +1 -0
- package/dist/utils/workflow-artifacts.js +801 -0
- package/dist/utils/workflow-artifacts.js.map +1 -0
- package/dist/utils/workflow-save.d.ts +123 -0
- package/dist/utils/workflow-save.d.ts.map +1 -0
- package/dist/utils/workflow-save.js +734 -0
- package/dist/utils/workflow-save.js.map +1 -0
- package/dist/utils/workflow.d.ts +559 -0
- package/dist/utils/workflow.d.ts.map +1 -0
- package/dist/utils/workflow.js +613 -0
- package/dist/utils/workflow.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WiroApiError } from '../client.js';
|
|
3
|
+
import { ApiResultError, asString, isPlainObject, requireApiSuccess, toNumber, truncate, unixToIso, } from './api.js';
|
|
4
|
+
import { pollUntil, reportProgress } from './poll.js';
|
|
5
|
+
// Deployed agents and the agent catalog. Agent rows come back with the
|
|
6
|
+
// owner's stored credential values, billing links and account ids, none of
|
|
7
|
+
// which an assistant should pass on, so every row is rebuilt here from an
|
|
8
|
+
// allowlist instead of being passed through.
|
|
9
|
+
export const AGENT_DASHBOARD_URL = 'https://wiro.ai/panel/agents';
|
|
10
|
+
export function agentDashboardUrl(guid) {
|
|
11
|
+
return `${AGENT_DASHBOARD_URL}/${encodeURIComponent(guid)}`;
|
|
12
|
+
}
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Status
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
const STATUS_LABELS = [
|
|
17
|
+
'stopped',
|
|
18
|
+
'stopping',
|
|
19
|
+
'queued',
|
|
20
|
+
'starting',
|
|
21
|
+
'running',
|
|
22
|
+
'error',
|
|
23
|
+
'setup_required',
|
|
24
|
+
'unknown',
|
|
25
|
+
];
|
|
26
|
+
export const agentStatusLabelSchema = z.enum(STATUS_LABELS);
|
|
27
|
+
const STATUS_TEXT = {
|
|
28
|
+
stopped: 'Stopped',
|
|
29
|
+
stopping: 'Stopping',
|
|
30
|
+
queued: 'Queued',
|
|
31
|
+
starting: 'Starting',
|
|
32
|
+
running: 'Running',
|
|
33
|
+
error: 'Error',
|
|
34
|
+
setup_required: 'Setup required',
|
|
35
|
+
unknown: 'Unknown',
|
|
36
|
+
};
|
|
37
|
+
const STATUS_HINT = {
|
|
38
|
+
stopped: 'It is not running. control_agent with action start starts it.',
|
|
39
|
+
stopping: 'It is shutting down. If a restart was requested it is queued again on its own.',
|
|
40
|
+
queued: 'It is waiting to start. This can take a while.',
|
|
41
|
+
starting: 'It is starting up. If it does not come up within about two minutes it moves to error.',
|
|
42
|
+
running: 'It is live and accepts messages.',
|
|
43
|
+
error: 'The start failed or the agent stopped unexpectedly. control_agent with action start retries, and get_agent_activity with kind logs may show why.',
|
|
44
|
+
setup_required: 'It has no paid plan yet, or required credentials are missing. It does not leave this state on its own.',
|
|
45
|
+
unknown: 'The status could not be read.',
|
|
46
|
+
};
|
|
47
|
+
export function readAgentStatus(row) {
|
|
48
|
+
const status = toNumber(row.status);
|
|
49
|
+
return status === null ? null : Math.trunc(status);
|
|
50
|
+
}
|
|
51
|
+
export function agentStatusLabel(status) {
|
|
52
|
+
if (status === null || status === undefined || !Number.isInteger(status))
|
|
53
|
+
return 'unknown';
|
|
54
|
+
return status >= 0 && status <= 6 ? STATUS_LABELS[status] : 'unknown';
|
|
55
|
+
}
|
|
56
|
+
/** "Running (4)" */
|
|
57
|
+
export function describeAgentStatus(status) {
|
|
58
|
+
const label = agentStatusLabel(status);
|
|
59
|
+
return status === null || status === undefined
|
|
60
|
+
? STATUS_TEXT[label]
|
|
61
|
+
: `${STATUS_TEXT[label]} (${status})`;
|
|
62
|
+
}
|
|
63
|
+
export function agentStatusHint(status) {
|
|
64
|
+
return STATUS_HINT[agentStatusLabel(status)];
|
|
65
|
+
}
|
|
66
|
+
/** Stopping, queued and starting move on by themselves; every other status stays until someone acts. */
|
|
67
|
+
export function isTransitionalStatus(status) {
|
|
68
|
+
return status === 1 || status === 2 || status === 3;
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Prices, plans and credits
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
export const AGENT_TIERS = ['starter', 'pro'];
|
|
74
|
+
export const tierPriceSchema = z.object({
|
|
75
|
+
priceUsd: z.number(),
|
|
76
|
+
monthlyCredits: z.number(),
|
|
77
|
+
});
|
|
78
|
+
export const tierPricingSchema = z.object({
|
|
79
|
+
starter: tierPriceSchema,
|
|
80
|
+
pro: tierPriceSchema,
|
|
81
|
+
});
|
|
82
|
+
function toTierPrice(value) {
|
|
83
|
+
if (!isPlainObject(value))
|
|
84
|
+
return null;
|
|
85
|
+
const priceUsd = toNumber(value.priceUsd);
|
|
86
|
+
const credits = toNumber(value.credits);
|
|
87
|
+
if (priceUsd === null || credits === null)
|
|
88
|
+
return null;
|
|
89
|
+
return { priceUsd, monthlyCredits: credits };
|
|
90
|
+
}
|
|
91
|
+
/** Starter and Pro prices, or null when Wiro could not price the agent. */
|
|
92
|
+
export function toTierPricing(value) {
|
|
93
|
+
if (!isPlainObject(value))
|
|
94
|
+
return null;
|
|
95
|
+
const starter = toTierPrice(value.starter);
|
|
96
|
+
const pro = toTierPrice(value.pro);
|
|
97
|
+
return starter && pro ? { starter, pro } : null;
|
|
98
|
+
}
|
|
99
|
+
export function asTier(value) {
|
|
100
|
+
return value === 'starter' || value === 'pro' ? value : undefined;
|
|
101
|
+
}
|
|
102
|
+
export function formatUsd(value) {
|
|
103
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
104
|
+
return 'unknown';
|
|
105
|
+
return `$${value.toFixed(2)}`;
|
|
106
|
+
}
|
|
107
|
+
export function formatCredits(value) {
|
|
108
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
109
|
+
return 'unknown';
|
|
110
|
+
return Math.round(value).toLocaleString('en-US');
|
|
111
|
+
}
|
|
112
|
+
export function roundUsd(value) {
|
|
113
|
+
return Math.round(value * 100) / 100;
|
|
114
|
+
}
|
|
115
|
+
// A confirmed charge goes ahead only when a fresh quote still matches the
|
|
116
|
+
// amount the user approved; anything else is quoted again.
|
|
117
|
+
export const AMOUNT_TOLERANCE_USD = 0.005;
|
|
118
|
+
export function sameAmount(quoted, approved) {
|
|
119
|
+
return Math.abs(quoted - approved) <= AMOUNT_TOLERANCE_USD;
|
|
120
|
+
}
|
|
121
|
+
export const subscriptionSchema = z.object({
|
|
122
|
+
status: z.string(),
|
|
123
|
+
billing: z.enum(['wallet', 'card']),
|
|
124
|
+
switchingToWallet: z.boolean(),
|
|
125
|
+
amountUsd: z.number().nullable(),
|
|
126
|
+
periodEndsAt: z.string().optional(),
|
|
127
|
+
daysRemaining: z.number().nullable(),
|
|
128
|
+
cancelAtPeriodEnd: z.boolean(),
|
|
129
|
+
});
|
|
130
|
+
export function toSubscription(value, cancelAtPeriodEnd) {
|
|
131
|
+
if (!isPlainObject(value))
|
|
132
|
+
return null;
|
|
133
|
+
const provider = asString(value.provider) ?? 'stripe';
|
|
134
|
+
const periodEndsAt = unixToIso(value.currentperiodend);
|
|
135
|
+
return {
|
|
136
|
+
status: asString(value.status) ?? 'expired',
|
|
137
|
+
billing: provider === 'prepaid' ? 'wallet' : 'card',
|
|
138
|
+
switchingToWallet: provider !== 'prepaid' && value.nextprovider === 'prepaid',
|
|
139
|
+
amountUsd: toNumber(value.amount),
|
|
140
|
+
...(periodEndsAt ? { periodEndsAt } : {}),
|
|
141
|
+
daysRemaining: toNumber(value.daysremaining),
|
|
142
|
+
cancelAtPeriodEnd: value.pendingdowngrade === 'cancel' || cancelAtPeriodEnd === true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** How a new charge for this plan is paid right now. */
|
|
146
|
+
export function chargeRoute(subscription) {
|
|
147
|
+
if (!isPlainObject(subscription))
|
|
148
|
+
return null;
|
|
149
|
+
if (subscription.nextprovider === 'prepaid' || subscription.provider === 'prepaid')
|
|
150
|
+
return 'wallet';
|
|
151
|
+
return 'card';
|
|
152
|
+
}
|
|
153
|
+
export const creditsSchema = z.object({
|
|
154
|
+
monthly: z.number(),
|
|
155
|
+
extra: z.number(),
|
|
156
|
+
used: z.number(),
|
|
157
|
+
remaining: z.number(),
|
|
158
|
+
period: z.string().optional(),
|
|
159
|
+
});
|
|
160
|
+
export function toCredits(row) {
|
|
161
|
+
const period = asString(row.creditperiod);
|
|
162
|
+
return {
|
|
163
|
+
monthly: toNumber(row.monthlycredits) ?? 0,
|
|
164
|
+
extra: toNumber(row.extracredits) ?? 0,
|
|
165
|
+
used: toNumber(row.usedcredits) ?? 0,
|
|
166
|
+
remaining: toNumber(row.remainingcredits) ?? 0,
|
|
167
|
+
...(period ? { period } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Text helpers
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
const ENTITIES = {
|
|
174
|
+
' ': ' ',
|
|
175
|
+
'<': '<',
|
|
176
|
+
'>': '>',
|
|
177
|
+
'"': '"',
|
|
178
|
+
''': '\'',
|
|
179
|
+
'&': '&',
|
|
180
|
+
};
|
|
181
|
+
/** Plain text from catalog HTML. Links keep their target so setup steps stay usable. */
|
|
182
|
+
export function htmlToText(value, maxLength) {
|
|
183
|
+
const html = asString(value);
|
|
184
|
+
if (!html)
|
|
185
|
+
return '';
|
|
186
|
+
const text = html
|
|
187
|
+
.replace(/<a\b[^>]*?href\s*=\s*"([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, (_match, href, inner) => {
|
|
188
|
+
const label = inner.replace(/<[^>]*>/g, '').trim();
|
|
189
|
+
if (!/^https?:\/\//i.test(href) || label.includes(href))
|
|
190
|
+
return label;
|
|
191
|
+
return label ? `${label} (${href})` : href;
|
|
192
|
+
})
|
|
193
|
+
.replace(/<\s*br\s*\/?\s*>/gi, '\n')
|
|
194
|
+
.replace(/<li\b[^>]*>/gi, '\n- ')
|
|
195
|
+
.replace(/<\/(p|li|div|ol|ul|h[1-6])\s*>/gi, '\n')
|
|
196
|
+
.replace(/<[^>]*>/g, '')
|
|
197
|
+
.replace(/ |<|>|"|'|&/g, entity => ENTITIES[entity] ?? entity)
|
|
198
|
+
.split('\n')
|
|
199
|
+
.map(line => line.replace(/[ \t]+/g, ' ').trim())
|
|
200
|
+
.filter(Boolean)
|
|
201
|
+
.join('\n');
|
|
202
|
+
return maxLength ? truncate(text, maxLength) : text;
|
|
203
|
+
}
|
|
204
|
+
export function toStringArray(value) {
|
|
205
|
+
let list = value;
|
|
206
|
+
if (typeof value === 'string') {
|
|
207
|
+
try {
|
|
208
|
+
list = JSON.parse(value);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return value.split(',').map(item => item.trim()).filter(Boolean);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!Array.isArray(list))
|
|
215
|
+
return [];
|
|
216
|
+
return list
|
|
217
|
+
.map((item) => (isPlainObject(item) ? asString(item.name) : asString(item)))
|
|
218
|
+
.filter((item) => typeof item === 'string' && item.length > 0);
|
|
219
|
+
}
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Credentials, channels and tasks of a deployed agent
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
export const credentialStatusSchema = z.object({
|
|
224
|
+
key: z.string(),
|
|
225
|
+
title: z.string(),
|
|
226
|
+
optional: z.boolean(),
|
|
227
|
+
oauth: z.boolean(),
|
|
228
|
+
connected: z.boolean(),
|
|
229
|
+
filled: z.boolean(),
|
|
230
|
+
requiredMissing: z.boolean(),
|
|
231
|
+
});
|
|
232
|
+
/** Per-credential state only. Field values never leave this function. */
|
|
233
|
+
export function summarizeCredentials(credentials, skipKeys = new Set()) {
|
|
234
|
+
if (!isPlainObject(credentials))
|
|
235
|
+
return [];
|
|
236
|
+
const statuses = [];
|
|
237
|
+
for (const [key, group] of Object.entries(credentials)) {
|
|
238
|
+
if (key.startsWith('_') || skipKeys.has(key) || !isPlainObject(group))
|
|
239
|
+
continue;
|
|
240
|
+
const schema = isPlainObject(group._schema) ? group._schema : {};
|
|
241
|
+
statuses.push({
|
|
242
|
+
key,
|
|
243
|
+
title: asString(schema.title) ?? key,
|
|
244
|
+
optional: group.optional === true,
|
|
245
|
+
oauth: isPlainObject(schema.oauth_provider),
|
|
246
|
+
connected: group._connected === true,
|
|
247
|
+
filled: group._filled === true,
|
|
248
|
+
requiredMissing: group._required_missing === true,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return statuses;
|
|
252
|
+
}
|
|
253
|
+
export const channelStatusSchema = z.object({
|
|
254
|
+
id: z.string(),
|
|
255
|
+
title: z.string(),
|
|
256
|
+
credentialKey: z.string(),
|
|
257
|
+
enabled: z.boolean(),
|
|
258
|
+
configured: z.boolean(),
|
|
259
|
+
missingFields: z.array(z.string()),
|
|
260
|
+
});
|
|
261
|
+
export function summarizeChannels(value) {
|
|
262
|
+
if (!Array.isArray(value))
|
|
263
|
+
return [];
|
|
264
|
+
return value.filter(isPlainObject).map(channel => {
|
|
265
|
+
const id = asString(channel.id) ?? '';
|
|
266
|
+
return {
|
|
267
|
+
id,
|
|
268
|
+
title: asString(channel.title) ?? id,
|
|
269
|
+
credentialKey: asString(channel.credentialkey) ?? id,
|
|
270
|
+
enabled: channel.enabled === true,
|
|
271
|
+
configured: channel.configured === true,
|
|
272
|
+
missingFields: toStringArray(channel.missingfields),
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
export const agentTaskSchema = z.object({
|
|
277
|
+
key: z.string(),
|
|
278
|
+
description: z.string().optional(),
|
|
279
|
+
enabled: z.boolean(),
|
|
280
|
+
interval: z.string().nullable().optional(),
|
|
281
|
+
source: z.enum(['template', 'skill', 'user']).optional(),
|
|
282
|
+
instructions: z.string().optional(),
|
|
283
|
+
instructionsTruncated: z.boolean().optional(),
|
|
284
|
+
});
|
|
285
|
+
const TASK_SOURCES = {
|
|
286
|
+
'preset-strategy': 'template',
|
|
287
|
+
'skill-bundle': 'skill',
|
|
288
|
+
'user-created': 'user',
|
|
289
|
+
};
|
|
290
|
+
/** Custom instructions and scheduled tasks, with the instruction text cut short. */
|
|
291
|
+
export function summarizeTasks(value, maxInstructionLength = 300) {
|
|
292
|
+
if (!Array.isArray(value))
|
|
293
|
+
return [];
|
|
294
|
+
return value.filter(isPlainObject).map(task => {
|
|
295
|
+
const description = asString(task.description);
|
|
296
|
+
const interval = asString(task.interval) ?? asString(task.intervalexpr);
|
|
297
|
+
const instructions = asString(task.value) ?? '';
|
|
298
|
+
const shortened = truncate(instructions, maxInstructionLength);
|
|
299
|
+
const source = TASK_SOURCES[asString(task._source) ?? ''];
|
|
300
|
+
return {
|
|
301
|
+
key: asString(task.key) ?? '',
|
|
302
|
+
...(description ? { description } : {}),
|
|
303
|
+
enabled: task.enabled !== false,
|
|
304
|
+
...(interval !== undefined ? { interval: interval || null } : {}),
|
|
305
|
+
...(source ? { source } : {}),
|
|
306
|
+
...(shortened ? { instructions: shortened } : {}),
|
|
307
|
+
...(instructions.length > maxInstructionLength ? { instructionsTruncated: true } : {}),
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Deployed agent views
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
const agentTemplateRefSchema = z.object({
|
|
315
|
+
custom: z.boolean(),
|
|
316
|
+
guid: z.string().optional(),
|
|
317
|
+
slug: z.string().optional(),
|
|
318
|
+
title: z.string().optional(),
|
|
319
|
+
});
|
|
320
|
+
const modelSettingsSchema = z.object({
|
|
321
|
+
chat: z.string().nullable(),
|
|
322
|
+
cron: z.string().nullable(),
|
|
323
|
+
voicePrep: z.string().nullable(),
|
|
324
|
+
voicePostcall: z.string().nullable(),
|
|
325
|
+
});
|
|
326
|
+
const selectableModelSchema = z.object({
|
|
327
|
+
slug: z.string(),
|
|
328
|
+
label: z.string().optional(),
|
|
329
|
+
inputCreditsPer1M: z.number().optional(),
|
|
330
|
+
outputCreditsPer1M: z.number().optional(),
|
|
331
|
+
});
|
|
332
|
+
export const extraCreditPackSchema = z.object({
|
|
333
|
+
pack: z.string(),
|
|
334
|
+
credits: z.number(),
|
|
335
|
+
priceUsd: z.number(),
|
|
336
|
+
});
|
|
337
|
+
export const agentSummarySchema = z.object({
|
|
338
|
+
guid: z.string(),
|
|
339
|
+
title: z.string(),
|
|
340
|
+
status: z.number().int().nullable(),
|
|
341
|
+
statusLabel: agentStatusLabelSchema,
|
|
342
|
+
setupRequired: z.boolean(),
|
|
343
|
+
workspace: z.enum(['personal', 'team']),
|
|
344
|
+
template: agentTemplateRefSchema,
|
|
345
|
+
tier: z.string().optional(),
|
|
346
|
+
monthlyPriceUsd: z.number().nullable(),
|
|
347
|
+
credits: creditsSchema,
|
|
348
|
+
subscription: subscriptionSchema.nullable(),
|
|
349
|
+
timezone: z.string().nullable().optional(),
|
|
350
|
+
teamSessionMode: z.string().optional(),
|
|
351
|
+
skills: z.array(z.string()),
|
|
352
|
+
createdAt: z.string().optional(),
|
|
353
|
+
updatedAt: z.string().optional(),
|
|
354
|
+
});
|
|
355
|
+
export const agentDetailSchema = agentSummarySchema.extend({
|
|
356
|
+
description: z.string().optional(),
|
|
357
|
+
cover: z.string().optional(),
|
|
358
|
+
categories: z.array(z.string()),
|
|
359
|
+
pricing: tierPricingSchema.nullable(),
|
|
360
|
+
extraCreditPacks: z.array(extraCreditPackSchema),
|
|
361
|
+
models: modelSettingsSchema.optional(),
|
|
362
|
+
selectableModels: z.array(selectableModelSchema).optional(),
|
|
363
|
+
enabledCommands: z.array(z.string()).nullable(),
|
|
364
|
+
credentials: z.array(credentialStatusSchema),
|
|
365
|
+
missingCredentials: z.array(z.object({ key: z.string(), title: z.string(), oauth: z.boolean() })),
|
|
366
|
+
channels: z.array(channelStatusSchema),
|
|
367
|
+
scheduledTasks: z.array(agentTaskSchema),
|
|
368
|
+
customInstructions: z.array(agentTaskSchema),
|
|
369
|
+
queuedAt: z.string().optional(),
|
|
370
|
+
startedAt: z.string().optional(),
|
|
371
|
+
runningAt: z.string().optional(),
|
|
372
|
+
stoppedAt: z.string().optional(),
|
|
373
|
+
erroredAt: z.string().optional(),
|
|
374
|
+
dashboardUrl: z.string(),
|
|
375
|
+
});
|
|
376
|
+
/** The part of a new agent that matters right after deploying it. */
|
|
377
|
+
export const deployedAgentSchema = agentSummarySchema.extend({
|
|
378
|
+
missingCredentials: agentDetailSchema.shape.missingCredentials,
|
|
379
|
+
channels: agentDetailSchema.shape.channels,
|
|
380
|
+
dashboardUrl: z.string(),
|
|
381
|
+
});
|
|
382
|
+
export function toDeployedAgent(agent) {
|
|
383
|
+
// Object schemas drop the keys they do not declare.
|
|
384
|
+
return deployedAgentSchema.parse(agent);
|
|
385
|
+
}
|
|
386
|
+
function templateRef(row) {
|
|
387
|
+
const agent = isPlainObject(row.agent) ? row.agent : {};
|
|
388
|
+
const guid = asString(agent.guid) ?? asString(row.agentguid);
|
|
389
|
+
if (agent.custom === true || !guid)
|
|
390
|
+
return { custom: true };
|
|
391
|
+
const slug = asString(agent.slug);
|
|
392
|
+
const title = asString(agent.title);
|
|
393
|
+
return {
|
|
394
|
+
custom: false,
|
|
395
|
+
guid,
|
|
396
|
+
...(slug ? { slug } : {}),
|
|
397
|
+
...(title ? { title } : {}),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function timestamps(row, map) {
|
|
401
|
+
const out = {};
|
|
402
|
+
for (const [key, column] of Object.entries(map)) {
|
|
403
|
+
const iso = unixToIso(row[column]);
|
|
404
|
+
if (iso)
|
|
405
|
+
out[key] = iso;
|
|
406
|
+
}
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
/** The compact row used by list_my_agents. */
|
|
410
|
+
export function toAgentSummary(row) {
|
|
411
|
+
const status = readAgentStatus(row);
|
|
412
|
+
const tier = asString(row.tier);
|
|
413
|
+
const timezone = row.timezone === null ? null : asString(row.timezone);
|
|
414
|
+
const sessionMode = asString(row.teamsessionmode);
|
|
415
|
+
return {
|
|
416
|
+
guid: asString(row.guid) ?? '',
|
|
417
|
+
title: asString(row.title) ?? '',
|
|
418
|
+
status,
|
|
419
|
+
statusLabel: agentStatusLabel(status),
|
|
420
|
+
setupRequired: row.setuprequired === true || status === 6,
|
|
421
|
+
workspace: row.teamguid ? 'team' : 'personal',
|
|
422
|
+
template: templateRef(row),
|
|
423
|
+
...(tier ? { tier } : {}),
|
|
424
|
+
monthlyPriceUsd: toNumber(row.monthlypriceusd),
|
|
425
|
+
credits: toCredits(row),
|
|
426
|
+
subscription: toSubscription(row.subscription, row.stripecancelatperiodend),
|
|
427
|
+
...(timezone !== undefined ? { timezone } : {}),
|
|
428
|
+
...(sessionMode ? { teamSessionMode: sessionMode } : {}),
|
|
429
|
+
skills: toStringArray(row.skills ?? row.enabledSkills),
|
|
430
|
+
...timestamps(row, { createdAt: 'createdat', updatedAt: 'updatedat' }),
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/** The models an agent can switch to, with their credits per 1M input and output tokens when Wiro lists them. */
|
|
434
|
+
function selectableModels(tokenRates) {
|
|
435
|
+
if (!isPlainObject(tokenRates) || !isPlainObject(tokenRates.models))
|
|
436
|
+
return [];
|
|
437
|
+
return Object.entries(tokenRates.models)
|
|
438
|
+
.filter(([, meta]) => isPlainObject(meta) && meta.selectable === true)
|
|
439
|
+
.map(([slug, meta]) => {
|
|
440
|
+
const rates = meta;
|
|
441
|
+
const label = asString(rates.label);
|
|
442
|
+
const input = toNumber(rates.input_per_1m);
|
|
443
|
+
const output = toNumber(rates.output_per_1m);
|
|
444
|
+
return {
|
|
445
|
+
slug,
|
|
446
|
+
...(label ? { label } : {}),
|
|
447
|
+
...(input !== null ? { inputCreditsPer1M: input } : {}),
|
|
448
|
+
...(output !== null ? { outputCreditsPer1M: output } : {}),
|
|
449
|
+
};
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/** Slugs a deployed agent may switch its chat, cron or voice model to. */
|
|
453
|
+
export function selectableModelSlugs(row) {
|
|
454
|
+
return new Set(selectableModels(row.tokenRates).map(model => model.slug));
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* The full sanitized view of one deployed agent (Detail, Deploy and Update
|
|
458
|
+
* rows). Credential values, billing session links, account ids and the
|
|
459
|
+
* team id are dropped; only states, labels and settings remain.
|
|
460
|
+
*/
|
|
461
|
+
export function toAgentDetail(row) {
|
|
462
|
+
const summary = toAgentSummary(row);
|
|
463
|
+
const agent = isPlainObject(row.agent) ? row.agent : {};
|
|
464
|
+
const channels = summarizeChannels(row.communicationChannels);
|
|
465
|
+
const channelKeys = new Set(channels.map(channel => channel.credentialKey));
|
|
466
|
+
const credentials = summarizeCredentials(row.credentials, channelKeys);
|
|
467
|
+
const description = asString(row.description);
|
|
468
|
+
const cover = asString(row.cover) || asString(agent.cover);
|
|
469
|
+
const agentModel = isPlainObject(row.agentModel) ? row.agentModel : undefined;
|
|
470
|
+
const models = selectableModels(row.tokenRates);
|
|
471
|
+
// A custom build's setup waits only for the main credential of each active
|
|
472
|
+
// skill, which skillsmeta lists, so only those count as missing.
|
|
473
|
+
const activeKeys = summary.template.custom && isPlainObject(row.skillsmeta)
|
|
474
|
+
? new Set(Object.values(row.skillsmeta).filter(isPlainObject)
|
|
475
|
+
.filter(meta => meta.requires_credentials === true)
|
|
476
|
+
.map(meta => asString(meta.credential_key))
|
|
477
|
+
.filter((key) => Boolean(key)))
|
|
478
|
+
: null;
|
|
479
|
+
const packs = Array.isArray(agent.extracreditpacks)
|
|
480
|
+
? agent.extracreditpacks.filter(isPlainObject).filter(pack => pack.enabled !== false)
|
|
481
|
+
: [];
|
|
482
|
+
return {
|
|
483
|
+
...summary,
|
|
484
|
+
...(description ? { description: truncate(description, 2_000) } : {}),
|
|
485
|
+
...(cover ? { cover } : {}),
|
|
486
|
+
categories: toStringArray(row.categories),
|
|
487
|
+
pricing: toTierPricing(agent.tiers),
|
|
488
|
+
extraCreditPacks: packs.flatMap(pack => {
|
|
489
|
+
const name = asString(pack.packkey);
|
|
490
|
+
const credits = toNumber(pack.credits);
|
|
491
|
+
const priceUsd = toNumber(pack.priceusd);
|
|
492
|
+
return name && credits !== null && priceUsd !== null ? [{ pack: name, credits, priceUsd }] : [];
|
|
493
|
+
}),
|
|
494
|
+
...(agentModel
|
|
495
|
+
? {
|
|
496
|
+
models: {
|
|
497
|
+
chat: asString(agentModel.chatModel) ?? null,
|
|
498
|
+
cron: asString(agentModel.cronModel) ?? null,
|
|
499
|
+
voicePrep: asString(agentModel.voicePrepModel) ?? null,
|
|
500
|
+
voicePostcall: asString(agentModel.voicePostcallModel) ?? null,
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
: {}),
|
|
504
|
+
...(models.length > 0 ? { selectableModels: models } : {}),
|
|
505
|
+
enabledCommands: Array.isArray(row.enabledCommands) ? toStringArray(row.enabledCommands) : null,
|
|
506
|
+
credentials,
|
|
507
|
+
// A required sign-in that is not connected also holds back the setup, so it
|
|
508
|
+
// is listed as missing too.
|
|
509
|
+
missingCredentials: credentials
|
|
510
|
+
.filter(credential => credential.requiredMissing || (credential.oauth && !credential.optional && !credential.connected))
|
|
511
|
+
.filter(credential => !activeKeys || activeKeys.has(credential.key))
|
|
512
|
+
.map(({ key, title, oauth }) => ({ key, title, oauth })),
|
|
513
|
+
channels,
|
|
514
|
+
scheduledTasks: summarizeTasks(row.scheduledskills),
|
|
515
|
+
customInstructions: summarizeTasks(row.customskills),
|
|
516
|
+
...timestamps(row, {
|
|
517
|
+
queuedAt: 'queuedat',
|
|
518
|
+
startedAt: 'startedat',
|
|
519
|
+
runningAt: 'runningat',
|
|
520
|
+
stoppedAt: 'stopdat',
|
|
521
|
+
erroredAt: 'errordat',
|
|
522
|
+
}),
|
|
523
|
+
dashboardUrl: agentDashboardUrl(summary.guid),
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
export function formatSubscriptionLine(subscription) {
|
|
527
|
+
if (!subscription)
|
|
528
|
+
return 'No plan. manage_agent_billing with action subscribe starts one.';
|
|
529
|
+
const parts = [`${subscription.status}, billed ${subscription.billing === 'wallet' ? 'from the wallet' : 'by card'}`];
|
|
530
|
+
if (subscription.amountUsd !== null)
|
|
531
|
+
parts.push(`${formatUsd(subscription.amountUsd)} per 30 days`);
|
|
532
|
+
if (subscription.periodEndsAt) {
|
|
533
|
+
if (subscription.status !== 'active') {
|
|
534
|
+
parts.push(`period ended ${subscription.periodEndsAt}`);
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
parts.push(subscription.cancelAtPeriodEnd
|
|
538
|
+
? `cancelled, ends after ${subscription.periodEndsAt}`
|
|
539
|
+
: `renews after ${subscription.periodEndsAt}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (subscription.switchingToWallet)
|
|
543
|
+
parts.push('switching to wallet billing at period end');
|
|
544
|
+
return parts.join(', ');
|
|
545
|
+
}
|
|
546
|
+
export function formatAgentSummaryLines(agent) {
|
|
547
|
+
const template = agent.template.custom
|
|
548
|
+
? 'custom build'
|
|
549
|
+
: `template ${agent.template.title ?? agent.template.slug ?? agent.template.guid}`;
|
|
550
|
+
return [
|
|
551
|
+
`**Agent:** ${agent.title} (\`${agent.guid}\`), ${template}, ${agent.workspace} workspace`,
|
|
552
|
+
`**Status:** ${describeAgentStatus(agent.status)}. ${agentStatusHint(agent.status)}`,
|
|
553
|
+
`**Plan:** ${agent.tier ?? 'no tier'}; ${formatSubscriptionLine(agent.subscription)}`,
|
|
554
|
+
`**Credits:** ${formatCredits(agent.credits.remaining)} left (monthly ${formatCredits(agent.credits.monthly)}, extra ${formatCredits(agent.credits.extra)}, used ${formatCredits(agent.credits.used)})`,
|
|
555
|
+
];
|
|
556
|
+
}
|
|
557
|
+
const SHORTENED_NOTE = ' (its text is long; manage_agent_custom_skill history shows it in full)';
|
|
558
|
+
function formatRate(credits) {
|
|
559
|
+
return credits.toLocaleString('en-US', { maximumFractionDigits: 3 });
|
|
560
|
+
}
|
|
561
|
+
/** The chat and scheduled-task models, then every model the agent can switch to with its rates. */
|
|
562
|
+
function formatModelsLine(agent) {
|
|
563
|
+
const choices = agent.selectableModels ?? [];
|
|
564
|
+
const nameOf = (slug) => (slug ? choices.find(model => model.slug === slug)?.label ?? slug : 'the default model');
|
|
565
|
+
const parts = [];
|
|
566
|
+
if (agent.models)
|
|
567
|
+
parts.push(`chat ${nameOf(agent.models.chat)}, scheduled tasks ${nameOf(agent.models.cron)}.`);
|
|
568
|
+
if (choices.length > 0) {
|
|
569
|
+
const rates = choices.map(model => {
|
|
570
|
+
const name = model.label ? `${model.label} (${model.slug})` : model.slug;
|
|
571
|
+
return model.inputCreditsPer1M !== undefined && model.outputCreditsPer1M !== undefined
|
|
572
|
+
? `${name} ${formatRate(model.inputCreditsPer1M)} and ${formatRate(model.outputCreditsPer1M)}`
|
|
573
|
+
: `${name} rate unknown`;
|
|
574
|
+
});
|
|
575
|
+
parts.push(`Models you can switch to, in credits per 1M input and output tokens: ${rates.join('; ')}.`);
|
|
576
|
+
}
|
|
577
|
+
return parts.length > 0 ? `**Models:** ${parts.join(' ')}` : null;
|
|
578
|
+
}
|
|
579
|
+
export function formatAgentDetail(agent) {
|
|
580
|
+
const lines = formatAgentSummaryLines(agent);
|
|
581
|
+
if (agent.pricing) {
|
|
582
|
+
// This agent's own Starter and Pro prices, which can differ from its template's.
|
|
583
|
+
const { starter, pro } = agent.pricing;
|
|
584
|
+
lines.push(`**Prices now:** Starter ${formatUsd(starter.priceUsd)} per 30 days with ${formatCredits(starter.monthlyCredits)} credits per month; Pro ${formatUsd(pro.priceUsd)} per 30 days with ${formatCredits(pro.monthlyCredits)} credits per month. A new plan or an upgrade uses these.`);
|
|
585
|
+
}
|
|
586
|
+
const models = formatModelsLine(agent);
|
|
587
|
+
if (models)
|
|
588
|
+
lines.push(models);
|
|
589
|
+
if (agent.timezone !== undefined) {
|
|
590
|
+
lines.push(`**Time zone:** ${agent.timezone?.trim() ? agent.timezone : 'UTC, the default'}. Scheduled tasks run in it.`);
|
|
591
|
+
}
|
|
592
|
+
if (agent.setupRequired) {
|
|
593
|
+
lines.push(agent.missingCredentials.length > 0
|
|
594
|
+
? `**Setup required:** missing ${agent.missingCredentials.map(item => item.title).join(', ')}`
|
|
595
|
+
: '**Setup required:** yes');
|
|
596
|
+
}
|
|
597
|
+
if (agent.skills.length > 0)
|
|
598
|
+
lines.push(`**Skills:** ${agent.skills.join(', ')}`);
|
|
599
|
+
if (agent.channels.length > 0) {
|
|
600
|
+
lines.push(`**Channels:** ${agent.channels.map(channel => `${channel.title} ${channel.enabled ? 'on' : channel.configured ? 'configured' : 'off'}`).join(', ')}`);
|
|
601
|
+
}
|
|
602
|
+
if (agent.credentials.length > 0) {
|
|
603
|
+
lines.push('', '### Credentials', '');
|
|
604
|
+
for (const credential of agent.credentials) {
|
|
605
|
+
const state = credential.connected
|
|
606
|
+
? 'connected'
|
|
607
|
+
: credential.filled
|
|
608
|
+
? 'filled'
|
|
609
|
+
: credential.requiredMissing
|
|
610
|
+
? 'missing (required)'
|
|
611
|
+
: 'not set';
|
|
612
|
+
lines.push(`- ${credential.title} (\`${credential.key}\`): ${state}${credential.optional ? ', optional' : ''}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (agent.scheduledTasks.length > 0) {
|
|
616
|
+
lines.push('', '### Scheduled tasks', '');
|
|
617
|
+
for (const task of agent.scheduledTasks) {
|
|
618
|
+
lines.push(`- \`${task.key}\` ${task.enabled ? 'on' : 'off'}${task.interval ? `, ${task.interval}` : ''}${task.description ? `: ${task.description}` : ''}${task.instructionsTruncated ? SHORTENED_NOTE : ''}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (agent.customInstructions.length > 0) {
|
|
622
|
+
lines.push('', '### Custom instructions', '');
|
|
623
|
+
for (const task of agent.customInstructions) {
|
|
624
|
+
lines.push(`- \`${task.key}\`${task.description ? `: ${task.description}` : ''}${task.instructionsTruncated ? SHORTENED_NOTE : ''}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
lines.push('', `Dashboard: ${agent.dashboardUrl}`);
|
|
628
|
+
return lines.join('\n');
|
|
629
|
+
}
|
|
630
|
+
// Custom skill keys get the same canonical shape the server gives them, so a
|
|
631
|
+
// key can be compared with the template's own entries before deploying.
|
|
632
|
+
export function normalizeCustomSkillKey(input, interval) {
|
|
633
|
+
const trimmed = input.trim();
|
|
634
|
+
if (!trimmed)
|
|
635
|
+
return null;
|
|
636
|
+
let prefix;
|
|
637
|
+
let body;
|
|
638
|
+
if (trimmed.startsWith('cs-cron-')) {
|
|
639
|
+
prefix = 'cs-cron-';
|
|
640
|
+
body = trimmed.slice('cs-cron-'.length);
|
|
641
|
+
}
|
|
642
|
+
else if (trimmed.startsWith('cs-')) {
|
|
643
|
+
prefix = 'cs-';
|
|
644
|
+
body = trimmed.slice('cs-'.length);
|
|
645
|
+
}
|
|
646
|
+
else if (trimmed.startsWith('cron-')) {
|
|
647
|
+
prefix = 'cs-';
|
|
648
|
+
body = trimmed;
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
const scheduled = typeof interval === 'string'
|
|
652
|
+
&& !['', 'manual', 'false'].includes(interval.trim().toLowerCase());
|
|
653
|
+
prefix = scheduled ? 'cs-cron-' : 'cs-';
|
|
654
|
+
body = trimmed;
|
|
655
|
+
}
|
|
656
|
+
if (body.length === 0 || body.length > 60)
|
|
657
|
+
return null;
|
|
658
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(body) ? prefix + body : null;
|
|
659
|
+
}
|
|
660
|
+
const GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
661
|
+
export function isGuid(value) {
|
|
662
|
+
return GUID_PATTERN.test(value.trim());
|
|
663
|
+
}
|
|
664
|
+
/** The raw Detail row. Callers must pass it through toAgentDetail before returning anything. */
|
|
665
|
+
export async function readAgentDetail(client, guid, signal) {
|
|
666
|
+
const body = requireApiSuccess(await client.post('/UserAgent/Detail', { guid }, signal), 'The agent could not be read.');
|
|
667
|
+
const rows = Array.isArray(body.useragents) ? body.useragents.filter(isPlainObject) : [];
|
|
668
|
+
if (!rows[0])
|
|
669
|
+
throw new Error(`No agent was found for agent_guid "${guid}".`);
|
|
670
|
+
return rows[0];
|
|
671
|
+
}
|
|
672
|
+
// Every Detail read prices the agent, so polling stays slower than for tasks.
|
|
673
|
+
export function agentPollInterval(elapsedMs) {
|
|
674
|
+
return elapsedMs < 60_000 ? 5_000 : 10_000;
|
|
675
|
+
}
|
|
676
|
+
/** Reads the agent until its status is no longer stopping, queued or starting. */
|
|
677
|
+
export async function waitForAgentToSettle(client, guid, waitSeconds, extra, fallbackRow) {
|
|
678
|
+
const result = await pollUntil({
|
|
679
|
+
read: signal => readAgentDetail(client, guid, signal),
|
|
680
|
+
isDone: row => !isTransitionalStatus(readAgentStatus(row)),
|
|
681
|
+
timeoutMs: waitSeconds * 1000,
|
|
682
|
+
signal: extra?.signal,
|
|
683
|
+
interval: agentPollInterval,
|
|
684
|
+
onPoll: (row, elapsedMs) => reportProgress(extra, elapsedMs, `Agent status: ${describeAgentStatus(readAgentStatus(row))}`),
|
|
685
|
+
});
|
|
686
|
+
return {
|
|
687
|
+
row: result.value ?? fallbackRow,
|
|
688
|
+
settled: result.done,
|
|
689
|
+
elapsedMs: result.elapsedMs,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/** The call that starts on the first missing credential, with every argument it needs. */
|
|
693
|
+
export function missingCredentialStep(agent) {
|
|
694
|
+
const missing = agent.missingCredentials[0];
|
|
695
|
+
if (!missing)
|
|
696
|
+
return undefined;
|
|
697
|
+
const keys = agent.missingCredentials.map(item => item.key).join(', ');
|
|
698
|
+
return missing.oauth
|
|
699
|
+
? {
|
|
700
|
+
tool: 'connect_agent_oauth',
|
|
701
|
+
arguments: { action: 'connect', agent_guid: agent.guid, credential: missing.key },
|
|
702
|
+
reason: `Provide the missing credentials (${keys}). This starts with ${missing.title}, which the user connects by signing in.`,
|
|
703
|
+
}
|
|
704
|
+
: {
|
|
705
|
+
tool: 'set_agent_credentials',
|
|
706
|
+
arguments: { agent_guid: agent.guid, credential: missing.key },
|
|
707
|
+
reason: `Provide the missing credentials (${keys}). This lists the ${missing.title} fields to ask the user for.`,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
/** The most useful next call for an agent in this state, if any. */
|
|
711
|
+
export function nextStepForAgent(agent, waitSeconds) {
|
|
712
|
+
if (agent.status === 6 && agent.subscription?.status !== 'active') {
|
|
713
|
+
return {
|
|
714
|
+
tool: 'manage_agent_billing',
|
|
715
|
+
arguments: { agent_guid: agent.guid, action: 'subscribe' },
|
|
716
|
+
reason: 'The agent has no paid plan. This returns the price; nothing is charged without confirm: true.',
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const missing = agent.missingCredentials[0];
|
|
720
|
+
if (missing)
|
|
721
|
+
return missingCredentialStep(agent);
|
|
722
|
+
if (isTransitionalStatus(agent.status)) {
|
|
723
|
+
return {
|
|
724
|
+
tool: 'get_my_agent',
|
|
725
|
+
arguments: { agent_guid: agent.guid, wait_seconds: waitSeconds },
|
|
726
|
+
reason: 'Wait until the agent has settled. Do not repeat the action that started it.',
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
if (agent.status === 0 || agent.status === 5) {
|
|
730
|
+
return {
|
|
731
|
+
tool: 'control_agent',
|
|
732
|
+
arguments: { agent_guid: agent.guid, action: 'start' },
|
|
733
|
+
reason: agent.status === 5 ? 'Retry the failed start.' : 'Start the agent.',
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
738
|
+
// Refusals Wiro sends before it touches the wallet, or after rolling back
|
|
739
|
+
// everything it wrote. Only these are known to come before any charge, so any
|
|
740
|
+
// other result:false answer to a paid call leaves the outcome open.
|
|
741
|
+
const PRE_CHARGE_CODES = new Set([95, 96, 97, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109]);
|
|
742
|
+
const PRE_CHARGE_REFUSALS = [
|
|
743
|
+
/insufficient wallet balance/i,
|
|
744
|
+
/could not retrieve wallet balance/i,
|
|
745
|
+
/wallet deduction failed/i,
|
|
746
|
+
/subscription (is )?already active/i,
|
|
747
|
+
/already on (the )?(pro|starter) tier/i,
|
|
748
|
+
/no active subscription|active subscription required|no expired subscription found/i,
|
|
749
|
+
/stripe subscriptions must be/i,
|
|
750
|
+
/extra credit pack|pack key required|purchase request id/i,
|
|
751
|
+
/could not (initialize|resume) prepaid credit purchase|prepaid credit purchase cannot be resumed/i,
|
|
752
|
+
/downgrading to starter is not supported|targettier must be|tier must be 'starter' or 'pro'/i,
|
|
753
|
+
/failed to compute (target-tier )?pricing|must be greater than \$0|failed to apply pricing snapshot/i,
|
|
754
|
+
/failed to create subscription record|subscription record returned invalid id/i,
|
|
755
|
+
/payment for this agent is already being processed|checkout for this agent is already being opened/i,
|
|
756
|
+
/confirm this agent's previous checkout|card checkouts for a team agent|payment system not configured/i,
|
|
757
|
+
/agent not found|team admin|don't have access|access denied|must own this/i,
|
|
758
|
+
/switch to (the )?(team|personal) context|belongs to a different team/i,
|
|
759
|
+
/^request parameter \[/i,
|
|
760
|
+
/mutually exclusive|agent terms acceptance|customskills must be|skill key may only contain/i,
|
|
761
|
+
/enabledchannels is not accepted|credentials must be an object|credential field|is missing required fields/i,
|
|
762
|
+
/chat mode is not accepted|is managed by (wiro|the connection flow)|^agent-[a-z-]+$/i,
|
|
763
|
+
/^failed to deploy agent|failed to save communication channel settings|failed to save custom skill/i,
|
|
764
|
+
];
|
|
765
|
+
/** Whether a result:false answer is one that Wiro sends before any charge. */
|
|
766
|
+
export function isPreChargeRefusal(error) {
|
|
767
|
+
if (!(error instanceof ApiResultError))
|
|
768
|
+
return false;
|
|
769
|
+
if (error.codes.some(code => PRE_CHARGE_CODES.has(Number(code))))
|
|
770
|
+
return true;
|
|
771
|
+
return PRE_CHARGE_REFUSALS.some(pattern => pattern.test(error.message.trim()));
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* After a paid request was sent, whether it may still have gone through.
|
|
775
|
+
* Only the known refusals and client errors (other than a timeout or rate
|
|
776
|
+
* limit) settle it; any other answer, a network failure or a server error
|
|
777
|
+
* leaves it open.
|
|
778
|
+
*/
|
|
779
|
+
export function outcomeUnknown(error) {
|
|
780
|
+
if (error instanceof ApiResultError)
|
|
781
|
+
return !isPreChargeRefusal(error);
|
|
782
|
+
if (error instanceof WiroApiError) {
|
|
783
|
+
return error.status >= 500 || error.status === 408 || error.status === 429;
|
|
784
|
+
}
|
|
785
|
+
return true;
|
|
786
|
+
}
|
|
787
|
+
/** Advice for a status reached after a start, renew or deploy. */
|
|
788
|
+
export function settledStatusAdvice(status) {
|
|
789
|
+
switch (status) {
|
|
790
|
+
case 4:
|
|
791
|
+
return 'The agent is running. send_agent_message with agent set to its guid talks to it.';
|
|
792
|
+
case 5:
|
|
793
|
+
return 'The agent failed to start. get_agent_activity with kind logs may show why, and control_agent with action start retries.';
|
|
794
|
+
case 0:
|
|
795
|
+
return 'The agent is stopped. Right after a start this means it has no active plan and no credits left, or someone stopped it.';
|
|
796
|
+
case 6:
|
|
797
|
+
return 'The agent needs setup: it has no paid plan, or required credentials are missing.';
|
|
798
|
+
default:
|
|
799
|
+
return agentStatusHint(status);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
//# sourceMappingURL=user-agent.js.map
|