@wiro-ai/wiro-mcp 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -17
- package/dist/client.d.ts +25 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +129 -29
- package/dist/client.js.map +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +24 -17
- package/dist/instructions.js.map +1 -1
- package/dist/playbooks.d.ts.map +1 -1
- package/dist/playbooks.js +61 -48
- package/dist/playbooks.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +4 -3
- package/dist/prompts.js.map +1 -1
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/apply-agent-skills.d.ts.map +1 -1
- package/dist/tools/apply-agent-skills.js +400 -88
- package/dist/tools/apply-agent-skills.js.map +1 -1
- package/dist/tools/bind-agent-project.d.ts +4 -0
- package/dist/tools/bind-agent-project.d.ts.map +1 -0
- package/dist/tools/bind-agent-project.js +403 -0
- package/dist/tools/bind-agent-project.js.map +1 -0
- package/dist/tools/cancel-agent-message.d.ts.map +1 -1
- package/dist/tools/cancel-agent-message.js +6 -3
- package/dist/tools/cancel-agent-message.js.map +1 -1
- package/dist/tools/cancel-task.js +3 -3
- package/dist/tools/cancel-task.js.map +1 -1
- package/dist/tools/clone-workflow.d.ts.map +1 -1
- package/dist/tools/clone-workflow.js +5 -4
- package/dist/tools/clone-workflow.js.map +1 -1
- package/dist/tools/connect-agent-oauth.d.ts.map +1 -1
- package/dist/tools/connect-agent-oauth.js +144 -44
- package/dist/tools/connect-agent-oauth.js.map +1 -1
- package/dist/tools/control-agent.d.ts.map +1 -1
- package/dist/tools/control-agent.js +133 -45
- package/dist/tools/control-agent.js.map +1 -1
- package/dist/tools/create-workflow.d.ts +9 -6
- package/dist/tools/create-workflow.d.ts.map +1 -1
- package/dist/tools/create-workflow.js +13 -13
- package/dist/tools/create-workflow.js.map +1 -1
- package/dist/tools/delete-agent-session.d.ts.map +1 -1
- package/dist/tools/delete-agent-session.js +20 -14
- package/dist/tools/delete-agent-session.js.map +1 -1
- package/dist/tools/delete-agent.d.ts.map +1 -1
- package/dist/tools/delete-agent.js +14 -10
- package/dist/tools/delete-agent.js.map +1 -1
- package/dist/tools/delete-workflow.d.ts.map +1 -1
- package/dist/tools/delete-workflow.js +6 -5
- package/dist/tools/delete-workflow.js.map +1 -1
- package/dist/tools/deploy-agent.d.ts.map +1 -1
- package/dist/tools/deploy-agent.js +215 -47
- package/dist/tools/deploy-agent.js.map +1 -1
- package/dist/tools/disconnect-agent-oauth.d.ts.map +1 -1
- package/dist/tools/disconnect-agent-oauth.js +32 -12
- package/dist/tools/disconnect-agent-oauth.js.map +1 -1
- package/dist/tools/get-account.d.ts.map +1 -1
- package/dist/tools/get-account.js +157 -49
- package/dist/tools/get-account.js.map +1 -1
- package/dist/tools/get-agent-activity.d.ts.map +1 -1
- package/dist/tools/get-agent-activity.js +186 -36
- package/dist/tools/get-agent-activity.js.map +1 -1
- package/dist/tools/get-agent-conversation.d.ts.map +1 -1
- package/dist/tools/get-agent-conversation.js +65 -39
- package/dist/tools/get-agent-conversation.js.map +1 -1
- package/dist/tools/get-agent-message.d.ts.map +1 -1
- package/dist/tools/get-agent-message.js +6 -4
- package/dist/tools/get-agent-message.js.map +1 -1
- package/dist/tools/get-agent-template.d.ts.map +1 -1
- package/dist/tools/get-agent-template.js +33 -17
- package/dist/tools/get-agent-template.js.map +1 -1
- package/dist/tools/get-model-schema.d.ts.map +1 -1
- package/dist/tools/get-model-schema.js +7 -5
- package/dist/tools/get-model-schema.js.map +1 -1
- package/dist/tools/get-my-agent.d.ts.map +1 -1
- package/dist/tools/get-my-agent.js +54 -11
- package/dist/tools/get-my-agent.js.map +1 -1
- package/dist/tools/get-playbook.d.ts.map +1 -1
- package/dist/tools/get-playbook.js +3 -2
- package/dist/tools/get-playbook.js.map +1 -1
- package/dist/tools/get-task-price.d.ts +1 -1
- package/dist/tools/get-task-price.d.ts.map +1 -1
- package/dist/tools/get-task-price.js +29 -8
- package/dist/tools/get-task-price.js.map +1 -1
- package/dist/tools/get-task.d.ts.map +1 -1
- package/dist/tools/get-task.js +25 -9
- package/dist/tools/get-task.js.map +1 -1
- package/dist/tools/get-workflow.d.ts.map +1 -1
- package/dist/tools/get-workflow.js +46 -21
- package/dist/tools/get-workflow.js.map +1 -1
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +10 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kill-task.js +3 -3
- package/dist/tools/kill-task.js.map +1 -1
- package/dist/tools/list-agent-skills.d.ts.map +1 -1
- package/dist/tools/list-agent-skills.js +5 -4
- package/dist/tools/list-agent-skills.js.map +1 -1
- package/dist/tools/list-agent-templates.js +4 -4
- package/dist/tools/list-agent-templates.js.map +1 -1
- package/dist/tools/list-my-agents.d.ts.map +1 -1
- package/dist/tools/list-my-agents.js +14 -8
- package/dist/tools/list-my-agents.js.map +1 -1
- package/dist/tools/list-projects.d.ts +4 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +141 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-studio-results.js +3 -3
- package/dist/tools/list-studio-results.js.map +1 -1
- package/dist/tools/list-tasks.js +3 -3
- package/dist/tools/list-tasks.js.map +1 -1
- package/dist/tools/list-workflow-runs.d.ts.map +1 -1
- package/dist/tools/list-workflow-runs.js +6 -4
- package/dist/tools/list-workflow-runs.js.map +1 -1
- package/dist/tools/list-workflows.d.ts.map +1 -1
- package/dist/tools/list-workflows.js +7 -6
- package/dist/tools/list-workflows.js.map +1 -1
- package/dist/tools/manage-agent-billing.d.ts.map +1 -1
- package/dist/tools/manage-agent-billing.js +259 -87
- package/dist/tools/manage-agent-billing.js.map +1 -1
- package/dist/tools/manage-agent-custom-skill.d.ts.map +1 -1
- package/dist/tools/manage-agent-custom-skill.js +91 -21
- package/dist/tools/manage-agent-custom-skill.js.map +1 -1
- package/dist/tools/recommend-model.js +3 -3
- package/dist/tools/recommend-model.js.map +1 -1
- package/dist/tools/resume-workflow-run.d.ts.map +1 -1
- package/dist/tools/resume-workflow-run.js +3 -2
- package/dist/tools/resume-workflow-run.js.map +1 -1
- package/dist/tools/run-model.js +5 -5
- package/dist/tools/run-model.js.map +1 -1
- package/dist/tools/run-workflow.d.ts.map +1 -1
- package/dist/tools/run-workflow.js +3 -2
- package/dist/tools/run-workflow.js.map +1 -1
- package/dist/tools/save-workflow-from-agent.d.ts.map +1 -1
- package/dist/tools/save-workflow-from-agent.js +22 -20
- package/dist/tools/save-workflow-from-agent.js.map +1 -1
- package/dist/tools/schemas.d.ts +150 -127
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +76 -3
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/search-docs.js +3 -3
- package/dist/tools/search-docs.js.map +1 -1
- package/dist/tools/search-models.d.ts.map +1 -1
- package/dist/tools/search-models.js +7 -4
- package/dist/tools/search-models.js.map +1 -1
- package/dist/tools/send-agent-message.d.ts.map +1 -1
- package/dist/tools/send-agent-message.js +32 -11
- package/dist/tools/send-agent-message.js.map +1 -1
- package/dist/tools/set-agent-credentials.d.ts.map +1 -1
- package/dist/tools/set-agent-credentials.js +60 -59
- package/dist/tools/set-agent-credentials.js.map +1 -1
- package/dist/tools/update-agent.d.ts.map +1 -1
- package/dist/tools/update-agent.js +59 -21
- package/dist/tools/update-agent.js.map +1 -1
- package/dist/tools/update-workflow.d.ts +205 -6
- package/dist/tools/update-workflow.d.ts.map +1 -1
- package/dist/tools/update-workflow.js +20 -7
- package/dist/tools/update-workflow.js.map +1 -1
- package/dist/tools/upload-file.d.ts.map +1 -1
- package/dist/tools/upload-file.js +13 -9
- package/dist/tools/upload-file.js.map +1 -1
- package/dist/tools/wait-for-task.d.ts.map +1 -1
- package/dist/tools/wait-for-task.js +23 -5
- package/dist/tools/wait-for-task.js.map +1 -1
- package/dist/tools/wait-for-workflow-run.d.ts +18 -18
- package/dist/tools/wait-for-workflow-run.d.ts.map +1 -1
- package/dist/tools/wait-for-workflow-run.js +7 -6
- package/dist/tools/wait-for-workflow-run.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/agent-catalog.d.ts +168 -139
- package/dist/utils/agent-catalog.d.ts.map +1 -1
- package/dist/utils/agent-catalog.js +34 -14
- package/dist/utils/agent-catalog.js.map +1 -1
- package/dist/utils/agent-config.d.ts +111 -8
- package/dist/utils/agent-config.d.ts.map +1 -1
- package/dist/utils/agent-config.js +290 -40
- package/dist/utils/agent-config.js.map +1 -1
- package/dist/utils/agent-message.d.ts +41 -22
- package/dist/utils/agent-message.d.ts.map +1 -1
- package/dist/utils/agent-message.js +94 -31
- package/dist/utils/agent-message.js.map +1 -1
- package/dist/utils/agent-targets.d.ts +8 -4
- package/dist/utils/agent-targets.d.ts.map +1 -1
- package/dist/utils/agent-targets.js +51 -35
- package/dist/utils/agent-targets.js.map +1 -1
- package/dist/utils/agent-turns.d.ts +31 -24
- package/dist/utils/agent-turns.d.ts.map +1 -1
- package/dist/utils/agent-turns.js +154 -35
- package/dist/utils/agent-turns.js.map +1 -1
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +22 -1
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/format.d.ts +27 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +429 -82
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/json-schema-dialect.d.ts +8 -1
- package/dist/utils/json-schema-dialect.d.ts.map +1 -1
- package/dist/utils/json-schema-dialect.js +27 -1
- package/dist/utils/json-schema-dialect.js.map +1 -1
- package/dist/utils/media.d.ts +8 -0
- package/dist/utils/media.d.ts.map +1 -1
- package/dist/utils/media.js +29 -0
- package/dist/utils/media.js.map +1 -1
- package/dist/utils/poll.d.ts +5 -2
- package/dist/utils/poll.d.ts.map +1 -1
- package/dist/utils/poll.js +10 -9
- package/dist/utils/poll.js.map +1 -1
- package/dist/utils/quoted-text.d.ts +28 -0
- package/dist/utils/quoted-text.d.ts.map +1 -0
- package/dist/utils/quoted-text.js +64 -0
- package/dist/utils/quoted-text.js.map +1 -0
- package/dist/utils/structured.d.ts +2 -0
- package/dist/utils/structured.d.ts.map +1 -1
- package/dist/utils/structured.js +15 -2
- package/dist/utils/structured.js.map +1 -1
- package/dist/utils/studio-runs.d.ts +4 -4
- package/dist/utils/studio-runs.d.ts.map +1 -1
- package/dist/utils/studio-runs.js +11 -8
- package/dist/utils/studio-runs.js.map +1 -1
- package/dist/utils/user-agent.d.ts +376 -241
- package/dist/utils/user-agent.d.ts.map +1 -1
- package/dist/utils/user-agent.js +302 -89
- package/dist/utils/user-agent.js.map +1 -1
- package/dist/utils/wiro-project.d.ts +45 -0
- package/dist/utils/wiro-project.d.ts.map +1 -0
- package/dist/utils/wiro-project.js +66 -0
- package/dist/utils/wiro-project.js.map +1 -0
- package/dist/utils/workflow-agent.d.ts +4 -4
- package/dist/utils/workflow-save.d.ts +23 -5
- package/dist/utils/workflow-save.d.ts.map +1 -1
- package/dist/utils/workflow-save.js +130 -38
- package/dist/utils/workflow-save.js.map +1 -1
- package/dist/utils/workflow.d.ts +79 -61
- package/dist/utils/workflow.d.ts.map +1 -1
- package/dist/utils/workflow.js +149 -19
- package/dist/utils/workflow.js.map +1 -1
- package/dist/utils/workspace.d.ts +64 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +173 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +6 -4
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import { ApiResultError, asString, hasErrorCode, isPlainObject, requireApiSuccess, toNumber, truncate, unixToIso, } from './api.js';
|
|
2
|
+
import { AGENT_NOT_FOUND_TEXT, ApiResultError, asString, hasErrorCode, isAgentNotFound, isPlainObject, requireApiSuccess, toNumber, truncate, unixToIso, } from './api.js';
|
|
3
|
+
import { oneLine, quotedSpan } from './quoted-text.js';
|
|
4
|
+
import { credentialKeysSetupWaitsFor, isWalletNotRead, planEndsWithPeriod, STARTER_TASK_KEY, WALLET_NOT_READ_NOTE, WIRO_PROJECT_CREDENTIAL } from './user-agent.js';
|
|
3
5
|
// Helpers for configuring a deployed agent: its registry skills, credentials,
|
|
4
6
|
// sign-in connections and custom skills. The pure functions mirror the server
|
|
5
7
|
// rules closely so a bad request is caught before anything is written.
|
|
@@ -53,17 +55,66 @@ export function restartNote(status, what = 'change') {
|
|
|
53
55
|
case 6:
|
|
54
56
|
return `The agent still needs setup; the ${what} takes effect once it starts.`;
|
|
55
57
|
case 0:
|
|
58
|
+
return `The agent is stopped, so the ${what} takes effect the next time it starts.`;
|
|
56
59
|
case 5:
|
|
57
|
-
return `The agent
|
|
60
|
+
return `The agent stopped with an error, so the ${what} takes effect the next time it starts.`;
|
|
58
61
|
default:
|
|
59
62
|
return `The ${what} takes effect the next time the agent starts.`;
|
|
60
63
|
}
|
|
61
64
|
}
|
|
65
|
+
export function keyList(keys) {
|
|
66
|
+
return keys.length > 1 ? `${keys.slice(0, -1).join(', ')} and ${keys[keys.length - 1]}` : keys.join('');
|
|
67
|
+
}
|
|
68
|
+
function afterWriteNote(status) {
|
|
69
|
+
switch (status) {
|
|
70
|
+
case 1:
|
|
71
|
+
return 'The agent restarts to load the new values, which takes about a minute.';
|
|
72
|
+
case 2:
|
|
73
|
+
case 3:
|
|
74
|
+
return 'The agent is starting and loads the new values.';
|
|
75
|
+
case 4:
|
|
76
|
+
// A save that changes a value sets a running agent stopping before Wiro answers.
|
|
77
|
+
return 'The agent kept running: a save that matches what is stored does not restart it. If it should load new values now, control_agent with action restart does it.';
|
|
78
|
+
case 6:
|
|
79
|
+
return 'The agent still needs setup before it can start; get_my_agent shows what is missing.';
|
|
80
|
+
case 5:
|
|
81
|
+
return 'The agent stopped with an error; the values are used when it starts.';
|
|
82
|
+
case null:
|
|
83
|
+
return 'Check the agent with get_my_agent.';
|
|
84
|
+
default:
|
|
85
|
+
return `The agent is ${agentStatusLabel(status)}; the values are used when it starts.`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** What the setup still waits for and the tool that gives each one: a Wiro project, a sign-in or saved fields. */
|
|
89
|
+
export function setupStillMissingNote(missing) {
|
|
90
|
+
if (missing.length === 0)
|
|
91
|
+
return null;
|
|
92
|
+
const titles = (items) => items.map(item => item.title);
|
|
93
|
+
const isProject = (item) => item.key === WIRO_PROJECT_CREDENTIAL;
|
|
94
|
+
const steps = [
|
|
95
|
+
{ verb: 'Set', names: titles(missing.filter(item => !item.oauth && !isProject(item))), tool: 'set_agent_credentials' },
|
|
96
|
+
{ verb: 'Connect', names: titles(missing.filter(item => item.oauth && !isProject(item))), tool: 'connect_agent_oauth' },
|
|
97
|
+
{ verb: 'Connect', names: titles(missing.filter(isProject)), tool: 'bind_agent_project' },
|
|
98
|
+
].filter(step => step.names.length > 0);
|
|
99
|
+
const first = steps[0];
|
|
100
|
+
const how = steps.length === 1 && first
|
|
101
|
+
? `${first.verb} ${first.names.length > 1 ? 'them' : 'it'} with ${first.tool}`
|
|
102
|
+
: steps.map((step, index) => `${index === 0 ? step.verb : step.verb.toLowerCase()} ${keyList(step.names)} with ${step.tool}`).join(', ');
|
|
103
|
+
return `Setup is not finished: still missing ${keyList(titles(missing))}. ${how}, then check with get_my_agent.`;
|
|
104
|
+
}
|
|
105
|
+
/** When the agent loads the values, and the credentials its setup still waits for, at any status. */
|
|
106
|
+
export function afterWriteNotes(status, missing) {
|
|
107
|
+
const setup = setupStillMissingNote(missing);
|
|
108
|
+
if (!setup)
|
|
109
|
+
return [afterWriteNote(status)];
|
|
110
|
+
// At status 6 the general note says the same without the names.
|
|
111
|
+
return status === 6 ? [setup] : [afterWriteNote(status), setup];
|
|
112
|
+
}
|
|
62
113
|
export async function readUserAgent(client, agentGuid, signal) {
|
|
63
114
|
const body = requireApiSuccess(await client.post('/UserAgent/Detail', { guid: agentGuid }, signal), 'Could not read the agent.');
|
|
64
115
|
const row = Array.isArray(body.useragents) ? body.useragents[0] : undefined;
|
|
65
116
|
if (!isPlainObject(row))
|
|
66
|
-
throw new Error(
|
|
117
|
+
throw new Error(AGENT_NOT_FOUND_TEXT);
|
|
67
118
|
return row;
|
|
68
119
|
}
|
|
69
120
|
export function agentStatusOf(row) {
|
|
@@ -272,6 +323,71 @@ export function credentialsNeededFor(skills, registry, row) {
|
|
|
272
323
|
}))
|
|
273
324
|
.filter(entry => entry.credentials.length > 0);
|
|
274
325
|
}
|
|
326
|
+
/** The credential keys the given skills use, main and additional, by the same rule as credentialsNeededFor. */
|
|
327
|
+
export function credentialsUsedBySkills(skills, registry) {
|
|
328
|
+
const used = new Set();
|
|
329
|
+
for (const name of skills) {
|
|
330
|
+
const skill = registry.skills.get(name);
|
|
331
|
+
if (!skill?.requiresCredentials)
|
|
332
|
+
continue;
|
|
333
|
+
for (const key of skill.credentialKeys)
|
|
334
|
+
used.add(key);
|
|
335
|
+
}
|
|
336
|
+
return used;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Why connect_agent_oauth connect refuses a service on this agent, or null when
|
|
340
|
+
* it connects: one of the agent's active skills uses it, or one of its custom
|
|
341
|
+
* skills or scheduled tasks requires it. Wiro keeps such an entry off until the
|
|
342
|
+
* credential is connected, and connecting turns it on, so the entry counts
|
|
343
|
+
* whatever its enabled says. A template agent's setup also waits for the
|
|
344
|
+
* credentials its template requires, whichever skills are on, so those stay
|
|
345
|
+
* connectable.
|
|
346
|
+
*/
|
|
347
|
+
export function unusedCredential(row, credential, registry) {
|
|
348
|
+
const used = credentialsUsedBySkills(enabledSkillNames(row), registry);
|
|
349
|
+
for (const entry of customSkillEntries(row)) {
|
|
350
|
+
for (const key of entry.requiresCredentials)
|
|
351
|
+
used.add(key);
|
|
352
|
+
}
|
|
353
|
+
if (used.has(credential) || (!isCustomBuild(row) && credentialGroup(row, credential)?.optional === false))
|
|
354
|
+
return null;
|
|
355
|
+
return {
|
|
356
|
+
services: [...used].flatMap(key => {
|
|
357
|
+
const schema = credentialGroup(row, key)?._schema;
|
|
358
|
+
return isPlainObject(schema) && isPlainObject(schema.oauth_provider)
|
|
359
|
+
? [`${asString(schema.title) ?? key} (${key})`]
|
|
360
|
+
: [];
|
|
361
|
+
}),
|
|
362
|
+
skills: [...registry.skills.values()]
|
|
363
|
+
.filter(skill => !skill.deprecated && skill.requiresCredentials && skill.credentialKeys[0] === credential)
|
|
364
|
+
.map(skill => skill.name),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
export function turnOnSkillText(title, credential, skills) {
|
|
368
|
+
return skills.length > 0
|
|
369
|
+
? `To use ${title}, first turn on ${skills.join(' or ')} with apply_agent_skills; list_agent_skills with name "${credential}" shows how it connects.`
|
|
370
|
+
: '';
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The same rule for the answers that offer connect (status, and describing or
|
|
374
|
+
* saving a credential), as a note to show instead of that offer. Null when the
|
|
375
|
+
* service connects, and when the skills cannot be read: connect checks again.
|
|
376
|
+
*/
|
|
377
|
+
export async function unusedCredentialNote(client, row, credential, title, signal) {
|
|
378
|
+
try {
|
|
379
|
+
const unused = unusedCredential(row, credential, await loadSkillRegistry(client, signal));
|
|
380
|
+
if (!unused)
|
|
381
|
+
return null;
|
|
382
|
+
return [
|
|
383
|
+
`${title} is not used by any of this agent's active skills, custom skills or scheduled tasks, so connect_agent_oauth refuses it for now.`,
|
|
384
|
+
turnOnSkillText(title, credential, unused.skills),
|
|
385
|
+
].filter(Boolean).join(' ');
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
275
391
|
const IDEMPOTENCY_KEY_RE = /^mcp-[0-9a-f]{16}-([0-9a-f]{10})-([0-9a-f]{8})$/;
|
|
276
392
|
/** Fingerprint of one logical skill change, independent of the agent's current state. */
|
|
277
393
|
export function skillChangeDigest(agentGuid, requested, tier) {
|
|
@@ -284,10 +400,15 @@ export function skillChangeDigest(agentGuid, requested, tier) {
|
|
|
284
400
|
function cents(value) {
|
|
285
401
|
return value === null || !Number.isFinite(value) ? 'none' : String(Math.round(value * 100));
|
|
286
402
|
}
|
|
287
|
-
/**
|
|
288
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Fingerprint of what a quoted charge rests on: the monthly price before and after
|
|
405
|
+
* a change and, for a plan paid from the wallet, the period it is charged in. A
|
|
406
|
+
* renewal keeps the prices and starts a new period, which changes the charge.
|
|
407
|
+
*/
|
|
408
|
+
export function skillPriceTag(currentUsd, newUsd, periodEndUnix = null) {
|
|
409
|
+
const prices = `${cents(currentUsd)}:${cents(newUsd)}`;
|
|
289
410
|
return crypto.createHash('sha256')
|
|
290
|
-
.update(`${
|
|
411
|
+
.update(periodEndUnix === null ? prices : `${prices}:${periodEndUnix}`)
|
|
291
412
|
.digest('hex')
|
|
292
413
|
.slice(0, 8);
|
|
293
414
|
}
|
|
@@ -312,26 +433,45 @@ export function planBilling(row) {
|
|
|
312
433
|
const wallet = asString(row.teamguid) ? 'team' : 'personal';
|
|
313
434
|
const subscription = isPlainObject(row.subscription) ? row.subscription : null;
|
|
314
435
|
if (!subscription) {
|
|
315
|
-
return { plan: 'none', status: null, periodEnd: null,
|
|
436
|
+
return { plan: 'none', status: null, periodEnd: null, wallet, cancelAtPeriodEnd: false, switchingToWallet: false };
|
|
316
437
|
}
|
|
438
|
+
const plan = asString(subscription.provider) === 'prepaid' ? 'prepaid' : 'card';
|
|
317
439
|
return {
|
|
318
|
-
plan
|
|
440
|
+
plan,
|
|
319
441
|
status: asString(subscription.status) ?? null,
|
|
320
442
|
periodEnd: toNumber(subscription.currentperiodend),
|
|
321
|
-
daysRemaining: toNumber(subscription.daysremaining),
|
|
322
443
|
wallet,
|
|
323
|
-
cancelAtPeriodEnd: subscription
|
|
444
|
+
cancelAtPeriodEnd: planEndsWithPeriod(subscription, row.stripecancelatperiodend),
|
|
445
|
+
switchingToWallet: plan === 'card' && subscription.nextprovider === 'prepaid',
|
|
324
446
|
};
|
|
325
447
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
export function estimateProratedUsd(deltaUsd, periodEndUnix, nowMs = Date.now(), periodDays = 30) {
|
|
331
|
-
if (periodEndUnix === null || !Number.isFinite(deltaUsd))
|
|
448
|
+
const DAY_SECONDS = 86400;
|
|
449
|
+
/** The plan change of a pricing answer, or null when the agent has no active plan or Wiro sent none. */
|
|
450
|
+
export function readPlanChange(value) {
|
|
451
|
+
if (!isPlainObject(value))
|
|
332
452
|
return null;
|
|
333
|
-
|
|
334
|
-
|
|
453
|
+
return {
|
|
454
|
+
wallet: asString(value.provider) === 'prepaid',
|
|
455
|
+
periodEnd: toNumber(value.periodEnd),
|
|
456
|
+
secondsLeft: toNumber(value.secondsLeft),
|
|
457
|
+
creditsNow: toNumber(value.creditsNow),
|
|
458
|
+
creditsThisPeriod: toNumber(value.creditsThisPeriod),
|
|
459
|
+
creditsFromRenewal: toNumber(value.creditsFromRenewal),
|
|
460
|
+
chargeNowUsd: toNumber(value.chargeNowUsd),
|
|
461
|
+
creditNowUsd: toNumber(value.creditNowUsd),
|
|
462
|
+
usedCreditsUsd: toNumber(value.usedCreditsUsd),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
/** Whether a rise now pays the one-day minimum: under a day is left of the period. */
|
|
466
|
+
export function underOneDayLeft(secondsLeft) {
|
|
467
|
+
return secondsLeft !== null && secondsLeft < DAY_SECONDS;
|
|
468
|
+
}
|
|
469
|
+
/** The time left of a period in whole days, at least 1, for a sentence such as "(27 days)". */
|
|
470
|
+
export function daysLeftText(secondsLeft) {
|
|
471
|
+
if (secondsLeft === null || secondsLeft < 0)
|
|
472
|
+
return null;
|
|
473
|
+
const days = Math.max(1, Math.round(secondsLeft / DAY_SECONDS));
|
|
474
|
+
return `${days} ${days === 1 ? 'day' : 'days'}`;
|
|
335
475
|
}
|
|
336
476
|
const SKILL_PAYMENT_FAILED = 'The payment for this change did not go through, so the change was undone and nothing was charged.';
|
|
337
477
|
const DEPOSIT_PAGE = 'https://wiro.ai/panel/wallet/deposit/credit-card (for a team project, select that team in the dashboard first)';
|
|
@@ -355,7 +495,7 @@ export function describeSkillsApplyError(error, plan) {
|
|
|
355
495
|
const message = error instanceof Error ? error.message : String(error);
|
|
356
496
|
const body = error instanceof ApiResultError ? error.body : {};
|
|
357
497
|
const codes = error instanceof ApiResultError ? error.codes.map(code => toNumber(code)) : [];
|
|
358
|
-
const code = codes.find(candidate => candidate !== null && candidate >= 95 && candidate <=
|
|
498
|
+
const code = codes.find(candidate => candidate !== null && candidate >= 95 && candidate <= 110) ?? null;
|
|
359
499
|
const base = { code, message, retrySameKey: false };
|
|
360
500
|
switch (code) {
|
|
361
501
|
case 101: {
|
|
@@ -389,10 +529,14 @@ export function describeSkillsApplyError(error, plan) {
|
|
|
389
529
|
};
|
|
390
530
|
}
|
|
391
531
|
case 104:
|
|
532
|
+
// Two refusals share the code: another change to the agent's plan, or another payment from its wallet.
|
|
533
|
+
// Skill changes, an upgrade to Pro and a wallet plan's renewal take turns on an agent.
|
|
392
534
|
return {
|
|
393
535
|
...base,
|
|
394
536
|
retrySameKey: true,
|
|
395
|
-
guidance:
|
|
537
|
+
guidance: /wallet/i.test(message)
|
|
538
|
+
? 'Another payment from the same wallet was going through, so this change was not made and nothing was charged. It is not a low balance, and no funds need to be added. Offer to try again in a minute: repeat the call with the same idempotency_key.'
|
|
539
|
+
: 'Another change to this agent\'s skills or plan, such as an upgrade or a renewal, is still going through, and one runs at a time. Nothing was changed or charged by this call. Wait a few seconds, then repeat the call with the same idempotency_key. If it keeps saying so, it clears within 15 minutes.',
|
|
396
540
|
};
|
|
397
541
|
case 105:
|
|
398
542
|
return {
|
|
@@ -407,19 +551,26 @@ export function describeSkillsApplyError(error, plan) {
|
|
|
407
551
|
case 107:
|
|
408
552
|
return {
|
|
409
553
|
...base,
|
|
410
|
-
guidance: '
|
|
554
|
+
guidance: 'This idempotency_key cannot be used for this call, and nothing was changed or charged by it. Get a new quote: repeat the call without idempotency_key, show the quote to the user, and confirm with the key it gives.',
|
|
411
555
|
};
|
|
412
556
|
case 108:
|
|
413
557
|
return {
|
|
414
558
|
...base,
|
|
415
559
|
retrySameKey: true,
|
|
416
|
-
guidance: 'Nothing was changed.
|
|
560
|
+
guidance: 'Wiro could not read or save what this change needs just now, and the wallet balance can be part of that. It is not a low balance, so no funds need to be added. Nothing was changed or charged. Try again: repeat the call with the same idempotency_key.',
|
|
417
561
|
};
|
|
418
562
|
case 109:
|
|
563
|
+
// Wiro's sentence covers cards and wallets alike. The guidance names the fix for this plan.
|
|
419
564
|
return {
|
|
420
565
|
...base,
|
|
566
|
+
message: 'Payment processing failed for this skill change.',
|
|
421
567
|
guidance: skillPaymentGuidance(plan),
|
|
422
568
|
};
|
|
569
|
+
case 110:
|
|
570
|
+
return {
|
|
571
|
+
...base,
|
|
572
|
+
guidance: 'Do not repeat this change. Check the agent\'s skills and monthly price with get_my_agent and the wallet with get_account (recent_transactions) before changing anything, and contact Wiro support if they are not what you expect.',
|
|
573
|
+
};
|
|
423
574
|
case 95:
|
|
424
575
|
case 96:
|
|
425
576
|
case 97:
|
|
@@ -436,17 +587,54 @@ export function describeSkillsApplyError(error, plan) {
|
|
|
436
587
|
guidance: 'The new price could not be computed, so nothing was changed. Check the skill names with list_agent_skills.',
|
|
437
588
|
};
|
|
438
589
|
}
|
|
590
|
+
if (/^unknown skill: /i.test(message)) {
|
|
591
|
+
return {
|
|
592
|
+
...base,
|
|
593
|
+
guidance: 'Nothing was changed or charged. Use names from list_agent_skills.',
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (isWalletNotRead(error))
|
|
597
|
+
return { ...base, guidance: WALLET_NOT_READ_NOTE };
|
|
439
598
|
return { ...base, guidance: workspaceHint(message) ?? '' };
|
|
440
599
|
}
|
|
600
|
+
/**
|
|
601
|
+
* One refusal covers an agent that does not exist and an agent this project may
|
|
602
|
+
* not connect, and the fix for each is different. The agent's own read tells
|
|
603
|
+
* them apart.
|
|
604
|
+
*/
|
|
605
|
+
export async function connectionRefusalText(client, agentGuid, message, done, signal) {
|
|
606
|
+
const rule = `can only be ${done} with a project created by the person who deployed the agent: `
|
|
607
|
+
+ 'connect this server with such a project, by its API key or a browser sign-in.';
|
|
608
|
+
try {
|
|
609
|
+
await readUserAgent(client, agentGuid, signal);
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
if (isAgentNotFound(error))
|
|
613
|
+
return AGENT_NOT_FOUND_TEXT;
|
|
614
|
+
return `${message}\n\nCheck the guid with list_my_agents. If the agent is listed, integrations ${rule}`;
|
|
615
|
+
}
|
|
616
|
+
return `${message}\n\nIntegrations ${rule}`;
|
|
617
|
+
}
|
|
618
|
+
/** Wiro's answer when it could not clear a saved connection. Nothing says the connection is gone, and a repeat is safe. */
|
|
619
|
+
export const CONNECTION_NOT_CLEARED = 'The saved connection could not be cleared. Please try again.';
|
|
620
|
+
export const WORKSPACE_HINT = 'Connect with a project in the workspace (personal or team) that owns this agent, by its API key or a browser sign-in.';
|
|
441
621
|
/** The API names a dashboard concept ("team context"); here the workspace is the connected project's. */
|
|
442
622
|
export function workspaceHint(message) {
|
|
443
|
-
return /switch to the (team|personal)|different team|personal workspace/i.test(message)
|
|
444
|
-
? 'Connect with a project in the workspace (personal or team) that owns this agent, by its API key or a browser sign-in.'
|
|
445
|
-
: undefined;
|
|
623
|
+
return /switch to the (team|personal)|different team|personal workspace/i.test(message) ? WORKSPACE_HINT : undefined;
|
|
446
624
|
}
|
|
447
625
|
// A field's setup help is shown whole: its last steps, such as sharing a
|
|
448
626
|
// calendar with the service account, are part of the setup.
|
|
449
627
|
export const FIELD_HELP_MAX = 4_000;
|
|
628
|
+
// The Wiro project field. On the dashboard it is a project picker, and the help
|
|
629
|
+
// it comes with describes that picker, so this line is shown instead.
|
|
630
|
+
export const WIRO_PROJECT_NOTE = 'Connect a Wiro project with bind_agent_project: it takes the project of this connection or '
|
|
631
|
+
+ 'one from list_projects, and no key passes through this chat. The user can also pick the project on the agent\'s page in '
|
|
632
|
+
+ 'the Wiro dashboard. Saving a project API key with set_agent_credentials stays possible when the user gives one for this, '
|
|
633
|
+
+ 'and Wiro adds the project\'s secret by itself. Model runs the agent starts are billed to the wallet of the workspace '
|
|
634
|
+
+ 'that owns the project.';
|
|
635
|
+
export function isWiroProjectField(raw) {
|
|
636
|
+
return /^(custom:)?wiro-project-picker$/.test(asString(raw.type) ?? '');
|
|
637
|
+
}
|
|
450
638
|
export function htmlToText(value, maxLength = 400) {
|
|
451
639
|
const html = asString(value);
|
|
452
640
|
if (!html)
|
|
@@ -506,7 +694,7 @@ export function parseCredentialField(raw) {
|
|
|
506
694
|
...(toNumber(raw.maxsize) !== null ? { maxSizeKb: toNumber(raw.maxsize) } : {}),
|
|
507
695
|
...(asString(raw.item_type) ? { itemType: asString(raw.item_type) } : {}),
|
|
508
696
|
itemSchema: itemSchemaOf(raw).map(parseCredentialField).filter(field => field.key),
|
|
509
|
-
...(htmlToText(raw.help, FIELD_HELP_MAX) ? { help: htmlToText(raw.help, FIELD_HELP_MAX) } : {}),
|
|
697
|
+
...(!isWiroProjectField(raw) && htmlToText(raw.help, FIELD_HELP_MAX) ? { help: htmlToText(raw.help, FIELD_HELP_MAX) } : {}),
|
|
510
698
|
...(asString(raw.placeholder) ? { placeholder: asString(raw.placeholder) } : {}),
|
|
511
699
|
};
|
|
512
700
|
}
|
|
@@ -981,7 +1169,8 @@ export function prepareCredentialWrite(inputs, metas) {
|
|
|
981
1169
|
const mayClear = !field.required || meta.channel !== null;
|
|
982
1170
|
if (isEmptyValue(input.value)) {
|
|
983
1171
|
if (!mayClear) {
|
|
984
|
-
prepared.errors.push(`${field.label} is required and cannot be cleared
|
|
1172
|
+
prepared.errors.push(`${field.label} is required and cannot be cleared. To replace the saved value, send the new one. `
|
|
1173
|
+
+ 'To end the agent\'s access, revoke it with the service it belongs to.');
|
|
985
1174
|
continue;
|
|
986
1175
|
}
|
|
987
1176
|
const cleared = shape === 'string-array' || shape === 'object-array' ? [] : '';
|
|
@@ -1065,7 +1254,7 @@ export function uploadFileName(url, field, contentType) {
|
|
|
1065
1254
|
return `${base || field}.${extension}`;
|
|
1066
1255
|
}
|
|
1067
1256
|
// ---------------------------------------------------------------------------
|
|
1068
|
-
// Agent credential status (from /UserAgent/Detail
|
|
1257
|
+
// Agent credential status (from /UserAgent/Detail)
|
|
1069
1258
|
// ---------------------------------------------------------------------------
|
|
1070
1259
|
export function isFilledValue(value) {
|
|
1071
1260
|
if (value === null || value === undefined)
|
|
@@ -1078,6 +1267,38 @@ export function isFilledValue(value) {
|
|
|
1078
1267
|
return Object.values(value).some(isFilledValue);
|
|
1079
1268
|
return true;
|
|
1080
1269
|
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Whether a field of a saved credential holds what someone entered, counted the
|
|
1272
|
+
* way Wiro counts it for a card's filled state: a list of options counts only an
|
|
1273
|
+
* option that is selected, and a row of a list does not count for its selects
|
|
1274
|
+
* and switches, which come with a default.
|
|
1275
|
+
*/
|
|
1276
|
+
export function isCredentialFieldFilled(field, value) {
|
|
1277
|
+
if (field.itemSchema.length === 0 || !Array.isArray(value))
|
|
1278
|
+
return isCredentialValueFilled(value);
|
|
1279
|
+
return value.some(row => {
|
|
1280
|
+
if (!isPlainObject(row))
|
|
1281
|
+
return isCredentialValueFilled(row);
|
|
1282
|
+
return Object.entries(row).some(([key, item]) => {
|
|
1283
|
+
const inner = field.itemSchema.find(entry => entry.key === key);
|
|
1284
|
+
if (!inner)
|
|
1285
|
+
return isCredentialValueFilled(item);
|
|
1286
|
+
if (inner.type === 'select' || inner.type === 'boolean')
|
|
1287
|
+
return false;
|
|
1288
|
+
return isCredentialFieldFilled(inner, item);
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
function isCredentialValueFilled(value) {
|
|
1293
|
+
if (Array.isArray(value) && value.length > 0 && value.every(option => isPlainObject(option) && 'selected' in option)) {
|
|
1294
|
+
return value.some(option => isPlainObject(option) && option.selected === true);
|
|
1295
|
+
}
|
|
1296
|
+
if (Array.isArray(value))
|
|
1297
|
+
return value.some(isCredentialValueFilled);
|
|
1298
|
+
if (isPlainObject(value))
|
|
1299
|
+
return Object.values(value).some(isCredentialValueFilled);
|
|
1300
|
+
return isFilledValue(value);
|
|
1301
|
+
}
|
|
1081
1302
|
export function credentialGroup(row, key) {
|
|
1082
1303
|
const groups = isPlainObject(row.credentials) ? row.credentials : null;
|
|
1083
1304
|
const group = groups ? groups[key] : undefined;
|
|
@@ -1089,18 +1310,19 @@ export function credentialStatus(row, key, meta) {
|
|
|
1089
1310
|
return null;
|
|
1090
1311
|
const schema = isPlainObject(group._schema) ? group._schema : {};
|
|
1091
1312
|
const connected = group._connected === true;
|
|
1092
|
-
// A sign-in credential's required fields depend on its mode; connected is the signal there.
|
|
1093
|
-
|
|
1313
|
+
// A sign-in credential's required fields depend on its mode; connected is the signal there. Filled says every
|
|
1314
|
+
// required field holds a value, a template's own value that is not shown included.
|
|
1315
|
+
const missingFields = connected || !meta || meta.oauth || group._filled === true
|
|
1094
1316
|
? []
|
|
1095
1317
|
: meta.fields
|
|
1096
|
-
.filter(field => field.required && isWritableField(field) && !
|
|
1318
|
+
.filter(field => field.required && isWritableField(field) && !isCredentialFieldFilled(field, group[field.key]))
|
|
1097
1319
|
.map(field => field.label);
|
|
1098
1320
|
return {
|
|
1099
1321
|
key,
|
|
1100
1322
|
title: asString(schema.title) ?? meta?.title ?? key,
|
|
1101
1323
|
connected,
|
|
1102
1324
|
filled: group._filled === true,
|
|
1103
|
-
requiredMissing:
|
|
1325
|
+
requiredMissing: credentialKeysSetupWaitsFor(row).has(key),
|
|
1104
1326
|
optional: group.optional === true,
|
|
1105
1327
|
missingFields,
|
|
1106
1328
|
};
|
|
@@ -1156,6 +1378,17 @@ export function isAllowedRedirectUrl(url, agentGuid) {
|
|
|
1156
1378
|
export function ownAppRedirectUri(meta) {
|
|
1157
1379
|
return meta.oauth?.callbackPath ? `${OAUTH_CALLBACK_BASE}${meta.oauth.callbackPath}` : undefined;
|
|
1158
1380
|
}
|
|
1381
|
+
/** Goes with a list of missing fields that holds app keys, which may be saved already. */
|
|
1382
|
+
export function writeOnlyNote(mode) {
|
|
1383
|
+
return `App keys are not shown once saved. If they are saved already, connect with mode ${mode}.`;
|
|
1384
|
+
}
|
|
1385
|
+
/** The same note before a mode is chosen: it names each usable mode that takes app keys. */
|
|
1386
|
+
export function writeOnlyModesNote(requirements) {
|
|
1387
|
+
const modes = requirements
|
|
1388
|
+
.filter(requirement => requirement.available && requirement.fields.some(field => field.writeOnly))
|
|
1389
|
+
.map(requirement => requirement.mode);
|
|
1390
|
+
return modes.length > 0 ? writeOnlyNote(modes.join(' or ')) : null;
|
|
1391
|
+
}
|
|
1159
1392
|
function fieldsForMode(meta, mode) {
|
|
1160
1393
|
if (mode === 'wiro')
|
|
1161
1394
|
return [];
|
|
@@ -1180,11 +1413,15 @@ function fieldsForMode(meta, mode) {
|
|
|
1180
1413
|
export function connectionModeRequirements(meta, group) {
|
|
1181
1414
|
const modes = meta.connectionModes.length > 0 ? meta.connectionModes : ['wiro'];
|
|
1182
1415
|
return modes.map(mode => {
|
|
1183
|
-
const fields = fieldsForMode(meta, mode).map(field =>
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1416
|
+
const fields = fieldsForMode(meta, mode).map(field => {
|
|
1417
|
+
const isSet = group !== null && isFilledValue(group[field.key]);
|
|
1418
|
+
return {
|
|
1419
|
+
field: field.key,
|
|
1420
|
+
label: field.label,
|
|
1421
|
+
isSet,
|
|
1422
|
+
...(field.oauthManaged && !isSet ? { writeOnly: true } : {}),
|
|
1423
|
+
};
|
|
1424
|
+
});
|
|
1188
1425
|
if (mode === 'wiro') {
|
|
1189
1426
|
return {
|
|
1190
1427
|
mode,
|
|
@@ -1246,6 +1483,16 @@ export function chooseConnectionMode(meta, requirements) {
|
|
|
1246
1483
|
});
|
|
1247
1484
|
return ordered.find(requirement => requirement.fields.every(field => field.isSet))?.mode ?? null;
|
|
1248
1485
|
}
|
|
1486
|
+
/**
|
|
1487
|
+
* An account in a line of this server's text. The outside service sets its name,
|
|
1488
|
+
* and a service without an account choice gives that name as the id too, so both
|
|
1489
|
+
* are quoted and a repeat is left out.
|
|
1490
|
+
*/
|
|
1491
|
+
export function accountLabel(account) {
|
|
1492
|
+
const name = oneLine(account.name);
|
|
1493
|
+
const id = quotedSpan(account.id);
|
|
1494
|
+
return name && name !== oneLine(account.id) ? `${quotedSpan(name)} (${id})` : id;
|
|
1495
|
+
}
|
|
1249
1496
|
/** Candidate or saved accounts in one shape, whatever the provider calls the fields. */
|
|
1250
1497
|
export function toAccountRefs(list, picker) {
|
|
1251
1498
|
if (!Array.isArray(list))
|
|
@@ -1340,7 +1587,7 @@ export function buildChainedSelections(next, groups, selections) {
|
|
|
1340
1587
|
if (group.errorCode) {
|
|
1341
1588
|
return {
|
|
1342
1589
|
skippedParents: [],
|
|
1343
|
-
error: `${group.parent
|
|
1590
|
+
error: `${accountLabel(group.parent)} could not be read (${group.errorCode}), so nothing can be chosen for it. Leave it out.`,
|
|
1344
1591
|
};
|
|
1345
1592
|
}
|
|
1346
1593
|
const ids = chosen.get(group.parent.id) ?? [];
|
|
@@ -1349,7 +1596,7 @@ export function buildChainedSelections(next, groups, selections) {
|
|
|
1349
1596
|
if (!item) {
|
|
1350
1597
|
return {
|
|
1351
1598
|
skippedParents: [],
|
|
1352
|
-
error: `"${itemId}" is not available under ${group.parent
|
|
1599
|
+
error: `"${itemId}" is not available under ${accountLabel(group.parent)}. Available: ${group.items.map(accountLabel).join(', ') || 'none'}.`,
|
|
1353
1600
|
};
|
|
1354
1601
|
}
|
|
1355
1602
|
if (!ids.includes(item.id))
|
|
@@ -1421,6 +1668,8 @@ export function connectionState(status, options) {
|
|
|
1421
1668
|
return 'pick_accounts';
|
|
1422
1669
|
return 'not_connected';
|
|
1423
1670
|
}
|
|
1671
|
+
/** The entry that holds what an agent posts, sends or changes by itself until the user says yes. */
|
|
1672
|
+
export const APPROVAL_POLICY_KEY = 'cs-approval-policy';
|
|
1424
1673
|
const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1425
1674
|
/**
|
|
1426
1675
|
* Same rules as the API's key normalisation: cs-cron-x and cs-x stay, cron-x
|
|
@@ -1521,7 +1770,7 @@ export function resolveExistingCustomSkillKey(input, existingKeys, kind) {
|
|
|
1521
1770
|
return { key: matches[0] };
|
|
1522
1771
|
if (matches.length > 1)
|
|
1523
1772
|
return { error: `Both ${asSkill} and ${asTask} exist. Pass the full key.` };
|
|
1524
|
-
return { error: `This agent has no custom skill or scheduled task named "${input}". Pass the full key (cs-... or cs-cron-...)
|
|
1773
|
+
return { error: `This agent has no custom skill or scheduled task named "${input}". Pass the full key (cs-... or cs-cron-...). get_my_agent without entry lists its keys.` };
|
|
1525
1774
|
}
|
|
1526
1775
|
/** A light shape check; the API validates the expression fully. */
|
|
1527
1776
|
export function looksLikeCron(expression) {
|
|
@@ -1635,6 +1884,7 @@ export function customSkillEntries(row) {
|
|
|
1635
1884
|
source,
|
|
1636
1885
|
userCreated: source === 'user-created' || entry._user_created === true,
|
|
1637
1886
|
requiresCredentials: stringList(entry.requires_credentials),
|
|
1887
|
+
notWritten: key === STARTER_TASK_KEY && entry._edited === false,
|
|
1638
1888
|
};
|
|
1639
1889
|
}).filter(entry => entry.key);
|
|
1640
1890
|
}
|