@usenaive-sdk/cli 0.15.1 → 1.0.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 +43 -402
- package/dist/index.js +51796 -502
- package/package.json +22 -18
- package/dist/client.d.ts +0 -108
- package/dist/client.js +0 -458
- package/dist/client.js.map +0 -1
- package/dist/commands/account-kits.d.ts +0 -2
- package/dist/commands/account-kits.js +0 -71
- package/dist/commands/account-kits.js.map +0 -1
- package/dist/commands/aeo.d.ts +0 -2
- package/dist/commands/aeo.js +0 -451
- package/dist/commands/aeo.js.map +0 -1
- package/dist/commands/agent-profiles.d.ts +0 -11
- package/dist/commands/agent-profiles.js +0 -120
- package/dist/commands/agent-profiles.js.map +0 -1
- package/dist/commands/agents.d.ts +0 -2
- package/dist/commands/agents.js +0 -916
- package/dist/commands/agents.js.map +0 -1
- package/dist/commands/app-data.d.ts +0 -2
- package/dist/commands/app-data.js +0 -445
- package/dist/commands/app-data.js.map +0 -1
- package/dist/commands/approvals.d.ts +0 -2
- package/dist/commands/approvals.js +0 -242
- package/dist/commands/approvals.js.map +0 -1
- package/dist/commands/apps.d.ts +0 -9
- package/dist/commands/apps.js +0 -844
- package/dist/commands/apps.js.map +0 -1
- package/dist/commands/audio.d.ts +0 -9
- package/dist/commands/audio.js +0 -391
- package/dist/commands/audio.js.map +0 -1
- package/dist/commands/auth.d.ts +0 -31
- package/dist/commands/auth.js +0 -428
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/billing.d.ts +0 -2
- package/dist/commands/billing.js +0 -250
- package/dist/commands/billing.js.map +0 -1
- package/dist/commands/brain.d.ts +0 -2
- package/dist/commands/brain.js +0 -1300
- package/dist/commands/brain.js.map +0 -1
- package/dist/commands/browser.d.ts +0 -2
- package/dist/commands/browser.js +0 -378
- package/dist/commands/browser.js.map +0 -1
- package/dist/commands/business.d.ts +0 -2
- package/dist/commands/business.js +0 -566
- package/dist/commands/business.js.map +0 -1
- package/dist/commands/cards.d.ts +0 -2
- package/dist/commands/cards.js +0 -419
- package/dist/commands/cards.js.map +0 -1
- package/dist/commands/ceo.d.ts +0 -2
- package/dist/commands/ceo.js +0 -369
- package/dist/commands/ceo.js.map +0 -1
- package/dist/commands/clone.d.ts +0 -2
- package/dist/commands/clone.js +0 -203
- package/dist/commands/clone.js.map +0 -1
- package/dist/commands/companies.d.ts +0 -10
- package/dist/commands/companies.js +0 -283
- package/dist/commands/companies.js.map +0 -1
- package/dist/commands/company-data.d.ts +0 -2
- package/dist/commands/company-data.js +0 -168
- package/dist/commands/company-data.js.map +0 -1
- package/dist/commands/compute.d.ts +0 -2
- package/dist/commands/compute.js +0 -498
- package/dist/commands/compute.js.map +0 -1
- package/dist/commands/config.d.ts +0 -2
- package/dist/commands/config.js +0 -36
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/connections.d.ts +0 -8
- package/dist/commands/connections.js +0 -137
- package/dist/commands/connections.js.map +0 -1
- package/dist/commands/cron-jobs.d.ts +0 -2
- package/dist/commands/cron-jobs.js +0 -245
- package/dist/commands/cron-jobs.js.map +0 -1
- package/dist/commands/domains.d.ts +0 -2
- package/dist/commands/domains.js +0 -474
- package/dist/commands/domains.js.map +0 -1
- package/dist/commands/ecommerce.d.ts +0 -2
- package/dist/commands/ecommerce.js +0 -277
- package/dist/commands/ecommerce.js.map +0 -1
- package/dist/commands/email.d.ts +0 -2
- package/dist/commands/email.js +0 -336
- package/dist/commands/email.js.map +0 -1
- package/dist/commands/employees.d.ts +0 -2
- package/dist/commands/employees.js +0 -190
- package/dist/commands/employees.js.map +0 -1
- package/dist/commands/env.d.ts +0 -7
- package/dist/commands/env.js +0 -112
- package/dist/commands/env.js.map +0 -1
- package/dist/commands/formation.d.ts +0 -2
- package/dist/commands/formation.js +0 -273
- package/dist/commands/formation.js.map +0 -1
- package/dist/commands/identity.d.ts +0 -2
- package/dist/commands/identity.js +0 -110
- package/dist/commands/identity.js.map +0 -1
- package/dist/commands/images.d.ts +0 -2
- package/dist/commands/images.js +0 -371
- package/dist/commands/images.js.map +0 -1
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/init.js +0 -43
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/jobs.d.ts +0 -2
- package/dist/commands/jobs.js +0 -144
- package/dist/commands/jobs.js.map +0 -1
- package/dist/commands/keys.d.ts +0 -2
- package/dist/commands/keys.js +0 -156
- package/dist/commands/keys.js.map +0 -1
- package/dist/commands/legacy-auth.d.ts +0 -27
- package/dist/commands/legacy-auth.js +0 -64
- package/dist/commands/legacy-auth.js.map +0 -1
- package/dist/commands/llm.d.ts +0 -2
- package/dist/commands/llm.js +0 -125
- package/dist/commands/llm.js.map +0 -1
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -65
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.d.ts +0 -7
- package/dist/commands/logout.js +0 -55
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/logs.d.ts +0 -2
- package/dist/commands/logs.js +0 -42
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/loops.d.ts +0 -2
- package/dist/commands/loops.js +0 -260
- package/dist/commands/loops.js.map +0 -1
- package/dist/commands/media.d.ts +0 -2
- package/dist/commands/media.js +0 -183
- package/dist/commands/media.js.map +0 -1
- package/dist/commands/memory.d.ts +0 -2
- package/dist/commands/memory.js +0 -151
- package/dist/commands/memory.js.map +0 -1
- package/dist/commands/mobile.d.ts +0 -4
- package/dist/commands/mobile.js +0 -382
- package/dist/commands/mobile.js.map +0 -1
- package/dist/commands/module.d.ts +0 -8
- package/dist/commands/module.js +0 -48
- package/dist/commands/module.js.map +0 -1
- package/dist/commands/objectives.d.ts +0 -2
- package/dist/commands/objectives.js +0 -243
- package/dist/commands/objectives.js.map +0 -1
- package/dist/commands/payments.d.ts +0 -2
- package/dist/commands/payments.js +0 -131
- package/dist/commands/payments.js.map +0 -1
- package/dist/commands/people.d.ts +0 -2
- package/dist/commands/people.js +0 -197
- package/dist/commands/people.js.map +0 -1
- package/dist/commands/phone.d.ts +0 -2
- package/dist/commands/phone.js +0 -289
- package/dist/commands/phone.js.map +0 -1
- package/dist/commands/projects.d.ts +0 -13
- package/dist/commands/projects.js +0 -220
- package/dist/commands/projects.js.map +0 -1
- package/dist/commands/queue.d.ts +0 -2
- package/dist/commands/queue.js +0 -121
- package/dist/commands/queue.js.map +0 -1
- package/dist/commands/register.d.ts +0 -2
- package/dist/commands/register.js +0 -88
- package/dist/commands/register.js.map +0 -1
- package/dist/commands/report.d.ts +0 -17
- package/dist/commands/report.js +0 -109
- package/dist/commands/report.js.map +0 -1
- package/dist/commands/sandbox.d.ts +0 -2
- package/dist/commands/sandbox.js +0 -558
- package/dist/commands/sandbox.js.map +0 -1
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/search.js +0 -223
- package/dist/commands/search.js.map +0 -1
- package/dist/commands/seo.d.ts +0 -2
- package/dist/commands/seo.js +0 -528
- package/dist/commands/seo.js.map +0 -1
- package/dist/commands/sessions.d.ts +0 -6
- package/dist/commands/sessions.js +0 -109
- package/dist/commands/sessions.js.map +0 -1
- package/dist/commands/social-data.d.ts +0 -2
- package/dist/commands/social-data.js +0 -245
- package/dist/commands/social-data.js.map +0 -1
- package/dist/commands/social.d.ts +0 -2
- package/dist/commands/social.js +0 -407
- package/dist/commands/social.js.map +0 -1
- package/dist/commands/status.d.ts +0 -2
- package/dist/commands/status.js +0 -79
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/support.d.ts +0 -2
- package/dist/commands/support.js +0 -134
- package/dist/commands/support.js.map +0 -1
- package/dist/commands/tasks.d.ts +0 -2
- package/dist/commands/tasks.js +0 -409
- package/dist/commands/tasks.js.map +0 -1
- package/dist/commands/teams.d.ts +0 -69
- package/dist/commands/teams.js +0 -503
- package/dist/commands/teams.js.map +0 -1
- package/dist/commands/trading.d.ts +0 -2
- package/dist/commands/trading.js +0 -333
- package/dist/commands/trading.js.map +0 -1
- package/dist/commands/up.d.ts +0 -551
- package/dist/commands/up.js +0 -1149
- package/dist/commands/up.js.map +0 -1
- package/dist/commands/usage.d.ts +0 -2
- package/dist/commands/usage.js +0 -73
- package/dist/commands/usage.js.map +0 -1
- package/dist/commands/use.d.ts +0 -12
- package/dist/commands/use.js +0 -96
- package/dist/commands/use.js.map +0 -1
- package/dist/commands/users.d.ts +0 -10
- package/dist/commands/users.js +0 -82
- package/dist/commands/users.js.map +0 -1
- package/dist/commands/vault.d.ts +0 -2
- package/dist/commands/vault.js +0 -101
- package/dist/commands/vault.js.map +0 -1
- package/dist/commands/verification.d.ts +0 -2
- package/dist/commands/verification.js +0 -152
- package/dist/commands/verification.js.map +0 -1
- package/dist/commands/verify-email.d.ts +0 -19
- package/dist/commands/verify-email.js +0 -130
- package/dist/commands/verify-email.js.map +0 -1
- package/dist/commands/video.d.ts +0 -2
- package/dist/commands/video.js +0 -417
- package/dist/commands/video.js.map +0 -1
- package/dist/commands/voice.d.ts +0 -2
- package/dist/commands/voice.js +0 -516
- package/dist/commands/voice.js.map +0 -1
- package/dist/commands/wallet.d.ts +0 -2
- package/dist/commands/wallet.js +0 -244
- package/dist/commands/wallet.js.map +0 -1
- package/dist/commands/webhooks.d.ts +0 -13
- package/dist/commands/webhooks.js +0 -119
- package/dist/commands/webhooks.js.map +0 -1
- package/dist/commands/whoami.d.ts +0 -2
- package/dist/commands/whoami.js +0 -53
- package/dist/commands/whoami.js.map +0 -1
- package/dist/config.d.ts +0 -42
- package/dist/config.js +0 -83
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.js.map +0 -1
- package/dist/lib/brain-select.d.ts +0 -193
- package/dist/lib/brain-select.js +0 -403
- package/dist/lib/brain-select.js.map +0 -1
- package/dist/lib/cli-validation.d.ts +0 -12
- package/dist/lib/cli-validation.js +0 -46
- package/dist/lib/cli-validation.js.map +0 -1
- package/dist/lib/config-errors.d.ts +0 -4
- package/dist/lib/config-errors.js +0 -13
- package/dist/lib/config-errors.js.map +0 -1
- package/dist/lib/deprecation.d.ts +0 -158
- package/dist/lib/deprecation.js +0 -140
- package/dist/lib/deprecation.js.map +0 -1
- package/dist/lib/duration.d.ts +0 -8
- package/dist/lib/duration.js +0 -11
- package/dist/lib/duration.js.map +0 -1
- package/dist/lib/http-response.d.ts +0 -11
- package/dist/lib/http-response.js +0 -96
- package/dist/lib/http-response.js.map +0 -1
- package/dist/lib/llm-paywall.d.ts +0 -82
- package/dist/lib/llm-paywall.js +0 -231
- package/dist/lib/llm-paywall.js.map +0 -1
- package/dist/lib/pricing.d.ts +0 -132
- package/dist/lib/pricing.js +0 -148
- package/dist/lib/pricing.js.map +0 -1
- package/dist/lib/starter-config.d.ts +0 -13
- package/dist/lib/starter-config.js +0 -71
- package/dist/lib/starter-config.js.map +0 -1
- package/dist/lib/stream-frames.d.ts +0 -114
- package/dist/lib/stream-frames.js +0 -169
- package/dist/lib/stream-frames.js.map +0 -1
- package/dist/lib/teams-catalog.d.ts +0 -141
- package/dist/lib/teams-catalog.js +0 -841
- package/dist/lib/teams-catalog.js.map +0 -1
- package/dist/lib/unknown-command.d.ts +0 -71
- package/dist/lib/unknown-command.js +0 -132
- package/dist/lib/unknown-command.js.map +0 -1
- package/dist/lib/version-check.d.ts +0 -26
- package/dist/lib/version-check.js +0 -21
- package/dist/lib/version-check.js.map +0 -1
- package/dist/output.d.ts +0 -246
- package/dist/output.js +0 -539
- package/dist/output.js.map +0 -1
- package/dist/prompt.d.ts +0 -17
- package/dist/prompt.js +0 -70
- package/dist/prompt.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.js +0 -14
- package/dist/version.js.map +0 -1
package/dist/commands/agents.js
DELETED
|
@@ -1,916 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import { apiRequest, handleApiError, scopePathToUser, activeUserId, activeProjectId } from "../client.js";
|
|
3
|
-
import { loadConfig, getApiKey } from "../config.js";
|
|
4
|
-
import { agentOutput, agentOutputExit, agentError, humanMode } from "../output.js";
|
|
5
|
-
import { parseSse, highestSeq, redactFrame } from "../lib/stream-frames.js";
|
|
6
|
-
/** micro-USD → a dollar string. The only arithmetic in this file, and it is on
|
|
7
|
-
* a CAP (a configuration integer we set), never on the money ledger — every
|
|
8
|
-
* spent figure stays the decimal string the API sent. */
|
|
9
|
-
function usd(microUsd) {
|
|
10
|
-
return microUsd === undefined ? "—" : `$${(microUsd / 1_000_000).toFixed(2)}`;
|
|
11
|
-
}
|
|
12
|
-
function short(id) {
|
|
13
|
-
return id ? id.slice(0, 8) : "—";
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Every `next_steps` command is DERIVED from the response the API just
|
|
17
|
-
* returned, so the id in a suggestion is the id the API echoed rather than an
|
|
18
|
-
* `<id>` placeholder, and a hint restates a field it actually sent. Neither can
|
|
19
|
-
* drift from what the platform did.
|
|
20
|
-
*/
|
|
21
|
-
function agentSteps(a, extra = []) {
|
|
22
|
-
const id = a?.id;
|
|
23
|
-
if (!id)
|
|
24
|
-
return extra;
|
|
25
|
-
return [
|
|
26
|
-
{ command: `naive agents run ${id} "…"`, description: "Send work and stream it" },
|
|
27
|
-
{ command: `naive agents tasks ${id}`, description: "The task board" },
|
|
28
|
-
{ command: `naive agents spend ${id}`, description: "Spend against the cap" },
|
|
29
|
-
...extra,
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
|
-
/** Hints echoed off what the API returned — restated, never asserted. */
|
|
33
|
-
function agentHints(a) {
|
|
34
|
-
if (!a)
|
|
35
|
-
return [];
|
|
36
|
-
const out = [];
|
|
37
|
-
if (a.status)
|
|
38
|
-
out.push(`Status: ${a.status}${a.blocked_reason ? ` (${a.blocked_reason})` : ""}`);
|
|
39
|
-
// 🔴 The idle-cost claim, rendered. An agent showing nothing is the NORMAL
|
|
40
|
-
// state of a long-horizon agent, so "nothing scheduled" has to be legible as
|
|
41
|
-
// a fact rather than as an absence.
|
|
42
|
-
if (a.status === "idle") {
|
|
43
|
-
out.push(a.next_wake_at ? `Next wake: ${a.next_wake_at}` : "No alarm armed — this agent costs storage only.");
|
|
44
|
-
}
|
|
45
|
-
if (a.attention)
|
|
46
|
-
out.push("Needs attention.");
|
|
47
|
-
if (a.budget?.cap_micro_usd !== undefined) {
|
|
48
|
-
out.push(`Cap: ${usd(a.budget.cap_micro_usd)} per ${a.budget.period ?? "period"} (per task ${usd(a.budget.max_task_micro_usd)})`);
|
|
49
|
-
}
|
|
50
|
-
if (a.harness_sha256 === null)
|
|
51
|
-
out.push("harness_sha256 is null — this agent has not run yet.");
|
|
52
|
-
return out;
|
|
53
|
-
}
|
|
54
|
-
function agentOf(data) {
|
|
55
|
-
const d = (data ?? {});
|
|
56
|
-
return d.agent;
|
|
57
|
-
}
|
|
58
|
-
/** `--budget-usd 50` is what an operator types; micro-USD is what the API takes. */
|
|
59
|
-
function microFromUsd(v, field) {
|
|
60
|
-
if (v === undefined)
|
|
61
|
-
return undefined;
|
|
62
|
-
const n = Number(v);
|
|
63
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
64
|
-
agentError({
|
|
65
|
-
action: "agents.invalid",
|
|
66
|
-
error: { code: "invalid_input", message: `${field} must be a positive number of US dollars.` },
|
|
67
|
-
recovery_steps: [{ command: "naive agents create --help", description: "Show the flags" }],
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
return Math.round(n * 1_000_000);
|
|
71
|
-
}
|
|
72
|
-
function body(opts) {
|
|
73
|
-
const out = {};
|
|
74
|
-
if (opts.name)
|
|
75
|
-
out.name = opts.name;
|
|
76
|
-
if (opts.instructions)
|
|
77
|
-
out.instructions = opts.instructions;
|
|
78
|
-
if (opts.model)
|
|
79
|
-
out.model = opts.model;
|
|
80
|
-
if (opts.window)
|
|
81
|
-
out.completion_window = opts.window;
|
|
82
|
-
if (opts.delegateModel)
|
|
83
|
-
out.delegate_models = opts.delegateModel;
|
|
84
|
-
if (opts.secret)
|
|
85
|
-
out.secrets = opts.secret;
|
|
86
|
-
if (opts.connection)
|
|
87
|
-
out.connections = opts.connection;
|
|
88
|
-
if (opts.paused !== undefined)
|
|
89
|
-
out.paused = opts.paused;
|
|
90
|
-
const cap = microFromUsd(opts.budgetUsd, "--budget-usd");
|
|
91
|
-
const perTask = microFromUsd(opts.maxTaskUsd, "--max-task-usd");
|
|
92
|
-
if (cap !== undefined || perTask !== undefined) {
|
|
93
|
-
out.budget = {
|
|
94
|
-
cap_micro_usd: cap,
|
|
95
|
-
// Defaulting the per-task ceiling to a tenth of the cap is a choice, and
|
|
96
|
-
// it is the safe direction: an operator who never thinks about it still
|
|
97
|
-
// cannot lose the whole budget to one runaway task.
|
|
98
|
-
max_task_micro_usd: perTask ?? (cap !== undefined ? Math.max(1, Math.round(cap / 10)) : undefined),
|
|
99
|
-
period: opts.budgetPeriod ?? "month",
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const tools = {};
|
|
103
|
-
for (const k of ["webSearch", "browser", "storage", "delegation", "connections"]) {
|
|
104
|
-
if (opts[k] !== undefined)
|
|
105
|
-
tools[k === "webSearch" ? "web_search" : k] = opts[k];
|
|
106
|
-
}
|
|
107
|
-
if (opts.sandbox !== undefined)
|
|
108
|
-
tools.sandbox = opts.sandbox;
|
|
109
|
-
if (Object.keys(tools).length)
|
|
110
|
-
out.tools = tools;
|
|
111
|
-
if (opts.cron) {
|
|
112
|
-
out.schedule = opts.cron.map((spec) => {
|
|
113
|
-
// `--cron "0 3 * * *=Triage last night's issues"` — the work is required,
|
|
114
|
-
// because a schedule with no work is a bug that fires forever.
|
|
115
|
-
const eq = spec.indexOf("=");
|
|
116
|
-
if (eq === -1) {
|
|
117
|
-
agentError({
|
|
118
|
-
action: "agents.invalid",
|
|
119
|
-
error: {
|
|
120
|
-
code: "invalid_input",
|
|
121
|
-
message: `--cron must be "<5-field cron>=<the work to send>", got "${spec}".`,
|
|
122
|
-
},
|
|
123
|
-
recovery_steps: [{ command: `naive agents create --cron "0 3 * * *=Triage last night's issues"`, description: "The shape" }],
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
return { cron: spec.slice(0, eq).trim(), text: spec.slice(eq + 1), tz: opts.tz ?? "UTC" };
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
const limits = {};
|
|
130
|
-
if (opts.wakeMs)
|
|
131
|
-
limits.sliceWallMs = Number(opts.wakeMs);
|
|
132
|
-
if (opts.maxTurns)
|
|
133
|
-
limits.maxTurnsPerTask = Number(opts.maxTurns);
|
|
134
|
-
if (opts.maxToolCalls)
|
|
135
|
-
limits.maxToolCallsPerTurn = Number(opts.maxToolCalls);
|
|
136
|
-
if (Object.keys(limits).length)
|
|
137
|
-
out.limits = limits;
|
|
138
|
-
return out;
|
|
139
|
-
}
|
|
140
|
-
export const agentsCmd = new Command("agents")
|
|
141
|
-
.description("Agents (Vetta) — long-horizon agents that run on a schedule, spend a budget, and deliver")
|
|
142
|
-
.addHelpText("after", `
|
|
143
|
-
Subcommands:
|
|
144
|
-
naive agents status Configured, reachable, roster, harness hash
|
|
145
|
-
naive agents create --name n --model m … Create an agent (a budget is REQUIRED)
|
|
146
|
-
naive agents list Roster with status, next wake and cap
|
|
147
|
-
naive agents show <id> One agent, materialised config included
|
|
148
|
-
naive agents update <id> [flags] Any create flag, plus --paused / --no-paused
|
|
149
|
-
naive agents rm <id> Delete (confirm required; --yes in a pipe)
|
|
150
|
-
naive agents run <id> <text...> Send work and stream it
|
|
151
|
-
naive agents job <id> <text...> Send work and return the task
|
|
152
|
-
naive agents tasks <id> The task board — which IS the job queue
|
|
153
|
-
naive agents task <id> <task-id> One unit of work
|
|
154
|
-
naive agents cancel <id> <task-id> Cooperative cancel (never aborts mid-tool)
|
|
155
|
-
naive agents reply <id> <task-id> <text...> Answer a task parked on needs_input
|
|
156
|
-
naive agents logs <id> [--follow] The event trace; --follow tails it live
|
|
157
|
-
naive agents spend <id> Spend against the cap, by component or task
|
|
158
|
-
naive agents deliverables <id> Artifact manifests; --id fetches one
|
|
159
|
-
|
|
160
|
-
The model:
|
|
161
|
-
A turn is bounded; a task is not. The agent wakes on an alarm, does one turn,
|
|
162
|
-
commits, and sleeps. While it sleeps it costs storage and nothing else — which
|
|
163
|
-
is what "idle · no alarm armed" means in \`list\`.
|
|
164
|
-
|
|
165
|
-
Money:
|
|
166
|
-
A budget is REQUIRED at create. An agent with no cap is not creatable, because
|
|
167
|
-
a null cap means no cap AND no per-agent spend record at all. --budget-usd is
|
|
168
|
-
the cap; --max-task-usd is the per-task ceiling and defaults to a tenth of it.
|
|
169
|
-
|
|
170
|
-
Exit codes:
|
|
171
|
-
0 ok · 1 error · 3 success, but the terminal state is not success.
|
|
172
|
-
|
|
173
|
-
Examples:
|
|
174
|
-
$ naive agents create --name nightly-triage --model anthropic/claude-sonnet-4 \\
|
|
175
|
-
--budget-usd 50 --cron "0 3 * * *=Triage last night's Sentry issues"
|
|
176
|
-
$ naive agents run <id> "What changed in main today?"
|
|
177
|
-
$ naive agents logs <id> --follow --human
|
|
178
|
-
$ naive agents spend <id> --human
|
|
179
|
-
`);
|
|
180
|
-
/* ── 1. status ───────────────────────────────────────────────────────────── */
|
|
181
|
-
agentsCmd
|
|
182
|
-
.command("status")
|
|
183
|
-
.description("Primitive status: configured, reachable, roster, deployed harness hash")
|
|
184
|
-
.action(async () => {
|
|
185
|
-
const resp = await apiRequest("GET", "/v1/agents/status");
|
|
186
|
-
handleApiError("agents.status", resp);
|
|
187
|
-
const s = (resp.data ?? {});
|
|
188
|
-
agentOutput({
|
|
189
|
-
action: "agents.status",
|
|
190
|
-
result: resp.data,
|
|
191
|
-
next_steps: s.configured
|
|
192
|
-
? [{ command: "naive agents list", description: "The roster" }]
|
|
193
|
-
: [{ command: "naive agents create --help", description: "What creating one needs" }],
|
|
194
|
-
hints: [
|
|
195
|
-
`Configured: ${s.configured ? "yes" : "no"} · reachable: ${s.reachable ? "yes" : "no"}`,
|
|
196
|
-
`Agents: ${s.agents ?? 0}`,
|
|
197
|
-
// A hash the deployment reported, not one this CLI computed. A CLI that
|
|
198
|
-
// computed it would be attesting to a build it has never seen.
|
|
199
|
-
s.harness_sha256 ? `Harness: ${s.harness_sha256}` : "Harness hash unavailable — the runtime did not report one.",
|
|
200
|
-
...(s.error ? [`Runtime error: ${s.error}`] : []),
|
|
201
|
-
],
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
/* ── 2. create ───────────────────────────────────────────────────────────── */
|
|
205
|
-
agentsCmd
|
|
206
|
-
.command("create")
|
|
207
|
-
.description("Create an agent. A pinned model and a budget are required.")
|
|
208
|
-
.requiredOption("--name <name>", "1–64 chars of [a-zA-Z0-9 _-]")
|
|
209
|
-
.requiredOption("--model <slug>", "A PINNED slug, e.g. anthropic/claude-sonnet-4. Never a family or an alias.")
|
|
210
|
-
.requiredOption("--budget-usd <usd>", "Hard spend cap in US dollars. Required — an uncapped agent is not creatable.")
|
|
211
|
-
.option("--max-task-usd <usd>", "Per-task ceiling. Defaults to a tenth of the cap.")
|
|
212
|
-
.option("--budget-period <p>", "total | day | week | month | year", "month")
|
|
213
|
-
.option("--instructions <text>", "What this agent is for")
|
|
214
|
-
// THREE, not two. The flag always FORWARDED `standard` — it is a free string
|
|
215
|
-
// and the CLI never validated it — so the capability shipped and only
|
|
216
|
-
// `--help` was wrong. asap is dearest and fastest, flex cheapest and slowest,
|
|
217
|
-
// standard the middle that makes it a curve. The server does the refusing:
|
|
218
|
-
// a non-`asap` window on a model that does not declare one comes back
|
|
219
|
-
// `window_unavailable` rather than being quietly served as `asap`.
|
|
220
|
-
.option("--window <w>", "asap (default) | standard | flex — the SCHEDULING TIER, never the model. Tasks inherit it.")
|
|
221
|
-
.option("--delegate-model <slug...>", "Models a sub-agent may run. Empty = delegation off.")
|
|
222
|
-
.option("--cron <spec...>", 'A schedule: "<5-field cron>=<the work to send>"')
|
|
223
|
-
.option("--tz <iana>", "Timezone for --cron", "UTC")
|
|
224
|
-
.option("--web-search", "Allow web search")
|
|
225
|
-
.option("--browser", "Allow the browser")
|
|
226
|
-
.option("--sandbox <mode>", "none | auto | <workspace id>")
|
|
227
|
-
.option("--storage", "Allow writing deliverables to storage")
|
|
228
|
-
.option("--secret <name...>", "Vault entry NAMES to expose. NEVER values.")
|
|
229
|
-
.option("--connection <id...>", "Connection ids to attach")
|
|
230
|
-
.option("--wake-ms <ms>", "Wall budget per wake (30000–840000)")
|
|
231
|
-
.option("--max-turns <n>", "Turns per task")
|
|
232
|
-
.option("--max-tool-calls <n>", "Tool calls per turn (1–32)")
|
|
233
|
-
.action(async (opts) => {
|
|
234
|
-
const resp = await apiRequest("POST", "/v1/agents", body(opts));
|
|
235
|
-
handleApiError("agents.create", resp);
|
|
236
|
-
const a = agentOf(resp.data);
|
|
237
|
-
agentOutput({
|
|
238
|
-
action: "agents.create",
|
|
239
|
-
result: resp.data,
|
|
240
|
-
next_steps: agentSteps(a),
|
|
241
|
-
hints: [
|
|
242
|
-
...agentHints(a),
|
|
243
|
-
// Said once, at the only moment it changes what someone does next.
|
|
244
|
-
"Creating an agent queues no work. Send some with `naive agents job`.",
|
|
245
|
-
],
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
/* ── 3. list ─────────────────────────────────────────────────────────────── */
|
|
249
|
-
agentsCmd
|
|
250
|
-
.command("list")
|
|
251
|
-
.description("The roster: status, next wake, cap")
|
|
252
|
-
.option("--status <s>", "idle | running | blocked | braked | stopped")
|
|
253
|
-
.option("--limit <n>", "Page size (default 50, max 200)")
|
|
254
|
-
.option("--cursor <c>", "Keyset cursor from a previous page")
|
|
255
|
-
.action(async (opts) => {
|
|
256
|
-
const q = new URLSearchParams();
|
|
257
|
-
for (const k of ["status", "limit", "cursor"])
|
|
258
|
-
if (opts[k])
|
|
259
|
-
q.set(k, opts[k]);
|
|
260
|
-
const qs = q.toString();
|
|
261
|
-
const resp = await apiRequest("GET", `/v1/agents${qs ? `?${qs}` : ""}`);
|
|
262
|
-
handleApiError("agents.list", resp);
|
|
263
|
-
const items = (resp.data ?? {}).items ?? [];
|
|
264
|
-
if (humanMode()) {
|
|
265
|
-
if (!items.length) {
|
|
266
|
-
process.stdout.write("no agents\n");
|
|
267
|
-
}
|
|
268
|
-
for (const a of items) {
|
|
269
|
-
const wake = a.status === "idle" && !a.next_wake_at ? "no alarm armed" : (a.next_wake_at ?? "—");
|
|
270
|
-
process.stdout.write(`${short(a.id).padEnd(9)}${(a.name ?? "").padEnd(24)}${(a.status ?? "").padEnd(9)}` +
|
|
271
|
-
`${(a.model ?? "").padEnd(34)}cap ${usd(a.budget?.cap_micro_usd).padEnd(9)}${wake}\n`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
agentOutput({
|
|
275
|
-
action: "agents.list",
|
|
276
|
-
result: resp.data,
|
|
277
|
-
next_steps: items[0]?.id ? [{ command: `naive agents show ${items[0].id}`, description: "One agent" }] : [],
|
|
278
|
-
hints: [`${items.length} agent${items.length === 1 ? "" : "s"}`],
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
/* ── 4. show ─────────────────────────────────────────────────────────────── */
|
|
282
|
-
agentsCmd
|
|
283
|
-
.command("show <id>")
|
|
284
|
-
.description("One agent, with its MATERIALISED limits (the numbers it will actually run with)")
|
|
285
|
-
.action(async (id) => {
|
|
286
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}`);
|
|
287
|
-
handleApiError("agents.show", resp);
|
|
288
|
-
const a = agentOf(resp.data);
|
|
289
|
-
agentOutput({ action: "agents.show", result: resp.data, next_steps: agentSteps(a), hints: agentHints(a) });
|
|
290
|
-
});
|
|
291
|
-
/* ── 5. update ───────────────────────────────────────────────────────────── */
|
|
292
|
-
agentsCmd
|
|
293
|
-
.command("update <id>")
|
|
294
|
-
.description("Change any create field. `limits` and `tools` merge field by field.")
|
|
295
|
-
.option("--name <name>")
|
|
296
|
-
.option("--instructions <text>")
|
|
297
|
-
.option("--model <slug>")
|
|
298
|
-
.option("--window <w>", "asap | standard | flex — the agent's tier; its tasks inherit it")
|
|
299
|
-
.option("--budget-usd <usd>", "Raise or lower the cap. Raising it un-brakes a braked agent.")
|
|
300
|
-
.option("--max-task-usd <usd>")
|
|
301
|
-
.option("--budget-period <p>")
|
|
302
|
-
.option("--delegate-model <slug...>")
|
|
303
|
-
.option("--secret <name...>")
|
|
304
|
-
.option("--connection <id...>")
|
|
305
|
-
.option("--cron <spec...>")
|
|
306
|
-
.option("--tz <iana>")
|
|
307
|
-
.option("--wake-ms <ms>")
|
|
308
|
-
.option("--max-turns <n>")
|
|
309
|
-
.option("--max-tool-calls <n>")
|
|
310
|
-
/* A HOLD ON EXECUTION, and the help text says both halves because the
|
|
311
|
-
* missing half is what an operator gets wrong. It arms no wake and fires no
|
|
312
|
-
* cron; it does NOT refuse work, and it does not abort a running slice —
|
|
313
|
-
* `naive agents cancel` is that, cooperatively. */
|
|
314
|
-
.option("--paused", "Hold execution: arm no wake and fire no cron. Work sent meanwhile is queued, not refused")
|
|
315
|
-
.option("--no-paused", "Resume, and run whatever was held")
|
|
316
|
-
.action(async (id, opts) => {
|
|
317
|
-
const resp = await apiRequest("PATCH", `/v1/agents/${id}`, body(opts));
|
|
318
|
-
handleApiError("agents.update", resp);
|
|
319
|
-
const a = agentOf(resp.data);
|
|
320
|
-
agentOutput({ action: "agents.update", result: resp.data, next_steps: agentSteps(a), hints: agentHints(a) });
|
|
321
|
-
});
|
|
322
|
-
/* ── 6. rm ───────────────────────────────────────────────────────────────── */
|
|
323
|
-
agentsCmd
|
|
324
|
-
.command("rm <id>")
|
|
325
|
-
.description("Delete an agent: cancel queued work, stop the alarm chain. THE SPEND LEDGER IS KEPT.")
|
|
326
|
-
.option("--yes", "Skip the confirmation (required in a pipe)")
|
|
327
|
-
.action(async (id, opts) => {
|
|
328
|
-
if (!opts.yes) {
|
|
329
|
-
// Refuse rather than prompt. A prompt in a pipe either hangs or is
|
|
330
|
-
// answered by whatever byte arrives next, and this verb stops an agent
|
|
331
|
-
// mid-task.
|
|
332
|
-
agentError({
|
|
333
|
-
action: "agents.rm",
|
|
334
|
-
error: {
|
|
335
|
-
code: "invalid_input",
|
|
336
|
-
message: "Deleting an agent cancels its queued work and stops its alarm chain. Pass --yes to confirm.",
|
|
337
|
-
},
|
|
338
|
-
recovery_steps: [
|
|
339
|
-
{ command: `naive agents tasks ${id}`, description: "See what is queued first" },
|
|
340
|
-
{ command: `naive agents rm ${id} --yes`, description: "Delete it" },
|
|
341
|
-
],
|
|
342
|
-
});
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
const resp = await apiRequest("DELETE", `/v1/agents/${id}`);
|
|
346
|
-
handleApiError("agents.rm", resp);
|
|
347
|
-
agentOutput({
|
|
348
|
-
action: "agents.rm",
|
|
349
|
-
result: resp.data,
|
|
350
|
-
next_steps: [{ command: "naive agents list", description: "The remaining roster" }],
|
|
351
|
-
hints: ["The spend ledger is kept — deleting an agent does not change what it cost."],
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
/* ── 7–8. send work ──────────────────────────────────────────────────────── */
|
|
355
|
-
function taskFlags(cmd) {
|
|
356
|
-
return cmd
|
|
357
|
-
.option("--mode <m>", "act (default, every granted tool) | ask (read-only)")
|
|
358
|
-
// Shared by `job` and `run`. `taskBody` OMITS the field when the flag is
|
|
359
|
-
// absent, and an omitted window is what the runtime resolves against the
|
|
360
|
-
// agent's `completion_window` — so leaving the flag off INHERITS the agent.
|
|
361
|
-
// It stopped meaning `asap` when the edge stopped resolving the default and
|
|
362
|
-
// started forwarding `undefined`, leaving one component holding it.
|
|
363
|
-
.option("--window <w>", "asap | standard | flex — this task only; omitted inherits the agent")
|
|
364
|
-
.option("--priority <p>", "normal | low")
|
|
365
|
-
.option("--not-before <iso>", "Do not start before this ISO-8601 instant")
|
|
366
|
-
.option("--idempotency-key <k>", "A PERMANENT business key: the same key returns the same task, forever");
|
|
367
|
-
}
|
|
368
|
-
function taskBody(text, opts) {
|
|
369
|
-
return {
|
|
370
|
-
text: text.join(" "),
|
|
371
|
-
mode: opts.mode,
|
|
372
|
-
window: opts.window,
|
|
373
|
-
priority: opts.priority,
|
|
374
|
-
not_before: opts.notBefore,
|
|
375
|
-
idempotency_key: opts.idempotencyKey,
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
taskFlags(agentsCmd.command("job <id> <text...>").description("Send work and return the task record"))
|
|
379
|
-
.action(async (id, text, opts) => {
|
|
380
|
-
const resp = await apiRequest("POST", `/v1/agents/${id}/tasks`, taskBody(text, opts));
|
|
381
|
-
handleApiError("agents.job", resp);
|
|
382
|
-
const t = (resp.data ?? {}).task;
|
|
383
|
-
agentOutput({
|
|
384
|
-
action: "agents.job",
|
|
385
|
-
result: resp.data,
|
|
386
|
-
next_steps: t?.id
|
|
387
|
-
? [
|
|
388
|
-
{ command: `naive agents logs ${id} --task ${t.id} --follow`, description: "Watch it" },
|
|
389
|
-
{ command: `naive agents task ${id} ${t.id}`, description: "Check on it later" },
|
|
390
|
-
]
|
|
391
|
-
: [],
|
|
392
|
-
// 202 is a fact worth restating: the row is committed and the alarm is
|
|
393
|
-
// armed before this returned. Nothing is sitting in a client-side queue.
|
|
394
|
-
hints: [`Status: ${t?.status ?? "queued"} — durably queued, alarm armed.`],
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
taskFlags(agentsCmd.command("run <id> <text...>").description("Send work and stream it (NDJSON, or --human)"))
|
|
398
|
-
.action(async (id, text, opts) => {
|
|
399
|
-
// 🔴 `flex` IS NOT REFUSED HERE, and the deletion is the point.
|
|
400
|
-
//
|
|
401
|
-
// The refusal was written when `flex` meant submit-then-poll, which has no
|
|
402
|
-
// tokens to stream. Every window now produces a real token stream, so
|
|
403
|
-
// `POST /:id/tasks` accepts and streams all three.
|
|
404
|
-
//
|
|
405
|
-
// The SDK dropped its identical client-side throw for the reason that
|
|
406
|
-
// applies twice as hard to a CLI: a client-side refusal is the worst place
|
|
407
|
-
// for a rule that has stopped being true, because a deploy cannot relax it
|
|
408
|
-
// — only a new install can. This copy outlived the rule by a release, and
|
|
409
|
-
// `naive agents run --window flex` refused a request the route serves.
|
|
410
|
-
//
|
|
411
|
-
// A window the platform cannot serve is still refused — server-side, at the
|
|
412
|
-
// gate, as `window_unavailable`, before a provider is chosen and before
|
|
413
|
-
// anything is spent. That is the right place for it.
|
|
414
|
-
await streamEvents("agents.run", `/v1/agents/${id}/tasks`, "POST", taskBody(text, opts), { follow: false });
|
|
415
|
-
});
|
|
416
|
-
/* ── 9–12. the board ─────────────────────────────────────────────────────── */
|
|
417
|
-
agentsCmd
|
|
418
|
-
.command("tasks <id>")
|
|
419
|
-
.description("The task board — which IS the job queue. One noun, one table.")
|
|
420
|
-
.option("--status <s>", "queued | running | waiting | done | failed | cancelled | braked")
|
|
421
|
-
.option("--source <s>", "api | schedule | webhook | agent | self")
|
|
422
|
-
.option("--since <t>", "ISO-8601")
|
|
423
|
-
.option("--limit <n>")
|
|
424
|
-
.option("--cursor <c>")
|
|
425
|
-
.action(async (id, opts) => {
|
|
426
|
-
const q = new URLSearchParams();
|
|
427
|
-
for (const k of ["status", "source", "since", "limit", "cursor"])
|
|
428
|
-
if (opts[k])
|
|
429
|
-
q.set(k, opts[k]);
|
|
430
|
-
const qs = q.toString();
|
|
431
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/tasks${qs ? `?${qs}` : ""}`);
|
|
432
|
-
handleApiError("agents.tasks", resp);
|
|
433
|
-
const items = (resp.data ?? {}).items ?? [];
|
|
434
|
-
if (humanMode()) {
|
|
435
|
-
for (const t of items) {
|
|
436
|
-
process.stdout.write(`${short(t.id).padEnd(9)}${String(t.status ?? "").padEnd(11)}` +
|
|
437
|
-
`${String(t.window ?? "").padEnd(6)}t${String(t.turns ?? 0).padEnd(5)}` +
|
|
438
|
-
`${String(t.title ?? t.text ?? "").slice(0, 60)}\n`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
agentOutput({
|
|
442
|
-
action: "agents.tasks",
|
|
443
|
-
result: resp.data,
|
|
444
|
-
next_steps: items[0]?.id ? [{ command: `naive agents task ${id} ${items[0].id}`, description: "One task" }] : [],
|
|
445
|
-
hints: [`${items.length} task${items.length === 1 ? "" : "s"}`],
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
agentsCmd
|
|
449
|
-
.command("task <id> <task-id>")
|
|
450
|
-
.description("One unit of work. Exits 3 when its terminal state is not success.")
|
|
451
|
-
.action(async (id, taskId) => {
|
|
452
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/tasks/${taskId}`);
|
|
453
|
-
handleApiError("agents.task", resp);
|
|
454
|
-
const t = (resp.data ?? {}).task ?? {};
|
|
455
|
-
const status = String(t.status ?? "");
|
|
456
|
-
agentOutputExit({
|
|
457
|
-
action: "agents.task",
|
|
458
|
-
result: resp.data,
|
|
459
|
-
next_steps: [
|
|
460
|
-
{ command: `naive agents logs ${id} --task ${taskId}`, description: "The event trace" },
|
|
461
|
-
{ command: `naive agents spend ${id} --task ${taskId}`, description: "The authoritative cost" },
|
|
462
|
-
],
|
|
463
|
-
hints: [
|
|
464
|
-
// 🔴 `spent_micro_usd` is the runtime's own synchronous counter, which
|
|
465
|
-
// the pre-call budget gate needs. `spend --task` prices from the ledger.
|
|
466
|
-
// The two are never printed in one column, and the `~` says which is
|
|
467
|
-
// which without the reader having to know.
|
|
468
|
-
`Spent (running counter): ~${usd(t.spent_micro_usd)} — authoritative: naive agents spend ${id} --task ${taskId}`,
|
|
469
|
-
...(t.cancel_requested_at ? [`Cancelling since ${t.cancel_requested_at} — cancel never aborts mid-tool.`] : []),
|
|
470
|
-
...(t.blocked_reason ? [`Blocked: ${t.blocked_reason}`] : []),
|
|
471
|
-
],
|
|
472
|
-
// Exit 3, not 1: the command did what it said, and the WORK did not
|
|
473
|
-
// succeed. A CI script reading 0 as "the work is done" would otherwise
|
|
474
|
-
// count a failed task as a pass.
|
|
475
|
-
exitCode: ["failed", "cancelled", "braked"].includes(status) ? 3 : 0,
|
|
476
|
-
});
|
|
477
|
-
});
|
|
478
|
-
agentsCmd
|
|
479
|
-
.command("cancel <id> <task-id>")
|
|
480
|
-
.description("Cooperative cancel — it never aborts mid-tool, so it can take as long as the current tool")
|
|
481
|
-
.action(async (id, taskId) => {
|
|
482
|
-
const resp = await apiRequest("POST", `/v1/agents/${id}/tasks/${taskId}/cancel`, {});
|
|
483
|
-
handleApiError("agents.cancel", resp);
|
|
484
|
-
const t = (resp.data ?? {}).task ?? {};
|
|
485
|
-
agentOutput({
|
|
486
|
-
action: "agents.cancel",
|
|
487
|
-
result: resp.data,
|
|
488
|
-
next_steps: [{ command: `naive agents task ${id} ${taskId}`, description: "Watch it wind down" }],
|
|
489
|
-
hints: [
|
|
490
|
-
"Cancel is cooperative: the task ends after its current tool, and that tool is billed.",
|
|
491
|
-
...(t.cancel_requested_at ? [`Requested at ${t.cancel_requested_at}`] : []),
|
|
492
|
-
],
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
agentsCmd
|
|
496
|
-
.command("reply <id> <task-id> <text...>")
|
|
497
|
-
.description("Answer a task parked on needs_input. Without this the task is unrecoverable.")
|
|
498
|
-
.action(async (id, taskId, text) => {
|
|
499
|
-
const resp = await apiRequest("POST", `/v1/agents/${id}/tasks/${taskId}/reply`, { text: text.join(" ") });
|
|
500
|
-
handleApiError("agents.reply", resp);
|
|
501
|
-
agentOutput({
|
|
502
|
-
action: "agents.reply",
|
|
503
|
-
result: resp.data,
|
|
504
|
-
next_steps: [{ command: `naive agents logs ${id} --task ${taskId} --follow`, description: "Watch it resume" }],
|
|
505
|
-
hints: ["The alarm is re-armed; the task resumes on its next wake."],
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
/* ── 13. logs ────────────────────────────────────────────────────────────── */
|
|
509
|
-
agentsCmd
|
|
510
|
-
.command("logs <id>")
|
|
511
|
-
.description("The event trace. --follow tails it live; --after resumes from a position.")
|
|
512
|
-
.option("--task <task-id>", "Only this task")
|
|
513
|
-
.option("--kind <k>", "Only this event kind")
|
|
514
|
-
.option("--after <seq>", "Resume strictly after this seq. A POSITION, never a time.")
|
|
515
|
-
.option("--limit <n>", "Page size (paged mode only)")
|
|
516
|
-
.option("--follow", "Tail the live stream instead of reading a page")
|
|
517
|
-
.option("--show-args", "Print tool arguments instead of a digest (interactive terminals only)")
|
|
518
|
-
.action(async (id, opts) => {
|
|
519
|
-
const q = new URLSearchParams();
|
|
520
|
-
for (const [flag, key] of [["task", "task"], ["kind", "kind"], ["after", "after"], ["limit", "limit"]]) {
|
|
521
|
-
if (opts[flag])
|
|
522
|
-
q.set(key, String(opts[flag]));
|
|
523
|
-
}
|
|
524
|
-
if (!opts.follow) {
|
|
525
|
-
const qs = q.toString();
|
|
526
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/events${qs ? `?${qs}` : ""}`);
|
|
527
|
-
handleApiError("agents.logs", resp);
|
|
528
|
-
const page = (resp.data ?? {});
|
|
529
|
-
agentOutput({
|
|
530
|
-
action: "agents.logs",
|
|
531
|
-
result: resp.data,
|
|
532
|
-
next_steps: [{ command: `naive agents logs ${id} --follow`, description: "Tail it live" }],
|
|
533
|
-
hints: [
|
|
534
|
-
`${page.items?.length ?? 0} events`,
|
|
535
|
-
// 🔴 A truncated replay is a FACT ABOUT RETENTION, not a failure — but
|
|
536
|
-
// it must never be silent. The log is the source of truth, so a gap in
|
|
537
|
-
// it that nobody was told about is worse than a loud one.
|
|
538
|
-
...(page.truncated
|
|
539
|
-
? [`⚠ replay truncated — earliest retained seq ${page.earliest_seq} (20,000-row cap); starting there`]
|
|
540
|
-
: []),
|
|
541
|
-
],
|
|
542
|
-
});
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
await streamEvents("agents.logs", `/v1/agents/${id}/events?${q.toString()}`, "GET", undefined, {
|
|
546
|
-
follow: true,
|
|
547
|
-
showArgs: opts.showArgs === true,
|
|
548
|
-
after: opts.after === undefined ? undefined : Number(opts.after),
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
/* ── 14. spend ───────────────────────────────────────────────────────────── */
|
|
552
|
-
agentsCmd
|
|
553
|
-
.command("spend <id>")
|
|
554
|
-
.description("Spend against the cap. Every paid component prints, INCLUDING the zeros.")
|
|
555
|
-
.option("--by <b>", "component (default) | task")
|
|
556
|
-
.option("--task <task-id>", "The authoritative per-task figure")
|
|
557
|
-
.action(async (id, opts) => {
|
|
558
|
-
const q = new URLSearchParams();
|
|
559
|
-
if (opts.by)
|
|
560
|
-
q.set("by", opts.by);
|
|
561
|
-
if (opts.task)
|
|
562
|
-
q.set("task", opts.task);
|
|
563
|
-
const qs = q.toString();
|
|
564
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/spend${qs ? `?${qs}` : ""}`);
|
|
565
|
-
handleApiError("agents.spend", resp);
|
|
566
|
-
const s = (resp.data ?? {});
|
|
567
|
-
if (humanMode()) {
|
|
568
|
-
process.stdout.write(` cap ${usd(s.cap_micro_usd)} per ${s.period ?? "period"}${s.hard ? " (hard)" : ""}\n`);
|
|
569
|
-
for (const g of s.groups ?? []) {
|
|
570
|
-
// A component with a zero is printed WITH its zero. A paid tool absent
|
|
571
|
-
// from this view is exactly the hole that lets one bill unnoticed.
|
|
572
|
-
const why = g.rate_configured === false ? " (rate not configured)" : g.calls === 0 ? " (0 calls)" : "";
|
|
573
|
-
process.stdout.write(` ${g.key.padEnd(14)}${g.credits.padStart(12)} cr${why}\n`);
|
|
574
|
-
}
|
|
575
|
-
process.stdout.write(` ${"total".padEnd(14)}${(s.spent_credits ?? "0.0000").padStart(12)} cr\n`);
|
|
576
|
-
/* 🔴 THE SECOND TOTAL, PRINTED UNDER THE FIRST AND NEVER ADDED TO IT.
|
|
577
|
-
* `total` is what the agent's model calls COST, from the runtime's meter;
|
|
578
|
-
* `billed` is what the credit ledger holds. They differ today because an
|
|
579
|
-
* agent's inference writes no ledger row at all — which is why this view
|
|
580
|
-
* read `0.0000` for agents with real spend before it learned to read the
|
|
581
|
-
* meter. An operator seeing one number would have no way to know which. */
|
|
582
|
-
if (s.billed_credits !== undefined) {
|
|
583
|
-
process.stdout.write(` ${"billed".padEnd(14)}${s.billed_credits.padStart(12)} cr (credit ledger)\n`);
|
|
584
|
-
}
|
|
585
|
-
if (s.metered?.available === false) {
|
|
586
|
-
process.stdout.write(" no agents runtime on this deployment — the ledger is the only figure\n");
|
|
587
|
-
}
|
|
588
|
-
else if ((s.metered?.unpriced_calls ?? 0) > 0) {
|
|
589
|
-
// Counted, and costing nothing, because nobody priced them. Silence
|
|
590
|
-
// here would make an unpriced call indistinguishable from a free one.
|
|
591
|
-
process.stdout.write(` ${s.metered.unpriced_calls} metered call(s) carry no price and add nothing above\n`);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
agentOutput({
|
|
595
|
-
action: "agents.spend",
|
|
596
|
-
result: resp.data,
|
|
597
|
-
next_steps: [
|
|
598
|
-
{ command: `naive agents update ${id} --budget-usd <usd>`, description: "Raise the cap (this un-brakes a braked agent)" },
|
|
599
|
-
],
|
|
600
|
-
// The CLI NEVER computes money: every figure here is the string the API
|
|
601
|
-
// sent. The only number this file derives is the cap, which is a
|
|
602
|
-
// configuration integer, not a ledger sum.
|
|
603
|
-
hints: [
|
|
604
|
-
`Spent ${s.spent_credits ?? "0.0000"} credits against a cap of ${usd(s.cap_micro_usd)}.`,
|
|
605
|
-
...(s.billed_credits === undefined
|
|
606
|
-
? []
|
|
607
|
-
: [
|
|
608
|
-
`Metered by the agent's runtime. The credit ledger holds ${s.billed_credits} — model calls are not billed through it. Do not add the two.`,
|
|
609
|
-
]),
|
|
610
|
-
],
|
|
611
|
-
});
|
|
612
|
-
});
|
|
613
|
-
/* ── 15. deliverables ────────────────────────────────────────────────────── */
|
|
614
|
-
agentsCmd
|
|
615
|
-
.command("deliverables <id>")
|
|
616
|
-
.description("Artifact manifests. --id fetches one with a fresh download url and inline text.")
|
|
617
|
-
.option("--task <task-id>", "Only this task")
|
|
618
|
-
.option("--id <deliverable-id>", "Fetch one")
|
|
619
|
-
.option("--limit <n>")
|
|
620
|
-
.action(async (id, opts) => {
|
|
621
|
-
if (opts.id) {
|
|
622
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/deliverables/${opts.id}`);
|
|
623
|
-
handleApiError("agents.deliverables", resp);
|
|
624
|
-
const d = (resp.data ?? {});
|
|
625
|
-
agentOutput({
|
|
626
|
-
action: "agents.deliverables",
|
|
627
|
-
result: resp.data,
|
|
628
|
-
next_steps: [],
|
|
629
|
-
hints: [
|
|
630
|
-
// 🔴 Both halves are honest. A null url is a real state — the
|
|
631
|
-
// deployment may have no storage sink, and then the inline text is
|
|
632
|
-
// the whole artifact. Printing a url that 403s would be worse.
|
|
633
|
-
d.download_url
|
|
634
|
-
? "download_url expires in about an hour and is unauthenticated once issued — fetch it again rather than caching it."
|
|
635
|
-
: "No download url on this deployment; `content` carries the artifact inline.",
|
|
636
|
-
...(d.content === null && !d.download_url ? ["This deliverable carries no bytes — check `no_artifact_reason`."] : []),
|
|
637
|
-
],
|
|
638
|
-
});
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
const q = new URLSearchParams();
|
|
642
|
-
for (const k of ["task", "limit"])
|
|
643
|
-
if (opts[k])
|
|
644
|
-
q.set(k, opts[k]);
|
|
645
|
-
const qs = q.toString();
|
|
646
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/deliverables${qs ? `?${qs}` : ""}`);
|
|
647
|
-
handleApiError("agents.deliverables", resp);
|
|
648
|
-
const items = (resp.data ?? {}).items ?? [];
|
|
649
|
-
agentOutput({
|
|
650
|
-
action: "agents.deliverables",
|
|
651
|
-
result: resp.data,
|
|
652
|
-
next_steps: items[0]?.id
|
|
653
|
-
? [{ command: `naive agents deliverables ${id} --id ${items[0].id}`, description: "Fetch it" }]
|
|
654
|
-
: [],
|
|
655
|
-
hints: [`${items.length} deliverable${items.length === 1 ? "" : "s"}`],
|
|
656
|
-
});
|
|
657
|
-
});
|
|
658
|
-
/* ── 16–18. inbound webhooks ─────────────────────────────────────────────── */
|
|
659
|
-
/** 🔴 THE SECRET PRINTS ONCE, AND THE HUMAN LINE SAYS SO. It stays in the JSON
|
|
660
|
-
* envelope — a CLI that hides a value from `--json` is a CLI you cannot script
|
|
661
|
-
* — but a secret scrolled past unremarked is a secret nobody stored, and the
|
|
662
|
-
* next event is a support request for a value that exists nowhere. */
|
|
663
|
-
agentsCmd
|
|
664
|
-
.command("webhook-add <id>")
|
|
665
|
-
.description("Mint an inbound URL for this agent. A signed POST to it wakes the agent.")
|
|
666
|
-
.option("--name <name>", "Label it — which provider is on the other end")
|
|
667
|
-
.action(async (id, opts) => {
|
|
668
|
-
const resp = await apiRequest("POST", `/v1/agents/${id}/webhooks`, opts.name ? { name: opts.name } : {});
|
|
669
|
-
handleApiError("agents.webhook-add", resp);
|
|
670
|
-
const w_ = (resp.data ?? {});
|
|
671
|
-
if (humanMode()) {
|
|
672
|
-
process.stdout.write(` url ${w_.url ?? ""}\n`);
|
|
673
|
-
process.stdout.write(` secret ${w_.secret ?? ""}\n`);
|
|
674
|
-
process.stdout.write(" ⚠ the secret prints once. Store it now; rotate if you lose it.\n");
|
|
675
|
-
}
|
|
676
|
-
agentOutput({
|
|
677
|
-
action: "agents.webhook-add",
|
|
678
|
-
result: resp.data,
|
|
679
|
-
next_steps: [
|
|
680
|
-
{ command: `naive agents webhooks ${id}`, description: "List this agent's URLs (never the secrets)" },
|
|
681
|
-
{ command: `naive agents logs ${id} --follow`, description: "Watch the agent wake when the first event lands" },
|
|
682
|
-
],
|
|
683
|
-
hints: [
|
|
684
|
-
"Sign `${unix_seconds}.${raw_body}` with HMAC-SHA256; hex digest in X-Naive-Signature, the same seconds in " +
|
|
685
|
-
"X-Naive-Timestamp. The timestamp is inside the signed content on purpose — that is what bounds a replay to 5m.",
|
|
686
|
-
],
|
|
687
|
-
});
|
|
688
|
-
});
|
|
689
|
-
agentsCmd
|
|
690
|
-
.command("webhooks <id>")
|
|
691
|
-
.description("The inbound URLs bound to this agent. Secrets are never returned.")
|
|
692
|
-
.action(async (id) => {
|
|
693
|
-
const resp = await apiRequest("GET", `/v1/agents/${id}/webhooks`);
|
|
694
|
-
handleApiError("agents.webhooks", resp);
|
|
695
|
-
const hooks = (resp.data ?? {}).webhooks ?? [];
|
|
696
|
-
if (humanMode()) {
|
|
697
|
-
if (hooks.length === 0)
|
|
698
|
-
process.stdout.write(" no inbound URLs. `naive agents webhook-add <id>` mints one.\n");
|
|
699
|
-
// `never fired` separates "the provider never called" from "we called and
|
|
700
|
-
// dropped" — the first question when someone reports a dead webhook.
|
|
701
|
-
for (const h of hooks) {
|
|
702
|
-
const seen = h.last_event_at ? String(h.last_event_at) : "never fired";
|
|
703
|
-
process.stdout.write(` ${String(h.id ?? "")} ${String(h.url ?? "")} ${h.active ? "active" : "inactive"} ${seen}\n`);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
agentOutput({
|
|
707
|
-
action: "agents.webhooks",
|
|
708
|
-
result: resp.data,
|
|
709
|
-
next_steps: hooks[0]?.id
|
|
710
|
-
? [{ command: `naive agents webhook-rm ${id} ${String(hooks[0].id)}`, description: "Take it down" }]
|
|
711
|
-
: [{ command: `naive agents webhook-add ${id}`, description: "Mint one" }],
|
|
712
|
-
hints: [`${hooks.length} inbound URL${hooks.length === 1 ? "" : "s"}`],
|
|
713
|
-
});
|
|
714
|
-
});
|
|
715
|
-
agentsCmd
|
|
716
|
-
.command("webhook-rm <id> <hook-id>")
|
|
717
|
-
.description("Take an inbound URL down. Refuses if it removed nothing.")
|
|
718
|
-
.action(async (id, hookId) => {
|
|
719
|
-
const resp = await apiRequest("DELETE", `/v1/agents/${id}/webhooks/${hookId}`);
|
|
720
|
-
// 404 here is the honest answer, not a nuisance: "the URL is dead" and "the
|
|
721
|
-
// URL is still live and I no longer control it" must not share a response.
|
|
722
|
-
handleApiError("agents.webhook-rm", resp);
|
|
723
|
-
agentOutput({
|
|
724
|
-
action: "agents.webhook-rm",
|
|
725
|
-
result: resp.data,
|
|
726
|
-
next_steps: [{ command: `naive agents webhooks ${id}`, description: "Confirm it is gone" }],
|
|
727
|
-
hints: ["The URL now 404s. Anything still posting to it is told nothing exists there."],
|
|
728
|
-
});
|
|
729
|
-
});
|
|
730
|
-
/* ── the stream ──────────────────────────────────────────────────────────── */
|
|
731
|
-
/**
|
|
732
|
-
* One streaming implementation for `run` and `logs --follow`.
|
|
733
|
-
*
|
|
734
|
-
* NDJSON, one object per line, terminated by an end frame with a reason and a
|
|
735
|
-
* status — never a JSON array, because a stream has no closing bracket, and
|
|
736
|
-
* never `[DONE]`, which belongs to the OpenAI-compatible lane.
|
|
737
|
-
*
|
|
738
|
-
* The SSE parsing and the redaction are `lib/stream-frames.ts`, unchanged: a
|
|
739
|
-
* second parser in one binary is a second place for a chunk boundary to lose an
|
|
740
|
-
* event, and the redactor already knows both secret shapes that can appear
|
|
741
|
-
* inside an arbitrary tool argument.
|
|
742
|
-
*/
|
|
743
|
-
async function streamEvents(action, path, method, payload, opts) {
|
|
744
|
-
// `--show-args` requires an interactive terminal, so a pipe or a CI job
|
|
745
|
-
// cannot turn it on — the same rule `naive teams watch` applies, and for the
|
|
746
|
-
// same reason: tool arguments are the one place an unaudited value arrives.
|
|
747
|
-
const showArgs = opts.showArgs === true && process.stdout.isTTY === true;
|
|
748
|
-
const config = loadConfig();
|
|
749
|
-
const headers = {
|
|
750
|
-
Authorization: `Bearer ${getApiKey()}`,
|
|
751
|
-
Accept: "text/event-stream",
|
|
752
|
-
...(method === "POST" ? { "Content-Type": "application/json" } : {}),
|
|
753
|
-
};
|
|
754
|
-
/**
|
|
755
|
-
* 🔴 THE SUBJECT, WHICH THIS FUNCTION USED TO DROP ON THE FLOOR.
|
|
756
|
-
*
|
|
757
|
-
* Every other agents command goes through `apiRequest`, which rewrites
|
|
758
|
-
* `/v1/agents/...` to `/v1/users/<subject>/agents/...` and attaches the
|
|
759
|
-
* subject and project headers. `streamEvents` calls `fetch` directly, and it
|
|
760
|
-
* did neither — so `naive agents run` and `naive agents logs --follow`, the
|
|
761
|
-
* two commands an operator uses to WATCH an agent, were the only two that
|
|
762
|
-
* reached the COMPANY mount.
|
|
763
|
-
*
|
|
764
|
-
* That is not a cosmetic difference. On the company mount `subjectUserId` is
|
|
765
|
-
* empty, so the second half of `scopedAgent`'s predicate —
|
|
766
|
-
* `if (scope.tenantUserId && row.vetta.tenantUserId !== scope.tenantUserId)
|
|
767
|
-
* throw notFound()` — is INERT, and a company-wide key tails any agent in the
|
|
768
|
-
* company, including another team's. The event log carries the transcript.
|
|
769
|
-
* R25 exists precisely to stop that, and these two commands walked around it.
|
|
770
|
-
*
|
|
771
|
-
* It could not be seen from `agents-command.test.ts`, which mocks
|
|
772
|
-
* `apiRequest` — the very function that does the rewrite — and never reaches
|
|
773
|
-
* this code path at all.
|
|
774
|
-
*/
|
|
775
|
-
const subject = activeUserId();
|
|
776
|
-
if (subject)
|
|
777
|
-
headers["X-Naive-User-Id"] = subject;
|
|
778
|
-
const project = activeProjectId();
|
|
779
|
-
if (project)
|
|
780
|
-
headers["X-Naive-Project-Id"] = project;
|
|
781
|
-
const scopedPath = scopePathToUser(path, subject);
|
|
782
|
-
// A POSITION, never a time. The server replays strictly greater.
|
|
783
|
-
if (opts.after !== undefined)
|
|
784
|
-
headers["Last-Event-ID"] = String(opts.after);
|
|
785
|
-
let response;
|
|
786
|
-
try {
|
|
787
|
-
response = await fetch(`${config.base_url}${scopedPath}`, {
|
|
788
|
-
method,
|
|
789
|
-
headers,
|
|
790
|
-
body: payload ? JSON.stringify(payload) : undefined,
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
catch (error) {
|
|
794
|
-
agentError({
|
|
795
|
-
action,
|
|
796
|
-
error: { code: "upstream_error", message: error instanceof Error ? error.message : String(error) },
|
|
797
|
-
recovery_steps: [{ command: "naive agents status", description: "Is the runtime reachable?" }],
|
|
798
|
-
});
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
if (!response.ok || !response.body) {
|
|
802
|
-
let data = {};
|
|
803
|
-
try {
|
|
804
|
-
data = JSON.parse(await response.text());
|
|
805
|
-
}
|
|
806
|
-
catch {
|
|
807
|
-
/* a non-JSON body falls through to the generic branch */
|
|
808
|
-
}
|
|
809
|
-
handleApiError(action, { status: response.status, data });
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
const decoder = new TextDecoder();
|
|
813
|
-
let buffer = "";
|
|
814
|
-
let cursor = opts.after;
|
|
815
|
-
let finalStatus;
|
|
816
|
-
let ended = false;
|
|
817
|
-
for await (const chunk of response.body) {
|
|
818
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
819
|
-
const { events, rest } = parseSse(buffer);
|
|
820
|
-
buffer = rest;
|
|
821
|
-
for (const ev of events) {
|
|
822
|
-
let frame;
|
|
823
|
-
try {
|
|
824
|
-
frame = JSON.parse(ev.data);
|
|
825
|
-
}
|
|
826
|
-
catch {
|
|
827
|
-
continue; // a keep-alive or a comment, not a frame
|
|
828
|
-
}
|
|
829
|
-
cursor = highestSeq(cursor, frame);
|
|
830
|
-
const f = frame;
|
|
831
|
-
if (f.kind === "task_finished") {
|
|
832
|
-
finalStatus = f.data?.status ?? finalStatus;
|
|
833
|
-
ended = true;
|
|
834
|
-
}
|
|
835
|
-
if (humanMode())
|
|
836
|
-
renderHumanEvent(frame, showArgs);
|
|
837
|
-
else
|
|
838
|
-
process.stdout.write(`${JSON.stringify(redactFrame(frame, { showArgs }))}\n`);
|
|
839
|
-
// Without --follow the command stops at the terminal frame. With it, the
|
|
840
|
-
// caller has asked to keep watching past this task — which is the right
|
|
841
|
-
// default for something left open on a second monitor, because an agent's
|
|
842
|
-
// NORMAL state is silence between wakes.
|
|
843
|
-
if (ended && !opts.follow)
|
|
844
|
-
break;
|
|
845
|
-
}
|
|
846
|
-
if (ended && !opts.follow)
|
|
847
|
-
break;
|
|
848
|
-
}
|
|
849
|
-
// Anything still in `buffer` is a partial event that never got its blank
|
|
850
|
-
// line. Half a JSON object is worse than none, so it is dropped deliberately.
|
|
851
|
-
if (!humanMode()) {
|
|
852
|
-
process.stdout.write(`${JSON.stringify({ type: "end", reason: ended ? "task_terminal" : "stream_closed", status: finalStatus ?? null, seq: cursor ?? null })}\n`);
|
|
853
|
-
}
|
|
854
|
-
// Exit 3 on a terminal state that is not success — the work is what the
|
|
855
|
-
// caller cares about, and exit 0 would report a failed task as a pass.
|
|
856
|
-
if (finalStatus && ["failed", "cancelled", "braked"].includes(finalStatus))
|
|
857
|
-
process.exitCode = 3;
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* The human renderer. Append-only, no spinner and no cursor movement, so
|
|
861
|
-
* `| tee` and scrollback both work.
|
|
862
|
-
*/
|
|
863
|
-
function renderHumanEvent(frame, showArgs) {
|
|
864
|
-
const f = redactFrame(frame, { showArgs });
|
|
865
|
-
const t = (f.at ?? "").slice(11, 19) || "--:--:--";
|
|
866
|
-
const d = f.data ?? {};
|
|
867
|
-
const w = process.stdout.write.bind(process.stdout);
|
|
868
|
-
switch (f.kind) {
|
|
869
|
-
case "text_delta":
|
|
870
|
-
w(String(d.text ?? ""));
|
|
871
|
-
return;
|
|
872
|
-
case "tool_call":
|
|
873
|
-
// `initiator` is printed and NEVER totalled against the other kind.
|
|
874
|
-
// Summing agent-chosen and harness-fired calls is what once let a null
|
|
875
|
-
// (zero agent-chosen calls) read as a positive.
|
|
876
|
-
w(`\n${t} ▸ ${String(d.name ?? "").padEnd(12)}${String(d.argsPreview ?? "")} (${String(d.initiator ?? "?")})\n`);
|
|
877
|
-
return;
|
|
878
|
-
case "tool_result":
|
|
879
|
-
w(`${t} ${String(d.status ?? "")} ${String(d.preview ?? "").slice(0, 100)}\n`);
|
|
880
|
-
return;
|
|
881
|
-
case "turn_started":
|
|
882
|
-
w(`${t} turn ${String(d.turn ?? "")} · ${String(d.model ?? "")}${Number(d.depth ?? 0) > 0 ? ` · depth ${d.depth}` : ""}\n`);
|
|
883
|
-
return;
|
|
884
|
-
case "usage": {
|
|
885
|
-
// 🔴 A NULL TIER RENDERS AS `—`, NEVER AS 0. Null means NOT MEASURED, and
|
|
886
|
-
// a column containing one must never be summed.
|
|
887
|
-
const tier = (k) => (d[k] === null || d[k] === undefined ? "—" : String(d[k]));
|
|
888
|
-
w(`${t} in ${tier("input")} · cache_read ${tier("cacheRead")} · cw5m ${tier("cacheWrite5m")} · cw1h ${tier("cacheWrite1h")} · out ${tier("output")} · ${String(d.window ?? "")}\n`);
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
case "budget": {
|
|
892
|
-
// `create` prints the per-task ceiling, so the stream prints what that
|
|
893
|
-
// ceiling has left. Printed only when one is declared (`0` ⇒ none), so an
|
|
894
|
-
// agent without one reads exactly as this line did before.
|
|
895
|
-
const ceiling = Number(d.maxTaskMicroUsd ?? 0);
|
|
896
|
-
const perTask = ceiling > 0 ? ` · this task ${usd(Number(d.taskMicroUsd ?? 0))} of ${usd(ceiling)}` : "";
|
|
897
|
-
w(`${t} budget ${usd(Number(d.spentMicroUsd ?? 0))} of ${usd(Number(d.capMicroUsd ?? 0))}${perTask}\n`);
|
|
898
|
-
return;
|
|
899
|
-
}
|
|
900
|
-
case "delivered":
|
|
901
|
-
w(`${t} ✓ delivered${d.final ? " (final)" : ""} · ${String(d.summary ?? "")}\n`);
|
|
902
|
-
return;
|
|
903
|
-
case "tools_offered":
|
|
904
|
-
w(`${t} tools offered: ${d.tools?.length ?? 0} · ${String(d.totalChars ?? "")} chars\n`);
|
|
905
|
-
return;
|
|
906
|
-
case "task_finished":
|
|
907
|
-
w(`${t} ■ ${String(d.status ?? "")}\n`);
|
|
908
|
-
return;
|
|
909
|
-
case "error":
|
|
910
|
-
w(`${t} ✗ ${String(d.message ?? "")}${d.retryable ? " (retryable)" : ""}\n`);
|
|
911
|
-
return;
|
|
912
|
-
default:
|
|
913
|
-
w(`${t} ${f.kind ?? "?"}\n`);
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
//# sourceMappingURL=agents.js.map
|