isorite 0.7.0 → 0.8.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/README.md +1 -0
- package/dist/main.js +609 -96
- package/docs/setup-for-agents.md +3 -0
- package/docs/setup.md +20 -0
- package/docs/terminal.md +15 -0
- package/package.json +1 -1
- package/plugin/server.ts +4 -2
- package/web/_app/immutable/assets/{0.BKnIhFWM.css → 0.CTbO0owi.css} +1 -1
- package/web/_app/immutable/chunks/B-65IYX-.js +6 -0
- package/web/_app/immutable/chunks/{62dhNtvj.js → B91efLFT.js} +3 -3
- package/web/_app/immutable/chunks/{Cn-vtDpV.js → BjPiibIz.js} +1 -1
- package/web/_app/immutable/chunks/{BNjH_DUx.js → BrXPCJaM.js} +1 -1
- package/web/_app/immutable/chunks/{B7GA1FFB.js → BtUtjfIh.js} +1 -1
- package/web/_app/immutable/chunks/{CYNZ4aV_.js → CCiHXe5d.js} +1 -1
- package/web/_app/immutable/chunks/{DHfKGggc.js → CGzfi9Nl.js} +1 -1
- package/web/_app/immutable/chunks/CnP_Si9g.js +1 -0
- package/web/_app/immutable/chunks/{CWmfjLLv.js → D_JZ0swv.js} +1 -1
- package/web/_app/immutable/chunks/{WBFggAXs.js → DgZW3JtG.js} +1 -1
- package/web/_app/immutable/chunks/{CVbgxz8Y.js → Di_ac7WN.js} +1 -1
- package/web/_app/immutable/chunks/{DsBJWNWe.js → NmqB5ax3.js} +1 -1
- package/web/_app/immutable/chunks/{BoNMG-YE.js → dvrIP-Hm.js} +1 -1
- package/web/_app/immutable/chunks/hOAYc4Tf.js +1 -0
- package/web/_app/immutable/entry/{app.Dt52eXt2.js → app.DO9lj7h3.js} +2 -2
- package/web/_app/immutable/entry/start.Bg5TZs6n.js +1 -0
- package/web/_app/immutable/nodes/0.N1e1uAFq.js +26 -0
- package/web/_app/immutable/nodes/{1.B5qkcrvB.js → 1.BRTPLKyU.js} +1 -1
- package/web/_app/immutable/nodes/2.BWh1AYEd.js +1 -0
- package/web/_app/immutable/nodes/3.Cgqgn7Zv.js +1 -0
- package/web/_app/immutable/nodes/{4.BhNJiqT-.js → 4.DYW7Fxdy.js} +1 -1
- package/web/_app/immutable/nodes/{5.DXTbGemS.js → 5.Dkvkif0E.js} +1 -1
- package/web/_app/immutable/nodes/6.BinB0fuq.js +1 -0
- package/web/_app/version.json +1 -1
- package/web/index.html +15 -15
- package/web/_app/immutable/chunks/B3dCo0od.js +0 -1
- package/web/_app/immutable/chunks/D6Vzg2HD.js +0 -6
- package/web/_app/immutable/chunks/DkvLWnSd.js +0 -1
- package/web/_app/immutable/entry/start.mjqIl2PI.js +0 -1
- package/web/_app/immutable/nodes/0.DIhIOwuo.js +0 -26
- package/web/_app/immutable/nodes/2.CK3q7brK.js +0 -1
- package/web/_app/immutable/nodes/3.CrUewa31.js +0 -1
- package/web/_app/immutable/nodes/6.BZWaFgFP.js +0 -1
package/dist/main.js
CHANGED
|
@@ -1622,7 +1622,7 @@ function locate$1(note, p) {
|
|
|
1622
1622
|
}
|
|
1623
1623
|
/** The note with a proposal written in. For a rewrite, `region` writes that one alone; without it, every region
|
|
1624
1624
|
* still open. Null when the place (or, for a rewrite, any of the places asked for) cannot be found. */
|
|
1625
|
-
function written(note, p, region) {
|
|
1625
|
+
function written$1(note, p, region) {
|
|
1626
1626
|
if (p.form === "rewrite") {
|
|
1627
1627
|
const places = (region !== void 0 ? [region] : p.open ?? p.regions.map((_, k) => k)).map((k) => ({
|
|
1628
1628
|
k,
|
|
@@ -2704,6 +2704,43 @@ say plainly what you would need to look up to say more.`;
|
|
|
2704
2704
|
}));
|
|
2705
2705
|
//#endregion
|
|
2706
2706
|
//#region packages/engine/src/lookup.ts
|
|
2707
|
+
/** Which base a call names, and its arguments for that base: `base`, or a note's id written `memory:tools/x`. */
|
|
2708
|
+
function baseOf(args) {
|
|
2709
|
+
const { base, ...rest } = args;
|
|
2710
|
+
let named = typeof base === "string" ? base.trim() : "";
|
|
2711
|
+
for (const k of ["id", "note"]) {
|
|
2712
|
+
const m = typeof rest[k] === "string" ? /^([a-z0-9][a-z0-9-]{0,39}):(?!\/\/)(.+)$/.exec(rest[k]) : null;
|
|
2713
|
+
if (m) {
|
|
2714
|
+
named ||= m[1];
|
|
2715
|
+
if (m[1] === named) rest[k] = m[2];
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
return {
|
|
2719
|
+
base: named,
|
|
2720
|
+
args: rest
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
/** The tools' definitions with `base`, for a request that has other bases lent to it. */
|
|
2724
|
+
function withBase(defs, names, always = false) {
|
|
2725
|
+
if (!names.length && !always) return defs;
|
|
2726
|
+
const base = {
|
|
2727
|
+
type: "string",
|
|
2728
|
+
description: `Another knowledge base lent to you, by name: ${names.length ? names.join(", ") : "one brief_base names"}. This base when not given. A note there may also be named <base>:folder/name.`
|
|
2729
|
+
};
|
|
2730
|
+
return defs.map((d) => ({
|
|
2731
|
+
...d,
|
|
2732
|
+
function: {
|
|
2733
|
+
...d.function,
|
|
2734
|
+
parameters: {
|
|
2735
|
+
...d.function.parameters,
|
|
2736
|
+
properties: {
|
|
2737
|
+
...d.function.parameters.properties,
|
|
2738
|
+
base
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
}));
|
|
2743
|
+
}
|
|
2707
2744
|
/** A note's id from what a model writes for one: /basics/entropy.md, basics/entropy, [x](/basics/entropy.md). */
|
|
2708
2745
|
function noteId(b, raw) {
|
|
2709
2746
|
const id = String(raw ?? "").trim().replace(/^.*\]\(/, "").replace(/\)$/, "").replace(/\s+"[^"]*"$/, "").split("#")[0].replace(/^\/+/, "").replace(/\.md$/i, "");
|
|
@@ -2779,17 +2816,19 @@ var init_lookup = __esmMin((() => {
|
|
|
2779
2816
|
named = /* @__PURE__ */ new Map();
|
|
2780
2817
|
b;
|
|
2781
2818
|
similar;
|
|
2782
|
-
|
|
2819
|
+
away;
|
|
2820
|
+
constructor(b, note, similar = null, away = null) {
|
|
2783
2821
|
this.b = b;
|
|
2784
2822
|
this.held = [note];
|
|
2785
2823
|
this.similar = similar;
|
|
2824
|
+
this.away = away;
|
|
2786
2825
|
}
|
|
2787
2826
|
defs() {
|
|
2788
|
-
return this.similar ? [
|
|
2827
|
+
return withBase(this.similar ? [
|
|
2789
2828
|
TOOLS[0],
|
|
2790
2829
|
FIND_SIMILAR,
|
|
2791
2830
|
...TOOLS.slice(1)
|
|
2792
|
-
] : TOOLS;
|
|
2831
|
+
] : TOOLS, this.away?.lent().map((l) => l.name) ?? []);
|
|
2793
2832
|
}
|
|
2794
2833
|
/** The notes opened, each once, in order. */
|
|
2795
2834
|
read() {
|
|
@@ -2809,6 +2848,19 @@ var init_lookup = __esmMin((() => {
|
|
|
2809
2848
|
notes: []
|
|
2810
2849
|
};
|
|
2811
2850
|
let text;
|
|
2851
|
+
const there = baseOf(args);
|
|
2852
|
+
if (there.base) {
|
|
2853
|
+
try {
|
|
2854
|
+
text = await this.elsewhere(name, there.base, there.args, step);
|
|
2855
|
+
} catch (err) {
|
|
2856
|
+
text = this.fail(step, `That could not be looked up: ${err.message}`);
|
|
2857
|
+
}
|
|
2858
|
+
this.steps.push(step);
|
|
2859
|
+
return {
|
|
2860
|
+
text,
|
|
2861
|
+
step
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2812
2864
|
try {
|
|
2813
2865
|
text = name === "search_notes" ? this.search(args, step) : name === "find_similar" ? await this.meaning(args, step) : name === "outline_note" ? this.outline(args, step) : name === "read_note" ? this.section(args, step) : this.fail(step, `There is no tool ${name}.`);
|
|
2814
2866
|
} catch (err) {
|
|
@@ -2820,6 +2872,22 @@ var init_lookup = __esmMin((() => {
|
|
|
2820
2872
|
step
|
|
2821
2873
|
};
|
|
2822
2874
|
}
|
|
2875
|
+
/** A look in another base lent to the request: what that base gives, said as being from there. */
|
|
2876
|
+
async elsewhere(name, base, args, step) {
|
|
2877
|
+
const lent = this.away?.lent().map((l) => l.name) ?? [];
|
|
2878
|
+
if (!lent.includes(base)) return this.fail(step, lent.length ? `There is no base ${base} lent here: the bases are ${lent.join(", ")}. Leave base out for this one.` : `There is no base ${base} lent here: leave base out.`);
|
|
2879
|
+
const note = typeof args.id === "string" ? args.id.replace(/^\/+/, "").replace(/\.md$/i, "") : void 0;
|
|
2880
|
+
step.how = name === "search_notes" ? "keyword" : name === "find_similar" ? "meaning" : "read";
|
|
2881
|
+
step.away = {
|
|
2882
|
+
base,
|
|
2883
|
+
...note ? { note } : {},
|
|
2884
|
+
...typeof args.section === "string" && args.section ? { section: args.section } : {}
|
|
2885
|
+
};
|
|
2886
|
+
const query = String(args.query ?? args.text ?? "").trim();
|
|
2887
|
+
if (query) step.query = query;
|
|
2888
|
+
step.said = note ? `Read ${note} in ${base}` : query ? `Searched ${base} for "${query}"` : `Looked in ${base}`;
|
|
2889
|
+
return this.away.look(base, name, args);
|
|
2890
|
+
}
|
|
2823
2891
|
fail(step, why) {
|
|
2824
2892
|
step.failed = true;
|
|
2825
2893
|
step.said = why;
|
|
@@ -3147,6 +3215,7 @@ function turnMessages(a) {
|
|
|
3147
3215
|
const held = reads.length ? `# Notes you read earlier in this conversation\n\n${reads.map(sentText).join("\n\n")}` : "";
|
|
3148
3216
|
const aside = a.aside?.length ? `# Read earlier, set aside\n\n${a.aside.map((n) => `- ${n.title} (/${n.id}.md)`).join("\n")}` : "";
|
|
3149
3217
|
const agents = a.agents?.trim() ? `# Their instructions for this knowledge base (AGENTS.md)\n\n${a.agents.trim()}` : "";
|
|
3218
|
+
const lent = a.lookup && a.lent?.length ? `# Other knowledge bases lent to you\n\nYou may read these with the same look-up tools, giving their base; you change notes only in this one. Look in them where what is asked may be there: how the person works, their tools, what was settled before.\n\n${a.lent.map((l) => `- ${l.name}: ${l.about}`).join("\n")}` : "";
|
|
3150
3219
|
const known = understood.length ? `# Their understanding, as you have recorded it\n\n${understood.map(sentText).join("\n\n")}` : "";
|
|
3151
3220
|
const broken = a.broken?.length ? `# Code links that no longer resolve\n\nSince you were last told, these links in their notes point to files no longer in their repository. Mention it only where it bears on what they ask.\n\n${a.broken.map((b) => `- in ${b.title} (/${b.id}.md): ${b.href}`).join("\n")}` : "";
|
|
3152
3221
|
const request = `# What they ask now\n\n${a.asked}`;
|
|
@@ -3161,6 +3230,7 @@ function turnMessages(a) {
|
|
|
3161
3230
|
role: "user",
|
|
3162
3231
|
content: [
|
|
3163
3232
|
agents,
|
|
3233
|
+
lent,
|
|
3164
3234
|
earlier,
|
|
3165
3235
|
known,
|
|
3166
3236
|
gave,
|
|
@@ -3174,7 +3244,7 @@ function turnMessages(a) {
|
|
|
3174
3244
|
{
|
|
3175
3245
|
name: "instructions and tools",
|
|
3176
3246
|
detail: `${a.mode} mode${checking ? ", checking" : ""}${a.lookup ? "" : ", no look-ups"}`,
|
|
3177
|
-
tokens: estimate(system) + (a.lookup ? 500 : 0) + turnTools(a.mode, a.sent).length * 120
|
|
3247
|
+
tokens: estimate(system) + estimate(lent) + (a.lookup ? 500 : 0) + turnTools(a.mode, a.sent).length * 120
|
|
3178
3248
|
},
|
|
3179
3249
|
{
|
|
3180
3250
|
name: "earlier in this conversation",
|
|
@@ -3749,7 +3819,7 @@ function keepDone(before, next) {
|
|
|
3749
3819
|
return next;
|
|
3750
3820
|
}
|
|
3751
3821
|
}
|
|
3752
|
-
var SIGN_IN_MS, REPLY_TOKENS, NOTE_TOKENS, DRAFT_PREFIX, DRAFT_DIR, isDraftId, bare, inBase, BAD_PART, TURN_ID, escapeRe, MAX_SENT, BRIEF, NOTE_ARG, MARKED, EDIT_NOTE, WRITE_NOTE, Engine;
|
|
3822
|
+
var SIGN_IN_MS, fromBase, REPLY_TOKENS, NOTE_TOKENS, DRAFT_PREFIX, DRAFT_DIR, isDraftId, bare, inBase, BAD_PART, TURN_ID, escapeRe, MAX_SENT, BRIEF, NOTE_ARG, MARKED, EDIT_NOTE, WRITE_NOTE, Engine;
|
|
3753
3823
|
var init_engine = __esmMin((() => {
|
|
3754
3824
|
init_src$1();
|
|
3755
3825
|
init_api();
|
|
@@ -3762,6 +3832,7 @@ var init_engine = __esmMin((() => {
|
|
|
3762
3832
|
init_meaning();
|
|
3763
3833
|
init_provider();
|
|
3764
3834
|
SIGN_IN_MS = 6e5;
|
|
3835
|
+
fromBase = (base, text) => `(From the base ${base}: name its notes ${base}:<id>, or give base "${base}".)\n${text}`;
|
|
3765
3836
|
REPLY_TOKENS = 2500;
|
|
3766
3837
|
NOTE_TOKENS = 6e3;
|
|
3767
3838
|
DRAFT_PREFIX = "@draft/";
|
|
@@ -4023,7 +4094,7 @@ var init_engine = __esmMin((() => {
|
|
|
4023
4094
|
}));
|
|
4024
4095
|
}
|
|
4025
4096
|
async agentTools() {
|
|
4026
|
-
|
|
4097
|
+
const tools = [
|
|
4027
4098
|
BRIEF,
|
|
4028
4099
|
TOOLS[0],
|
|
4029
4100
|
...this.similar ? [FIND_SIMILAR] : [],
|
|
@@ -4031,9 +4102,48 @@ var init_engine = __esmMin((() => {
|
|
|
4031
4102
|
EDIT_NOTE,
|
|
4032
4103
|
WRITE_NOTE
|
|
4033
4104
|
];
|
|
4105
|
+
return this.opts.elsewhere ? withBase(tools, this.lent().map((l) => l.name), true) : tools;
|
|
4106
|
+
}
|
|
4107
|
+
/** The bases lent to this one's sessions, not this one. */
|
|
4108
|
+
lent() {
|
|
4109
|
+
return (this.opts.elsewhere?.lent() ?? []).filter((l) => l.name !== this.opts.name);
|
|
4110
|
+
}
|
|
4111
|
+
/** Another base lent here, by name, or why there is none of that name. */
|
|
4112
|
+
other(base) {
|
|
4113
|
+
const lent = this.lent();
|
|
4114
|
+
const engine = lent.some((l) => l.name === base) ? this.opts.elsewhere?.base(base) : null;
|
|
4115
|
+
if (!engine) throw new EngineError(lent.length ? `there is no base ${base} lent here: the bases are ${lent.map((l) => l.name).join(", ")}; leave base out for ${this.opts.name}` : `there is no base ${base} lent here: leave base out`, 404);
|
|
4116
|
+
return engine;
|
|
4117
|
+
}
|
|
4118
|
+
/** For Iso: a look in the other bases lent here, as their own look-ups give it, drawn on no Atlas. */
|
|
4119
|
+
get away() {
|
|
4120
|
+
if (!this.opts.elsewhere) return null;
|
|
4121
|
+
return {
|
|
4122
|
+
lent: () => this.lent(),
|
|
4123
|
+
look: async (base, tool, args) => fromBase(base, (await new Lookup(this.other(base).bundle, "", this.other(base).similar).call(tool, JSON.stringify(args))).text)
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
/** What this base is, in a line: its AGENTS.md's first line of text, else its name and size. */
|
|
4127
|
+
async about() {
|
|
4128
|
+
return ((await this.agents()).text ?? "").split("\n").map((l) => l.replace(/^#+\s*/, "").trim()).find((l) => l && !l.startsWith("---")) ?? `${this.bundle.concepts.size} notes`;
|
|
4034
4129
|
}
|
|
4035
4130
|
async agentLook({ agent, session, tool, args, asked = "" }) {
|
|
4036
|
-
|
|
4131
|
+
const there = baseOf(args);
|
|
4132
|
+
if (there.base && there.base !== this.opts.name) {
|
|
4133
|
+
const done = await this.other(there.base).agentLook({
|
|
4134
|
+
agent,
|
|
4135
|
+
session,
|
|
4136
|
+
tool,
|
|
4137
|
+
args: there.args,
|
|
4138
|
+
asked
|
|
4139
|
+
});
|
|
4140
|
+
return { text: fromBase(there.base, done.text) };
|
|
4141
|
+
}
|
|
4142
|
+
args = there.args;
|
|
4143
|
+
if (tool === BRIEF.function.name) {
|
|
4144
|
+
const lent = this.lent();
|
|
4145
|
+
return { text: this.brief() + (lent.length ? `\n\nOther knowledge bases lent to you, each with its own practice (brief_base with its base, before you use it):\n${lent.map((l) => `- ${l.name}: ${l.about}`).join("\n")}\nPass base to any tool to look or write there.` : "") };
|
|
4146
|
+
}
|
|
4037
4147
|
if (tool === EDIT_NOTE.function.name || tool === WRITE_NOTE.function.name) {
|
|
4038
4148
|
const str = (v, name) => {
|
|
4039
4149
|
if (typeof v !== "string") throw new EngineError(`${tool} needs ${name}, as text`, 400);
|
|
@@ -4075,6 +4185,7 @@ var init_engine = __esmMin((() => {
|
|
|
4075
4185
|
session
|
|
4076
4186
|
});
|
|
4077
4187
|
}
|
|
4188
|
+
if (!busy) for (const l of this.lent()) await this.opts.elsewhere?.base(l.name)?.agentStatus(agent, session, false);
|
|
4078
4189
|
}
|
|
4079
4190
|
async agentWrite({ agent, session, note, asked, edit }) {
|
|
4080
4191
|
const who = `agent:${agent}`;
|
|
@@ -4600,7 +4711,7 @@ var init_engine = __esmMin((() => {
|
|
|
4600
4711
|
tier: req.tier,
|
|
4601
4712
|
place
|
|
4602
4713
|
});
|
|
4603
|
-
const lookup = new Lookup(this.bundle, req.note, this.similar);
|
|
4714
|
+
const lookup = new Lookup(this.bundle, req.note, this.similar, this.away);
|
|
4604
4715
|
(async () => {
|
|
4605
4716
|
let ran = null, offered = false;
|
|
4606
4717
|
try {
|
|
@@ -5203,6 +5314,7 @@ var init_engine = __esmMin((() => {
|
|
|
5203
5314
|
sent: [...sent, ...reads],
|
|
5204
5315
|
aside,
|
|
5205
5316
|
...agentsText ? { agents: agentsText } : {},
|
|
5317
|
+
...this.lent().length ? { lent: this.lent() } : {},
|
|
5206
5318
|
asked: req.asked ?? ""
|
|
5207
5319
|
});
|
|
5208
5320
|
const ratio = hand.ratio ?? 1, scaled = (n) => Math.round(n * ratio);
|
|
@@ -5551,6 +5663,7 @@ var init_engine = __esmMin((() => {
|
|
|
5551
5663
|
sent,
|
|
5552
5664
|
aside: w.aside,
|
|
5553
5665
|
...w.agentsText ? { agents: w.agentsText } : {},
|
|
5666
|
+
...this.lent().length ? { lent: this.lent() } : {},
|
|
5554
5667
|
asked: req.asked,
|
|
5555
5668
|
broken: await this.brokenNews("iso")
|
|
5556
5669
|
});
|
|
@@ -5600,7 +5713,7 @@ var init_engine = __esmMin((() => {
|
|
|
5600
5713
|
});
|
|
5601
5714
|
};
|
|
5602
5715
|
const who = `iso:${this.provider.name}/${this.provider.tiers[req.tier]}`;
|
|
5603
|
-
const lookup = new Lookup(this.bundle, req.given.find((g) => g.kind !== "folder")?.id ?? "", this.similar);
|
|
5716
|
+
const lookup = new Lookup(this.bundle, req.given.find((g) => g.kind !== "folder")?.id ?? "", this.similar, this.away);
|
|
5604
5717
|
const looks = new Set(lookup.defs().map((d) => d.function.name));
|
|
5605
5718
|
const tools = {
|
|
5606
5719
|
defs: () => [...lookupOn ? lookup.defs() : [], ...own],
|
|
@@ -5611,7 +5724,7 @@ var init_engine = __esmMin((() => {
|
|
|
5611
5724
|
try {
|
|
5612
5725
|
args = JSON.parse(raw || "{}");
|
|
5613
5726
|
} catch {}
|
|
5614
|
-
const id = noteId(this.bundle, args.id), want = typeof args.section === "string" ? args.section.replace(/^#+\s*/, "").trim() : "";
|
|
5727
|
+
const id = args.base ? null : noteId(this.bundle, args.id), want = typeof args.section === "string" ? args.section.replace(/^#+\s*/, "").trim() : "";
|
|
5615
5728
|
const held = id ? inHand.get(id) : void 0;
|
|
5616
5729
|
if (id && held && (held === "whole" || want && held.includes(want))) {
|
|
5617
5730
|
const step = {
|
|
@@ -5635,6 +5748,8 @@ var init_engine = __esmMin((() => {
|
|
|
5635
5748
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) args = parsed;
|
|
5636
5749
|
} catch {}
|
|
5637
5750
|
if (!own.some((d) => d.function.name === name)) return { text: `There is no tool ${name} in ${mode} mode.` };
|
|
5751
|
+
const there = baseOf(args);
|
|
5752
|
+
if (there.base && there.base !== this.opts.name) return { text: `Notes are changed only in ${this.opts.name}, the base you are in: ${there.base} is lent to you to read.` };
|
|
5638
5753
|
return { text: await this.turnTool(name, args, {
|
|
5639
5754
|
turn,
|
|
5640
5755
|
conversation,
|
|
@@ -6224,10 +6339,10 @@ var init_engine = __esmMin((() => {
|
|
|
6224
6339
|
let text;
|
|
6225
6340
|
if (p.form === "rewrite") {
|
|
6226
6341
|
if (region !== void 0 && !p.open?.includes(region)) throw new EngineError("that change is no longer waiting: it was kept or removed", 409);
|
|
6227
|
-
text = written(now, p, region);
|
|
6342
|
+
text = written$1(now, p, region);
|
|
6228
6343
|
if (text === null) throw new EngineError(region === void 0 ? "a passage this would rewrite is no longer in the note as it was. Keep the others one by one." : "the passage this would rewrite is no longer in the note as it was", 409);
|
|
6229
6344
|
} else {
|
|
6230
|
-
text = written(now, p) ?? written(now, {
|
|
6345
|
+
text = written$1(now, p) ?? written$1(now, {
|
|
6231
6346
|
...p,
|
|
6232
6347
|
after: null,
|
|
6233
6348
|
before: null
|
|
@@ -6780,6 +6895,62 @@ function gatewayTlsFrom(config, dir, env = process.env) {
|
|
|
6780
6895
|
async function hasModel$1(config = configPath()) {
|
|
6781
6896
|
return await new ConfigSecrets(config).get("provider.key") !== null || providerConfig(config).url !== OPENROUTER.url;
|
|
6782
6897
|
}
|
|
6898
|
+
const BASE_NAME = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
6899
|
+
const expand = (p) => p === "~" ? homedir() : p.startsWith("~/") ? join(homedir(), p.slice(2)) : resolve(p);
|
|
6900
|
+
/** A path as written in the file: under the home folder, with ~. */
|
|
6901
|
+
const written = (p) => {
|
|
6902
|
+
const home = homedir();
|
|
6903
|
+
return p === home ? "~" : p.startsWith(home + "/") ? "~" + p.slice(home.length) : p;
|
|
6904
|
+
};
|
|
6905
|
+
/** Where the bases are listed, beside the configuration. */
|
|
6906
|
+
const basesPath = (config = configPath()) => join(dirname(config), "bases.toml");
|
|
6907
|
+
/** The bases listed, in the order written. A row without a path, or with a name isorite
|
|
6908
|
+
* would not give one, is passed over. */
|
|
6909
|
+
function knownBases(config = configPath()) {
|
|
6910
|
+
return Object.entries(readConfig(basesPath(config))).flatMap(([name, v]) => {
|
|
6911
|
+
const row = table(v);
|
|
6912
|
+
return BASE_NAME.test(name) && typeof row.path === "string" && row.path ? [{
|
|
6913
|
+
name,
|
|
6914
|
+
path: expand(row.path),
|
|
6915
|
+
lend: row.lend === true
|
|
6916
|
+
}] : [];
|
|
6917
|
+
});
|
|
6918
|
+
}
|
|
6919
|
+
/** A name for a base from its folder: small letters, digits and dashes. */
|
|
6920
|
+
function baseName(folder) {
|
|
6921
|
+
return basename(folder).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "base";
|
|
6922
|
+
}
|
|
6923
|
+
/** The base at `folder` by its name in the configuration, listed first if it is not: under its folder's name, or
|
|
6924
|
+
* that name with -2, -3… where another folder has it. `as` names it, and `lend` lends it. */
|
|
6925
|
+
function rememberBase(folder, { path = configPath(), as, lend } = {}) {
|
|
6926
|
+
const at = resolve(folder), had = knownBases(path).find((b) => b.path === at);
|
|
6927
|
+
if (had && lend === void 0) return had.name;
|
|
6928
|
+
const bases = readConfig(basesPath(path));
|
|
6929
|
+
let name = had?.name;
|
|
6930
|
+
if (!name) {
|
|
6931
|
+
const stem = as && BASE_NAME.test(as) ? as : baseName(at), taken = new Set(Object.keys(bases));
|
|
6932
|
+
name = stem;
|
|
6933
|
+
for (let n = 2; taken.has(name); n++) name = `${stem.slice(0, 36)}-${n}`;
|
|
6934
|
+
}
|
|
6935
|
+
bases[name] = {
|
|
6936
|
+
...table(bases[name]),
|
|
6937
|
+
path: written(at),
|
|
6938
|
+
...lend === void 0 ? {} : { lend }
|
|
6939
|
+
};
|
|
6940
|
+
writeConfig(bases, basesPath(path));
|
|
6941
|
+
return name;
|
|
6942
|
+
}
|
|
6943
|
+
/** Lend a known base to every session, or stop lending it. False when there is no base of that name. */
|
|
6944
|
+
function lendBase(name, lend, path = configPath()) {
|
|
6945
|
+
const bases = readConfig(basesPath(path));
|
|
6946
|
+
if (!BASE_NAME.test(name) || !bases[name]) return false;
|
|
6947
|
+
bases[name] = {
|
|
6948
|
+
...table(bases[name]),
|
|
6949
|
+
lend
|
|
6950
|
+
};
|
|
6951
|
+
writeConfig(bases, basesPath(path));
|
|
6952
|
+
return true;
|
|
6953
|
+
}
|
|
6783
6954
|
//#endregion
|
|
6784
6955
|
//#region packages/server/src/model.ts
|
|
6785
6956
|
/** Where the server looks for the model, in order, and why each place is there. */
|
|
@@ -6821,7 +6992,7 @@ function modelInfo(dir) {
|
|
|
6821
6992
|
} catch {}
|
|
6822
6993
|
const graph = sha(readFileSync(join(dir, "model.onnx"))), granite = graph === FILES[0][2];
|
|
6823
6994
|
const pooling = own.pooling === "mean" ? "mean" : "cls";
|
|
6824
|
-
const name = typeof own.name === "string" && own.name ? own.name : granite ? NAME$
|
|
6995
|
+
const name = typeof own.name === "string" && own.name ? own.name : granite ? NAME$2 : basename(dir);
|
|
6825
6996
|
return {
|
|
6826
6997
|
dir,
|
|
6827
6998
|
name,
|
|
@@ -7003,11 +7174,11 @@ async function modelCommand(from, say = console.log, env = process.env, sources
|
|
|
7003
7174
|
return 1;
|
|
7004
7175
|
}
|
|
7005
7176
|
}
|
|
7006
|
-
var NAME$
|
|
7177
|
+
var NAME$2, MODEL, RELEASE, PACKAGE, HUB, FILES, sha, userModelsDir, userModelDir, hasModel, ModelFetchError, modelFiles;
|
|
7007
7178
|
var init_model = __esmMin((() => {
|
|
7008
7179
|
init_src();
|
|
7009
|
-
NAME$
|
|
7010
|
-
MODEL = `${NAME$
|
|
7180
|
+
NAME$2 = "granite-embedding-small-english-r2";
|
|
7181
|
+
MODEL = `${NAME$2}-q8`;
|
|
7011
7182
|
RELEASE = "https://github.com/lachlanjs/isorite/releases/download/model-granite-1.0.0/isorite-model-granite-1.0.0.tgz";
|
|
7012
7183
|
PACKAGE = "isorite-model-granite@1.0.0";
|
|
7013
7184
|
HUB = `https://huggingface.co/onnx-community/granite-embedding-small-english-r2-ONNX/resolve/1dc7835ba0cb9c76a3618d0bf0c427c97671b3c8`;
|
|
@@ -7035,7 +7206,7 @@ var init_model = __esmMin((() => {
|
|
|
7035
7206
|
];
|
|
7036
7207
|
sha = (buf) => createHash("sha256").update(buf).digest("hex");
|
|
7037
7208
|
userModelsDir = (env = process.env) => join(env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "isorite", "models");
|
|
7038
|
-
userModelDir = (env = process.env) => join(userModelsDir(env), NAME$
|
|
7209
|
+
userModelDir = (env = process.env) => join(userModelsDir(env), NAME$2);
|
|
7039
7210
|
hasModel = (dir) => existsSync(join(dir, "model.onnx")) && existsSync(join(dir, "tokenizer.json"));
|
|
7040
7211
|
ModelFetchError = class extends Error {
|
|
7041
7212
|
tried;
|
|
@@ -7223,7 +7394,132 @@ function relay(live, key) {
|
|
|
7223
7394
|
};
|
|
7224
7395
|
}
|
|
7225
7396
|
//#endregion
|
|
7397
|
+
//#region packages/server/src/bases.ts
|
|
7398
|
+
const NAME$1 = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
7399
|
+
var Bases = class {
|
|
7400
|
+
first;
|
|
7401
|
+
#config;
|
|
7402
|
+
#make;
|
|
7403
|
+
#open = /* @__PURE__ */ new Map();
|
|
7404
|
+
#ready = /* @__PURE__ */ new Map();
|
|
7405
|
+
#about = /* @__PURE__ */ new Map();
|
|
7406
|
+
#holds = 0;
|
|
7407
|
+
#idle;
|
|
7408
|
+
/** `make` opens a base; `idle` is called when the last hold is let go (a server isorite term started). */
|
|
7409
|
+
constructor({ config, first, make, idle }) {
|
|
7410
|
+
this.#config = config;
|
|
7411
|
+
this.first = first;
|
|
7412
|
+
this.#make = make;
|
|
7413
|
+
this.#idle = idle;
|
|
7414
|
+
}
|
|
7415
|
+
/** The first base, and every lent one. */
|
|
7416
|
+
async start() {
|
|
7417
|
+
await this.get(this.first);
|
|
7418
|
+
await Promise.all(knownBases(this.#config).filter((b) => b.lend).map((b) => this.get(b.name)?.catch((e) => console.error(`isorite: ${b.name} (${b.path}) could not be opened: ${e.message}`))));
|
|
7419
|
+
}
|
|
7420
|
+
/** A known base, opened if it is not yet; null where none has that name. */
|
|
7421
|
+
get(name) {
|
|
7422
|
+
const had = this.#open.get(name);
|
|
7423
|
+
if (had) return had;
|
|
7424
|
+
const known = NAME$1.test(name) ? knownBases(this.#config).find((b) => b.name === name) : void 0;
|
|
7425
|
+
if (!known) return null;
|
|
7426
|
+
const opening = this.#make(known).then(async (opened) => {
|
|
7427
|
+
this.#ready.set(name, opened);
|
|
7428
|
+
this.#about.set(name, await opened.engine.about());
|
|
7429
|
+
opened.engine.events((e) => {
|
|
7430
|
+
if (e.type === "note-changed") opened.engine.about().then((a) => this.#about.set(name, a));
|
|
7431
|
+
});
|
|
7432
|
+
return opened;
|
|
7433
|
+
});
|
|
7434
|
+
opening.catch(() => this.#open.delete(name));
|
|
7435
|
+
this.#open.set(name, opening);
|
|
7436
|
+
return opening;
|
|
7437
|
+
}
|
|
7438
|
+
/** Every open base, for what is kept into use in all of them (the provider). */
|
|
7439
|
+
each(fn) {
|
|
7440
|
+
for (const o of this.#ready.values()) fn(o.engine);
|
|
7441
|
+
}
|
|
7442
|
+
lent() {
|
|
7443
|
+
return knownBases(this.#config).filter((b) => b.lend && this.#ready.has(b.name)).map((b) => ({
|
|
7444
|
+
name: b.name,
|
|
7445
|
+
about: this.#about.get(b.name) ?? ""
|
|
7446
|
+
}));
|
|
7447
|
+
}
|
|
7448
|
+
base(name) {
|
|
7449
|
+
return this.#ready.get(name)?.engine ?? null;
|
|
7450
|
+
}
|
|
7451
|
+
list() {
|
|
7452
|
+
return knownBases(this.#config).map((b) => ({
|
|
7453
|
+
name: b.name,
|
|
7454
|
+
path: b.path,
|
|
7455
|
+
lend: b.lend,
|
|
7456
|
+
open: this.#ready.has(b.name)
|
|
7457
|
+
}));
|
|
7458
|
+
}
|
|
7459
|
+
async lend(name, on) {
|
|
7460
|
+
if (!lendBase(name, on, this.#config)) return false;
|
|
7461
|
+
if (on) await this.get(name);
|
|
7462
|
+
return true;
|
|
7463
|
+
}
|
|
7464
|
+
async open(path) {
|
|
7465
|
+
const name = rememberBase(resolve(path), { path: this.#config });
|
|
7466
|
+
await this.get(name);
|
|
7467
|
+
return {
|
|
7468
|
+
name,
|
|
7469
|
+
first: name === this.first
|
|
7470
|
+
};
|
|
7471
|
+
}
|
|
7472
|
+
hold() {
|
|
7473
|
+
this.#holds++;
|
|
7474
|
+
let let_go = false;
|
|
7475
|
+
return () => {
|
|
7476
|
+
if (let_go) return;
|
|
7477
|
+
let_go = true;
|
|
7478
|
+
if (--this.#holds === 0) setTimeout(() => {
|
|
7479
|
+
if (this.#holds === 0) this.#idle?.();
|
|
7480
|
+
}, 1e3).unref();
|
|
7481
|
+
};
|
|
7482
|
+
}
|
|
7483
|
+
/** A request to the server: to a base's API by its address, /b/<name>/api/…, or to the first. A base's page is
|
|
7484
|
+
* the app's own page with ?base=<name> (SvelteKit's hash router needs its page at the root it was built for):
|
|
7485
|
+
* /b/<name> is sent there. */
|
|
7486
|
+
async fetch(req, env) {
|
|
7487
|
+
const url = new URL(req.url);
|
|
7488
|
+
const m = /^\/b\/([^/]+)(\/.*)?$/.exec(url.pathname);
|
|
7489
|
+
if (!m || !NAME$1.test(m[1])) return (await this.get(this.first)).fetch(req, env);
|
|
7490
|
+
const name = m[1];
|
|
7491
|
+
if (!m[2]?.startsWith("/api/")) return new Response(null, {
|
|
7492
|
+
status: 302,
|
|
7493
|
+
headers: { Location: `/?base=${name}${m[2]?.startsWith("/#") ? m[2].slice(1) : ""}` }
|
|
7494
|
+
});
|
|
7495
|
+
const opening = this.get(name);
|
|
7496
|
+
if (!opening) return new Response(JSON.stringify({ error: `no base ${name} is known here: isorite bases lists them` }), {
|
|
7497
|
+
status: 404,
|
|
7498
|
+
headers: { "Content-Type": "application/json" }
|
|
7499
|
+
});
|
|
7500
|
+
let opened;
|
|
7501
|
+
try {
|
|
7502
|
+
opened = await opening;
|
|
7503
|
+
} catch (e) {
|
|
7504
|
+
return new Response(JSON.stringify({ error: `${name} could not be opened: ${e.message}` }), {
|
|
7505
|
+
status: 500,
|
|
7506
|
+
headers: { "Content-Type": "application/json" }
|
|
7507
|
+
});
|
|
7508
|
+
}
|
|
7509
|
+
url.pathname = m[2];
|
|
7510
|
+
const body = req.method === "GET" || req.method === "HEAD" ? null : req.body;
|
|
7511
|
+
return opened.fetch(new Request(url, {
|
|
7512
|
+
method: req.method,
|
|
7513
|
+
headers: req.headers,
|
|
7514
|
+
body,
|
|
7515
|
+
signal: req.signal,
|
|
7516
|
+
...body ? { duplex: "half" } : {}
|
|
7517
|
+
}), env);
|
|
7518
|
+
}
|
|
7519
|
+
};
|
|
7520
|
+
//#endregion
|
|
7226
7521
|
//#region packages/server/src/terminal.ts
|
|
7522
|
+
init_src();
|
|
7227
7523
|
const OUTPUT = 4e3;
|
|
7228
7524
|
const LOOKUPS = /* @__PURE__ */ new Set([
|
|
7229
7525
|
"search_notes",
|
|
@@ -7234,6 +7530,7 @@ const LOOKUPS = /* @__PURE__ */ new Set([
|
|
|
7234
7530
|
]);
|
|
7235
7531
|
/** isorite's tools that change a note: their line opens it, where the agent's words are marked. */
|
|
7236
7532
|
const CHANGES = /* @__PURE__ */ new Set(["edit_note", "write_note"]);
|
|
7533
|
+
const isorite = (name) => LOOKUPS.has(name) || CHANGES.has(name);
|
|
7237
7534
|
/** What a tool call acted on, in a line. */
|
|
7238
7535
|
function titleOf(name, input = {}) {
|
|
7239
7536
|
const s = (k) => typeof input[k] === "string" ? input[k] : "";
|
|
@@ -7281,16 +7578,21 @@ function toTurns(messages, { live = /* @__PURE__ */ new Map(), ours = /* @__PURE
|
|
|
7281
7578
|
const input = c.state.input ?? {};
|
|
7282
7579
|
const output = (c.state.content ?? []).map((x) => x.text ?? "").join("").slice(0, OUTPUT);
|
|
7283
7580
|
const status = c.state.status === "completed" ? "done" : /error|fail/.test(c.state.status) ? "failed" : "running";
|
|
7581
|
+
const { base, args } = isorite(c.name) ? baseOf(input) : {
|
|
7582
|
+
base: "",
|
|
7583
|
+
args: input
|
|
7584
|
+
};
|
|
7284
7585
|
const item = {
|
|
7285
7586
|
kind: "tool",
|
|
7286
7587
|
id: c.id,
|
|
7287
7588
|
name: c.name,
|
|
7288
|
-
title: titleOf(c.name,
|
|
7589
|
+
title: (base ? `${base}: ` : "") + titleOf(c.name, args),
|
|
7289
7590
|
status,
|
|
7290
7591
|
output
|
|
7291
7592
|
};
|
|
7292
|
-
const named = LOOKUPS.has(c.name) ?
|
|
7593
|
+
const named = LOOKUPS.has(c.name) ? args.id : CHANGES.has(c.name) ? args.note : void 0;
|
|
7293
7594
|
if (typeof named === "string" && named) item.note = named.replace(/^\//, "").replace(/\.md$/, "");
|
|
7595
|
+
if (base) item.base = base;
|
|
7294
7596
|
const diff = diffOf(c.name, input);
|
|
7295
7597
|
if (diff) item.diff = diff;
|
|
7296
7598
|
turn.items.push(item);
|
|
@@ -7722,7 +8024,7 @@ const AgentStatus = z.object({
|
|
|
7722
8024
|
session: z.string().min(1).max(200),
|
|
7723
8025
|
busy: z.boolean()
|
|
7724
8026
|
}).strict();
|
|
7725
|
-
function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal, gateway, setup }) {
|
|
8027
|
+
function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal, gateway, setup, bases, base }) {
|
|
7726
8028
|
const app = new Hono();
|
|
7727
8029
|
const allowed = new Set([...allowHosts].map((h) => h.toLowerCase()));
|
|
7728
8030
|
const reply = (c, status, value) => c.body(JSON.stringify(value), status, {
|
|
@@ -7925,9 +8227,52 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal
|
|
|
7925
8227
|
post("/api/settings/provider/models", z.object({ draft: SetupDraft.nullable() }).strict(), async (c, b) => ({ models: await setup.models(local(c) ? b.draft : null) }));
|
|
7926
8228
|
post("/api/settings/report", z.object({}).strict(), async () => ({ text: await setup.report() }));
|
|
7927
8229
|
}
|
|
8230
|
+
if (bases) {
|
|
8231
|
+
app.get("/api/bases", (c) => answer(c, async () => ({
|
|
8232
|
+
here: base ?? "",
|
|
8233
|
+
local: local(c),
|
|
8234
|
+
bases: bases.list()
|
|
8235
|
+
})));
|
|
8236
|
+
const mine = (c) => {
|
|
8237
|
+
if (!local(c)) throw new EngineError("the bases are changed only from this machine", 403);
|
|
8238
|
+
};
|
|
8239
|
+
post("/api/bases/lend", z.object({
|
|
8240
|
+
name: z.string().max(40),
|
|
8241
|
+
lend: z.boolean()
|
|
8242
|
+
}).strict(), async (c, b) => {
|
|
8243
|
+
mine(c);
|
|
8244
|
+
if (!await bases.lend(b.name, b.lend)) throw new EngineError(`no base ${b.name} is known here`, 404);
|
|
8245
|
+
return { bases: bases.list() };
|
|
8246
|
+
});
|
|
8247
|
+
post("/api/bases/open", z.object({ path: z.string().min(1).max(4e3) }).strict(), async (c, b) => {
|
|
8248
|
+
mine(c);
|
|
8249
|
+
if (!isAbsolute(b.path) || !existsSync(b.path) || !statSync(b.path).isDirectory()) throw new EngineError(`no folder at ${b.path}`, 404);
|
|
8250
|
+
return bases.open(b.path);
|
|
8251
|
+
});
|
|
8252
|
+
app.get("/api/server/hold", (c) => {
|
|
8253
|
+
if (!local(c)) return refuse(c, 403, "held only from this machine");
|
|
8254
|
+
return streamSSE(c, async (stream) => {
|
|
8255
|
+
const release = bases.hold();
|
|
8256
|
+
const beat = setInterval(() => {
|
|
8257
|
+
stream.write(": held\n\n");
|
|
8258
|
+
}, 25e3);
|
|
8259
|
+
await stream.writeSSE({
|
|
8260
|
+
event: "held",
|
|
8261
|
+
data: "{}"
|
|
8262
|
+
});
|
|
8263
|
+
await new Promise((done) => stream.onAbort(done));
|
|
8264
|
+
clearInterval(beat);
|
|
8265
|
+
release();
|
|
8266
|
+
});
|
|
8267
|
+
});
|
|
8268
|
+
}
|
|
7928
8269
|
app.get("/api/agent/tools", (c) => answer(c, () => api.agentTools()));
|
|
7929
8270
|
post("/api/agent/look", AgentLook, (_c, b) => api.agentLook(b));
|
|
7930
|
-
app.get("/api/agent/base", (c) => answer(c, async () => ({
|
|
8271
|
+
app.get("/api/agent/base", (c) => answer(c, async () => ({
|
|
8272
|
+
notes: notes ?? null,
|
|
8273
|
+
name: base ?? null,
|
|
8274
|
+
lent: (bases?.list() ?? []).filter((b) => b.lend && b.name !== base).map((b) => b.name)
|
|
8275
|
+
})));
|
|
7931
8276
|
post("/api/agent/write", AgentChange, (_c, b) => api.agentWrite(b));
|
|
7932
8277
|
post("/api/agent/send", z.object({
|
|
7933
8278
|
note: z.string().max(400),
|
|
@@ -8368,7 +8713,7 @@ var Setup = class {
|
|
|
8368
8713
|
};
|
|
8369
8714
|
//#endregion
|
|
8370
8715
|
//#region packages/server/src/version.ts
|
|
8371
|
-
const VERSION = "0.
|
|
8716
|
+
const VERSION = "0.8.0";
|
|
8372
8717
|
//#endregion
|
|
8373
8718
|
//#region packages/server/src/studybase.ts
|
|
8374
8719
|
init_src$1();
|
|
@@ -8439,15 +8784,16 @@ function newBase(path) {
|
|
|
8439
8784
|
makeOwn(at);
|
|
8440
8785
|
return at;
|
|
8441
8786
|
}
|
|
8442
|
-
async function openEngine({ notes }, device = deviceId(), config = configPath(), http, model = modelDir(), secrets = new ConfigSecrets(config)) {
|
|
8787
|
+
async function openEngine({ notes }, device = deviceId(), config = configPath(), http, model = modelDir(), secrets = new ConfigSecrets(config), extras = {}) {
|
|
8443
8788
|
const provider = providerConfig(config);
|
|
8444
8789
|
const own = makeOwn(notes);
|
|
8790
|
+
const embedder = extras.embedder !== void 0 ? extras.embedder : model ? nodeEmbedder(model) : null;
|
|
8445
8791
|
return Engine.open({
|
|
8446
8792
|
notes: new NodeFiles(notes),
|
|
8447
8793
|
own: new NodeFiles(own),
|
|
8448
8794
|
clock: systemClock,
|
|
8449
8795
|
device,
|
|
8450
|
-
name: basename(notes),
|
|
8796
|
+
name: extras.name ?? basename(notes),
|
|
8451
8797
|
http: http ?? gatewayHttp(provider, gatewayTls(config)),
|
|
8452
8798
|
secrets,
|
|
8453
8799
|
provider,
|
|
@@ -8455,13 +8801,14 @@ async function openEngine({ notes }, device = deviceId(), config = configPath(),
|
|
|
8455
8801
|
const dir = resolve(notes, path);
|
|
8456
8802
|
return existsSync(dir) && statSync(dir).isDirectory() ? new NodeFiles(dir) : null;
|
|
8457
8803
|
},
|
|
8458
|
-
...
|
|
8459
|
-
embedder
|
|
8804
|
+
...embedder ? {
|
|
8805
|
+
embedder,
|
|
8460
8806
|
cache: {
|
|
8461
8807
|
files: new NodeFiles(cacheDir()),
|
|
8462
8808
|
name: `vectors-${createHash("sha256").update(notes).digest("hex").slice(0, 16)}.json`
|
|
8463
8809
|
}
|
|
8464
|
-
} : {}
|
|
8810
|
+
} : {},
|
|
8811
|
+
...extras.elsewhere ? { elsewhere: extras.elsewhere } : {}
|
|
8465
8812
|
});
|
|
8466
8813
|
}
|
|
8467
8814
|
/** The guide's finder, as a provider: no key, no sign-in, and a name that says what it is. */
|
|
@@ -8486,16 +8833,49 @@ async function liveProvider(config, stand) {
|
|
|
8486
8833
|
}
|
|
8487
8834
|
/** A link most terminals let you click (OSC 8), when the output is a terminal; the address as it is otherwise. */
|
|
8488
8835
|
const link$1 = (url, tty = Boolean(process.stdout.isTTY)) => tty ? `\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\` : url;
|
|
8489
|
-
/**
|
|
8490
|
-
*
|
|
8491
|
-
|
|
8836
|
+
/** The server's bases, opened: the one named (remembered in bases.toml, M20) and every lent one, each with its
|
|
8837
|
+
* engine and app, and the server's answer to a request for any of them. `stand`: in place of the provider, an
|
|
8838
|
+
* answerer of its own, as the guide's finder is when no model is set up (M16), asked while there is no model here.
|
|
8839
|
+
* `idle`: what to do once the last of those holding the server lets it go. */
|
|
8840
|
+
async function openServer(path, { host = "127.0.0.1", port = 4767, allowHosts = [], stand, idle, config = configPath(), model = modelDir() } = {}) {
|
|
8492
8841
|
const opened = locate(path);
|
|
8493
|
-
const config = configPath();
|
|
8494
8842
|
const current = () => liveProvider(config, stand);
|
|
8495
8843
|
let live = await current();
|
|
8496
|
-
const secrets = new ConfigSecrets(config);
|
|
8497
|
-
const
|
|
8498
|
-
|
|
8844
|
+
const secrets = new ConfigSecrets(config), device = deviceId(config);
|
|
8845
|
+
const first = rememberBase(opened.notes, { path: config });
|
|
8846
|
+
const embedder = model ? nodeEmbedder(model) : null;
|
|
8847
|
+
const token = randomBytes(24).toString("base64url"), loopback = LOOPBACK.has(host);
|
|
8848
|
+
const gateway = loopback ? relay(() => live, () => secrets.get(KEY)) : void 0;
|
|
8849
|
+
const web = existsSync(join(WEB_DIR, "index.html")) ? WEB_DIR : null;
|
|
8850
|
+
const bases = new Bases({
|
|
8851
|
+
config,
|
|
8852
|
+
first,
|
|
8853
|
+
...idle ? { idle } : {},
|
|
8854
|
+
make: async (known) => {
|
|
8855
|
+
const api = await openEngine({ notes: known.path }, device, config, live.http, model, secrets, {
|
|
8856
|
+
name: known.name,
|
|
8857
|
+
elsewhere: bases,
|
|
8858
|
+
embedder
|
|
8859
|
+
});
|
|
8860
|
+
api.setProvider(live.provider, live.http);
|
|
8861
|
+
return {
|
|
8862
|
+
engine: api,
|
|
8863
|
+
fetch: createApp({
|
|
8864
|
+
api,
|
|
8865
|
+
token,
|
|
8866
|
+
loopback,
|
|
8867
|
+
web,
|
|
8868
|
+
allowHosts,
|
|
8869
|
+
notes: known.path,
|
|
8870
|
+
terminal: new Terminal(),
|
|
8871
|
+
gateway,
|
|
8872
|
+
setup,
|
|
8873
|
+
bases,
|
|
8874
|
+
base: known.name
|
|
8875
|
+
}).fetch
|
|
8876
|
+
};
|
|
8877
|
+
}
|
|
8878
|
+
});
|
|
8499
8879
|
const setup = new Setup({
|
|
8500
8880
|
config,
|
|
8501
8881
|
notes: opened.notes,
|
|
@@ -8503,25 +8883,29 @@ async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [],
|
|
|
8503
8883
|
port,
|
|
8504
8884
|
kept: async () => {
|
|
8505
8885
|
live = await current();
|
|
8506
|
-
api.setProvider(live.provider, live.http);
|
|
8886
|
+
bases.each((api) => api.setProvider(live.provider, live.http));
|
|
8507
8887
|
}
|
|
8508
8888
|
});
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
const terminal = new Terminal();
|
|
8512
|
-
const app = createApp({
|
|
8513
|
-
api,
|
|
8514
|
-
token: randomBytes(24).toString("base64url"),
|
|
8515
|
-
loopback: LOOPBACK.has(host),
|
|
8516
|
-
web,
|
|
8517
|
-
allowHosts,
|
|
8889
|
+
await bases.start();
|
|
8890
|
+
return {
|
|
8518
8891
|
notes: opened.notes,
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8892
|
+
bases,
|
|
8893
|
+
embedder: Boolean(embedder),
|
|
8894
|
+
fetch: (req, env) => bases.fetch(req, env)
|
|
8895
|
+
};
|
|
8896
|
+
}
|
|
8897
|
+
/** `held`: started by isorite term or mcp, it stops once the last of them lets it go. */
|
|
8898
|
+
async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [], stand, held = false } = {}) {
|
|
8899
|
+
let stop = () => void 0;
|
|
8900
|
+
const { notes, bases, embedder, fetch } = await openServer(path, {
|
|
8901
|
+
host,
|
|
8902
|
+
port,
|
|
8903
|
+
allowHosts,
|
|
8904
|
+
...stand ? { stand } : {},
|
|
8905
|
+
...held ? { idle: () => stop() } : {}
|
|
8522
8906
|
});
|
|
8523
8907
|
const server = serve({
|
|
8524
|
-
fetch
|
|
8908
|
+
fetch,
|
|
8525
8909
|
hostname: host,
|
|
8526
8910
|
port
|
|
8527
8911
|
});
|
|
@@ -8529,11 +8913,13 @@ async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [],
|
|
|
8529
8913
|
console.error(e.code === "EADDRINUSE" ? `port ${port} is in use (isorite serve --port <n>)` : e.message);
|
|
8530
8914
|
process.exit(1);
|
|
8531
8915
|
});
|
|
8532
|
-
console.log(`isorite: ${
|
|
8533
|
-
console.log(
|
|
8534
|
-
|
|
8916
|
+
console.log(`isorite: ${notes}`);
|
|
8917
|
+
console.log(embedder ? " search by meaning: on" : " search by meaning: off (isorite model fetches its model)");
|
|
8918
|
+
const lent = bases.lent().filter((l) => l.name !== bases.first).map((l) => l.name);
|
|
8919
|
+
if (lent.length) console.log(` lent to every session: ${lent.join(", ")} (isorite bases)`);
|
|
8920
|
+
console.log(existsSync(join(WEB_DIR, "index.html")) ? ` ${link$1(`http://localhost:${port}/`)}` : ` the app is not built (npm run build); the engine alone is at http://localhost:${port}/api/`);
|
|
8535
8921
|
if (process.env.SSH_CONNECTION && LOOPBACK.has(host)) console.log(` over SSH, the link opens on this machine; from your own, first run: ssh -L ${port}:localhost:${port} ${hostname()}`);
|
|
8536
|
-
|
|
8922
|
+
stop = () => {
|
|
8537
8923
|
server.close();
|
|
8538
8924
|
process.exit(0);
|
|
8539
8925
|
};
|
|
@@ -8569,49 +8955,77 @@ async function serving(url) {
|
|
|
8569
8955
|
return null;
|
|
8570
8956
|
}
|
|
8571
8957
|
}
|
|
8572
|
-
/** The isorite server on the port, and how to let it go: one already running for this base
|
|
8573
|
-
* no base is named), or one started for the base, its output kept in a
|
|
8958
|
+
/** The isorite server on the port, and how to let it go: one already running (for this base, or holding it as
|
|
8959
|
+
* well from now on, M20; or for any, when no base is named), or one started for the base, its output kept in a
|
|
8960
|
+
* log. `url` is the base's own address there. A server this started stops once the last of those using it lets it
|
|
8961
|
+
* go; one you started yourself goes on. */
|
|
8574
8962
|
async function ensureServer(path, { port = 4767, env = process.env, wait = 12e4 } = {}) {
|
|
8575
|
-
const
|
|
8963
|
+
const root = `http://127.0.0.1:${port}`;
|
|
8576
8964
|
const opened = path === void 0 ? null : locate(path);
|
|
8577
|
-
|
|
8578
|
-
if (name
|
|
8579
|
-
if (opened
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
server.kill();
|
|
8607
|
-
throw new StudyError(`the isorite server did not start; its output is in ${log}`);
|
|
8965
|
+
let name = await serving(root), started = null;
|
|
8966
|
+
if (name === null) {
|
|
8967
|
+
if (!opened) throw new StudyError(`nothing is serving on port ${port}: name a base`);
|
|
8968
|
+
const log = join(tmpdir(), `isorite-term-${port}.log`);
|
|
8969
|
+
const out = openSync(log, "w");
|
|
8970
|
+
started = spawn(process.execPath, [
|
|
8971
|
+
MAIN,
|
|
8972
|
+
"serve",
|
|
8973
|
+
path,
|
|
8974
|
+
"--port",
|
|
8975
|
+
String(port),
|
|
8976
|
+
"--held"
|
|
8977
|
+
], {
|
|
8978
|
+
stdio: [
|
|
8979
|
+
"ignore",
|
|
8980
|
+
out,
|
|
8981
|
+
out
|
|
8982
|
+
],
|
|
8983
|
+
env,
|
|
8984
|
+
detached: true
|
|
8985
|
+
});
|
|
8986
|
+
const exited = new Promise((done) => started.once("exit", () => done()));
|
|
8987
|
+
const begun = Date.now();
|
|
8988
|
+
while ((name = await serving(root)) === null) {
|
|
8989
|
+
if (started.exitCode !== null || Date.now() - begun > wait) {
|
|
8990
|
+
started.kill();
|
|
8991
|
+
throw new StudyError(`the isorite server did not start; its output is in ${log}`);
|
|
8992
|
+
}
|
|
8993
|
+
await Promise.race([exited, new Promise((r) => setTimeout(r, 250))]);
|
|
8608
8994
|
}
|
|
8609
|
-
|
|
8995
|
+
started.unref();
|
|
8610
8996
|
}
|
|
8997
|
+
let url = root;
|
|
8998
|
+
if (opened) {
|
|
8999
|
+
const at = await fetch(`${root}/api/bases/open`, {
|
|
9000
|
+
method: "POST",
|
|
9001
|
+
headers: {
|
|
9002
|
+
"Content-Type": "application/json",
|
|
9003
|
+
"x-isorite-token": await tokenOf(root),
|
|
9004
|
+
Origin: root
|
|
9005
|
+
},
|
|
9006
|
+
body: JSON.stringify({ path: opened.notes })
|
|
9007
|
+
}).catch(() => null);
|
|
9008
|
+
if (at?.ok) {
|
|
9009
|
+
const base = await at.json();
|
|
9010
|
+
if (!base.first) url = `${root}/b/${base.name}`;
|
|
9011
|
+
} else if (name !== basename(opened.notes)) throw new StudyError(`port ${port} is serving ${name}, not ${basename(opened.notes)}: stop it, or use another port (--port <n>)`);
|
|
9012
|
+
}
|
|
9013
|
+
const holding = new AbortController();
|
|
9014
|
+
const held = fetch(`${root}/api/server/hold`, { signal: holding.signal }).then((r) => {
|
|
9015
|
+
if (r.ok && r.body) (async () => {
|
|
9016
|
+
try {
|
|
9017
|
+
for await (const _ of r.body);
|
|
9018
|
+
} catch {}
|
|
9019
|
+
})();
|
|
9020
|
+
return r.ok;
|
|
9021
|
+
}, () => false);
|
|
8611
9022
|
return {
|
|
8612
9023
|
url,
|
|
8613
9024
|
release: () => {
|
|
8614
|
-
|
|
9025
|
+
holding.abort();
|
|
9026
|
+
held.then((ok) => {
|
|
9027
|
+
if (!ok && started) started.kill();
|
|
9028
|
+
});
|
|
8615
9029
|
}
|
|
8616
9030
|
};
|
|
8617
9031
|
}
|
|
@@ -8632,7 +9046,7 @@ async function tell(url, path, body) {
|
|
|
8632
9046
|
headers: {
|
|
8633
9047
|
"Content-Type": "application/json",
|
|
8634
9048
|
"x-isorite-token": token,
|
|
8635
|
-
Origin: url
|
|
9049
|
+
Origin: new URL(url).origin
|
|
8636
9050
|
},
|
|
8637
9051
|
body: JSON.stringify(body)
|
|
8638
9052
|
});
|
|
@@ -8723,9 +9137,9 @@ async function modelsFor(env, server) {
|
|
|
8723
9137
|
}
|
|
8724
9138
|
/** What `isorite term` says before OpenCode takes the screen, and again when it gives it back: where isorite is in
|
|
8725
9139
|
* the browser, which models the terminal uses and where they come from, and, over SSH, how to reach the browser. */
|
|
8726
|
-
function where(port, models, env, tty) {
|
|
9140
|
+
function where(port, models, env, tty, page = "/") {
|
|
8727
9141
|
const names = models.gateway?.models.join(", ");
|
|
8728
|
-
const lines = [`isorite: ${link$1(`http://localhost:${port}
|
|
9142
|
+
const lines = [`isorite: ${link$1(`http://localhost:${port}${page}`, tty)}`, models.from === "environment" ? ` the terminal's models: ${names}, at ${models.gateway.url} (ISORITE_GATEWAY_URL)` : models.from === "config" ? ` the terminal's models: ${names}, as config.toml names them, through the isorite server` : " the terminal's models: OpenCode's own, as no key or certificate is named in config.toml"];
|
|
8729
9143
|
if (env.SSH_CONNECTION) lines.push(` over SSH, the link opens on this machine; from your own, first run: ssh -L ${port}:localhost:${port} ${hostname()}`);
|
|
8730
9144
|
return lines;
|
|
8731
9145
|
}
|
|
@@ -8752,7 +9166,7 @@ async function term(path, { port = 4767, cwd = process.cwd(), env: given = proce
|
|
|
8752
9166
|
throw e;
|
|
8753
9167
|
}
|
|
8754
9168
|
const { gateway, env } = models;
|
|
8755
|
-
const said = where(port, models, given);
|
|
9169
|
+
const said = where(port, models, given, void 0, new URL(server.url).pathname.replace(/\/?$/, "/"));
|
|
8756
9170
|
for (const line of said) console.log(line);
|
|
8757
9171
|
const agents = path === void 0 ? null : join(locate(path).notes, AGENTS_FILE);
|
|
8758
9172
|
const home = writeHome(defaultHome(env), {
|
|
@@ -9225,7 +9639,7 @@ async function mcp(path, { port = 4767, agent = "mcp", quiet = QUIET_MS, input =
|
|
|
9225
9639
|
headers: {
|
|
9226
9640
|
"Content-Type": "application/json",
|
|
9227
9641
|
[TOKEN_HEADER]: await token,
|
|
9228
|
-
Origin: url
|
|
9642
|
+
Origin: new URL(url).origin
|
|
9229
9643
|
},
|
|
9230
9644
|
body: JSON.stringify(body)
|
|
9231
9645
|
});
|
|
@@ -9255,7 +9669,7 @@ async function mcp(path, { port = 4767, agent = "mcp", quiet = QUIET_MS, input =
|
|
|
9255
9669
|
name: "isorite",
|
|
9256
9670
|
version: "0.1.0"
|
|
9257
9671
|
},
|
|
9258
|
-
instructions: "A knowledge base of notes. Call brief_base first, then look things up with search_notes, outline_note and read_note, following a note's links before searching again. Cite a note by its title and section."
|
|
9672
|
+
instructions: "A knowledge base of notes. Call brief_base first, then look things up with search_notes, outline_note and read_note, following a note's links before searching again. Cite a note by its title and section. Other knowledge bases lent to you (the agent's memory among them) are named by brief_base: pass base to the same tools to look or write there, and follow the practice each one's brief gives."
|
|
9259
9673
|
};
|
|
9260
9674
|
if (rpc.method === "ping") return {};
|
|
9261
9675
|
if (rpc.method === "tools/list") return { tools: (await get("/api/agent/tools")).map(({ function: t }) => ({
|
|
@@ -10491,6 +10905,98 @@ async function writeGeometry(folder, now = Date.now()) {
|
|
|
10491
10905
|
return { notes };
|
|
10492
10906
|
}
|
|
10493
10907
|
//#endregion
|
|
10908
|
+
//#region packages/server/src/memory.ts
|
|
10909
|
+
init_src$1();
|
|
10910
|
+
const MEMORY_FOLDER = join(homedir(), "isorite-memory");
|
|
10911
|
+
/** The memory's practice, as the agent is to follow it: read with brief_base, whichever base a session is for. */
|
|
10912
|
+
const MEMORY_AGENTS = `# What you keep across projects: the person, their tools, and what you have learned working with them.
|
|
10913
|
+
|
|
10914
|
+
This knowledge base is your memory. It is lent to every session, whatever project the session is for. Look in it,
|
|
10915
|
+
and keep it, with the knowledge base tools, giving \`base: "memory"\`.
|
|
10916
|
+
|
|
10917
|
+
## When to look
|
|
10918
|
+
|
|
10919
|
+
- At the start of a task: search for the person, the project's tools and the kind of work.
|
|
10920
|
+
- Before asking the person something they may have told you before.
|
|
10921
|
+
|
|
10922
|
+
## What to keep
|
|
10923
|
+
|
|
10924
|
+
Keep what will be useful in another project, or in a later session of this one:
|
|
10925
|
+
- **you/**: how the person likes to work, what they have corrected you on or confirmed, and why.
|
|
10926
|
+
- **tools/**: tools, services and machines, and how they are set up and reached.
|
|
10927
|
+
- **practice/**: ways of working that went well or badly, and mistakes not to repeat.
|
|
10928
|
+
- **projects/**: what is going on in each project that its own files don't say: goals, decisions, constraints,
|
|
10929
|
+
with dates as dates.
|
|
10930
|
+
|
|
10931
|
+
Don't keep what a project's own files, notes or history already say, what matters only to this conversation, or
|
|
10932
|
+
anything secret: keys, passwords, tokens.
|
|
10933
|
+
|
|
10934
|
+
## How to keep it
|
|
10935
|
+
|
|
10936
|
+
- One note for each thing worth keeping: its title says it, its first lines give the fact, then **Why:** and
|
|
10937
|
+
**When it applies:**.
|
|
10938
|
+
- Link it to the notes it touches.
|
|
10939
|
+
- Update the note that is there rather than writing a second one, and delete a note that turns out to be wrong.
|
|
10940
|
+
- Write as you learn, not at the end: the person sees each note you write marked as yours, and can undo it.
|
|
10941
|
+
`;
|
|
10942
|
+
/** Make the memory, or say where it is: the folder (as isorite new makes it), a git repository of its own, its
|
|
10943
|
+
* practice, and its row in bases.toml, lent. */
|
|
10944
|
+
function memory(folder = MEMORY_FOLDER, { config = configPath(), out = console.log } = {}) {
|
|
10945
|
+
const at = resolve(folder), known = knownBases(config);
|
|
10946
|
+
const had = known.find((b) => b.path === at) ?? (folder === MEMORY_FOLDER ? known.find((b) => b.name === "memory") : void 0);
|
|
10947
|
+
if (had && existsSync(join(had.path, "AGENTS.md"))) {
|
|
10948
|
+
out(`the memory is ${had.name}, at ${had.path}: ${had.lend ? "lent to every session" : `not lent (isorite bases lend ${had.name})`}`);
|
|
10949
|
+
return 0;
|
|
10950
|
+
}
|
|
10951
|
+
if (!existsSync(join(at, "AGENTS.md"))) {
|
|
10952
|
+
if (existsSync(at) && readdirSync(at).some((f) => f !== ".isorite" && f !== ".git")) throw new StudyError(`${at} has files in it already, and no ${AGENTS_FILE}: name another folder, isorite memory <folder>`);
|
|
10953
|
+
newBase(at);
|
|
10954
|
+
writeFileSync(join(at, AGENTS_FILE), MEMORY_AGENTS);
|
|
10955
|
+
try {
|
|
10956
|
+
if (!existsSync(join(at, ".git"))) execFileSync("git", ["init", "--quiet"], {
|
|
10957
|
+
cwd: at,
|
|
10958
|
+
stdio: "ignore"
|
|
10959
|
+
});
|
|
10960
|
+
} catch {
|
|
10961
|
+
out(" (git is not here: the memory is not a repository yet)");
|
|
10962
|
+
}
|
|
10963
|
+
out(`isorite memory: made at ${at}, a private repository of its own: push it where you like`);
|
|
10964
|
+
}
|
|
10965
|
+
const name = rememberBase(at, {
|
|
10966
|
+
path: config,
|
|
10967
|
+
as: "memory",
|
|
10968
|
+
lend: true
|
|
10969
|
+
});
|
|
10970
|
+
out(` ${name}: lent to every session's agent and Iso, as listed in ${basesPath(config)}`);
|
|
10971
|
+
out(` watch it learn: isorite serve ${at}, or switch to ${name} in the page of a running server`);
|
|
10972
|
+
return 0;
|
|
10973
|
+
}
|
|
10974
|
+
/** isorite bases: the bases this device knows; `lend <name>` and `keep <name>` (lend it no longer). */
|
|
10975
|
+
function bases(args, { config = configPath(), out = console.log } = {}) {
|
|
10976
|
+
const [verb, name] = args;
|
|
10977
|
+
if (verb === "lend" || verb === "keep") {
|
|
10978
|
+
const known = knownBases(config).find((b) => b.name === name);
|
|
10979
|
+
if (!known) {
|
|
10980
|
+
out(`no base ${name ?? ""} is known: isorite bases lists them`);
|
|
10981
|
+
return 2;
|
|
10982
|
+
}
|
|
10983
|
+
rememberBase(known.path, {
|
|
10984
|
+
path: config,
|
|
10985
|
+
lend: verb === "lend"
|
|
10986
|
+
});
|
|
10987
|
+
out(`${known.name}: ${verb === "lend" ? "lent to every session" : "not lent"}`);
|
|
10988
|
+
return 0;
|
|
10989
|
+
}
|
|
10990
|
+
const known = knownBases(config);
|
|
10991
|
+
if (!known.length) {
|
|
10992
|
+
out("no bases yet: each one you open is listed here (isorite serve, term, mcp); isorite memory makes the agent's memory");
|
|
10993
|
+
return 0;
|
|
10994
|
+
}
|
|
10995
|
+
const width = Math.max(...known.map((b) => b.name.length));
|
|
10996
|
+
for (const b of known) out(`${b.name.padEnd(width)} ${b.lend ? "lent " : " "} ${b.path}${existsSync(b.path) ? "" : " (not there)"}`);
|
|
10997
|
+
return 0;
|
|
10998
|
+
}
|
|
10999
|
+
//#endregion
|
|
10494
11000
|
//#region packages/server/src/main.ts
|
|
10495
11001
|
init_src$1();
|
|
10496
11002
|
init_model();
|
|
@@ -10519,6 +11025,10 @@ const USAGE = `isorite: an agentic harness for understanding
|
|
|
10519
11025
|
provider (asked a few words, a few hundred tokens), the terminal and
|
|
10520
11026
|
the notes; --report gives it as one block, secrets hidden, to paste
|
|
10521
11027
|
to someone (or an AI) helping
|
|
11028
|
+
isorite memory [<folder>] the agent's memory: a knowledge base (~/isorite-memory unless named),
|
|
11029
|
+
a git repository of its own, lent to every session's agent and Iso
|
|
11030
|
+
isorite bases [lend|keep <name>] the bases this device knows, each listed as it is opened; lend one to
|
|
11031
|
+
every session, or keep it to itself
|
|
10522
11032
|
isorite help setup how isorite is set up, every setting and failure: written for an AI
|
|
10523
11033
|
helping you set it up (docs/setup.md is for people)
|
|
10524
11034
|
isorite guide [<folder>] [--port <n>] the guide: a knowledge base about isorite, with things to do in it,
|
|
@@ -10557,7 +11067,8 @@ function main(argv) {
|
|
|
10557
11067
|
const allowHosts = rest.flatMap((a, i) => a === "--allow-host" && rest[i + 1] ? [rest[i + 1]] : []);
|
|
10558
11068
|
return serve$1(rest[0], {
|
|
10559
11069
|
...port === void 0 ? {} : { port },
|
|
10560
|
-
allowHosts
|
|
11070
|
+
allowHosts,
|
|
11071
|
+
held: rest.includes("--held")
|
|
10561
11072
|
}).then(() => new Promise(() => void 0), (e) => {
|
|
10562
11073
|
if (e instanceof StudyError) {
|
|
10563
11074
|
console.error(e.message);
|
|
@@ -10613,6 +11124,8 @@ function main(argv) {
|
|
|
10613
11124
|
}
|
|
10614
11125
|
return modelCommand(rest.includes("--from") ? from : void 0);
|
|
10615
11126
|
}
|
|
11127
|
+
if (command === "memory") return memory(rest.find((a) => !a.startsWith("--")));
|
|
11128
|
+
if (command === "bases") return bases(rest);
|
|
10616
11129
|
if (command === "--version" || command === "-v") {
|
|
10617
11130
|
console.log(VERSION);
|
|
10618
11131
|
return 0;
|