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