@standardagents/code 0.6.4 → 0.6.6
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/dist/index.js +130 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -319,6 +319,25 @@ var ApiClient = class {
|
|
|
319
319
|
return null;
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
|
+
/** Preview the cost of changing to `sessions` parallel sessions (default +1).
|
|
323
|
+
* Returns null when billing is unavailable / there's no subscription. */
|
|
324
|
+
async sessionsQuote(threadId, sessions) {
|
|
325
|
+
try {
|
|
326
|
+
return await this.json(`/api/threads/${threadId}/sessions_quote`, {
|
|
327
|
+
method: "POST",
|
|
328
|
+
body: JSON.stringify(typeof sessions === "number" ? { sessions } : {})
|
|
329
|
+
});
|
|
330
|
+
} catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/** Apply a parallel-session change (charges via Stripe). Throws on failure. */
|
|
335
|
+
async sessionsUpgrade(threadId, sessions) {
|
|
336
|
+
return this.json(`/api/threads/${threadId}/sessions_upgrade`, {
|
|
337
|
+
method: "POST",
|
|
338
|
+
body: JSON.stringify({ sessions })
|
|
339
|
+
});
|
|
340
|
+
}
|
|
322
341
|
// ── skills (instance-global Agent Skills library) ─────────────────────────
|
|
323
342
|
/** Installed skills — metadata only. Includes disabled skills. */
|
|
324
343
|
async listSkills() {
|
|
@@ -365,11 +384,17 @@ var ApiClient = class {
|
|
|
365
384
|
};
|
|
366
385
|
|
|
367
386
|
// src/permissions.ts
|
|
387
|
+
function riskCeiling(state) {
|
|
388
|
+
let ceiling = state.level;
|
|
389
|
+
for (const granted of state.allowRisk) {
|
|
390
|
+
if (granted > ceiling) ceiling = granted;
|
|
391
|
+
}
|
|
392
|
+
return ceiling;
|
|
393
|
+
}
|
|
368
394
|
function decide(state, tool, risk, hasPermissionRequest) {
|
|
369
395
|
const effectiveRisk = typeof risk === "number" ? Math.min(5, Math.max(1, risk)) : hasPermissionRequest ? 3 : 1;
|
|
370
396
|
if (state.alwaysAllow.has(tool)) return "allow";
|
|
371
|
-
|
|
372
|
-
return effectiveRisk <= state.level ? "allow" : "ask";
|
|
397
|
+
return effectiveRisk <= riskCeiling(state) ? "allow" : "ask";
|
|
373
398
|
}
|
|
374
399
|
var CATASTROPHIC_PATTERNS = [
|
|
375
400
|
/\brm\s+(-[a-z]*\s+)*-[a-z]*f[a-z]*\s+(-[a-z]*\s+)*(\/|~|\$HOME|\/\*|\.\s*$|\/\s*$)/i,
|
|
@@ -3350,7 +3375,7 @@ var Tui = class _Tui {
|
|
|
3350
3375
|
const options = [
|
|
3351
3376
|
{ value: "allow", label: "Allow once", shortcut: "y", color: C.green },
|
|
3352
3377
|
{ value: "always", label: "Always allow this tool", shortcut: "a", color: C.cyan },
|
|
3353
|
-
{ value: "always_risk", label: `Allow
|
|
3378
|
+
{ value: "always_risk", label: `Allow level ${risk} and below this session`, shortcut: "l", color: C.cyan },
|
|
3354
3379
|
{ value: "deny", label: "Deny", shortcut: "n", color: C.red }
|
|
3355
3380
|
];
|
|
3356
3381
|
let idx = 0;
|
|
@@ -4917,6 +4942,8 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
|
|
|
4917
4942
|
let editingQueued = false;
|
|
4918
4943
|
const shownIds = /* @__PURE__ */ new Set();
|
|
4919
4944
|
const pendingSent = /* @__PURE__ */ new Map();
|
|
4945
|
+
let lastSent = null;
|
|
4946
|
+
let upgradeInFlight = false;
|
|
4920
4947
|
let tokensIn = 0;
|
|
4921
4948
|
let tokensOut = 0;
|
|
4922
4949
|
let liveOut = 0;
|
|
@@ -4986,7 +5013,11 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4986
5013
|
tui.setGoal(data);
|
|
4987
5014
|
}
|
|
4988
5015
|
},
|
|
4989
|
-
|
|
5016
|
+
// A failed turn whose message is the lease service's at-limit denial → offer
|
|
5017
|
+
// an in-terminal upgrade right where the friction happened. (offerUpgrade /
|
|
5018
|
+
// isSessionLimitError are defined below; this callback only fires mid-session.)
|
|
5019
|
+
onError: (err) => {
|
|
5020
|
+
if (isSessionLimitError(err)) void offerUpgrade({ auto: true });
|
|
4990
5021
|
}
|
|
4991
5022
|
});
|
|
4992
5023
|
const activeSubagents = /* @__PURE__ */ new Map();
|
|
@@ -5119,6 +5150,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5119
5150
|
const extFor = (mime) => ({ "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp" })[mime] ?? "bin";
|
|
5120
5151
|
const toAttachments = (images) => images.map((img) => ({ name: `image-${img.seq}.${extFor(img.mime)}`, mimeType: img.mime, data: img.data }));
|
|
5121
5152
|
const sendNow = async (text, images = []) => {
|
|
5153
|
+
lastSent = { text, images };
|
|
5122
5154
|
tui.printUserMessage(text);
|
|
5123
5155
|
const key = text.trim();
|
|
5124
5156
|
pendingSent.set(key, (pendingSent.get(key) ?? 0) + 1);
|
|
@@ -5157,6 +5189,88 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5157
5189
|
link?.preauthed ? `${c.gray}\u2192 account dashboard opened in your browser (signed in)${c.reset}` : `${c.gray}\u2192 opened ${target} \u2014 sign in with your account email${c.reset}`
|
|
5158
5190
|
);
|
|
5159
5191
|
};
|
|
5192
|
+
const ordinal = (n) => {
|
|
5193
|
+
const s = ["th", "st", "nd", "rd"], v = n % 100;
|
|
5194
|
+
return `${n}${s[(v - 20) % 10] ?? s[v] ?? s[0]}`;
|
|
5195
|
+
};
|
|
5196
|
+
const fmtCost = (q) => typeof q.amount_cents === "number" && q.amount_cents !== 0 ? `$${(Math.abs(q.amount_cents) / 100).toFixed(2)}` : null;
|
|
5197
|
+
const renderUpgradePanel = (q) => {
|
|
5198
|
+
const dots = [];
|
|
5199
|
+
for (let i = 0; i < q.max; i++) {
|
|
5200
|
+
if (i < q.current) dots.push(`${c.teal}\u25CF${c.reset}`);
|
|
5201
|
+
else if (i === q.current) dots.push(`${c.bold}${gradientText("\uFF0B")}${c.reset}`);
|
|
5202
|
+
else dots.push(`${c.dim}\xB7${c.reset}`);
|
|
5203
|
+
}
|
|
5204
|
+
const cost = fmtCost(q);
|
|
5205
|
+
const lines = [
|
|
5206
|
+
"",
|
|
5207
|
+
`${c.bold}${gradientText("\u2726 Add a parallel session")}${c.reset}`,
|
|
5208
|
+
"",
|
|
5209
|
+
`${dots.join(" ")} ${c.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c.reset}`
|
|
5210
|
+
];
|
|
5211
|
+
if (q.ends_trial) {
|
|
5212
|
+
lines.push(
|
|
5213
|
+
`${c.yellow}Your $5 trial covers 1 session. Adding a ${ordinal(q.sessions)} ends the trial${c.reset}`,
|
|
5214
|
+
`${c.yellow}and starts your $49/mo plan now${cost ? ` \u2014 ${c.bold}${cost} charged today${c.reset}${c.yellow}` : ""}.${c.reset}`
|
|
5215
|
+
);
|
|
5216
|
+
} else if (cost) {
|
|
5217
|
+
lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${c.bold}${cost} charged now${c.reset}.`);
|
|
5218
|
+
} else {
|
|
5219
|
+
lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 prorated on your next invoice.`);
|
|
5220
|
+
}
|
|
5221
|
+
lines.push("");
|
|
5222
|
+
return lines.join("\n");
|
|
5223
|
+
};
|
|
5224
|
+
const offerUpgrade = async (opts = {}) => {
|
|
5225
|
+
if (upgradeInFlight) return;
|
|
5226
|
+
upgradeInFlight = true;
|
|
5227
|
+
try {
|
|
5228
|
+
const quote = await api.sessionsQuote(threadId);
|
|
5229
|
+
if (!quote) {
|
|
5230
|
+
const link = await api.accountLink(threadId).catch(() => null);
|
|
5231
|
+
const target = link?.url ?? "https://standardcode.ai/account";
|
|
5232
|
+
openUrl(target);
|
|
5233
|
+
tui.print(`${c.gray}\u2192 opened ${target} to manage your plan${c.reset}`);
|
|
5234
|
+
return;
|
|
5235
|
+
}
|
|
5236
|
+
if (quote.current >= quote.max) {
|
|
5237
|
+
tui.print(`${c.yellow}You're at the maximum of ${quote.max} parallel sessions.${c.reset}`);
|
|
5238
|
+
return;
|
|
5239
|
+
}
|
|
5240
|
+
tui.print(renderUpgradePanel(quote));
|
|
5241
|
+
const cost = fmtCost(quote);
|
|
5242
|
+
const confirmLabel = quote.ends_trial ? `End trial & start now${cost ? ` \u2014 pay ${cost} today` : ""}` : `Confirm${cost ? ` \u2014 pay ${cost} now` : " \u2014 add the session"}`;
|
|
5243
|
+
const choice = await tui.select(`Add a ${ordinal(quote.sessions)} parallel session?`, [
|
|
5244
|
+
{ label: confirmLabel, value: "go" },
|
|
5245
|
+
{ label: "Not now", value: "no" }
|
|
5246
|
+
]);
|
|
5247
|
+
if (choice !== "go") {
|
|
5248
|
+
tui.print(`${c.gray}No change made \u2014 you can upgrade anytime with /upgrade.${c.reset}`);
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
5251
|
+
tui.print(`${c.gray}Applying\u2026${c.reset}`);
|
|
5252
|
+
let applied;
|
|
5253
|
+
try {
|
|
5254
|
+
applied = await api.sessionsUpgrade(threadId, quote.sessions);
|
|
5255
|
+
} catch (e) {
|
|
5256
|
+
tui.print(`${c.red}\u2717${c.reset} Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5257
|
+
return;
|
|
5258
|
+
}
|
|
5259
|
+
if (!applied?.ok) {
|
|
5260
|
+
tui.print(`${c.red}\u2717${c.reset} Upgrade failed: ${applied?.error ?? "unknown error"}`);
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
5263
|
+
const n = applied.sessions ?? quote.sessions;
|
|
5264
|
+
tui.print(`${c.green}\u2713${c.reset} ${c.bold}${gradientText(`You now have ${n} parallel session${n === 1 ? "" : "s"}.`)}${c.reset}`);
|
|
5265
|
+
if (opts.auto && lastSent) {
|
|
5266
|
+
tui.print(`${c.gray}Continuing\u2026${c.reset}`);
|
|
5267
|
+
await sendNow(lastSent.text, lastSent.images);
|
|
5268
|
+
}
|
|
5269
|
+
} finally {
|
|
5270
|
+
upgradeInFlight = false;
|
|
5271
|
+
}
|
|
5272
|
+
};
|
|
5273
|
+
const isSessionLimitError = (msg) => /thread limit reached|simultaneous (thread|session)s?.*in use|no active standard code subscription/i.test(msg);
|
|
5160
5274
|
const skillsCtl = {
|
|
5161
5275
|
list: () => api.listSkills(),
|
|
5162
5276
|
setEnabled: (name, enabled) => api.setSkillEnabled(name, enabled),
|
|
@@ -5214,6 +5328,12 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5214
5328
|
hint: "billing & sessions, opens in browser",
|
|
5215
5329
|
run: () => runAccountCommand()
|
|
5216
5330
|
},
|
|
5331
|
+
{
|
|
5332
|
+
name: "upgrade",
|
|
5333
|
+
label: "Add a parallel session",
|
|
5334
|
+
hint: "run more sessions at once",
|
|
5335
|
+
run: () => offerUpgrade()
|
|
5336
|
+
},
|
|
5217
5337
|
{ name: "background", label: "Background processes", hint: "list / stop", run: () => runProcessMenu(tui, bgMgr) },
|
|
5218
5338
|
{ name: "keybindings", label: "Keyboard shortcuts", run: () => showKeybindings(tui) },
|
|
5219
5339
|
{ name: "update", label: "Check for updates", hint: "check for a newer version", run: () => runUpdateCommand(tui) },
|
|
@@ -5314,6 +5434,11 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5314
5434
|
if (shownIds.has(m.id) || m.status === "pending") continue;
|
|
5315
5435
|
shownIds.add(m.id);
|
|
5316
5436
|
const text = messageText(m.content).trim();
|
|
5437
|
+
const denial = typeof m.error === "string" && m.error || text;
|
|
5438
|
+
if (denial && isSessionLimitError(denial)) {
|
|
5439
|
+
void offerUpgrade({ auto: true });
|
|
5440
|
+
continue;
|
|
5441
|
+
}
|
|
5317
5442
|
if (m.role === "assistant" && text) printAssistant(tui, text);
|
|
5318
5443
|
else if (m.role === "system" && text) tui.print(`${c.dim}${text}${c.reset}`);
|
|
5319
5444
|
else if (m.role === "user" && text) {
|
|
@@ -5552,7 +5677,7 @@ async function runApprovalsMenu(tui, perm, save) {
|
|
|
5552
5677
|
}
|
|
5553
5678
|
const items = [
|
|
5554
5679
|
...tools.map((t) => ({ label: `Tool: ${t}`, hint: "always allowed", value: `tool:${t}` })),
|
|
5555
|
-
...risks.map((r) => ({ label: `
|
|
5680
|
+
...risks.map((r) => ({ label: `Risk level ${r} and below`, hint: "auto-accepted", value: `risk:${r}` })),
|
|
5556
5681
|
{ label: "Clear all approvals", hint: "", value: "clear" }
|
|
5557
5682
|
];
|
|
5558
5683
|
const picked = await tui.select(
|