@standardagents/code 0.9.6 → 0.9.7
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 +14 -0
- package/dist/index.js +128 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,6 +104,20 @@ standardcode -e -- <dir> # prompt for the endpoint for this run
|
|
|
104
104
|
On first run it connects to the hosted Standard Code instance
|
|
105
105
|
(`https://api.standardcode.ai`) and walks you through a one-time browser sign-in — press
|
|
106
106
|
Enter, approve in the browser, and your key is stored in `~/.standardagents/credentials`.
|
|
107
|
+
### Choosing an agent
|
|
108
|
+
|
|
109
|
+
Session start opens with an agent picker: **Frontier One** (coming soon),
|
|
110
|
+
**Unlimited One** (the default — adaptive high/low routing), **Sama One**
|
|
111
|
+
(OpenAI-only, powered by your own ChatGPT authorization), and **Dario**
|
|
112
|
+
(unavailable). Skip the menu with `--agent unlimited` or `--agent sama`.
|
|
113
|
+
|
|
114
|
+
Sama One is BYOK: the first time you pick it, the CLI opens
|
|
115
|
+
`https://standardcode.ai/app` where you connect your ChatGPT account once.
|
|
116
|
+
That connection installs the authorization in your account's secret
|
|
117
|
+
environment on the instance — shared by the CLI, web console, and macOS app —
|
|
118
|
+
and the CLI itself never sees or stores the OpenAI key. Sama One sessions are
|
|
119
|
+
not limited by the simultaneous-thread lease.
|
|
120
|
+
|
|
107
121
|
You only do this once per machine. It then offers to **resume** a session tagged for this
|
|
108
122
|
project + machine, or **start a new one**.
|
|
109
123
|
|
package/dist/index.js
CHANGED
|
@@ -249,6 +249,27 @@ var ApiClient = class {
|
|
|
249
249
|
async verify() {
|
|
250
250
|
return (await this.verifyDetailed()).ok;
|
|
251
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Re-point a thread's ROOT agent (mid-session agent switch). The runtime
|
|
254
|
+
* treats this as an explicit handoff: the full conversation is preserved
|
|
255
|
+
* and the next turn plays on the new agent. Published spec surface
|
|
256
|
+
* (`PATCH /api/threads/:id { agent_id }`), same as every head uses.
|
|
257
|
+
*/
|
|
258
|
+
async setThreadAgent(threadId, agentId) {
|
|
259
|
+
await this.json(`/api/threads/${threadId}`, {
|
|
260
|
+
method: "PATCH",
|
|
261
|
+
body: JSON.stringify({ agent_id: agentId })
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Whether the account has completed the Sama One (OpenSama) ChatGPT
|
|
266
|
+
* connection. Secret env variables come back as metadata only — the
|
|
267
|
+
* instance never returns the OpenAI key value to a client.
|
|
268
|
+
*/
|
|
269
|
+
async openSamaStatus() {
|
|
270
|
+
const body = await this.json("/api/users/me/env");
|
|
271
|
+
return (body.variables ?? []).some((entry) => entry.name === "OPENSAMA_OPENAI_API_KEY");
|
|
272
|
+
}
|
|
252
273
|
/**
|
|
253
274
|
* Check the endpoint + token and say PRECISELY what's wrong when they
|
|
254
275
|
* fail. "That token didn't work" is a lie when the real problem is a typo'd
|
|
@@ -5648,7 +5669,7 @@ var Tui = class _Tui {
|
|
|
5648
5669
|
*/
|
|
5649
5670
|
select(title, items) {
|
|
5650
5671
|
return new Promise((resolve) => {
|
|
5651
|
-
let idx = 0;
|
|
5672
|
+
let idx = Math.max(0, items.findIndex((it) => !it.disabled));
|
|
5652
5673
|
this.beginTakeover();
|
|
5653
5674
|
const cols2 = process.stdout.columns || 80;
|
|
5654
5675
|
const geo = inputBoxGeometry(cols2);
|
|
@@ -5669,7 +5690,7 @@ ${pad}${title}
|
|
|
5669
5690
|
let label = it.label.replace(/\s+/g, " ").trim();
|
|
5670
5691
|
if (label.length > labelMax) label = label.slice(0, Math.max(0, labelMax - 1)) + "\u2026";
|
|
5671
5692
|
const pointer = sel ? `${C.magenta}\u276F${C.reset} ` : " ";
|
|
5672
|
-
const styledLabel = sel ? `${C.bold}${C.cyan}${label}${C.reset}` : `${C.dim}${label}${C.reset}`;
|
|
5693
|
+
const styledLabel = it.disabled ? `${C.gray}${label}${C.reset}` : sel ? `${C.bold}${C.cyan}${label}${C.reset}` : `${C.dim}${label}${C.reset}`;
|
|
5673
5694
|
let content = `${pointer}${styledLabel}`;
|
|
5674
5695
|
if (hintW) {
|
|
5675
5696
|
const used = pointerW + label.length;
|
|
@@ -5714,7 +5735,7 @@ ${pad}${title}
|
|
|
5714
5735
|
idx = (idx + 1) % items.length;
|
|
5715
5736
|
draw(true);
|
|
5716
5737
|
} else if (key.name === "return" || key.name === "enter") {
|
|
5717
|
-
close(items[idx].value);
|
|
5738
|
+
if (!items[idx].disabled) close(items[idx].value);
|
|
5718
5739
|
} else if (key.name === "escape") {
|
|
5719
5740
|
close(void 0);
|
|
5720
5741
|
}
|
|
@@ -5899,6 +5920,13 @@ var AGENT_ID_VARIANTS = [
|
|
|
5899
5920
|
"standard_code_low_agent",
|
|
5900
5921
|
"standard_code_high_agent"
|
|
5901
5922
|
];
|
|
5923
|
+
var OPENSAMA_AGENT_ID = "opensama_agent";
|
|
5924
|
+
var AGENT_CHOICES = [
|
|
5925
|
+
{ id: "frontier_one", title: "Frontier One", description: "coming soon", disabled: true },
|
|
5926
|
+
{ id: AGENT_ID, title: "Unlimited One", description: "adaptive high/low routing" },
|
|
5927
|
+
{ id: OPENSAMA_AGENT_ID, title: "Sama One", description: "ChatGPT Pro sign-in required" },
|
|
5928
|
+
{ id: "dario", title: "Dario", description: "unavailable", disabled: true }
|
|
5929
|
+
];
|
|
5902
5930
|
var PRODUCTION_ENDPOINT = "https://api.standardcode.ai";
|
|
5903
5931
|
function readVersion() {
|
|
5904
5932
|
try {
|
|
@@ -6822,7 +6850,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
6822
6850
|
};
|
|
6823
6851
|
const sweep = async () => {
|
|
6824
6852
|
try {
|
|
6825
|
-
const threads = await api.listThreads(AGENT_ID_VARIANTS, [runnerTag]);
|
|
6853
|
+
const threads = await api.listThreads([...AGENT_ID_VARIANTS, OPENSAMA_AGENT_ID], [runnerTag]);
|
|
6826
6854
|
const recent = threads.sort((a, b) => (b.created_at ?? 0) - (a.created_at ?? 0)).slice(0, MAX_WORKERS);
|
|
6827
6855
|
for (const t of recent) {
|
|
6828
6856
|
await attach(t.id, t.tags, (t.created_at ?? 0) * 1e3);
|
|
@@ -7436,6 +7464,9 @@ function printUsage() {
|
|
|
7436
7464
|
" If url is omitted, prompt for it.",
|
|
7437
7465
|
" Credentials are remembered for that endpoint, but",
|
|
7438
7466
|
" the saved default endpoint is not changed.",
|
|
7467
|
+
" --agent <name> Pick the coding agent without the menu:",
|
|
7468
|
+
" 'unlimited' (Unlimited One, default) or 'sama'",
|
|
7469
|
+
" (Sama One \u2014 requires the ChatGPT connection).",
|
|
7439
7470
|
" -h, --help Show this help.",
|
|
7440
7471
|
""
|
|
7441
7472
|
].join("\n")
|
|
@@ -7449,6 +7480,18 @@ function parseArgs2(args) {
|
|
|
7449
7480
|
parsed.help = true;
|
|
7450
7481
|
continue;
|
|
7451
7482
|
}
|
|
7483
|
+
if (arg === "--agent") {
|
|
7484
|
+
const value = args[i + 1];
|
|
7485
|
+
if (!value || value.startsWith("-")) throw new Error("--agent requires a name (unlimited or sama).");
|
|
7486
|
+
parsed.agent = value;
|
|
7487
|
+
i++;
|
|
7488
|
+
continue;
|
|
7489
|
+
}
|
|
7490
|
+
if (arg.startsWith("--agent=")) {
|
|
7491
|
+
parsed.agent = arg.slice("--agent=".length);
|
|
7492
|
+
if (!parsed.agent) throw new Error("--agent requires a name (unlimited or sama).");
|
|
7493
|
+
continue;
|
|
7494
|
+
}
|
|
7452
7495
|
if (arg === "--endpoint" || arg === "-e") {
|
|
7453
7496
|
const value = args[i + 1];
|
|
7454
7497
|
if (value && !value.startsWith("-")) {
|
|
@@ -7798,6 +7841,45 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
|
|
|
7798
7841
|
void registerProject(api, identity, projectDir).catch(() => {
|
|
7799
7842
|
});
|
|
7800
7843
|
const tui = new Tui(1);
|
|
7844
|
+
let selectedAgent = AGENT_ID;
|
|
7845
|
+
if (cliArgs.agent) {
|
|
7846
|
+
const wanted = cliArgs.agent.toLowerCase();
|
|
7847
|
+
if (["sama", "opensama", "sama_one", OPENSAMA_AGENT_ID].includes(wanted)) {
|
|
7848
|
+
selectedAgent = OPENSAMA_AGENT_ID;
|
|
7849
|
+
} else if (["unlimited", "standard", "unlimited_one", AGENT_ID].includes(wanted)) {
|
|
7850
|
+
selectedAgent = AGENT_ID;
|
|
7851
|
+
} else {
|
|
7852
|
+
stdout.write(`${c3.red}error:${c3.reset} Unknown agent "${cliArgs.agent}". Use unlimited or sama.
|
|
7853
|
+
`);
|
|
7854
|
+
process.exit(1);
|
|
7855
|
+
}
|
|
7856
|
+
} else {
|
|
7857
|
+
const picked = await tui.select(
|
|
7858
|
+
`${c3.bold}${gradientText("Which agent?")}${c3.reset} ${c3.dim}\u2191\u2193 \xB7 enter \xB7 esc${c3.reset}`,
|
|
7859
|
+
AGENT_CHOICES.map((choice) => ({
|
|
7860
|
+
label: choice.title,
|
|
7861
|
+
hint: choice.description,
|
|
7862
|
+
value: choice.id,
|
|
7863
|
+
disabled: choice.disabled
|
|
7864
|
+
}))
|
|
7865
|
+
);
|
|
7866
|
+
if (!picked) process.exit(0);
|
|
7867
|
+
selectedAgent = picked;
|
|
7868
|
+
}
|
|
7869
|
+
if (selectedAgent === OPENSAMA_AGENT_ID) {
|
|
7870
|
+
const connecting = startLoader("Checking your ChatGPT connection");
|
|
7871
|
+
const hasKey = await api.openSamaStatus().catch(() => false);
|
|
7872
|
+
connecting.stop();
|
|
7873
|
+
if (!hasKey) {
|
|
7874
|
+
stdout.write(
|
|
7875
|
+
`${c3.yellow}Sama One needs your ChatGPT account connected first.${c3.reset}
|
|
7876
|
+
Opening ${c3.teal}https://standardcode.ai/app${c3.reset} \u2014 connect ChatGPT there, then run standardcode again.
|
|
7877
|
+
`
|
|
7878
|
+
);
|
|
7879
|
+
openUrl("https://standardcode.ai/app");
|
|
7880
|
+
process.exit(0);
|
|
7881
|
+
}
|
|
7882
|
+
}
|
|
7801
7883
|
const home = os7.homedir();
|
|
7802
7884
|
const tildeDir = projectDir.startsWith(home) ? "~" + projectDir.slice(home.length) : projectDir;
|
|
7803
7885
|
const shortDir = tildeDir.length > 38 ? "\u2026" + tildeDir.slice(-37) : tildeDir;
|
|
@@ -7846,14 +7928,17 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
|
|
|
7846
7928
|
const loadingSessions = startLoader("Loading sessions");
|
|
7847
7929
|
let existing = [];
|
|
7848
7930
|
try {
|
|
7849
|
-
existing = await api.listThreads(
|
|
7931
|
+
existing = await api.listThreads(
|
|
7932
|
+
selectedAgent === OPENSAMA_AGENT_ID ? [OPENSAMA_AGENT_ID] : AGENT_ID_VARIANTS,
|
|
7933
|
+
resumeTags
|
|
7934
|
+
);
|
|
7850
7935
|
} catch {
|
|
7851
7936
|
existing = [];
|
|
7852
7937
|
}
|
|
7853
7938
|
const summaries = existing.length > 0 ? await summarizeThreads(api, existing.slice(0, 8)) : [];
|
|
7854
7939
|
loadingSessions.stop();
|
|
7855
7940
|
const createSessionThread = async () => {
|
|
7856
|
-
const id = await api.createThread(
|
|
7941
|
+
const id = await api.createThread(selectedAgent, tags);
|
|
7857
7942
|
if (session.mode === "remote" && session.runner && session.remotePath) {
|
|
7858
7943
|
await api.kvSet(id, "session_info", { cwd: session.remotePath, machine: session.runner.name }).catch(() => {
|
|
7859
7944
|
});
|
|
@@ -7900,6 +7985,37 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
|
|
|
7900
7985
|
function shortenPath(p, max = 38) {
|
|
7901
7986
|
return p.length > max ? "\u2026" + p.slice(-(max - 1)) : p;
|
|
7902
7987
|
}
|
|
7988
|
+
async function runAgentSwitchMenu(tui, api, threadId) {
|
|
7989
|
+
const picked = await tui.select(
|
|
7990
|
+
`${c3.bold}${gradientText("Switch agent")}${c3.reset} ${c3.dim}takes effect on the next message${c3.reset}`,
|
|
7991
|
+
AGENT_CHOICES.map((choice) => ({
|
|
7992
|
+
label: choice.title,
|
|
7993
|
+
hint: choice.description,
|
|
7994
|
+
value: choice.id,
|
|
7995
|
+
disabled: choice.disabled
|
|
7996
|
+
}))
|
|
7997
|
+
);
|
|
7998
|
+
if (!picked) return;
|
|
7999
|
+
if (picked === OPENSAMA_AGENT_ID) {
|
|
8000
|
+
const hasKey = await api.openSamaStatus().catch(() => false);
|
|
8001
|
+
if (!hasKey) {
|
|
8002
|
+
tui.print(
|
|
8003
|
+
`${c3.yellow}Sama One needs your ChatGPT account connected first \u2014 opening ${c3.teal}standardcode.ai/app${c3.reset}${c3.yellow}.${c3.reset}`
|
|
8004
|
+
);
|
|
8005
|
+
openUrl("https://standardcode.ai/app");
|
|
8006
|
+
return;
|
|
8007
|
+
}
|
|
8008
|
+
}
|
|
8009
|
+
const title = AGENT_CHOICES.find((choice) => choice.id === picked)?.title ?? picked;
|
|
8010
|
+
try {
|
|
8011
|
+
await api.setThreadAgent(threadId, picked);
|
|
8012
|
+
tui.print(`${c3.green}\u2713${c3.reset} Session handed to ${c3.bold}${title}${c3.reset} \u2014 applies from your next message.`);
|
|
8013
|
+
} catch (e) {
|
|
8014
|
+
tui.print(
|
|
8015
|
+
`${c3.red}\u2717 couldn't switch agent:${c3.reset} ${c3.gray}${e instanceof Error ? e.message : String(e)}${c3.reset}`
|
|
8016
|
+
);
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
7903
8019
|
async function pickRemoteProject(tui, api, runner) {
|
|
7904
8020
|
const ENTER_PATH = "__enter_path__";
|
|
7905
8021
|
const projects = Object.entries(runner.projects).sort(
|
|
@@ -8611,6 +8727,12 @@ ${c3.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
8611
8727
|
run: () => runMcpMenu(tui, mcpCtl)
|
|
8612
8728
|
}
|
|
8613
8729
|
],
|
|
8730
|
+
{
|
|
8731
|
+
name: "agent",
|
|
8732
|
+
label: "Switch agent",
|
|
8733
|
+
hint: "hand this session to a different agent",
|
|
8734
|
+
run: () => runAgentSwitchMenu(tui, api, threadId)
|
|
8735
|
+
},
|
|
8614
8736
|
{
|
|
8615
8737
|
name: "machines",
|
|
8616
8738
|
label: "Your machines",
|