@standardagents/code 0.9.6 → 0.9.8
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 +151 -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,35 @@ ${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 ok = await ensureSamaAuth(tui, api);
|
|
7871
|
+
if (!ok) process.exit(0);
|
|
7872
|
+
}
|
|
7801
7873
|
const home = os7.homedir();
|
|
7802
7874
|
const tildeDir = projectDir.startsWith(home) ? "~" + projectDir.slice(home.length) : projectDir;
|
|
7803
7875
|
const shortDir = tildeDir.length > 38 ? "\u2026" + tildeDir.slice(-37) : tildeDir;
|
|
@@ -7846,14 +7918,17 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
|
|
|
7846
7918
|
const loadingSessions = startLoader("Loading sessions");
|
|
7847
7919
|
let existing = [];
|
|
7848
7920
|
try {
|
|
7849
|
-
existing = await api.listThreads(
|
|
7921
|
+
existing = await api.listThreads(
|
|
7922
|
+
selectedAgent === OPENSAMA_AGENT_ID ? [OPENSAMA_AGENT_ID] : AGENT_ID_VARIANTS,
|
|
7923
|
+
resumeTags
|
|
7924
|
+
);
|
|
7850
7925
|
} catch {
|
|
7851
7926
|
existing = [];
|
|
7852
7927
|
}
|
|
7853
7928
|
const summaries = existing.length > 0 ? await summarizeThreads(api, existing.slice(0, 8)) : [];
|
|
7854
7929
|
loadingSessions.stop();
|
|
7855
7930
|
const createSessionThread = async () => {
|
|
7856
|
-
const id = await api.createThread(
|
|
7931
|
+
const id = await api.createThread(selectedAgent, tags);
|
|
7857
7932
|
if (session.mode === "remote" && session.runner && session.remotePath) {
|
|
7858
7933
|
await api.kvSet(id, "session_info", { cwd: session.remotePath, machine: session.runner.name }).catch(() => {
|
|
7859
7934
|
});
|
|
@@ -7900,6 +7975,70 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
|
|
|
7900
7975
|
function shortenPath(p, max = 38) {
|
|
7901
7976
|
return p.length > max ? "\u2026" + p.slice(-(max - 1)) : p;
|
|
7902
7977
|
}
|
|
7978
|
+
async function ensureSamaAuth(tui, api) {
|
|
7979
|
+
const checking = startLoader("Checking your ChatGPT connection");
|
|
7980
|
+
const already = await api.openSamaStatus().catch(() => false);
|
|
7981
|
+
checking.stop();
|
|
7982
|
+
if (already) return true;
|
|
7983
|
+
const picked = await tui.select(
|
|
7984
|
+
`${c3.bold}${gradientText("Authenticate with ChatGPT")}${c3.reset} ${c3.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c3.reset}`,
|
|
7985
|
+
[
|
|
7986
|
+
{
|
|
7987
|
+
label: "Continue with ChatGPT",
|
|
7988
|
+
hint: "connect once \u2014 unlocks your whole account",
|
|
7989
|
+
value: "continue"
|
|
7990
|
+
},
|
|
7991
|
+
{ label: "Not now", hint: "go back", value: "cancel" }
|
|
7992
|
+
]
|
|
7993
|
+
);
|
|
7994
|
+
if (picked !== "continue") return false;
|
|
7995
|
+
openUrl("https://standardcode.ai/app?connect=sama");
|
|
7996
|
+
tui.print(
|
|
7997
|
+
`${c3.dim}Finish connecting ChatGPT in the browser \u2014 waiting here for the authorization to land on your account\u2026${c3.reset}`
|
|
7998
|
+
);
|
|
7999
|
+
const waiting = startLoader("Waiting for your ChatGPT authorization");
|
|
8000
|
+
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
8001
|
+
while (Date.now() < deadline) {
|
|
8002
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
8003
|
+
if (await api.openSamaStatus().catch(() => false)) {
|
|
8004
|
+
waiting.stop();
|
|
8005
|
+
tui.print(
|
|
8006
|
+
`${c3.green}\u2713${c3.reset} ChatGPT connected \u2014 Sama One is now unlocked on your account (terminal, web, and macOS app).`
|
|
8007
|
+
);
|
|
8008
|
+
return true;
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
waiting.stop();
|
|
8012
|
+
tui.print(
|
|
8013
|
+
`${c3.yellow}Still not connected.${c3.reset} Finish the flow at ${c3.teal}standardcode.ai/app${c3.reset} and pick Sama One again.`
|
|
8014
|
+
);
|
|
8015
|
+
return false;
|
|
8016
|
+
}
|
|
8017
|
+
async function runAgentSwitchMenu(tui, api, threadId) {
|
|
8018
|
+
const picked = await tui.select(
|
|
8019
|
+
`${c3.bold}${gradientText("Switch agent")}${c3.reset} ${c3.dim}takes effect on the next message${c3.reset}`,
|
|
8020
|
+
AGENT_CHOICES.map((choice) => ({
|
|
8021
|
+
label: choice.title,
|
|
8022
|
+
hint: choice.description,
|
|
8023
|
+
value: choice.id,
|
|
8024
|
+
disabled: choice.disabled
|
|
8025
|
+
}))
|
|
8026
|
+
);
|
|
8027
|
+
if (!picked) return;
|
|
8028
|
+
if (picked === OPENSAMA_AGENT_ID) {
|
|
8029
|
+
const ok = await ensureSamaAuth(tui, api);
|
|
8030
|
+
if (!ok) return;
|
|
8031
|
+
}
|
|
8032
|
+
const title = AGENT_CHOICES.find((choice) => choice.id === picked)?.title ?? picked;
|
|
8033
|
+
try {
|
|
8034
|
+
await api.setThreadAgent(threadId, picked);
|
|
8035
|
+
tui.print(`${c3.green}\u2713${c3.reset} Session handed to ${c3.bold}${title}${c3.reset} \u2014 applies from your next message.`);
|
|
8036
|
+
} catch (e) {
|
|
8037
|
+
tui.print(
|
|
8038
|
+
`${c3.red}\u2717 couldn't switch agent:${c3.reset} ${c3.gray}${e instanceof Error ? e.message : String(e)}${c3.reset}`
|
|
8039
|
+
);
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
7903
8042
|
async function pickRemoteProject(tui, api, runner) {
|
|
7904
8043
|
const ENTER_PATH = "__enter_path__";
|
|
7905
8044
|
const projects = Object.entries(runner.projects).sort(
|
|
@@ -8611,6 +8750,12 @@ ${c3.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
8611
8750
|
run: () => runMcpMenu(tui, mcpCtl)
|
|
8612
8751
|
}
|
|
8613
8752
|
],
|
|
8753
|
+
{
|
|
8754
|
+
name: "agent",
|
|
8755
|
+
label: "Switch agent",
|
|
8756
|
+
hint: "hand this session to a different agent",
|
|
8757
|
+
run: () => runAgentSwitchMenu(tui, api, threadId)
|
|
8758
|
+
},
|
|
8614
8759
|
{
|
|
8615
8760
|
name: "machines",
|
|
8616
8761
|
label: "Your machines",
|