@standardagents/code 0.6.3 → 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 +159 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -305,6 +305,39 @@ var ApiClient = class {
|
|
|
305
305
|
} catch {
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
|
+
/** Mint a pre-authed standardcode.ai account-dashboard URL for this thread's
|
|
309
|
+
* user. Returns null when the chain is unavailable — the caller falls back
|
|
310
|
+
* to the plain dashboard URL (sign-in by inbox link). */
|
|
311
|
+
async accountLink(threadId) {
|
|
312
|
+
try {
|
|
313
|
+
const res = await this.json(
|
|
314
|
+
`/api/threads/${threadId}/account_link`,
|
|
315
|
+
{ method: "POST" }
|
|
316
|
+
);
|
|
317
|
+
return res?.url ? { url: res.url, preauthed: res.preauthed === true } : null;
|
|
318
|
+
} catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
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
|
+
}
|
|
308
341
|
// ── skills (instance-global Agent Skills library) ─────────────────────────
|
|
309
342
|
/** Installed skills — metadata only. Includes disabled skills. */
|
|
310
343
|
async listSkills() {
|
|
@@ -351,11 +384,17 @@ var ApiClient = class {
|
|
|
351
384
|
};
|
|
352
385
|
|
|
353
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
|
+
}
|
|
354
394
|
function decide(state, tool, risk, hasPermissionRequest) {
|
|
355
395
|
const effectiveRisk = typeof risk === "number" ? Math.min(5, Math.max(1, risk)) : hasPermissionRequest ? 3 : 1;
|
|
356
396
|
if (state.alwaysAllow.has(tool)) return "allow";
|
|
357
|
-
|
|
358
|
-
return effectiveRisk <= state.level ? "allow" : "ask";
|
|
397
|
+
return effectiveRisk <= riskCeiling(state) ? "allow" : "ask";
|
|
359
398
|
}
|
|
360
399
|
var CATASTROPHIC_PATTERNS = [
|
|
361
400
|
/\brm\s+(-[a-z]*\s+)*-[a-z]*f[a-z]*\s+(-[a-z]*\s+)*(\/|~|\$HOME|\/\*|\.\s*$|\/\s*$)/i,
|
|
@@ -3336,7 +3375,7 @@ var Tui = class _Tui {
|
|
|
3336
3375
|
const options = [
|
|
3337
3376
|
{ value: "allow", label: "Allow once", shortcut: "y", color: C.green },
|
|
3338
3377
|
{ value: "always", label: "Always allow this tool", shortcut: "a", color: C.cyan },
|
|
3339
|
-
{ value: "always_risk", label: `Allow
|
|
3378
|
+
{ value: "always_risk", label: `Allow level ${risk} and below this session`, shortcut: "l", color: C.cyan },
|
|
3340
3379
|
{ value: "deny", label: "Deny", shortcut: "n", color: C.red }
|
|
3341
3380
|
];
|
|
3342
3381
|
let idx = 0;
|
|
@@ -4903,6 +4942,8 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
|
|
|
4903
4942
|
let editingQueued = false;
|
|
4904
4943
|
const shownIds = /* @__PURE__ */ new Set();
|
|
4905
4944
|
const pendingSent = /* @__PURE__ */ new Map();
|
|
4945
|
+
let lastSent = null;
|
|
4946
|
+
let upgradeInFlight = false;
|
|
4906
4947
|
let tokensIn = 0;
|
|
4907
4948
|
let tokensOut = 0;
|
|
4908
4949
|
let liveOut = 0;
|
|
@@ -4972,7 +5013,11 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
4972
5013
|
tui.setGoal(data);
|
|
4973
5014
|
}
|
|
4974
5015
|
},
|
|
4975
|
-
|
|
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 });
|
|
4976
5021
|
}
|
|
4977
5022
|
});
|
|
4978
5023
|
const activeSubagents = /* @__PURE__ */ new Map();
|
|
@@ -5105,6 +5150,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5105
5150
|
const extFor = (mime) => ({ "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp" })[mime] ?? "bin";
|
|
5106
5151
|
const toAttachments = (images) => images.map((img) => ({ name: `image-${img.seq}.${extFor(img.mime)}`, mimeType: img.mime, data: img.data }));
|
|
5107
5152
|
const sendNow = async (text, images = []) => {
|
|
5153
|
+
lastSent = { text, images };
|
|
5108
5154
|
tui.printUserMessage(text);
|
|
5109
5155
|
const key = text.trim();
|
|
5110
5156
|
pendingSent.set(key, (pendingSent.get(key) ?? 0) + 1);
|
|
@@ -5134,6 +5180,97 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5134
5180
|
tui.print(`${c.red}\u2717${c.reset} couldn't start compaction: ${err.message}`);
|
|
5135
5181
|
}
|
|
5136
5182
|
};
|
|
5183
|
+
const runAccountCommand = async () => {
|
|
5184
|
+
tui.print(`${c.gray}Opening your account\u2026${c.reset}`);
|
|
5185
|
+
const link = await api.accountLink(threadId).catch(() => null);
|
|
5186
|
+
const target = link?.url ?? "https://standardcode.ai/account";
|
|
5187
|
+
openUrl(target);
|
|
5188
|
+
tui.print(
|
|
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}`
|
|
5190
|
+
);
|
|
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);
|
|
5137
5274
|
const skillsCtl = {
|
|
5138
5275
|
list: () => api.listSkills(),
|
|
5139
5276
|
setEnabled: (name, enabled) => api.setSkillEnabled(name, enabled),
|
|
@@ -5185,6 +5322,18 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5185
5322
|
hint: "list / install / manage",
|
|
5186
5323
|
run: () => runSkillsMenu(tui, skillsCtl)
|
|
5187
5324
|
},
|
|
5325
|
+
{
|
|
5326
|
+
name: "account",
|
|
5327
|
+
label: "Manage your account",
|
|
5328
|
+
hint: "billing & sessions, opens in browser",
|
|
5329
|
+
run: () => runAccountCommand()
|
|
5330
|
+
},
|
|
5331
|
+
{
|
|
5332
|
+
name: "upgrade",
|
|
5333
|
+
label: "Add a parallel session",
|
|
5334
|
+
hint: "run more sessions at once",
|
|
5335
|
+
run: () => offerUpgrade()
|
|
5336
|
+
},
|
|
5188
5337
|
{ name: "background", label: "Background processes", hint: "list / stop", run: () => runProcessMenu(tui, bgMgr) },
|
|
5189
5338
|
{ name: "keybindings", label: "Keyboard shortcuts", run: () => showKeybindings(tui) },
|
|
5190
5339
|
{ name: "update", label: "Check for updates", hint: "check for a newer version", run: () => runUpdateCommand(tui) },
|
|
@@ -5285,6 +5434,11 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5285
5434
|
if (shownIds.has(m.id) || m.status === "pending") continue;
|
|
5286
5435
|
shownIds.add(m.id);
|
|
5287
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
|
+
}
|
|
5288
5442
|
if (m.role === "assistant" && text) printAssistant(tui, text);
|
|
5289
5443
|
else if (m.role === "system" && text) tui.print(`${c.dim}${text}${c.reset}`);
|
|
5290
5444
|
else if (m.role === "user" && text) {
|
|
@@ -5523,7 +5677,7 @@ async function runApprovalsMenu(tui, perm, save) {
|
|
|
5523
5677
|
}
|
|
5524
5678
|
const items = [
|
|
5525
5679
|
...tools.map((t) => ({ label: `Tool: ${t}`, hint: "always allowed", value: `tool:${t}` })),
|
|
5526
|
-
...risks.map((r) => ({ label: `
|
|
5680
|
+
...risks.map((r) => ({ label: `Risk level ${r} and below`, hint: "auto-accepted", value: `risk:${r}` })),
|
|
5527
5681
|
{ label: "Clear all approvals", hint: "", value: "clear" }
|
|
5528
5682
|
];
|
|
5529
5683
|
const picked = await tui.select(
|