@guangnao/agent-cli 1.1.5 → 1.1.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/dist/cli.js +181 -40
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2950,27 +2950,44 @@ function parseImageUrl(url) {
|
|
|
2950
2950
|
var isDataImage = (r) => "base64" in r;
|
|
2951
2951
|
|
|
2952
2952
|
// src/llm/anthropic.ts
|
|
2953
|
+
var noTemperature = /* @__PURE__ */ new Set();
|
|
2954
|
+
var TEMP_DEPRECATED = /temperature.{0,40}deprecated|deprecated.{0,40}temperature/i;
|
|
2953
2955
|
var anthropic = (cfg2) => {
|
|
2954
2956
|
const base2 = (cfg2.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
|
|
2955
2957
|
const headers = { "content-type": "application/json", "x-api-key": cfg2.apiKey, "anthropic-version": "2023-06-01" };
|
|
2956
2958
|
return async (req) => {
|
|
2957
2959
|
const system = req.messages.filter((m) => m.role === "system").map((m) => m.content).join("\n");
|
|
2958
|
-
const
|
|
2960
|
+
const mkBody = (withTemp) => ({
|
|
2959
2961
|
model: cfg2.model,
|
|
2960
2962
|
max_tokens: req.maxTokens ?? 4096,
|
|
2961
2963
|
// Anthropic requires this
|
|
2962
|
-
temperature: cfg2.temperature ?? 0,
|
|
2964
|
+
...withTemp ? { temperature: cfg2.temperature ?? 0 } : {},
|
|
2965
|
+
// newest models deprecated it → omit (see below)
|
|
2963
2966
|
...system ? { system } : {},
|
|
2964
2967
|
messages: toMsgs(req.messages),
|
|
2965
2968
|
...req.tools?.length ? { tools: req.tools.map(toTool2) } : {}
|
|
2969
|
+
});
|
|
2970
|
+
const run2 = (withTemp) => {
|
|
2971
|
+
const body = mkBody(withTemp);
|
|
2972
|
+
if (req.onDelta) return stream2(base2, headers, body, req.onDelta, req.signal);
|
|
2973
|
+
return blocking(base2, headers, body, req.signal);
|
|
2966
2974
|
};
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2975
|
+
try {
|
|
2976
|
+
return await run2(!noTemperature.has(cfg2.model));
|
|
2977
|
+
} catch (e) {
|
|
2978
|
+
if (!noTemperature.has(cfg2.model) && TEMP_DEPRECATED.test(String(e.message))) {
|
|
2979
|
+
noTemperature.add(cfg2.model);
|
|
2980
|
+
return await run2(false);
|
|
2981
|
+
}
|
|
2982
|
+
throw e;
|
|
2983
|
+
}
|
|
2972
2984
|
};
|
|
2973
2985
|
};
|
|
2986
|
+
async function blocking(base2, headers, body, signal) {
|
|
2987
|
+
const res = await fetch(`${base2}/messages`, { method: "POST", headers, body: JSON.stringify(body), signal });
|
|
2988
|
+
if (!res.ok) throw new Error(`llm ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
2989
|
+
return fromReply(await res.json());
|
|
2990
|
+
}
|
|
2974
2991
|
function fromReply(data) {
|
|
2975
2992
|
const blocks = data.content ?? [];
|
|
2976
2993
|
const text2 = blocks.filter((b) => b.type === "text").map((b) => b.text).join("") || void 0;
|
|
@@ -3175,6 +3192,22 @@ var PROVIDER_LABEL = {
|
|
|
3175
3192
|
google: "Gemini"
|
|
3176
3193
|
};
|
|
3177
3194
|
var labelOf = (provider2) => PROVIDER_LABEL[provider2.toLowerCase()] ?? provider2;
|
|
3195
|
+
var VENDOR_PREFIX = [
|
|
3196
|
+
["claude", "Anthropic"],
|
|
3197
|
+
["gpt", "OpenAI"],
|
|
3198
|
+
["codex", "OpenAI"],
|
|
3199
|
+
["chatgpt", "OpenAI"],
|
|
3200
|
+
["o1", "OpenAI"],
|
|
3201
|
+
["o3", "OpenAI"],
|
|
3202
|
+
["o4", "OpenAI"],
|
|
3203
|
+
["gemini", "Gemini"],
|
|
3204
|
+
["deepseek", "DeepSeek"]
|
|
3205
|
+
];
|
|
3206
|
+
function modelProviderLabel(modelId, fallbackProvider) {
|
|
3207
|
+
const id = modelId.toLowerCase();
|
|
3208
|
+
for (const [prefix, vendor] of VENDOR_PREFIX) if (id.startsWith(prefix)) return vendor;
|
|
3209
|
+
return labelOf(fallbackProvider);
|
|
3210
|
+
}
|
|
3178
3211
|
var defaultModel = {
|
|
3179
3212
|
gapi: "gmodel-pro",
|
|
3180
3213
|
gmodel: "gmodel-pro",
|
|
@@ -3247,7 +3280,9 @@ async function listModels(provider2, cfg2) {
|
|
|
3247
3280
|
return (d2.models ?? []).filter((m) => m.supportedGenerationMethods?.includes("generateContent")).map((m) => (m.name ?? "").replace(/^models\//, "")).filter(Boolean);
|
|
3248
3281
|
}
|
|
3249
3282
|
if (p === "anthropic" || p === "claude") {
|
|
3250
|
-
const
|
|
3283
|
+
const base3 = cfg2.baseUrl?.replace(/\/$/, "");
|
|
3284
|
+
const url = base3 ? `${base3}/models` : "https://api.anthropic.com/v1/models";
|
|
3285
|
+
const r2 = await fetch(url, { headers: { "x-api-key": cfg2.apiKey, authorization: `Bearer ${cfg2.apiKey}`, "anthropic-version": "2023-06-01" } });
|
|
3251
3286
|
if (!r2.ok) return [];
|
|
3252
3287
|
const d2 = await r2.json();
|
|
3253
3288
|
return (d2.data ?? []).map((m) => m.id ?? "").filter(Boolean);
|
|
@@ -3378,7 +3413,7 @@ var shell = {
|
|
|
3378
3413
|
};
|
|
3379
3414
|
|
|
3380
3415
|
// src/tools/read_file.ts
|
|
3381
|
-
import { readFileSync
|
|
3416
|
+
import { readFileSync } from "node:fs";
|
|
3382
3417
|
|
|
3383
3418
|
// src/tools/file.ts
|
|
3384
3419
|
import { resolve, isAbsolute, relative } from "node:path";
|
|
@@ -3395,17 +3430,23 @@ function safePath(p) {
|
|
|
3395
3430
|
var MAX = 64 * 1024;
|
|
3396
3431
|
var readFile = {
|
|
3397
3432
|
name: "read_file",
|
|
3398
|
-
schema: { name: "read_file", description: "read a UTF-8 text file (confined to the launch dir)", parameters: { type: "object", required: ["path"], properties: {
|
|
3399
|
-
path: { type: "string", description: "file path, relative to the launch dir" }
|
|
3433
|
+
schema: { name: "read_file", description: "read a UTF-8 text file (confined to the launch dir). Large files page: pass `offset` (and the result\u2019s `nextOffset`) to read the rest.", parameters: { type: "object", required: ["path"], properties: {
|
|
3434
|
+
path: { type: "string", description: "file path, relative to the launch dir" },
|
|
3435
|
+
offset: { type: "number", description: "char index to start at (default 0); use the previous result\u2019s `nextOffset` to continue" },
|
|
3436
|
+
limit: { type: "number", description: `max chars to return (default & cap ${MAX})` }
|
|
3400
3437
|
} } },
|
|
3401
3438
|
run: (i) => {
|
|
3402
|
-
const { path } = i;
|
|
3439
|
+
const { path, offset, limit } = i;
|
|
3403
3440
|
const abs = safePath(path);
|
|
3404
3441
|
if (!abs) return { error: `path escapes the working directory: ${path}` };
|
|
3405
3442
|
try {
|
|
3406
|
-
const size = statSync(abs).size;
|
|
3407
3443
|
const content = readFileSync(abs, "utf8");
|
|
3408
|
-
|
|
3444
|
+
const total = content.length;
|
|
3445
|
+
const start = Math.max(0, Math.floor(offset ?? 0));
|
|
3446
|
+
if (start >= total) return total === 0 ? { path, content: "" } : { path, content: "", total, note: `offset ${start} is at/past end of file (${total} chars)` };
|
|
3447
|
+
const slice = content.slice(start, start + Math.min(limit ?? MAX, MAX));
|
|
3448
|
+
const end = start + slice.length;
|
|
3449
|
+
return end < total ? { path, content: slice, truncated: true, total, offset: start, nextOffset: end, note: `showing chars ${start}\u2013${end} of ${total}; call read_file again with offset=${end} for the rest` } : { path, content: slice, ...start > 0 ? { offset: start, total } : {} };
|
|
3409
3450
|
} catch (e) {
|
|
3410
3451
|
return { error: e instanceof Error ? e.message : String(e) };
|
|
3411
3452
|
}
|
|
@@ -3435,7 +3476,7 @@ var listDir = {
|
|
|
3435
3476
|
};
|
|
3436
3477
|
|
|
3437
3478
|
// src/tools/grep.ts
|
|
3438
|
-
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync
|
|
3479
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
3439
3480
|
import { join } from "node:path";
|
|
3440
3481
|
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".next", "coverage", ".cache"]);
|
|
3441
3482
|
var MAX_HITS = 100;
|
|
@@ -3474,7 +3515,7 @@ var grep = {
|
|
|
3474
3515
|
const rootLen = base().length + 1;
|
|
3475
3516
|
for (const f of walk(root)) {
|
|
3476
3517
|
try {
|
|
3477
|
-
if (
|
|
3518
|
+
if (statSync(f).size > MAX_FILE) continue;
|
|
3478
3519
|
} catch {
|
|
3479
3520
|
continue;
|
|
3480
3521
|
}
|
|
@@ -5401,13 +5442,99 @@ function nursery(sys) {
|
|
|
5401
5442
|
};
|
|
5402
5443
|
}
|
|
5403
5444
|
|
|
5445
|
+
// ../agent-os/src/kernel/blob.ts
|
|
5446
|
+
var PAGE = 8 * 1024;
|
|
5447
|
+
var GREP_MAX = 50;
|
|
5448
|
+
var BlobStore = class {
|
|
5449
|
+
constructor(cap = 4 * 1024 * 1024) {
|
|
5450
|
+
this.cap = cap;
|
|
5451
|
+
}
|
|
5452
|
+
cap;
|
|
5453
|
+
map = /* @__PURE__ */ new Map();
|
|
5454
|
+
// insertion order = LRU age (re-set on touch → newest)
|
|
5455
|
+
seq = 0;
|
|
5456
|
+
bytes = 0;
|
|
5457
|
+
// 4 MB total
|
|
5458
|
+
/** Store content; return a fresh handle. Evict the oldest blobs while over the char cap. */
|
|
5459
|
+
put(content) {
|
|
5460
|
+
const handle = `blob_${(++this.seq).toString(36)}`;
|
|
5461
|
+
this.map.set(handle, content);
|
|
5462
|
+
this.bytes += content.length;
|
|
5463
|
+
while (this.bytes > this.cap && this.map.size > 1) {
|
|
5464
|
+
const [old, v] = this.map.entries().next().value;
|
|
5465
|
+
this.map.delete(old);
|
|
5466
|
+
this.bytes -= v.length;
|
|
5467
|
+
}
|
|
5468
|
+
return handle;
|
|
5469
|
+
}
|
|
5470
|
+
touch(handle) {
|
|
5471
|
+
const v = this.map.get(handle);
|
|
5472
|
+
if (v === void 0) return void 0;
|
|
5473
|
+
this.map.delete(handle);
|
|
5474
|
+
this.map.set(handle, v);
|
|
5475
|
+
return v;
|
|
5476
|
+
}
|
|
5477
|
+
/** A character window of a blob (paged like read_file). */
|
|
5478
|
+
read(handle, offset = 0, limit = PAGE) {
|
|
5479
|
+
const v = this.touch(handle);
|
|
5480
|
+
if (v === void 0) return { error: `unknown blob handle: ${handle}` };
|
|
5481
|
+
const total = v.length;
|
|
5482
|
+
const start = Math.max(0, Math.floor(offset));
|
|
5483
|
+
if (start >= total) return { handle, content: "", total, note: `offset ${start} is at/past end (${total} chars)` };
|
|
5484
|
+
const slice = v.slice(start, start + Math.min(Math.max(1, Math.floor(limit)), PAGE));
|
|
5485
|
+
const end = start + slice.length;
|
|
5486
|
+
return end < total ? { handle, content: slice, truncated: true, total, offset: start, nextOffset: end, note: `chars ${start}\u2013${end} of ${total}; call blob_read with offset=${end} for more` } : { handle, content: slice, total, offset: start };
|
|
5487
|
+
}
|
|
5488
|
+
/** Search a blob for a JS regex; return a short SNIPPET (char offset + surrounding window) per match. Snippet
|
|
5489
|
+
* rather than whole line because a blob is often one long line (JSON.stringify escapes newlines) — a line view
|
|
5490
|
+
* would return the entire blob and just overflow again. The offset lets the model blob_read around it. */
|
|
5491
|
+
grep(handle, pattern, max = GREP_MAX) {
|
|
5492
|
+
const v = this.touch(handle);
|
|
5493
|
+
if (v === void 0) return { error: `unknown blob handle: ${handle}` };
|
|
5494
|
+
let re;
|
|
5495
|
+
try {
|
|
5496
|
+
re = new RegExp(pattern, "g");
|
|
5497
|
+
} catch {
|
|
5498
|
+
return { error: `invalid pattern: ${pattern}` };
|
|
5499
|
+
}
|
|
5500
|
+
const PAD = 80, matches = [];
|
|
5501
|
+
let m;
|
|
5502
|
+
while ((m = re.exec(v)) && matches.length < max) {
|
|
5503
|
+
const at = m.index, from = Math.max(0, at - PAD), to = Math.min(v.length, at + m[0].length + PAD);
|
|
5504
|
+
matches.push(`@${at}: ${from > 0 ? "\u2026" : ""}${v.slice(from, to)}${to < v.length ? "\u2026" : ""}`);
|
|
5505
|
+
if (re.lastIndex === at) re.lastIndex++;
|
|
5506
|
+
}
|
|
5507
|
+
return { handle, total: v.length, matches, ...matches.length >= max ? { truncated: true } : {} };
|
|
5508
|
+
}
|
|
5509
|
+
};
|
|
5510
|
+
var BLOB_TOOLS = [
|
|
5511
|
+
{ name: "blob_read", description: "read a window of a large stored tool output by its blob handle (paged like read_file; use when a result said it was stored as blob_\u2026)", parameters: { type: "object", required: ["handle"], properties: {
|
|
5512
|
+
handle: { type: "string", description: "the blob_\u2026 handle from a truncated result" },
|
|
5513
|
+
offset: { type: "number", description: "char index to start at (default 0; use the previous result\u2019s nextOffset)" },
|
|
5514
|
+
limit: { type: "number", description: `max chars (default & cap ${PAGE})` }
|
|
5515
|
+
} } },
|
|
5516
|
+
{ name: "blob_grep", description: "find lines matching a JS regex inside a large stored tool output, by blob handle (search without re-reading it all)", parameters: { type: "object", required: ["handle", "pattern"], properties: {
|
|
5517
|
+
handle: { type: "string", description: "the blob_\u2026 handle" },
|
|
5518
|
+
pattern: { type: "string", description: "JS regex, matched per line" }
|
|
5519
|
+
} } }
|
|
5520
|
+
];
|
|
5521
|
+
var BLOB_NAMES = new Set(BLOB_TOOLS.map((t) => t.name));
|
|
5522
|
+
var isBlobTool = (name) => BLOB_NAMES.has(name);
|
|
5523
|
+
function execBlobTool(store, name, input) {
|
|
5524
|
+
const a = input ?? {};
|
|
5525
|
+
if (!a.handle) return { error: "handle is required" };
|
|
5526
|
+
if (name === "blob_read") return store.read(a.handle, a.offset, a.limit);
|
|
5527
|
+
if (name === "blob_grep") return a.pattern ? store.grep(a.handle, a.pattern) : { error: "pattern is required" };
|
|
5528
|
+
return { error: `not a blob tool: ${name}` };
|
|
5529
|
+
}
|
|
5530
|
+
|
|
5404
5531
|
// ../agent-os/src/userland/agent.ts
|
|
5405
5532
|
var DEFAULT_SYSTEM = "You are a capable autonomous agent. Break the task into steps and keep going \u2014 investigate with tools, then act \u2014 until it is fully done; never stop at a plan or a partial result. Verify before answering, then reply concisely.";
|
|
5406
5533
|
var HARD_MAX = 384;
|
|
5407
5534
|
var MAX_TOOL_CHARS = 8e3;
|
|
5408
5535
|
var isMessage = (b) => !!b && typeof b.role === "string";
|
|
5409
|
-
var capResult = (s) => s.length
|
|
5410
|
-
\u2026[
|
|
5536
|
+
var capResult = (s, blobs) => s.length <= MAX_TOOL_CHARS ? s : s.slice(0, MAX_TOOL_CHARS) + `
|
|
5537
|
+
\u2026[+${s.length - MAX_TOOL_CHARS} chars \u2014 full result stored as ${blobs.put(s)}; call blob_read(handle, offset=${MAX_TOOL_CHARS}) or blob_grep(handle, pattern) for the rest]`;
|
|
5411
5538
|
function orderForCache(segs) {
|
|
5412
5539
|
const lead = [], sums = [], rest = [];
|
|
5413
5540
|
for (const s of segs) (s.pinned && !s.summary ? lead : s.summary ? sums : rest).push(s);
|
|
@@ -5435,7 +5562,7 @@ function unproductive(out) {
|
|
|
5435
5562
|
const st = o["status"];
|
|
5436
5563
|
return typeof st === "number" && (st < 200 || st >= 300);
|
|
5437
5564
|
}
|
|
5438
|
-
async function runTurn(sys, llm, tools2, spec) {
|
|
5565
|
+
async function runTurn(sys, llm, tools2, spec, blobs) {
|
|
5439
5566
|
const ceil = Math.max(1, Math.min(spec.maxSteps ?? HARD_MAX, HARD_MAX));
|
|
5440
5567
|
const base2 = Math.max(1, spec.baseSteps ?? 16);
|
|
5441
5568
|
const stallMax = Math.max(1, spec.stallLimit ?? 3);
|
|
@@ -5458,6 +5585,7 @@ async function runTurn(sys, llm, tools2, spec) {
|
|
|
5458
5585
|
}
|
|
5459
5586
|
const messages = promptMessages();
|
|
5460
5587
|
let offer = noTools ? void 0 : spec.tools;
|
|
5588
|
+
if (offer && tools2) offer = [...offer, ...BLOB_TOOLS];
|
|
5461
5589
|
if (steer && offer && del) offer = offer.filter((t) => !del.withhold.includes(t.name));
|
|
5462
5590
|
const reply = await sys.read(llm, { messages, tools: offer, maxTokens: spec.maxTokens });
|
|
5463
5591
|
for (const s of sys.workingSet()) {
|
|
@@ -5478,13 +5606,15 @@ async function runTurn(sys, llm, tools2, spec) {
|
|
|
5478
5606
|
}
|
|
5479
5607
|
}
|
|
5480
5608
|
if (fresh.length) {
|
|
5481
|
-
const
|
|
5482
|
-
|
|
5609
|
+
const viaDevice = fresh.filter((f) => !isBlobTool(f.req.tool));
|
|
5610
|
+
const devOuts = viaDevice.length ? await sys.readAll(tools2, viaDevice.map((f) => f.req)) : [];
|
|
5611
|
+
let di = 0;
|
|
5612
|
+
for (const f of fresh) cache.set(f.key, isBlobTool(f.req.tool) ? execBlobTool(blobs, f.req.tool, f.req.input) : devOuts[di++]);
|
|
5483
5613
|
}
|
|
5484
5614
|
let progressed = false;
|
|
5485
5615
|
for (let i = 0; i < calls.length; i++) {
|
|
5486
5616
|
const out = cache.get(keys[i]);
|
|
5487
|
-
await sys.remember({ role: "tool", toolCallId: calls[i].id, name: calls[i].name, content: capResult(JSON.stringify(out)) });
|
|
5617
|
+
await sys.remember({ role: "tool", toolCallId: calls[i].id, name: calls[i].name, content: capResult(JSON.stringify(out), blobs) });
|
|
5488
5618
|
if (ran.has(keys[i]) && !unproductive(out)) progressed = true;
|
|
5489
5619
|
}
|
|
5490
5620
|
if (del) {
|
|
@@ -5535,7 +5665,7 @@ function reactAgent(spec) {
|
|
|
5535
5665
|
const { llm, tools: tools2, parent } = await openDevices(sys);
|
|
5536
5666
|
await sys.remember({ role: "system", content: spec.system ?? DEFAULT_SYSTEM }, { pin: true });
|
|
5537
5667
|
await sys.remember({ role: "user", content: spec.task ?? "" }, { pin: true });
|
|
5538
|
-
const r = await runTurn(sys, llm, tools2, spec);
|
|
5668
|
+
const r = await runTurn(sys, llm, tools2, spec, new BlobStore());
|
|
5539
5669
|
if (parent) await sys.send(parent, { $: "result", answer: r.answer, steps: r.steps });
|
|
5540
5670
|
} };
|
|
5541
5671
|
}
|
|
@@ -5553,13 +5683,14 @@ function conversationAgent(spec = {}) {
|
|
|
5553
5683
|
} else {
|
|
5554
5684
|
await sys.remember({ role: "system", content: spec.system ?? DEFAULT_SYSTEM }, { pin: true });
|
|
5555
5685
|
}
|
|
5686
|
+
const blobs = new BlobStore();
|
|
5556
5687
|
for (; ; ) {
|
|
5557
5688
|
const msg = await sys.recv();
|
|
5558
5689
|
const userTurn = { role: "user", content: msg.body.task, ...msg.body.images?.length ? { images: msg.body.images } : {} };
|
|
5559
5690
|
await sys.remember(userTurn);
|
|
5560
5691
|
let r;
|
|
5561
5692
|
try {
|
|
5562
|
-
r = await runTurn(sys, llm, tools2, spec);
|
|
5693
|
+
r = await runTurn(sys, llm, tools2, spec, blobs);
|
|
5563
5694
|
} catch (e) {
|
|
5564
5695
|
r = { answer: "", steps: 0, error: e instanceof Error ? e.message : String(e) };
|
|
5565
5696
|
}
|
|
@@ -6002,7 +6133,7 @@ import { homedir } from "node:os";
|
|
|
6002
6133
|
import { join as join3 } from "node:path";
|
|
6003
6134
|
|
|
6004
6135
|
// src/skills/loader.ts
|
|
6005
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync6, existsSync as existsSync4, statSync as
|
|
6136
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync6, existsSync as existsSync4, statSync as statSync2, openSync, readSync, closeSync } from "node:fs";
|
|
6006
6137
|
import { join as join2, basename, extname } from "node:path";
|
|
6007
6138
|
var FM_RE = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
6008
6139
|
var HEAD_BYTES = 8192;
|
|
@@ -6048,7 +6179,7 @@ function loadSkillsDir(dir) {
|
|
|
6048
6179
|
for (const entry of readdirSync3(dir)) {
|
|
6049
6180
|
try {
|
|
6050
6181
|
const p = join2(dir, entry);
|
|
6051
|
-
if (
|
|
6182
|
+
if (statSync2(p).isDirectory()) {
|
|
6052
6183
|
const md = join2(p, "SKILL.md");
|
|
6053
6184
|
if (existsSync4(md)) out.push(lazyFromFile(md, entry));
|
|
6054
6185
|
} else if (extname(entry) === ".md") out.push(lazyFromFile(p, basename(entry, ".md")));
|
|
@@ -9875,7 +10006,7 @@ function codingReflect(plan) {
|
|
|
9875
10006
|
}
|
|
9876
10007
|
|
|
9877
10008
|
// src/agent/session.ts
|
|
9878
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5, statSync as
|
|
10009
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5, statSync as statSync3, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "node:fs";
|
|
9879
10010
|
import { homedir as homedir3 } from "node:os";
|
|
9880
10011
|
import { join as join7 } from "node:path";
|
|
9881
10012
|
var oneLine = (s) => typeof s === "string" ? s.replace(/\s+/g, " ").trim() : "";
|
|
@@ -9933,7 +10064,7 @@ function sessionIdsByRecency() {
|
|
|
9933
10064
|
if (!existsSync8(dir)) return [];
|
|
9934
10065
|
return readdirSync5(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
9935
10066
|
try {
|
|
9936
|
-
return { id: f.slice(0, -5), mt:
|
|
10067
|
+
return { id: f.slice(0, -5), mt: statSync3(join7(dir, f)).mtimeMs };
|
|
9937
10068
|
} catch {
|
|
9938
10069
|
return { id: f.slice(0, -5), mt: 0 };
|
|
9939
10070
|
}
|
|
@@ -9947,12 +10078,12 @@ function loadSessionMeta(id) {
|
|
|
9947
10078
|
// src/ui/app.ts
|
|
9948
10079
|
import { resolve as resolve3, join as join11 } from "node:path";
|
|
9949
10080
|
import { homedir as homedir6, tmpdir as tmpdir3 } from "node:os";
|
|
9950
|
-
import { statSync as
|
|
10081
|
+
import { statSync as statSync5, readFileSync as readFileSync14, unlinkSync as unlinkSync6 } from "node:fs";
|
|
9951
10082
|
|
|
9952
10083
|
// package.json
|
|
9953
10084
|
var package_default = {
|
|
9954
10085
|
name: "@guangnao/agent-cli",
|
|
9955
|
-
version: "1.1.
|
|
10086
|
+
version: "1.1.7",
|
|
9956
10087
|
description: "AgentOS terminal CLI \u2014 drive a real LLM agent on the microkernel; strongest-UX shell, advanced agent core",
|
|
9957
10088
|
type: "module",
|
|
9958
10089
|
bin: {
|
|
@@ -10110,7 +10241,7 @@ var modeLabel = (m) => m === "auto" ? "auto" : m === "acceptEdits" ? "auto-edits
|
|
|
10110
10241
|
|
|
10111
10242
|
// src/ui/clipboard.ts
|
|
10112
10243
|
import { execFile } from "node:child_process";
|
|
10113
|
-
import { existsSync as existsSync10, readFileSync as readFileSync12, statSync as
|
|
10244
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12, statSync as statSync4, unlinkSync as unlinkSync5 } from "node:fs";
|
|
10114
10245
|
import { tmpdir } from "node:os";
|
|
10115
10246
|
import { join as join9 } from "node:path";
|
|
10116
10247
|
var MACOS_JXA = `(function(){
|
|
@@ -10214,7 +10345,7 @@ async function shrinkImage(src) {
|
|
|
10214
10345
|
const maxEdge = dims ? Math.max(dims.w, dims.h) : Infinity;
|
|
10215
10346
|
let origBytes = Infinity;
|
|
10216
10347
|
try {
|
|
10217
|
-
origBytes =
|
|
10348
|
+
origBytes = statSync4(src).size;
|
|
10218
10349
|
} catch {
|
|
10219
10350
|
}
|
|
10220
10351
|
if (maxEdge <= MAX_EDGE && origBytes <= PASSTHROUGH_BYTES) return fallback;
|
|
@@ -10230,7 +10361,7 @@ async function shrinkImage(src) {
|
|
|
10230
10361
|
await sips([...resize, "-s", "format", "png", src, "--out", pngOut]);
|
|
10231
10362
|
let pngBytes = Infinity;
|
|
10232
10363
|
try {
|
|
10233
|
-
pngBytes =
|
|
10364
|
+
pngBytes = statSync4(pngOut).size;
|
|
10234
10365
|
} catch {
|
|
10235
10366
|
}
|
|
10236
10367
|
if (pngBytes > PNG_TO_JPEG_BYTES) {
|
|
@@ -11675,7 +11806,10 @@ ${c.dim(cut(p.system, 300))}`);
|
|
|
11675
11806
|
return;
|
|
11676
11807
|
}
|
|
11677
11808
|
const choice = await this.dock.requestSplitSelect(
|
|
11678
|
-
opts.map((o) =>
|
|
11809
|
+
opts.map((o) => {
|
|
11810
|
+
const vendor = modelProviderLabel(o.model, o.provider);
|
|
11811
|
+
return { label: o.model, hint: vendor, value: `${o.provider}|${o.model}`, detail: `provider: ${vendor}${`${vendor} \xB7 ${o.model}` === m.current ? " (current)" : ""}` };
|
|
11812
|
+
}),
|
|
11679
11813
|
"switch model"
|
|
11680
11814
|
);
|
|
11681
11815
|
if (choice) this.switchModel(choice);
|
|
@@ -11683,12 +11817,13 @@ ${c.dim(cut(p.system, 300))}`);
|
|
|
11683
11817
|
switchModel(v) {
|
|
11684
11818
|
const sep = v.indexOf("|"), provider2 = v.slice(0, sep), model2 = v.slice(sep + 1);
|
|
11685
11819
|
this.chat = this.opts.modelMenu.build(provider2, model2);
|
|
11686
|
-
this.opts.modelMenu.
|
|
11820
|
+
this.opts.modelMenu.persist?.(provider2, model2);
|
|
11821
|
+
this.opts.modelMenu.current = `${modelProviderLabel(model2, provider2)} \xB7 ${model2}`;
|
|
11687
11822
|
const history = this.convo?.dump();
|
|
11688
11823
|
this.convo?.close();
|
|
11689
11824
|
this.convo = void 0;
|
|
11690
11825
|
if (history?.length) this.ensureConvo(history);
|
|
11691
|
-
this.note(`${c.done("model")} ${c.dim("\u2192")} ${c.bold(model2)} ${c.dim("(" +
|
|
11826
|
+
this.note(`${c.done("model")} ${c.dim("\u2192")} ${c.bold(model2)} ${c.dim("(" + modelProviderLabel(model2, provider2) + ")")}`);
|
|
11692
11827
|
this.dock.setStatusLine(this.statusLine());
|
|
11693
11828
|
}
|
|
11694
11829
|
/** /login — re-run the first-run setup wizard (pick provider → paste key → pick model), exactly the flow a
|
|
@@ -11915,7 +12050,7 @@ ${c.done("\u2713")} ${c.dim("Saved connection cleared. Run ")}${c.bold("agentos"
|
|
|
11915
12050
|
static dirOk(d) {
|
|
11916
12051
|
if (!d) return false;
|
|
11917
12052
|
try {
|
|
11918
|
-
return
|
|
12053
|
+
return statSync5(d).isDirectory();
|
|
11919
12054
|
} catch {
|
|
11920
12055
|
return false;
|
|
11921
12056
|
}
|
|
@@ -11943,7 +12078,7 @@ ${c.dim("set: /cwd <path> \xB7 global default: /cwd -g <path>")}`);
|
|
|
11943
12078
|
const expanded = path === "~" || path.startsWith("~/") ? homedir6() + path.slice(1) : path;
|
|
11944
12079
|
const abs = resolve3(process.env["AGENTOS_CWD"] || process.cwd(), expanded);
|
|
11945
12080
|
try {
|
|
11946
|
-
if (!
|
|
12081
|
+
if (!statSync5(abs).isDirectory()) {
|
|
11947
12082
|
this.note(c.fail(`not a directory: ${abs}`));
|
|
11948
12083
|
return;
|
|
11949
12084
|
}
|
|
@@ -12257,8 +12392,14 @@ var keyOf = (p) => ({
|
|
|
12257
12392
|
function modelMenu() {
|
|
12258
12393
|
const keyed = ["gapi", "deepseek", "openai", "openai-responses", "gemini", "anthropic"].filter((p) => keyOf(p));
|
|
12259
12394
|
return {
|
|
12260
|
-
current: `${
|
|
12395
|
+
current: `${modelProviderLabel(model, provider)} \xB7 ${model}`,
|
|
12261
12396
|
build: (p, m) => makeChat(p, { apiKey: keyOf(p) ?? apiKey, model: m, baseUrl }),
|
|
12397
|
+
persist: (p, m) => {
|
|
12398
|
+
provider = p;
|
|
12399
|
+
model = m;
|
|
12400
|
+
saveConfig({ provider: p, model: m });
|
|
12401
|
+
},
|
|
12402
|
+
// /models choice survives restart (apiKey/baseUrl already saved)
|
|
12262
12403
|
async list() {
|
|
12263
12404
|
const lists = await Promise.all(keyed.map(async (p) => {
|
|
12264
12405
|
const ids = await listModels(p, { apiKey: keyOf(p) ?? apiKey, model: defaultModel[p] ?? model, baseUrl });
|
|
@@ -12277,7 +12418,7 @@ async function relogin() {
|
|
|
12277
12418
|
model = r.model;
|
|
12278
12419
|
baseUrl = process.env.LLM_BASE_URL ?? loadConfig().baseUrl;
|
|
12279
12420
|
buildChats();
|
|
12280
|
-
return { chat: rawChat, cheapChat, label: `${
|
|
12421
|
+
return { chat: rawChat, cheapChat, label: `${modelProviderLabel(model, provider)} \xB7 ${model}` };
|
|
12281
12422
|
}
|
|
12282
12423
|
async function main() {
|
|
12283
12424
|
if (!apiKey && !process.env.AGENTOS_OFFLINE) {
|