isorite 0.14.0 → 0.15.0
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/main.js +342 -38
- package/docs/terminal.md +3 -1
- package/package.json +1 -1
- package/web/_app/immutable/assets/{0.BPP604gH.css → 0.CtWz9d2A.css} +1 -1
- package/web/_app/immutable/chunks/{BWE_HS0c.js → 0B47b24t.js} +1 -1
- package/web/_app/immutable/chunks/{CzYPMYd4.js → 1j--pDqt.js} +1 -1
- package/web/_app/immutable/chunks/{e2LGvGEb.js → AnnzTlE8.js} +1 -1
- package/web/_app/immutable/chunks/B7Bf3hc7.js +1 -0
- package/web/_app/immutable/chunks/{DRcTlHZP.js → B7LnLClC.js} +1 -1
- package/web/_app/immutable/chunks/B9TKPEJD.js +1 -0
- package/web/_app/immutable/chunks/{ZwM5k8r0.js → BAVnZFPa.js} +1 -1
- package/web/_app/immutable/chunks/{CPSlCQsu.js → BDJlNL-I.js} +3 -3
- package/web/_app/immutable/chunks/{BiATTH-R.js → BLe29cGh.js} +1 -1
- package/web/_app/immutable/chunks/{C8h1oeAZ.js → BLzZIf7l.js} +1 -1
- package/web/_app/immutable/chunks/{DCZYeMlH.js → BPpUSQ2Y.js} +1 -1
- package/web/_app/immutable/chunks/{kG4HctVa.js → BQeONalw.js} +1 -1
- package/web/_app/immutable/chunks/{CtkUy_15.js → BZX8cNYL.js} +1 -1
- package/web/_app/immutable/chunks/{CIYFNih-.js → B_x6_Y3m.js} +1 -1
- package/web/_app/immutable/chunks/{Bb_N98K9.js → BiHyHFlW.js} +1 -1
- package/web/_app/immutable/chunks/{NyWzYSz1.js → BsY4iMci.js} +1 -1
- package/web/_app/immutable/chunks/{DKqMzyyE.js → BtcGx-KQ.js} +1 -1
- package/web/_app/immutable/chunks/{BK31dpt6.js → Bytzbt5l.js} +1 -1
- package/web/_app/immutable/chunks/{DDAG55ce.js → C1FDDux-.js} +1 -1
- package/web/_app/immutable/chunks/C7qBBO-i.js +1 -0
- package/web/_app/immutable/chunks/{CdY1G_wk.js → C9Qdslk3.js} +1 -1
- package/web/_app/immutable/chunks/{CMIt1Ma8.js → CSKABQj2.js} +1 -1
- package/web/_app/immutable/chunks/CSTv2fL0.js +1 -0
- package/web/_app/immutable/chunks/{PsfIQCJ1.js → CUB9vnd0.js} +1 -1
- package/web/_app/immutable/chunks/{DrVtdHUW.js → CUNetmvQ.js} +1 -1
- package/web/_app/immutable/chunks/{Djr0h_v1.js → CVqwkR8N.js} +1 -1
- package/web/_app/immutable/chunks/{CTDYUZPz.js → CWc-mNzo.js} +1 -1
- package/web/_app/immutable/chunks/{CryuQgNh.js → CZSmkKvQ.js} +1 -1
- package/web/_app/immutable/chunks/{BA78_vo5.js → CcE14TDI.js} +1 -1
- package/web/_app/immutable/chunks/{DjUnonHY.js → Ch5Z7cbP.js} +1 -1
- package/web/_app/immutable/chunks/{DFuPQaNa.js → Cpjqsnqr.js} +1 -1
- package/web/_app/immutable/chunks/{Bd-WxALw.js → CwHHpacD.js} +1 -1
- package/web/_app/immutable/chunks/{Dzi94aDH.js → D1MhxXyn.js} +1 -1
- package/web/_app/immutable/chunks/{DBLoWLMb.js → D4EpFhYZ.js} +1 -1
- package/web/_app/immutable/chunks/{UDPiRmKc.js → D7jLdCNu.js} +1 -1
- package/web/_app/immutable/chunks/{CFf_dAzX.js → DAVMwq6b.js} +1 -1
- package/web/_app/immutable/chunks/{C2npZzkn.js → DEQsS4Ez.js} +1 -1
- package/web/_app/immutable/chunks/{B-DzK7Gb.js → DFo4P1vU.js} +1 -1
- package/web/_app/immutable/chunks/{CdmC_l9F.js → DH_19--P.js} +1 -1
- package/web/_app/immutable/chunks/{l1LsK1sB.js → DJQ6favH.js} +1 -1
- package/web/_app/immutable/chunks/{BvPxdlBc.js → DJT73ARj.js} +1 -1
- package/web/_app/immutable/chunks/{BCGaqnao.js → DW5uy3Jw.js} +1 -1
- package/web/_app/immutable/chunks/{BT_btfh0.js → DWwu8a1P.js} +1 -1
- package/web/_app/immutable/chunks/{BhEqJU15.js → DY0M2GMn.js} +1 -1
- package/web/_app/immutable/chunks/{TanDZqdf.js → DbtvzL7r.js} +1 -1
- package/web/_app/immutable/chunks/{BGW1TKtR.js → Djo-ijeo.js} +1 -1
- package/web/_app/immutable/chunks/DtEVmiYo.js +1 -0
- package/web/_app/immutable/chunks/{CT4_O9X_.js → DxRGLDbV.js} +1 -1
- package/web/_app/immutable/chunks/{DxVFSMVD.js → Dx_7xlS4.js} +36 -36
- package/web/_app/immutable/chunks/{D6sVhEz3.js → Ih6r6QR2.js} +1 -1
- package/web/_app/immutable/chunks/{BXBhaEho.js → Kem7IXj7.js} +1 -1
- package/web/_app/immutable/chunks/LbnMg2Kc.js +1 -0
- package/web/_app/immutable/chunks/{Ct_EK1xu.js → MrW4zd8l.js} +1 -1
- package/web/_app/immutable/chunks/{BqipEJXw.js → Mwr1r9aw.js} +1 -1
- package/web/_app/immutable/chunks/{D1LK3xRu.js → OG9GsKke.js} +1 -1
- package/web/_app/immutable/chunks/{B35sQTcK.js → ZTEErEOd.js} +1 -1
- package/web/_app/immutable/chunks/{Cf-5I5_6.js → e7GRa0yc.js} +1 -1
- package/web/_app/immutable/chunks/{C5Z-Y7Lu.js → f7mFVWQS.js} +1 -1
- package/web/_app/immutable/chunks/{Bj-5n5Pb.js → hjSZ041d.js} +1 -1
- package/web/_app/immutable/chunks/{DHJ8FcFD.js → mhWk5y4o.js} +1 -1
- package/web/_app/immutable/chunks/{DymLl-qY.js → wD7RSG2d.js} +1 -1
- package/web/_app/immutable/chunks/{CMikI1NP.js → zasx25UV.js} +3 -3
- package/web/_app/immutable/entry/app.Av5W3V8v.js +2 -0
- package/web/_app/immutable/entry/start.CbpxCvWi.js +1 -0
- package/web/_app/immutable/nodes/0.BTVYyqUm.js +3 -0
- package/web/_app/immutable/nodes/1.DR-b7PMP.js +1 -0
- package/web/_app/immutable/nodes/2.BePyTqoa.js +1 -0
- package/web/_app/immutable/nodes/3.D2rfIXQP.js +1 -0
- package/web/_app/immutable/nodes/4.7tuUcODS.js +15 -0
- package/web/_app/immutable/nodes/5.Cs9hcGU9.js +1 -0
- package/web/_app/immutable/nodes/6.DzvF0ZPt.js +1 -0
- package/web/_app/version.json +1 -1
- package/web/index.html +15 -15
- package/web/_app/immutable/chunks/BPrp4s2r.js +0 -1
- package/web/_app/immutable/chunks/CAlIuau0.js +0 -1
- package/web/_app/immutable/chunks/Cl5gQm2e.js +0 -1
- package/web/_app/immutable/chunks/DFKYo3Wx.js +0 -1
- package/web/_app/immutable/chunks/X3aTZRe_.js +0 -1
- package/web/_app/immutable/chunks/ej_0XxR7.js +0 -1
- package/web/_app/immutable/entry/app.BzA5QYtw.js +0 -2
- package/web/_app/immutable/entry/start.BOh0Gj31.js +0 -1
- package/web/_app/immutable/nodes/0.BmaxMLzC.js +0 -2
- package/web/_app/immutable/nodes/1.IQDu8pD1.js +0 -1
- package/web/_app/immutable/nodes/2.D2Oi-ADx.js +0 -1
- package/web/_app/immutable/nodes/3.CF1W85jb.js +0 -1
- package/web/_app/immutable/nodes/4.BMYBn904.js +0 -15
- package/web/_app/immutable/nodes/5.1vm6WZdM.js +0 -1
- package/web/_app/immutable/nodes/6.C9ia8cDm.js +0 -1
package/dist/main.js
CHANGED
|
@@ -2865,7 +2865,7 @@ var Conflict = class extends EngineError {
|
|
|
2865
2865
|
};
|
|
2866
2866
|
//#endregion
|
|
2867
2867
|
//#region packages/engine/src/lookup.ts
|
|
2868
|
-
const str$
|
|
2868
|
+
const str$2 = { type: "string" };
|
|
2869
2869
|
const fn$1 = (name, description, properties, required) => ({
|
|
2870
2870
|
type: "function",
|
|
2871
2871
|
function: {
|
|
@@ -2881,9 +2881,9 @@ const fn$1 = (name, description, properties, required) => ({
|
|
|
2881
2881
|
});
|
|
2882
2882
|
const TOOLS = [
|
|
2883
2883
|
fn$1("search_notes", "Keyword search over the knowledge base. Returns note ids, titles, types, one-sentence descriptions and a one-line snippet. Search by the words a note would use.", {
|
|
2884
|
-
query: str$
|
|
2884
|
+
query: str$2,
|
|
2885
2885
|
under: {
|
|
2886
|
-
...str$
|
|
2886
|
+
...str$2,
|
|
2887
2887
|
description: "Only notes in this folder."
|
|
2888
2888
|
},
|
|
2889
2889
|
limit: {
|
|
@@ -2892,24 +2892,24 @@ const TOOLS = [
|
|
|
2892
2892
|
}
|
|
2893
2893
|
}, ["query"]),
|
|
2894
2894
|
fn$1("outline_note", "A note's description, its headings, the notes it links to and the notes that link to it, without its body. Use it to choose a section to read.", { id: {
|
|
2895
|
-
...str$
|
|
2895
|
+
...str$2,
|
|
2896
2896
|
description: "The note's id or path: basics/entropy or /basics/entropy.md."
|
|
2897
2897
|
} }, ["id"]),
|
|
2898
2898
|
fn$1("read_note", "Read one section of a note, by its heading, or the whole note when no section is named. Prefer a section. A picture in it is : you have its words, not the picture.", {
|
|
2899
|
-
id: str$
|
|
2899
|
+
id: str$2,
|
|
2900
2900
|
section: {
|
|
2901
|
-
...str$
|
|
2901
|
+
...str$2,
|
|
2902
2902
|
description: "A heading's text, as outline_note gives it."
|
|
2903
2903
|
}
|
|
2904
2904
|
}, ["id"])
|
|
2905
2905
|
];
|
|
2906
2906
|
const FIND_SIMILAR = fn$1("find_similar", "Find notes by meaning: notes that say something like the text you give, even in quite other words. Slower and vaguer than search_notes: use it after search_notes and the links of the notes in hand have not found what you need, or when you cannot guess the words a note would use. Give a question or a sentence, not keywords. Returns each note's nearest section, with how alike it is (0 to 1).", {
|
|
2907
2907
|
text: {
|
|
2908
|
-
...str$
|
|
2908
|
+
...str$2,
|
|
2909
2909
|
description: "A question, or a sentence saying what you are looking for."
|
|
2910
2910
|
},
|
|
2911
2911
|
under: {
|
|
2912
|
-
...str$
|
|
2912
|
+
...str$2,
|
|
2913
2913
|
description: "Only notes in this folder."
|
|
2914
2914
|
},
|
|
2915
2915
|
limit: {
|
|
@@ -3279,7 +3279,7 @@ or try; not for what an answer or a note says as well.
|
|
|
3279
3279
|
problem, fix that and write the whole file again.
|
|
3280
3280
|
4. Say in a sentence what it shows, linked: [what it shows](/folder/name.html).
|
|
3281
3281
|
Link it from the note it belongs to if they want it there.`;
|
|
3282
|
-
const str = { type: "string" };
|
|
3282
|
+
const str$1 = { type: "string" };
|
|
3283
3283
|
const fn = (name, description, properties, required) => ({
|
|
3284
3284
|
type: "function",
|
|
3285
3285
|
function: {
|
|
@@ -3295,16 +3295,16 @@ const fn = (name, description, properties, required) => ({
|
|
|
3295
3295
|
});
|
|
3296
3296
|
const ASK_QUESTION = fn("ask_question", "Ask them a question back, kept as an open question in their base, about one or more of their notes.", {
|
|
3297
3297
|
title: {
|
|
3298
|
-
...str,
|
|
3298
|
+
...str$1,
|
|
3299
3299
|
description: "The question, short, ending in a question mark."
|
|
3300
3300
|
},
|
|
3301
3301
|
text: {
|
|
3302
|
-
...str,
|
|
3302
|
+
...str$1,
|
|
3303
3303
|
description: "A line or two on what is asked, or empty."
|
|
3304
3304
|
},
|
|
3305
3305
|
about: {
|
|
3306
3306
|
type: "array",
|
|
3307
|
-
items: str,
|
|
3307
|
+
items: str$1,
|
|
3308
3308
|
description: "The ids of the notes it is about."
|
|
3309
3309
|
}
|
|
3310
3310
|
}, [
|
|
@@ -3313,7 +3313,7 @@ const ASK_QUESTION = fn("ask_question", "Ask them a question back, kept as an op
|
|
|
3313
3313
|
"about"
|
|
3314
3314
|
]);
|
|
3315
3315
|
const READ_DATA = fn("read_data", "A data file's shape: its columns with their types and ranges, the row count and the first rows. Read it before writing anything that uses the file, so the column names are real.", { path: {
|
|
3316
|
-
...str,
|
|
3316
|
+
...str$1,
|
|
3317
3317
|
description: "The file's path in the base: folder/name.csv."
|
|
3318
3318
|
} }, ["path"]);
|
|
3319
3319
|
const ARTIFACT_RECIPE = fn("artifact_recipe", "A recipe for an artifact: a complete working one to start from, the kit's reference, and the rules write_artifact checks. dashboard: numbers, charts and a table of one data file. compare: two data files side by side. simulation: a model with parameters and play. explorable: inputs, derived numbers and linked views. viewer: a 3D model.", { name: {
|
|
@@ -3328,21 +3328,21 @@ const ARTIFACT_RECIPE = fn("artifact_recipe", "A recipe for an artifact: a compl
|
|
|
3328
3328
|
} }, ["name"]);
|
|
3329
3329
|
const WRITE_ARTIFACT = fn("write_artifact", "Write an artifact: a whole HTML file on the kit. The title, description and uses given here are written into it. It is checked, and run where it can be; the result says what to fix. Nothing is written while a rule is broken.", {
|
|
3330
3330
|
path: {
|
|
3331
|
-
...str,
|
|
3331
|
+
...str$1,
|
|
3332
3332
|
description: "Where: folder/name.html, beside the notes it belongs to."
|
|
3333
3333
|
},
|
|
3334
|
-
title: str,
|
|
3334
|
+
title: str$1,
|
|
3335
3335
|
description: {
|
|
3336
|
-
...str,
|
|
3336
|
+
...str$1,
|
|
3337
3337
|
description: "One sentence: what it shows, and what can be done with it."
|
|
3338
3338
|
},
|
|
3339
3339
|
uses: {
|
|
3340
3340
|
type: "array",
|
|
3341
|
-
items: str,
|
|
3341
|
+
items: str$1,
|
|
3342
3342
|
description: "The data or model files it reads, relative to it (runs.csv). Those its markup and script name are added."
|
|
3343
3343
|
},
|
|
3344
3344
|
html: {
|
|
3345
|
-
...str,
|
|
3345
|
+
...str$1,
|
|
3346
3346
|
description: "The whole file."
|
|
3347
3347
|
}
|
|
3348
3348
|
}, [
|
|
@@ -3353,16 +3353,16 @@ const WRITE_ARTIFACT = fn("write_artifact", "Write an artifact: a whole HTML fil
|
|
|
3353
3353
|
"html"
|
|
3354
3354
|
]);
|
|
3355
3355
|
const READ_ARTIFACT = fn("read_artifact", "An artifact's whole file, to change it and write it again.", { path: {
|
|
3356
|
-
...str,
|
|
3356
|
+
...str$1,
|
|
3357
3357
|
description: "folder/name.html"
|
|
3358
3358
|
} }, ["path"]);
|
|
3359
3359
|
const OFFER_TEXT = fn("offer_text", "Offer them their own answers, lightly joined, to insert at the end of a note as their writing. Only their words.", {
|
|
3360
3360
|
id: {
|
|
3361
|
-
...str,
|
|
3361
|
+
...str$1,
|
|
3362
3362
|
description: "The note's id, or AGENTS.md for a line of their instructions for the base."
|
|
3363
3363
|
},
|
|
3364
3364
|
text: {
|
|
3365
|
-
...str,
|
|
3365
|
+
...str$1,
|
|
3366
3366
|
description: "The passage, in Markdown."
|
|
3367
3367
|
}
|
|
3368
3368
|
}, ["id", "text"]);
|
|
@@ -4232,6 +4232,10 @@ var Engine = class Engine {
|
|
|
4232
4232
|
return null;
|
|
4233
4233
|
}
|
|
4234
4234
|
}
|
|
4235
|
+
async deleteAgentRecord(agent, session) {
|
|
4236
|
+
if (!AGENT_ID.test(agent) || !session || session.length > 200) throw new EngineError("an agent's session is named by the agent and its session's id");
|
|
4237
|
+
await this.opts.own.remove(agentRecordPath(agent, session));
|
|
4238
|
+
}
|
|
4235
4239
|
async agentRecords() {
|
|
4236
4240
|
const own = this.opts.own, all = [];
|
|
4237
4241
|
for (const a of await own.list(AGENTS_DIR).catch(() => [])) {
|
|
@@ -4648,6 +4652,10 @@ var Engine = class Engine {
|
|
|
4648
4652
|
turns: readTurns(text)
|
|
4649
4653
|
};
|
|
4650
4654
|
}
|
|
4655
|
+
async deleteConversation(id) {
|
|
4656
|
+
if (!TURN_ID.test(id)) throw new EngineError("not a conversation's id");
|
|
4657
|
+
await this.opts.own.remove(conversationPath(id));
|
|
4658
|
+
}
|
|
4651
4659
|
async conversations() {
|
|
4652
4660
|
const study = this.opts.own;
|
|
4653
4661
|
const names = await study.list(CONVERSATIONS_DIR).catch(() => []);
|
|
@@ -5320,6 +5328,47 @@ const EMPTY = {
|
|
|
5320
5328
|
const textOf = (blocks) => (Array.isArray(blocks) ? blocks : [blocks]).map((b) => b?.type === "text" ? b.text ?? "" : b?.type === "resource_link" ? `@${b.name ?? b.uri ?? ""}` : "").join("");
|
|
5321
5329
|
const lines = (v) => v ? v.replace(/\n$/, "").split("\n") : [];
|
|
5322
5330
|
const keyOf = (agent, session) => `${agent}\u0000${session}`;
|
|
5331
|
+
const str = (v) => typeof v === "string" ? v : "";
|
|
5332
|
+
/** The agent's slash commands, from available_commands_update. */
|
|
5333
|
+
const commandsOf = (v) => (Array.isArray(v) ? v : []).filter((c) => str(c.name)).map((c) => {
|
|
5334
|
+
const hint = str(c.input?.hint);
|
|
5335
|
+
return {
|
|
5336
|
+
name: str(c.name).replace(/^\//, ""),
|
|
5337
|
+
description: str(c.description),
|
|
5338
|
+
...hint ? { hint } : {}
|
|
5339
|
+
};
|
|
5340
|
+
});
|
|
5341
|
+
/** The agent's modes, from a session's answer or a current_mode_update. */
|
|
5342
|
+
const modesOf = (v) => {
|
|
5343
|
+
const m = v;
|
|
5344
|
+
const options = (m?.availableModes ?? []).filter((o) => str(o.id)).map((o) => ({
|
|
5345
|
+
value: str(o.id),
|
|
5346
|
+
name: str(o.name) || str(o.id),
|
|
5347
|
+
...str(o.description) ? { description: str(o.description) } : {}
|
|
5348
|
+
}));
|
|
5349
|
+
return options.length ? {
|
|
5350
|
+
kind: "mode",
|
|
5351
|
+
id: "mode",
|
|
5352
|
+
name: "Mode",
|
|
5353
|
+
current: str(m?.currentModeId),
|
|
5354
|
+
options
|
|
5355
|
+
} : null;
|
|
5356
|
+
};
|
|
5357
|
+
/** The agent's options (the model, say), whose choices may be grouped. */
|
|
5358
|
+
const configsOf = (v) => (Array.isArray(v) ? v : []).filter((o) => str(o.id) && Array.isArray(o.options)).flatMap((o) => {
|
|
5359
|
+
const options = o.options.flatMap((x) => Array.isArray(x.options) ? x.options : [x]).filter((x) => str(x.value)).map((x) => ({
|
|
5360
|
+
value: str(x.value),
|
|
5361
|
+
name: str(x.name) || str(x.value),
|
|
5362
|
+
...str(x.description) ? { description: str(x.description) } : {}
|
|
5363
|
+
}));
|
|
5364
|
+
return options.length ? [{
|
|
5365
|
+
kind: "config",
|
|
5366
|
+
id: str(o.id),
|
|
5367
|
+
name: str(o.name) || str(o.id),
|
|
5368
|
+
current: str(o.currentValue),
|
|
5369
|
+
options
|
|
5370
|
+
}] : [];
|
|
5371
|
+
});
|
|
5323
5372
|
/** The sessions the wrappers carry, as the pane draws them. One is shown: the one you chose, else the last to start. */
|
|
5324
5373
|
var AcpFollower = class {
|
|
5325
5374
|
#links = /* @__PURE__ */ new Map();
|
|
@@ -5402,6 +5451,8 @@ var AcpFollower = class {
|
|
|
5402
5451
|
size: live.size,
|
|
5403
5452
|
ours: this.#ours,
|
|
5404
5453
|
sessions: this.#sessions(),
|
|
5454
|
+
commands: live.commands,
|
|
5455
|
+
settings: [...live.modes ? [live.modes] : [], ...live.configs],
|
|
5405
5456
|
...problem
|
|
5406
5457
|
};
|
|
5407
5458
|
else if (read) this.#view = {
|
|
@@ -5635,6 +5686,42 @@ var AcpFollower = class {
|
|
|
5635
5686
|
link.opening = "";
|
|
5636
5687
|
link.send({ type: "open" });
|
|
5637
5688
|
}
|
|
5689
|
+
/** Set a mode or option of the session shown (the agent's own: its model, its plan mode). Optimistic: the agent's
|
|
5690
|
+
* answer or update settles it. */
|
|
5691
|
+
set(kind, id, value) {
|
|
5692
|
+
const live = this.#need();
|
|
5693
|
+
const setting = kind === "mode" ? live.modes : live.configs.find((c) => c.id === id);
|
|
5694
|
+
if (!setting || !setting.options.some((o) => o.value === value)) throw new Error(`${live.link.name || "The agent"} offers no such choice`);
|
|
5695
|
+
if (kind === "mode") live.modes = {
|
|
5696
|
+
...setting,
|
|
5697
|
+
current: value
|
|
5698
|
+
};
|
|
5699
|
+
else live.configs = live.configs.map((c) => c.id === id ? {
|
|
5700
|
+
...c,
|
|
5701
|
+
current: value
|
|
5702
|
+
} : c);
|
|
5703
|
+
live.link.send({
|
|
5704
|
+
type: "set",
|
|
5705
|
+
session: live.session,
|
|
5706
|
+
kind,
|
|
5707
|
+
id,
|
|
5708
|
+
value
|
|
5709
|
+
});
|
|
5710
|
+
this.#tell();
|
|
5711
|
+
}
|
|
5712
|
+
/** Remove what isorite kept of a session that is not live. What the agent keeps of its own is not touched. */
|
|
5713
|
+
async forget(agent, session) {
|
|
5714
|
+
const key = keyOf(agent, session);
|
|
5715
|
+
if (this.#lives.has(key)) throw new Error("that session is live: stop it before removing what is kept of it");
|
|
5716
|
+
if (!this.#api?.deleteAgentRecord) throw new Error("nothing is kept here");
|
|
5717
|
+
await this.#api.deleteAgentRecord(agent, session);
|
|
5718
|
+
if (this.#shown === key) {
|
|
5719
|
+
this.#shown = null;
|
|
5720
|
+
this.#chosen = false;
|
|
5721
|
+
this.#reading = null;
|
|
5722
|
+
}
|
|
5723
|
+
await this.#readKept();
|
|
5724
|
+
}
|
|
5638
5725
|
/** Ask the attached agents for their sessions again: the drawer is being looked at. */
|
|
5639
5726
|
refresh() {
|
|
5640
5727
|
for (const link of this.#links.values()) if (link.can.list) link.send({ type: "list" });
|
|
@@ -5713,7 +5800,11 @@ var AcpFollower = class {
|
|
|
5713
5800
|
break;
|
|
5714
5801
|
}
|
|
5715
5802
|
case "session/new":
|
|
5716
|
-
if (typeof m.result?.sessionId === "string")
|
|
5803
|
+
if (typeof m.result?.sessionId === "string") {
|
|
5804
|
+
const opened = this.#open(link, m.result.sessionId, key.startsWith("isorite-") ? "ours" : "in", typeof params.cwd === "string" ? params.cwd : "");
|
|
5805
|
+
opened.modes = modesOf(m.result.modes);
|
|
5806
|
+
opened.configs = configsOf(m.result.configOptions);
|
|
5807
|
+
}
|
|
5717
5808
|
break;
|
|
5718
5809
|
case "session/load":
|
|
5719
5810
|
case "session/resume":
|
|
@@ -5722,9 +5813,20 @@ var AcpFollower = class {
|
|
|
5722
5813
|
this.#lives.delete(keyOf(link.agent, live.session));
|
|
5723
5814
|
break;
|
|
5724
5815
|
}
|
|
5816
|
+
if (m.result?.modes) live.modes = modesOf(m.result.modes);
|
|
5817
|
+
if (m.result?.configOptions) live.configs = configsOf(m.result.configOptions);
|
|
5725
5818
|
this.#loaded(live);
|
|
5726
5819
|
}
|
|
5727
5820
|
break;
|
|
5821
|
+
case "session/set_mode":
|
|
5822
|
+
if (live && !m.error && live.modes) live.modes = {
|
|
5823
|
+
...live.modes,
|
|
5824
|
+
current: str(params.modeId)
|
|
5825
|
+
};
|
|
5826
|
+
break;
|
|
5827
|
+
case "session/set_config_option":
|
|
5828
|
+
if (live && !m.error && m.result?.configOptions) live.configs = configsOf(m.result.configOptions);
|
|
5829
|
+
break;
|
|
5728
5830
|
case "session/prompt":
|
|
5729
5831
|
if (live) {
|
|
5730
5832
|
const stop = m.error ? "failed" : m.result?.stopReason === "cancelled" ? "stopped" : m.result?.stopReason === "end_turn" ? "done" : "failed";
|
|
@@ -5788,7 +5890,10 @@ var AcpFollower = class {
|
|
|
5788
5890
|
loading: false,
|
|
5789
5891
|
replaying: false,
|
|
5790
5892
|
pending: null,
|
|
5791
|
-
given: /* @__PURE__ */ new Set()
|
|
5893
|
+
given: /* @__PURE__ */ new Set(),
|
|
5894
|
+
commands: [],
|
|
5895
|
+
modes: null,
|
|
5896
|
+
configs: []
|
|
5792
5897
|
};
|
|
5793
5898
|
live.link = link;
|
|
5794
5899
|
if (cwd) live.cwd = cwd;
|
|
@@ -5978,7 +6083,19 @@ var AcpFollower = class {
|
|
|
5978
6083
|
if (typeof cost === "number") live.cost = cost;
|
|
5979
6084
|
break;
|
|
5980
6085
|
}
|
|
5981
|
-
case "session_info_update":
|
|
6086
|
+
case "session_info_update":
|
|
6087
|
+
if (typeof u.title === "string") live.title = u.title;
|
|
6088
|
+
break;
|
|
6089
|
+
case "available_commands_update":
|
|
6090
|
+
live.commands = commandsOf(u.availableCommands);
|
|
6091
|
+
break;
|
|
6092
|
+
case "current_mode_update":
|
|
6093
|
+
if (live.modes && typeof u.currentModeId === "string") live.modes = {
|
|
6094
|
+
...live.modes,
|
|
6095
|
+
current: u.currentModeId
|
|
6096
|
+
};
|
|
6097
|
+
break;
|
|
6098
|
+
case "config_option_update": live.configs = configsOf(u.configOptions);
|
|
5982
6099
|
}
|
|
5983
6100
|
}
|
|
5984
6101
|
/** The note a path names, if it is a Markdown file of the base (relative paths read from the session's folder). */
|
|
@@ -6038,7 +6155,7 @@ var AcpFollower = class {
|
|
|
6038
6155
|
};
|
|
6039
6156
|
//#endregion
|
|
6040
6157
|
//#region packages/server/src/version.ts
|
|
6041
|
-
const VERSION = "0.
|
|
6158
|
+
const VERSION = "0.15.0";
|
|
6042
6159
|
//#endregion
|
|
6043
6160
|
//#region packages/server/src/connect.ts
|
|
6044
6161
|
const HARNESSES = [{
|
|
@@ -6310,6 +6427,27 @@ function detectAgents(env = process.env) {
|
|
|
6310
6427
|
}];
|
|
6311
6428
|
});
|
|
6312
6429
|
}
|
|
6430
|
+
function listAgents(env = process.env) {
|
|
6431
|
+
const found = detectAgents(env).map((d) => ({
|
|
6432
|
+
...d,
|
|
6433
|
+
found: true
|
|
6434
|
+
}));
|
|
6435
|
+
const have = new Set(found.map((d) => d.id));
|
|
6436
|
+
const rest = KNOWN.filter((k) => !have.has(k.id)).map((k) => ({
|
|
6437
|
+
id: k.id,
|
|
6438
|
+
name: k.name,
|
|
6439
|
+
command: k.command,
|
|
6440
|
+
path: "",
|
|
6441
|
+
keys: k.keys,
|
|
6442
|
+
keySet: false,
|
|
6443
|
+
ready: false,
|
|
6444
|
+
found: false,
|
|
6445
|
+
note: `${k.name} isn't on this machine's PATH.`
|
|
6446
|
+
}));
|
|
6447
|
+
return [...found, ...rest];
|
|
6448
|
+
}
|
|
6449
|
+
/** The ACP command of an agent known by name (`isorite acp omp`), or null. */
|
|
6450
|
+
const knownCommand = (id) => KNOWN.find((k) => k.id === id)?.command ?? null;
|
|
6313
6451
|
/** Why an agent that stopped may have: the key it reads isn't set where isorite was started, for the command run. */
|
|
6314
6452
|
function missingKey(command, env = process.env) {
|
|
6315
6453
|
const k = KNOWN.find((x) => x.command === command);
|
|
@@ -6563,6 +6701,108 @@ var AgentRunner = class {
|
|
|
6563
6701
|
}
|
|
6564
6702
|
};
|
|
6565
6703
|
//#endregion
|
|
6704
|
+
//#region packages/server/src/own-repo.ts
|
|
6705
|
+
const q = (s) => /^[\w@%+=:,./-]+$/.test(s) ? s : `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
6706
|
+
const slashes = (p) => p.split("\\").join("/");
|
|
6707
|
+
function git(cwd, args, env) {
|
|
6708
|
+
return execFileSync("git", args, {
|
|
6709
|
+
cwd,
|
|
6710
|
+
env: plainGitEnv(env),
|
|
6711
|
+
timeout: 1e4,
|
|
6712
|
+
killSignal: "SIGKILL",
|
|
6713
|
+
encoding: "utf8",
|
|
6714
|
+
stdio: [
|
|
6715
|
+
"ignore",
|
|
6716
|
+
"pipe",
|
|
6717
|
+
"pipe"
|
|
6718
|
+
]
|
|
6719
|
+
});
|
|
6720
|
+
}
|
|
6721
|
+
/** What this machine chose for a base, from bases.toml. */
|
|
6722
|
+
function historyChoice(base, config) {
|
|
6723
|
+
const v = table(readConfig(basesPath(config))[base]).history;
|
|
6724
|
+
return v === "project" || v === "separate" ? v : null;
|
|
6725
|
+
}
|
|
6726
|
+
function setHistoryChoice(base, choice, config) {
|
|
6727
|
+
const bases = readConfig(basesPath(config));
|
|
6728
|
+
if (!bases[base]) throw new Error(`${base} is not among the bases`);
|
|
6729
|
+
bases[base] = {
|
|
6730
|
+
...table(bases[base]),
|
|
6731
|
+
history: choice
|
|
6732
|
+
};
|
|
6733
|
+
writeConfig(bases, basesPath(config));
|
|
6734
|
+
}
|
|
6735
|
+
/** Where isorite's own folder is kept, and the commands for the person to run. Runs `git ls-files`, `remote` and
|
|
6736
|
+
* `rev-parse`, which only read. */
|
|
6737
|
+
function ownRepoInfo(own, choice = null, env = process.env) {
|
|
6738
|
+
const project = repoOf(dirname(own));
|
|
6739
|
+
const base = {
|
|
6740
|
+
state: "none",
|
|
6741
|
+
path: own,
|
|
6742
|
+
project,
|
|
6743
|
+
remotes: [],
|
|
6744
|
+
choice,
|
|
6745
|
+
ask: false,
|
|
6746
|
+
commands: [],
|
|
6747
|
+
problem: ""
|
|
6748
|
+
};
|
|
6749
|
+
try {
|
|
6750
|
+
if (existsSync(join(own, ".git"))) {
|
|
6751
|
+
const remotes = git(own, ["remote"], env).split("\n").filter(Boolean);
|
|
6752
|
+
return {
|
|
6753
|
+
...base,
|
|
6754
|
+
state: "own",
|
|
6755
|
+
remotes,
|
|
6756
|
+
commands: remotes.length ? [`git -C ${q(own)} push`] : [`git -C ${q(own)} remote add origin <the address of your private repository>`, `git -C ${q(own)} push -u origin HEAD`]
|
|
6757
|
+
};
|
|
6758
|
+
}
|
|
6759
|
+
if (!project) return base;
|
|
6760
|
+
const rel = slashes(relative(project, own));
|
|
6761
|
+
const tracked = git(project, [
|
|
6762
|
+
"ls-files",
|
|
6763
|
+
"--",
|
|
6764
|
+
rel
|
|
6765
|
+
], env).trim() !== "";
|
|
6766
|
+
const path = git(project, [
|
|
6767
|
+
"rev-parse",
|
|
6768
|
+
"--git-path",
|
|
6769
|
+
"info/exclude"
|
|
6770
|
+
], env).trim();
|
|
6771
|
+
const exclude = isAbsolute(path) ? path : resolve(project, path);
|
|
6772
|
+
const apart = [
|
|
6773
|
+
`git -C ${q(own)} init`,
|
|
6774
|
+
`echo ${q(rel + "/")} >> ${q(exclude)}`,
|
|
6775
|
+
`git -C ${q(own)} add -A`,
|
|
6776
|
+
`git -C ${q(own)} commit -m "isorite: start"`
|
|
6777
|
+
];
|
|
6778
|
+
return {
|
|
6779
|
+
...base,
|
|
6780
|
+
state: tracked ? "tracked" : "shared",
|
|
6781
|
+
ask: choice === null,
|
|
6782
|
+
commands: tracked ? [
|
|
6783
|
+
`git -C ${q(project)} rm -r --cached ${q(rel)}`,
|
|
6784
|
+
...apart,
|
|
6785
|
+
`git -C ${q(project)} commit -m "stop tracking isorite's own files"`
|
|
6786
|
+
] : apart
|
|
6787
|
+
};
|
|
6788
|
+
} catch (e) {
|
|
6789
|
+
return {
|
|
6790
|
+
...base,
|
|
6791
|
+
state: "unavailable",
|
|
6792
|
+
problem: e.message.split("\n")[0] ?? "git did not answer"
|
|
6793
|
+
};
|
|
6794
|
+
}
|
|
6795
|
+
}
|
|
6796
|
+
/** For a base just made in a git project: ask whether its own files stay with the project or apart, keep the answer
|
|
6797
|
+
* for this machine, and say the commands for apart. Asks nothing outside a project. isorite runs none of it. */
|
|
6798
|
+
async function askWhereFilesLive(notes, ask, out, config, env = process.env) {
|
|
6799
|
+
if (!repoOf(notes)) return null;
|
|
6800
|
+
const choice = (await ask(`${notes} is in a git project. isorite's own files (.isorite/: journals, kept sessions, settings) can be committed with the project, or kept apart from it, so the people sharing it don't have them.\nKeep them [p]roject / [a]part? (p) `)).trim().toLowerCase().startsWith("a") ? "separate" : "project";
|
|
6801
|
+
setHistoryChoice(rememberBase(notes, { path: config }), choice, config);
|
|
6802
|
+
if (choice === "separate") out(`\nTo keep them apart, run these (isorite runs no git itself):\n${ownRepoInfo(join(notes, ".isorite"), "separate", env).commands.map((c) => ` ${c}`).join("\n")}\n`);
|
|
6803
|
+
return choice;
|
|
6804
|
+
}
|
|
6805
|
+
//#endregion
|
|
6566
6806
|
//#region packages/server/src/http.ts
|
|
6567
6807
|
const LOOPBACK = /* @__PURE__ */ new Set([
|
|
6568
6808
|
"localhost",
|
|
@@ -6639,7 +6879,7 @@ const AgentStatus = z.object({
|
|
|
6639
6879
|
session: z.string().min(1).max(200),
|
|
6640
6880
|
busy: z.boolean()
|
|
6641
6881
|
}).strict();
|
|
6642
|
-
function createApp({ api, token, loopback, web, allowHosts = [], notes, acp, bases, base, agents }) {
|
|
6882
|
+
function createApp({ api, token, loopback, web, allowHosts = [], notes, acp, bases, base, agents, config }) {
|
|
6643
6883
|
const app = new Hono();
|
|
6644
6884
|
const allowed = new Set([...allowHosts].map((h) => h.toLowerCase()));
|
|
6645
6885
|
const reply = (c, status, value) => c.body(JSON.stringify(value), status, {
|
|
@@ -6773,6 +7013,7 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, acp, bas
|
|
|
6773
7013
|
app.get("/api/agents/notes", (c) => answer(c, () => api.agentNotes()));
|
|
6774
7014
|
post("/api/agents/notes", z.object({ may: z.boolean() }).strict(), (_c, b) => api.setAgentNotes(b.may));
|
|
6775
7015
|
app.get("/api/conversations", (c) => answer(c, () => api.conversations()));
|
|
7016
|
+
post("/api/conversation/delete", z.object({ id: z.string().max(64) }).strict(), (_c, b) => api.deleteConversation(b.id));
|
|
6776
7017
|
app.get("/api/conversation", (c) => answer(c, async () => await api.conversation(c.req.query("id") || void 0) ?? {
|
|
6777
7018
|
id: "",
|
|
6778
7019
|
turns: []
|
|
@@ -6851,6 +7092,16 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, acp, bas
|
|
|
6851
7092
|
}).strict(), (_c, b) => api.agentReceived(b.id, b.agent));
|
|
6852
7093
|
post("/api/agent/status", AgentStatus, (_c, b) => api.agentStatus(b.agent, b.session, b.busy));
|
|
6853
7094
|
post("/api/agent/take", z.object({ id: z.string().max(64) }).strict(), (_c, b) => api.agentTake(b.id));
|
|
7095
|
+
if (notes) {
|
|
7096
|
+
const configFile = config ?? configPath();
|
|
7097
|
+
app.get("/api/own/repo", (c) => answer(c, async () => local(c) ? ownRepoInfo(join(notes, ".isorite"), base ? historyChoice(base, configFile) : null) : null));
|
|
7098
|
+
post("/api/own/choose", z.object({ choice: z.enum(["project", "separate"]) }).strict(), async (c, b) => {
|
|
7099
|
+
if (!local(c)) throw new EngineError("this is chosen only from this machine", 403);
|
|
7100
|
+
if (!base) throw new EngineError("this base has no name here", 409);
|
|
7101
|
+
setHistoryChoice(base, b.choice, configFile);
|
|
7102
|
+
return ownRepoInfo(join(notes, ".isorite"), b.choice);
|
|
7103
|
+
});
|
|
7104
|
+
}
|
|
6854
7105
|
if (notes) {
|
|
6855
7106
|
app.get("/api/connect", (c) => answer(c, async () => proposal(notes)));
|
|
6856
7107
|
post("/api/connect", z.object({ files: z.array(z.string().max(200)).max(10).optional() }).strict(), async (c, b) => {
|
|
@@ -6862,7 +7113,7 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, acp, bas
|
|
|
6862
7113
|
app.get("/api/acp/agent", (c) => answer(c, async () => agents.state()));
|
|
6863
7114
|
app.get("/api/acp/detected", (c) => answer(c, async () => {
|
|
6864
7115
|
if (!local(c)) throw new EngineError("agents are looked for only from this machine", 403);
|
|
6865
|
-
return
|
|
7116
|
+
return listAgents();
|
|
6866
7117
|
}));
|
|
6867
7118
|
app.get("/api/acp/registry", (c) => answer(c, async () => registryAgents().catch((e) => {
|
|
6868
7119
|
throw new EngineError(e.message, 502);
|
|
@@ -6967,6 +7218,23 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, acp, bas
|
|
|
6967
7218
|
}).strict(), async (_c, b) => {
|
|
6968
7219
|
await acp.follow(b.agent, b.session).catch(refused);
|
|
6969
7220
|
});
|
|
7221
|
+
post("/api/acp/forget", z.object({
|
|
7222
|
+
agent: z.string().max(40),
|
|
7223
|
+
session: z.string().min(1).max(200)
|
|
7224
|
+
}).strict(), async (_c, b) => {
|
|
7225
|
+
await acp.forget(b.agent, b.session).catch(refused);
|
|
7226
|
+
});
|
|
7227
|
+
post("/api/acp/set", z.object({
|
|
7228
|
+
kind: z.enum(["mode", "config"]),
|
|
7229
|
+
id: z.string().max(100),
|
|
7230
|
+
value: z.string().max(200)
|
|
7231
|
+
}).strict(), async (_c, b) => {
|
|
7232
|
+
try {
|
|
7233
|
+
acp.set(b.kind, b.id, b.value);
|
|
7234
|
+
} catch (e) {
|
|
7235
|
+
throw new EngineError(e.message, 409);
|
|
7236
|
+
}
|
|
7237
|
+
});
|
|
6970
7238
|
post("/api/acp/new", z.object({ agent: z.string().max(40) }).strict(), async (_c, b) => {
|
|
6971
7239
|
await acp.fresh(b.agent).catch(refused);
|
|
6972
7240
|
});
|
|
@@ -7457,7 +7725,8 @@ async function openServer(path, { host = "127.0.0.1", port = 4767, allowHosts =
|
|
|
7457
7725
|
acp,
|
|
7458
7726
|
bases,
|
|
7459
7727
|
base: known.name,
|
|
7460
|
-
...loopback ? { agents } : {}
|
|
7728
|
+
...loopback ? { agents } : {},
|
|
7729
|
+
config
|
|
7461
7730
|
}).fetch
|
|
7462
7731
|
};
|
|
7463
7732
|
}
|
|
@@ -7614,6 +7883,8 @@ async function doctor(folder, o = {}) {
|
|
|
7614
7883
|
const errors = issues.filter((i) => i.level === "error");
|
|
7615
7884
|
say("links", errors.length ? "fail" : issues.length || cycles.length ? "warn" : "ok", `${bundle.concepts.size} notes; ${errors.length} errors, ${issues.length - errors.length} warnings, ${cycles.length} requires cycles${issues.length ? `: ${issues.slice(0, 3).map((i) => `${i.path}: ${i.message}`).join("; ")}${issues.length > 3 ? `; and ${issues.length - 3} more (isorite check ${notes})` : ""}` : ""}`);
|
|
7616
7885
|
}
|
|
7886
|
+
const kept = ownRepoInfo(own, null, env);
|
|
7887
|
+
if (existsSync(own)) say("history", kept.state === "own" ? "ok" : kept.state === "unavailable" ? "skip" : kept.state === "none" ? "skip" : "ok", kept.state === "own" ? `${OWN_DIR}/ is a repository of its own${kept.remotes.length ? `, remote ${kept.remotes.join(", ")}` : `; to keep it elsewhere: git -C ${own} remote add origin <address>`}` : kept.state === "tracked" ? `${OWN_DIR}/ is tracked by the project's repository, so everyone sharing it has isorite's files; to keep them apart, run:\n ${kept.commands.join("\n ")}` : kept.state === "shared" ? `${OWN_DIR}/ is in the project's folder, to be committed with it; to keep it apart, run:\n ${kept.commands.join("\n ")}` : kept.state === "unavailable" ? `${OWN_DIR}/: git did not answer (${kept.problem})` : `${OWN_DIR}/ is in no git repository`);
|
|
7617
7888
|
const repository = existsSync(settings) ? parseSettings(readFileSync(settings, "utf8")).repository : void 0;
|
|
7618
7889
|
const found = repoOf(notes);
|
|
7619
7890
|
if (!repository && found) say("repository", "ok", `${found}: code links point here`);
|
|
@@ -8244,6 +8515,14 @@ async function acp(path, { agent, port = 4767, input, output, env = process.env,
|
|
|
8244
8515
|
toAgent(m);
|
|
8245
8516
|
}
|
|
8246
8517
|
if (c.type === "list" && can.list) ask("session/list", { cwd });
|
|
8518
|
+
if (c.type === "set") ask(c.kind === "mode" ? "session/set_mode" : "session/set_config_option", c.kind === "mode" ? {
|
|
8519
|
+
sessionId: c.session,
|
|
8520
|
+
modeId: c.value
|
|
8521
|
+
} : {
|
|
8522
|
+
sessionId: c.session,
|
|
8523
|
+
configId: c.id,
|
|
8524
|
+
value: c.value
|
|
8525
|
+
});
|
|
8247
8526
|
if (c.type === "open") {
|
|
8248
8527
|
const params = {
|
|
8249
8528
|
cwd,
|
|
@@ -9303,6 +9582,11 @@ function label(b, all) {
|
|
|
9303
9582
|
}
|
|
9304
9583
|
/** The choice `isorite` makes, and what it says. */
|
|
9305
9584
|
async function start(io) {
|
|
9585
|
+
const make = async (folder) => {
|
|
9586
|
+
const notes = io.make(folder);
|
|
9587
|
+
await io.made?.(notes);
|
|
9588
|
+
return notes;
|
|
9589
|
+
};
|
|
9306
9590
|
const found = findBase(io.cwd);
|
|
9307
9591
|
if (found.kind === "base") return io.open(found.notes);
|
|
9308
9592
|
if (found.kind === "several") {
|
|
@@ -9314,7 +9598,7 @@ async function start(io) {
|
|
|
9314
9598
|
if (found.kind === "repo") {
|
|
9315
9599
|
const notes = join(found.repo, "notes");
|
|
9316
9600
|
io.out(`isorite: no base in ${found.repo} yet.`);
|
|
9317
|
-
if (!existsSync(notes) && await io.confirm(`Make one at ${notes}?`)) return io.open(
|
|
9601
|
+
if (!existsSync(notes) && await io.confirm(`Make one at ${notes}?`)) return io.open(await make(notes));
|
|
9318
9602
|
if (existsSync(notes)) io.out(` ${notes} is there already: open it as it is with isorite serve ${notes}`);
|
|
9319
9603
|
}
|
|
9320
9604
|
const known = recent(io.config);
|
|
@@ -9332,7 +9616,7 @@ async function start(io) {
|
|
|
9332
9616
|
const pick = await io.choose("Open which?", rows.length);
|
|
9333
9617
|
if (pick === null) return 2;
|
|
9334
9618
|
if (pick < known.length) return io.open(known[pick].path);
|
|
9335
|
-
if (pick === known.length) return io.open(
|
|
9619
|
+
if (pick === known.length) return io.open(await make(join(io.cwd, "notes")));
|
|
9336
9620
|
return io.guide();
|
|
9337
9621
|
}
|
|
9338
9622
|
/** The model for search by meaning, offered on the first start without one (M26): asked once, fetched on a yes,
|
|
@@ -9495,6 +9779,9 @@ async function terminalStart({ port, noOpen }) {
|
|
|
9495
9779
|
console.log(` made ${made}`);
|
|
9496
9780
|
return made;
|
|
9497
9781
|
},
|
|
9782
|
+
made: async (notes) => {
|
|
9783
|
+
await askWhereFilesLive(notes, ask, console.log, configPath());
|
|
9784
|
+
},
|
|
9498
9785
|
guide: async () => {
|
|
9499
9786
|
await model();
|
|
9500
9787
|
await guide(void 0, { port: port ?? await freePort() });
|
|
@@ -9519,7 +9806,22 @@ function main(argv) {
|
|
|
9519
9806
|
if (command === "check" && rest.find((r) => !r.startsWith("--"))) return check(rest.find((r) => !r.startsWith("--")), console.log, rest.includes("--unused"));
|
|
9520
9807
|
if (command === "new" && rest[0]) {
|
|
9521
9808
|
const made = newBase(rest[0]);
|
|
9522
|
-
|
|
9809
|
+
const next = `${made}: a knowledge base, empty. Open it in the browser, and attach your agent from Iso's pane:\n isorite serve ${made}`;
|
|
9810
|
+
if (process.stdin.isTTY && process.stdout.isTTY && repoOf(made)) return (async () => {
|
|
9811
|
+
const { createInterface } = await import("node:readline/promises");
|
|
9812
|
+
const rl = createInterface({
|
|
9813
|
+
input: process.stdin,
|
|
9814
|
+
output: process.stdout
|
|
9815
|
+
});
|
|
9816
|
+
try {
|
|
9817
|
+
await askWhereFilesLive(made, (q) => rl.question(q), console.log, configPath());
|
|
9818
|
+
} finally {
|
|
9819
|
+
rl.close();
|
|
9820
|
+
}
|
|
9821
|
+
console.log(next);
|
|
9822
|
+
return 0;
|
|
9823
|
+
})();
|
|
9824
|
+
console.log(next);
|
|
9523
9825
|
return 0;
|
|
9524
9826
|
}
|
|
9525
9827
|
if (command === "serve" && rest[0]) {
|
|
@@ -9586,9 +9888,15 @@ function main(argv) {
|
|
|
9586
9888
|
const at = rest.indexOf(name);
|
|
9587
9889
|
return at < 0 ? void 0 : rest[at + 1];
|
|
9588
9890
|
};
|
|
9589
|
-
const
|
|
9891
|
+
const positional = rest.filter((a, i) => !a.startsWith("--") && ![
|
|
9892
|
+
"--port",
|
|
9893
|
+
"--agent",
|
|
9894
|
+
"--url"
|
|
9895
|
+
].includes(rest[i - 1] ?? ""));
|
|
9896
|
+
const named = !flag("--agent") && positional[0] && !existsSync(positional[0]) ? knownCommand(positional[0]) : null;
|
|
9897
|
+
const agent = flag("--agent") ?? named ?? void 0, port = flag("--port") === void 0 ? void 0 : Number(flag("--port"));
|
|
9590
9898
|
if (!agent) {
|
|
9591
|
-
console.error("isorite acp --agent \"<the agent's ACP command>\" [<notes>] [--port <n>] [--pane]");
|
|
9899
|
+
console.error("isorite acp <omp|opencode|claude|codex|hermes> [<notes>] [--port <n>]\nisorite acp --agent \"<the agent's ACP command>\" [<notes>] [--port <n>] [--pane]");
|
|
9592
9900
|
return 2;
|
|
9593
9901
|
}
|
|
9594
9902
|
if (port !== void 0 && !(Number.isInteger(port) && port > 0 && port < 65536)) {
|
|
@@ -9600,11 +9908,7 @@ function main(argv) {
|
|
|
9600
9908
|
console.error("--url takes a base's address on this machine");
|
|
9601
9909
|
return 2;
|
|
9602
9910
|
}
|
|
9603
|
-
return acp(
|
|
9604
|
-
"--port",
|
|
9605
|
-
"--agent",
|
|
9606
|
-
"--url"
|
|
9607
|
-
].includes(rest[i - 1] ?? "")), {
|
|
9911
|
+
return acp(named ? positional[1] : positional[0], {
|
|
9608
9912
|
agent,
|
|
9609
9913
|
pane,
|
|
9610
9914
|
...port === void 0 ? {} : { port },
|
package/docs/terminal.md
CHANGED
|
@@ -16,7 +16,7 @@ isorite # in the project: finds its base, opens the browser (isorite se
|
|
|
16
16
|
|
|
17
17
|
### In Iso's pane
|
|
18
18
|
|
|
19
|
-
With no agent attached, Iso's pane
|
|
19
|
+
With no agent attached, Iso's pane offers a card for each agent isorite knows (Claude Code, OpenCode, omp, Codex, Hermes), saying whether it is found on this machine's PATH and, for those that read a key, whether it is set where isorite was started (by name; no key is read or kept). *Use in this pane* starts it for the base; *in my terminal* gives isorite's MCP address and, to share the session live with the pane, the `isorite acp <agent>` line to run in an ACP front end such as Toad. *Another agent* takes one of the [ACP registry](https://agentclientprotocol.com/get-started/registry), or the command of one it doesn't list (`hermes acp`). The drawer's *change agent* is the same choice later. isorite starts it for the base, behind `isorite acp --pane`, in the repository the base is in, with the server's environment, and starts it again when the base is next opened. It runs a program, so it is chosen from this machine only; the choice is kept in `~/.config/isorite/bases.toml` (`agent`, or `agent_command`). The drawer's *stop it* stops it. A session isorite kept can be removed from the drawer (*remove*): that deletes isorite's copy only, never the agent's own history, and a live session is not removed.
|
|
20
20
|
|
|
21
21
|
A registry agent is run as the registry says: its program for this platform, fetched once into `~/.local/share/isorite/agents/<id>/<version>/` and checked against its SHA-256; else with `npx -y` or `uvx`. `ISORITE_ACP_REGISTRY` names another registry, an address or a file.
|
|
22
22
|
|
|
@@ -28,6 +28,8 @@ Toad in the terminal, Zed, or JetBrains: give it isorite as its agent, with the
|
|
|
28
28
|
isorite acp --agent "npx -y @agentclientprotocol/claude-agent-acp" ~/code/my-project/notes
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
For an agent isorite knows by name there is a shorter form: `isorite acp omp`, `opencode`, `claude`, `codex` or `hermes` (the pane's cards show the line for each). A folder of that name here is taken as the notes instead.
|
|
32
|
+
|
|
31
33
|
Run in a folder with a base in it or under it, the notes can be left out. This starts the isorite server for the notes, unless one is running on port 4767 (`--port <n>` for another), and the agent behind it. Every message passes through: the session is live in the front end and in the pane at once, asked from either. isorite's MCP is added to each session the agent opens, so the agent needs no config of its own for it.
|
|
32
34
|
|
|
33
35
|
### In the agent's own terminal
|