isorite 0.6.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 +4 -2
- package/dist/main.js +615 -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.CTbO0owi.css +1 -0
- package/web/_app/immutable/assets/NoteEditor.8wWtZK_A.css +1 -0
- package/web/_app/immutable/chunks/0M1khDs2.js +2 -0
- package/web/_app/immutable/chunks/2b4Gb5sE.js +1 -0
- package/web/_app/immutable/chunks/84nbDH0I.js +1 -0
- package/web/_app/immutable/chunks/8H7HVH4w.js +1 -0
- package/web/_app/immutable/chunks/8wVGBFSs.js +45 -0
- package/web/_app/immutable/chunks/AF4Hwhju.js +1 -0
- package/web/_app/immutable/chunks/B-65IYX-.js +6 -0
- package/web/_app/immutable/chunks/B45xgp2S.js +1 -0
- package/web/_app/immutable/chunks/B78wSQJQ.js +1 -0
- package/web/_app/immutable/chunks/B85D_PlV.js +5 -0
- package/web/_app/immutable/chunks/B91efLFT.js +673 -0
- package/web/_app/immutable/chunks/B9NhS7L_.js +1 -0
- package/web/_app/immutable/chunks/B9YLdnR_.js +1 -0
- package/web/_app/immutable/chunks/BEHa2uNW.js +1 -0
- package/web/_app/immutable/chunks/BEMVcqjs.js +1 -0
- package/web/_app/immutable/chunks/BEjRio4Q.js +1 -0
- package/web/_app/immutable/chunks/BEn0Cwpx.js +1 -0
- package/web/_app/immutable/chunks/BGbXxNFa.js +2 -0
- package/web/_app/immutable/chunks/BGfruO1u.js +1 -0
- package/web/_app/immutable/chunks/BHEdbvp9.js +1 -0
- package/web/_app/immutable/chunks/BKJx4mf9.js +1 -0
- package/web/_app/immutable/chunks/BL3mLym4.js +5 -0
- package/web/_app/immutable/chunks/BLp5-jwC.js +1 -0
- package/web/_app/immutable/chunks/BN6xhG8e.js +1 -0
- package/web/_app/immutable/chunks/BOVgKnng.js +1 -0
- package/web/_app/immutable/chunks/BPTmHmyx.js +1 -0
- package/web/_app/immutable/chunks/BPW39P52.js +1 -0
- package/web/_app/immutable/chunks/BRKS-CCX.js +5 -0
- package/web/_app/immutable/chunks/BRsoo5mP.js +1 -0
- package/web/_app/immutable/chunks/BSzj6Xyz.js +1 -0
- package/web/_app/immutable/chunks/BUZw3wgd.js +1 -0
- package/web/_app/immutable/chunks/BV0c4Hdn.js +1 -0
- package/web/_app/immutable/chunks/BVIeWmwY.js +1 -0
- package/web/_app/immutable/chunks/BWonG38x.js +1 -0
- package/web/_app/immutable/chunks/BXxG0Yva.js +1 -0
- package/web/_app/immutable/chunks/BZmnMkFw.js +1 -0
- package/web/_app/immutable/chunks/BZyAS7I4.js +1 -0
- package/web/_app/immutable/chunks/B_1Jd8HM.js +1 -0
- package/web/_app/immutable/chunks/Bbt0DTmZ.js +1 -0
- package/web/_app/immutable/chunks/Bffqt7tx.js +9 -0
- package/web/_app/immutable/chunks/BflJGmDX.js +1 -0
- package/web/_app/immutable/chunks/BgczuZKA.js +4 -0
- package/web/_app/immutable/chunks/BguXRgvc.js +2 -0
- package/web/_app/immutable/chunks/BjPiibIz.js +1 -0
- package/web/_app/immutable/chunks/BkSkiI9m.js +1 -0
- package/web/_app/immutable/chunks/Bq5hCPe1.js +2 -0
- package/web/_app/immutable/chunks/BrGmYbJT.js +2 -0
- package/web/_app/immutable/chunks/BrXPCJaM.js +1 -0
- package/web/_app/immutable/chunks/BrqJKzQU.js +2 -0
- package/web/_app/immutable/chunks/Brxvm_b1.js +4 -0
- package/web/_app/immutable/chunks/BsLNwr5A.js +2 -0
- package/web/_app/immutable/chunks/Bsdphhtm.js +1 -0
- package/web/_app/immutable/chunks/Bt6PBEDR.js +1 -0
- package/web/_app/immutable/chunks/BtUtjfIh.js +1 -0
- package/web/_app/immutable/chunks/BthdM_4N.js +1 -0
- package/web/_app/immutable/chunks/Bu8mWwtz.js +1 -0
- package/web/_app/immutable/chunks/BunrC_8P.js +4 -0
- package/web/_app/immutable/chunks/BwTct_5T.js +1 -0
- package/web/_app/immutable/chunks/BxTFtVqu.js +13 -0
- package/web/_app/immutable/chunks/BzI3yqBQ.js +3 -0
- package/web/_app/immutable/chunks/C-dfcq9W.js +7 -0
- package/web/_app/immutable/chunks/C2OyD2Y6.js +1 -0
- package/web/_app/immutable/chunks/C2dAgh7P.js +1 -0
- package/web/_app/immutable/chunks/C357ku5U.js +1 -0
- package/web/_app/immutable/chunks/C4jVoHG1.js +1 -0
- package/web/_app/immutable/chunks/C6Btt2Xs.js +1 -0
- package/web/_app/immutable/chunks/C9zpzAZU.js +1 -0
- package/web/_app/immutable/chunks/CCiHXe5d.js +1 -0
- package/web/_app/immutable/chunks/CDAjAYjS.js +1 -0
- package/web/_app/immutable/chunks/CETSRtnV.js +2 -0
- package/web/_app/immutable/chunks/CEufJyoj.js +1 -0
- package/web/_app/immutable/chunks/CGzfi9Nl.js +1 -0
- package/web/_app/immutable/chunks/CHbKgHy3.js +1 -0
- package/web/_app/immutable/chunks/CKSRTWxH.js +2 -0
- package/web/_app/immutable/chunks/CLVUDRRk.js +2 -0
- package/web/_app/immutable/chunks/CLVgA2GD.js +1 -0
- package/web/_app/immutable/chunks/CO2Y5y64.js +2 -0
- package/web/_app/immutable/chunks/CPF5uNy5.js +1 -0
- package/web/_app/immutable/chunks/CQadubS9.js +2 -0
- package/web/_app/immutable/chunks/CQfgDRfP.js +1 -0
- package/web/_app/immutable/chunks/CRIvE37b.js +2 -0
- package/web/_app/immutable/chunks/CRzLMJPS.js +1 -0
- package/web/_app/immutable/chunks/CTIncYxj.js +1 -0
- package/web/_app/immutable/chunks/CTscJvp6.js +2 -0
- package/web/_app/immutable/chunks/CUpZ7dEl.js +1 -0
- package/web/_app/immutable/chunks/CX-xckzu.js +4 -0
- package/web/_app/immutable/chunks/CXXgtw0p.js +1 -0
- package/web/_app/immutable/chunks/CZ8wTb_M.js +8 -0
- package/web/_app/immutable/chunks/C_eisQCp.js +1 -0
- package/web/_app/immutable/chunks/CdY1YAit.js +2 -0
- package/web/_app/immutable/chunks/Ce_hS451.js +1 -0
- package/web/_app/immutable/chunks/CgA1WoRv.js +1 -0
- package/web/_app/immutable/chunks/CggtIYAM.js +1 -0
- package/web/_app/immutable/chunks/ChfNwlCY.js +1 -0
- package/web/_app/immutable/chunks/ChtP43wD.js +1 -0
- package/web/_app/immutable/chunks/CiVjR8gq.js +3 -0
- package/web/_app/immutable/chunks/CjI9Y6RI.js +1 -0
- package/web/_app/immutable/chunks/ClOhbFR7.js +1 -0
- package/web/_app/immutable/chunks/CnP_Si9g.js +1 -0
- package/web/_app/immutable/chunks/CnaYoQib.js +1 -0
- package/web/_app/immutable/chunks/Co3PbHdE.js +1 -0
- package/web/_app/immutable/chunks/CoyQYage.js +1 -0
- package/web/_app/immutable/chunks/Cp5ooBfH.js +1 -0
- package/web/_app/immutable/chunks/Cp_JbuGh.js +2 -0
- package/web/_app/immutable/chunks/Cq5C893B.js +1 -0
- package/web/_app/immutable/chunks/CqHI_q-D.js +1 -0
- package/web/_app/immutable/chunks/CqftRsjg.js +1 -0
- package/web/_app/immutable/chunks/Cshzl8qu.js +1 -0
- package/web/_app/immutable/chunks/Cx3rYX2g.js +1 -0
- package/web/_app/immutable/chunks/CxDmU1I4.js +1 -0
- package/web/_app/immutable/chunks/CygcOYNo.js +1 -0
- package/web/_app/immutable/chunks/CzMBulkp.js +1 -0
- package/web/_app/immutable/chunks/D1ij_kNL.js +1 -0
- package/web/_app/immutable/chunks/D2Q8cUp1.js +1 -0
- package/web/_app/immutable/chunks/D4SKbj9K.js +6 -0
- package/web/_app/immutable/chunks/D4c0_ac3.js +1 -0
- package/web/_app/immutable/chunks/D4fLEcOQ.js +8 -0
- package/web/_app/immutable/chunks/D5EjA2JK.js +1 -0
- package/web/_app/immutable/chunks/D5jiJvOU.js +1 -0
- package/web/_app/immutable/chunks/D6QVYUu9.js +1 -0
- package/web/_app/immutable/chunks/D6RzAl64.js +1 -0
- package/web/_app/immutable/chunks/D8WpQPaX.js +1 -0
- package/web/_app/immutable/chunks/D9f6rSsU.js +1 -0
- package/web/_app/immutable/chunks/D9qYN0V5.js +1 -0
- package/web/_app/immutable/chunks/DCl_8MCW.js +1 -0
- package/web/_app/immutable/chunks/DECNRCf2.js +1 -0
- package/web/_app/immutable/chunks/DHLQUbN7.js +1 -0
- package/web/_app/immutable/chunks/DIGvaVgJ.js +2 -0
- package/web/_app/immutable/chunks/DKB6_1j6.js +1 -0
- package/web/_app/immutable/chunks/DKNOMI4Y.js +1 -0
- package/web/_app/immutable/chunks/DLa7NjNO.js +1 -0
- package/web/_app/immutable/chunks/DMAYXnpq.js +13 -0
- package/web/_app/immutable/chunks/DNzd62Q_.js +1 -0
- package/web/_app/immutable/chunks/DPCEkfD_.js +1 -0
- package/web/_app/immutable/chunks/DTF72YNO.js +1 -0
- package/web/_app/immutable/chunks/DTdlqvEb.js +12 -0
- package/web/_app/immutable/chunks/DTszW0OQ.js +1 -0
- package/web/_app/immutable/chunks/DWkQyNnU.js +1 -0
- package/web/_app/immutable/chunks/DXiLM1-G.js +1 -0
- package/web/_app/immutable/chunks/DZYaqCwi.js +1 -0
- package/web/_app/immutable/chunks/D_19S_Iu.js +1 -0
- package/web/_app/immutable/chunks/D_JZ0swv.js +1 -0
- package/web/_app/immutable/chunks/D_aBVMvK.js +11 -0
- package/web/_app/immutable/chunks/DciMwFJ1.js +1 -0
- package/web/_app/immutable/chunks/De_zI4J0.js +1 -0
- package/web/_app/immutable/chunks/DgAKBe0r.js +1 -0
- package/web/_app/immutable/chunks/DgZW3JtG.js +1 -0
- package/web/_app/immutable/chunks/Di_ac7WN.js +1 -0
- package/web/_app/immutable/chunks/DjVOVvNP.js +1 -0
- package/web/_app/immutable/chunks/Dp_KdFgX.js +1 -0
- package/web/_app/immutable/chunks/Ds8Pkiic.js +1 -0
- package/web/_app/immutable/chunks/DtbnBenW.js +1 -0
- package/web/_app/immutable/chunks/DuQMFDvy.js +1 -0
- package/web/_app/immutable/chunks/DwuoZtxw.js +1 -0
- package/web/_app/immutable/chunks/DxLkKbK2.js +5 -0
- package/web/_app/immutable/chunks/Dz-mlCAC.js +1 -0
- package/web/_app/immutable/chunks/EQVRMnZU.js +2 -0
- package/web/_app/immutable/chunks/GwMd3KmG.js +1 -0
- package/web/_app/immutable/chunks/K_3X9lyC.js +23 -0
- package/web/_app/immutable/chunks/{ZRnI3PpK.js → NmqB5ax3.js} +1 -1
- package/web/_app/immutable/chunks/PPSTk1ow.js +1 -0
- package/web/_app/immutable/chunks/S7ucR4pR.js +1 -0
- package/web/_app/immutable/chunks/TuFg0KqZ.js +4 -0
- package/web/_app/immutable/chunks/VlUiK86J.js +1 -0
- package/web/_app/immutable/chunks/_rHYGvON.js +1 -0
- package/web/_app/immutable/chunks/bcQIrid8.js +1 -0
- package/web/_app/immutable/chunks/dvrIP-Hm.js +1 -0
- package/web/_app/immutable/chunks/gfMMiTNf.js +1 -0
- package/web/_app/immutable/chunks/gpDK5sbt.js +1 -0
- package/web/_app/immutable/chunks/hOAYc4Tf.js +1 -0
- package/web/_app/immutable/chunks/kS471soi.js +1 -0
- package/web/_app/immutable/chunks/mbLqADPr.js +1 -0
- package/web/_app/immutable/chunks/nMarIA7J.js +3 -0
- package/web/_app/immutable/chunks/oBAE_ms0.js +1 -0
- package/web/_app/immutable/chunks/p9eYesGn.js +1 -0
- package/web/_app/immutable/chunks/qgSLujhx.js +1 -0
- package/web/_app/immutable/chunks/spNKdrPy.js +3 -0
- package/web/_app/immutable/chunks/tLWDfEkZ.js +1 -0
- package/web/_app/immutable/chunks/u8YMmbU0.js +1 -0
- package/web/_app/immutable/chunks/vk5Wkr31.js +1 -0
- package/web/_app/immutable/chunks/wCTXGvXm.js +2 -0
- package/web/_app/immutable/chunks/wj7G6pU5.js +5 -0
- package/web/_app/immutable/chunks/xvONpD0Z.js +1 -0
- package/web/_app/immutable/chunks/yxd8jSz3.js +1 -0
- package/web/_app/immutable/entry/app.DO9lj7h3.js +2 -0
- 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.BRTPLKyU.js +1 -0
- 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.DYW7Fxdy.js +2 -0
- package/web/_app/immutable/nodes/5.Dkvkif0E.js +5 -0
- package/web/_app/immutable/nodes/6.BinB0fuq.js +1 -0
- package/web/_app/immutable/nodes/{7.BdTE6U4H.js → 7.BxQaF3lD.js} +1 -1
- package/web/_app/version.json +1 -1
- package/web/index.html +22 -18
- package/web/_app/immutable/assets/0.LEIHnkPh.css +0 -1
- package/web/_app/immutable/assets/NoteEditor.BCjOff8L.css +0 -1
- package/web/_app/immutable/chunks/2DHY0Dzh.js +0 -666
- package/web/_app/immutable/chunks/BQRMn_tl.js +0 -1
- package/web/_app/immutable/chunks/C-352d8F.js +0 -1
- package/web/_app/immutable/chunks/CHxNLteG.js +0 -1
- package/web/_app/immutable/chunks/Cf6h5DpS.js +0 -20
- package/web/_app/immutable/chunks/D4A7-5k0.js +0 -1
- package/web/_app/immutable/chunks/Dijhflxl.js +0 -1
- package/web/_app/immutable/chunks/Ps0IPbdL.js +0 -1
- package/web/_app/immutable/chunks/nhmVyQdD.js +0 -3
- package/web/_app/immutable/chunks/rTqzqD6j.js +0 -1
- package/web/_app/immutable/chunks/y24aTX4O.js +0 -1
- package/web/_app/immutable/entry/app.BQ0r6jSu.js +0 -2
- package/web/_app/immutable/entry/start.CIQiYrSU.js +0 -1
- package/web/_app/immutable/nodes/0.zPrOkn79.js +0 -26
- package/web/_app/immutable/nodes/1.DfBkOAo2.js +0 -1
- package/web/_app/immutable/nodes/2.fp8Y5joG.js +0 -1
- package/web/_app/immutable/nodes/3.Do9cwxjS.js +0 -1
- package/web/_app/immutable/nodes/4.CGd6gzGJ.js +0 -2
- package/web/_app/immutable/nodes/5.Dtac-HK8.js +0 -5
- package/web/_app/immutable/nodes/6._-kZkWYe.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,
|
|
@@ -2665,6 +2665,12 @@ something at one place in the note they have open.`;
|
|
|
2665
2665
|
- Maths is LaTeX between dollar signs: $p(x)$ in a line, and a displayed
|
|
2666
2666
|
equation as a paragraph of its own, with $$ alone on the line before it
|
|
2667
2667
|
and $$ alone on the line after.
|
|
2668
|
+
- Code is a fenced block named with its language (\`\`\`python), so it is
|
|
2669
|
+
coloured by it.
|
|
2670
|
+
- A diagram is a fenced block in the language mermaid (Mermaid's syntax), and
|
|
2671
|
+
is drawn as the diagram. Draw one where a picture of how things stand to
|
|
2672
|
+
each other helps, or when asked; not by habit. Maths in a label goes
|
|
2673
|
+
between $$ and $$.
|
|
2668
2674
|
- Ordinary Markdown otherwise. No HTML.`;
|
|
2669
2675
|
FORMAT_RULES = FORMAT;
|
|
2670
2676
|
LOOKUP = (rounds, meaning) => `# Looking things up
|
|
@@ -2698,6 +2704,43 @@ say plainly what you would need to look up to say more.`;
|
|
|
2698
2704
|
}));
|
|
2699
2705
|
//#endregion
|
|
2700
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
|
+
}
|
|
2701
2744
|
/** A note's id from what a model writes for one: /basics/entropy.md, basics/entropy, [x](/basics/entropy.md). */
|
|
2702
2745
|
function noteId(b, raw) {
|
|
2703
2746
|
const id = String(raw ?? "").trim().replace(/^.*\]\(/, "").replace(/\)$/, "").replace(/\s+"[^"]*"$/, "").split("#")[0].replace(/^\/+/, "").replace(/\.md$/i, "");
|
|
@@ -2773,17 +2816,19 @@ var init_lookup = __esmMin((() => {
|
|
|
2773
2816
|
named = /* @__PURE__ */ new Map();
|
|
2774
2817
|
b;
|
|
2775
2818
|
similar;
|
|
2776
|
-
|
|
2819
|
+
away;
|
|
2820
|
+
constructor(b, note, similar = null, away = null) {
|
|
2777
2821
|
this.b = b;
|
|
2778
2822
|
this.held = [note];
|
|
2779
2823
|
this.similar = similar;
|
|
2824
|
+
this.away = away;
|
|
2780
2825
|
}
|
|
2781
2826
|
defs() {
|
|
2782
|
-
return this.similar ? [
|
|
2827
|
+
return withBase(this.similar ? [
|
|
2783
2828
|
TOOLS[0],
|
|
2784
2829
|
FIND_SIMILAR,
|
|
2785
2830
|
...TOOLS.slice(1)
|
|
2786
|
-
] : TOOLS;
|
|
2831
|
+
] : TOOLS, this.away?.lent().map((l) => l.name) ?? []);
|
|
2787
2832
|
}
|
|
2788
2833
|
/** The notes opened, each once, in order. */
|
|
2789
2834
|
read() {
|
|
@@ -2803,6 +2848,19 @@ var init_lookup = __esmMin((() => {
|
|
|
2803
2848
|
notes: []
|
|
2804
2849
|
};
|
|
2805
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
|
+
}
|
|
2806
2864
|
try {
|
|
2807
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}.`);
|
|
2808
2866
|
} catch (err) {
|
|
@@ -2814,6 +2872,22 @@ var init_lookup = __esmMin((() => {
|
|
|
2814
2872
|
step
|
|
2815
2873
|
};
|
|
2816
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
|
+
}
|
|
2817
2891
|
fail(step, why) {
|
|
2818
2892
|
step.failed = true;
|
|
2819
2893
|
step.said = why;
|
|
@@ -3141,6 +3215,7 @@ function turnMessages(a) {
|
|
|
3141
3215
|
const held = reads.length ? `# Notes you read earlier in this conversation\n\n${reads.map(sentText).join("\n\n")}` : "";
|
|
3142
3216
|
const aside = a.aside?.length ? `# Read earlier, set aside\n\n${a.aside.map((n) => `- ${n.title} (/${n.id}.md)`).join("\n")}` : "";
|
|
3143
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")}` : "";
|
|
3144
3219
|
const known = understood.length ? `# Their understanding, as you have recorded it\n\n${understood.map(sentText).join("\n\n")}` : "";
|
|
3145
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")}` : "";
|
|
3146
3221
|
const request = `# What they ask now\n\n${a.asked}`;
|
|
@@ -3155,6 +3230,7 @@ function turnMessages(a) {
|
|
|
3155
3230
|
role: "user",
|
|
3156
3231
|
content: [
|
|
3157
3232
|
agents,
|
|
3233
|
+
lent,
|
|
3158
3234
|
earlier,
|
|
3159
3235
|
known,
|
|
3160
3236
|
gave,
|
|
@@ -3168,7 +3244,7 @@ function turnMessages(a) {
|
|
|
3168
3244
|
{
|
|
3169
3245
|
name: "instructions and tools",
|
|
3170
3246
|
detail: `${a.mode} mode${checking ? ", checking" : ""}${a.lookup ? "" : ", no look-ups"}`,
|
|
3171
|
-
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
|
|
3172
3248
|
},
|
|
3173
3249
|
{
|
|
3174
3250
|
name: "earlier in this conversation",
|
|
@@ -3743,7 +3819,7 @@ function keepDone(before, next) {
|
|
|
3743
3819
|
return next;
|
|
3744
3820
|
}
|
|
3745
3821
|
}
|
|
3746
|
-
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;
|
|
3747
3823
|
var init_engine = __esmMin((() => {
|
|
3748
3824
|
init_src$1();
|
|
3749
3825
|
init_api();
|
|
@@ -3756,6 +3832,7 @@ var init_engine = __esmMin((() => {
|
|
|
3756
3832
|
init_meaning();
|
|
3757
3833
|
init_provider();
|
|
3758
3834
|
SIGN_IN_MS = 6e5;
|
|
3835
|
+
fromBase = (base, text) => `(From the base ${base}: name its notes ${base}:<id>, or give base "${base}".)\n${text}`;
|
|
3759
3836
|
REPLY_TOKENS = 2500;
|
|
3760
3837
|
NOTE_TOKENS = 6e3;
|
|
3761
3838
|
DRAFT_PREFIX = "@draft/";
|
|
@@ -4017,7 +4094,7 @@ var init_engine = __esmMin((() => {
|
|
|
4017
4094
|
}));
|
|
4018
4095
|
}
|
|
4019
4096
|
async agentTools() {
|
|
4020
|
-
|
|
4097
|
+
const tools = [
|
|
4021
4098
|
BRIEF,
|
|
4022
4099
|
TOOLS[0],
|
|
4023
4100
|
...this.similar ? [FIND_SIMILAR] : [],
|
|
@@ -4025,9 +4102,48 @@ var init_engine = __esmMin((() => {
|
|
|
4025
4102
|
EDIT_NOTE,
|
|
4026
4103
|
WRITE_NOTE
|
|
4027
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`;
|
|
4028
4129
|
}
|
|
4029
4130
|
async agentLook({ agent, session, tool, args, asked = "" }) {
|
|
4030
|
-
|
|
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
|
+
}
|
|
4031
4147
|
if (tool === EDIT_NOTE.function.name || tool === WRITE_NOTE.function.name) {
|
|
4032
4148
|
const str = (v, name) => {
|
|
4033
4149
|
if (typeof v !== "string") throw new EngineError(`${tool} needs ${name}, as text`, 400);
|
|
@@ -4069,6 +4185,7 @@ var init_engine = __esmMin((() => {
|
|
|
4069
4185
|
session
|
|
4070
4186
|
});
|
|
4071
4187
|
}
|
|
4188
|
+
if (!busy) for (const l of this.lent()) await this.opts.elsewhere?.base(l.name)?.agentStatus(agent, session, false);
|
|
4072
4189
|
}
|
|
4073
4190
|
async agentWrite({ agent, session, note, asked, edit }) {
|
|
4074
4191
|
const who = `agent:${agent}`;
|
|
@@ -4594,7 +4711,7 @@ var init_engine = __esmMin((() => {
|
|
|
4594
4711
|
tier: req.tier,
|
|
4595
4712
|
place
|
|
4596
4713
|
});
|
|
4597
|
-
const lookup = new Lookup(this.bundle, req.note, this.similar);
|
|
4714
|
+
const lookup = new Lookup(this.bundle, req.note, this.similar, this.away);
|
|
4598
4715
|
(async () => {
|
|
4599
4716
|
let ran = null, offered = false;
|
|
4600
4717
|
try {
|
|
@@ -5197,6 +5314,7 @@ var init_engine = __esmMin((() => {
|
|
|
5197
5314
|
sent: [...sent, ...reads],
|
|
5198
5315
|
aside,
|
|
5199
5316
|
...agentsText ? { agents: agentsText } : {},
|
|
5317
|
+
...this.lent().length ? { lent: this.lent() } : {},
|
|
5200
5318
|
asked: req.asked ?? ""
|
|
5201
5319
|
});
|
|
5202
5320
|
const ratio = hand.ratio ?? 1, scaled = (n) => Math.round(n * ratio);
|
|
@@ -5545,6 +5663,7 @@ var init_engine = __esmMin((() => {
|
|
|
5545
5663
|
sent,
|
|
5546
5664
|
aside: w.aside,
|
|
5547
5665
|
...w.agentsText ? { agents: w.agentsText } : {},
|
|
5666
|
+
...this.lent().length ? { lent: this.lent() } : {},
|
|
5548
5667
|
asked: req.asked,
|
|
5549
5668
|
broken: await this.brokenNews("iso")
|
|
5550
5669
|
});
|
|
@@ -5594,7 +5713,7 @@ var init_engine = __esmMin((() => {
|
|
|
5594
5713
|
});
|
|
5595
5714
|
};
|
|
5596
5715
|
const who = `iso:${this.provider.name}/${this.provider.tiers[req.tier]}`;
|
|
5597
|
-
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);
|
|
5598
5717
|
const looks = new Set(lookup.defs().map((d) => d.function.name));
|
|
5599
5718
|
const tools = {
|
|
5600
5719
|
defs: () => [...lookupOn ? lookup.defs() : [], ...own],
|
|
@@ -5605,7 +5724,7 @@ var init_engine = __esmMin((() => {
|
|
|
5605
5724
|
try {
|
|
5606
5725
|
args = JSON.parse(raw || "{}");
|
|
5607
5726
|
} catch {}
|
|
5608
|
-
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() : "";
|
|
5609
5728
|
const held = id ? inHand.get(id) : void 0;
|
|
5610
5729
|
if (id && held && (held === "whole" || want && held.includes(want))) {
|
|
5611
5730
|
const step = {
|
|
@@ -5629,6 +5748,8 @@ var init_engine = __esmMin((() => {
|
|
|
5629
5748
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) args = parsed;
|
|
5630
5749
|
} catch {}
|
|
5631
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.` };
|
|
5632
5753
|
return { text: await this.turnTool(name, args, {
|
|
5633
5754
|
turn,
|
|
5634
5755
|
conversation,
|
|
@@ -6218,10 +6339,10 @@ var init_engine = __esmMin((() => {
|
|
|
6218
6339
|
let text;
|
|
6219
6340
|
if (p.form === "rewrite") {
|
|
6220
6341
|
if (region !== void 0 && !p.open?.includes(region)) throw new EngineError("that change is no longer waiting: it was kept or removed", 409);
|
|
6221
|
-
text = written(now, p, region);
|
|
6342
|
+
text = written$1(now, p, region);
|
|
6222
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);
|
|
6223
6344
|
} else {
|
|
6224
|
-
text = written(now, p) ?? written(now, {
|
|
6345
|
+
text = written$1(now, p) ?? written$1(now, {
|
|
6225
6346
|
...p,
|
|
6226
6347
|
after: null,
|
|
6227
6348
|
before: null
|
|
@@ -6774,6 +6895,62 @@ function gatewayTlsFrom(config, dir, env = process.env) {
|
|
|
6774
6895
|
async function hasModel$1(config = configPath()) {
|
|
6775
6896
|
return await new ConfigSecrets(config).get("provider.key") !== null || providerConfig(config).url !== OPENROUTER.url;
|
|
6776
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
|
+
}
|
|
6777
6954
|
//#endregion
|
|
6778
6955
|
//#region packages/server/src/model.ts
|
|
6779
6956
|
/** Where the server looks for the model, in order, and why each place is there. */
|
|
@@ -6815,7 +6992,7 @@ function modelInfo(dir) {
|
|
|
6815
6992
|
} catch {}
|
|
6816
6993
|
const graph = sha(readFileSync(join(dir, "model.onnx"))), granite = graph === FILES[0][2];
|
|
6817
6994
|
const pooling = own.pooling === "mean" ? "mean" : "cls";
|
|
6818
|
-
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);
|
|
6819
6996
|
return {
|
|
6820
6997
|
dir,
|
|
6821
6998
|
name,
|
|
@@ -6997,11 +7174,11 @@ async function modelCommand(from, say = console.log, env = process.env, sources
|
|
|
6997
7174
|
return 1;
|
|
6998
7175
|
}
|
|
6999
7176
|
}
|
|
7000
|
-
var NAME$
|
|
7177
|
+
var NAME$2, MODEL, RELEASE, PACKAGE, HUB, FILES, sha, userModelsDir, userModelDir, hasModel, ModelFetchError, modelFiles;
|
|
7001
7178
|
var init_model = __esmMin((() => {
|
|
7002
7179
|
init_src();
|
|
7003
|
-
NAME$
|
|
7004
|
-
MODEL = `${NAME$
|
|
7180
|
+
NAME$2 = "granite-embedding-small-english-r2";
|
|
7181
|
+
MODEL = `${NAME$2}-q8`;
|
|
7005
7182
|
RELEASE = "https://github.com/lachlanjs/isorite/releases/download/model-granite-1.0.0/isorite-model-granite-1.0.0.tgz";
|
|
7006
7183
|
PACKAGE = "isorite-model-granite@1.0.0";
|
|
7007
7184
|
HUB = `https://huggingface.co/onnx-community/granite-embedding-small-english-r2-ONNX/resolve/1dc7835ba0cb9c76a3618d0bf0c427c97671b3c8`;
|
|
@@ -7029,7 +7206,7 @@ var init_model = __esmMin((() => {
|
|
|
7029
7206
|
];
|
|
7030
7207
|
sha = (buf) => createHash("sha256").update(buf).digest("hex");
|
|
7031
7208
|
userModelsDir = (env = process.env) => join(env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "isorite", "models");
|
|
7032
|
-
userModelDir = (env = process.env) => join(userModelsDir(env), NAME$
|
|
7209
|
+
userModelDir = (env = process.env) => join(userModelsDir(env), NAME$2);
|
|
7033
7210
|
hasModel = (dir) => existsSync(join(dir, "model.onnx")) && existsSync(join(dir, "tokenizer.json"));
|
|
7034
7211
|
ModelFetchError = class extends Error {
|
|
7035
7212
|
tried;
|
|
@@ -7217,7 +7394,132 @@ function relay(live, key) {
|
|
|
7217
7394
|
};
|
|
7218
7395
|
}
|
|
7219
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
|
|
7220
7521
|
//#region packages/server/src/terminal.ts
|
|
7522
|
+
init_src();
|
|
7221
7523
|
const OUTPUT = 4e3;
|
|
7222
7524
|
const LOOKUPS = /* @__PURE__ */ new Set([
|
|
7223
7525
|
"search_notes",
|
|
@@ -7228,6 +7530,7 @@ const LOOKUPS = /* @__PURE__ */ new Set([
|
|
|
7228
7530
|
]);
|
|
7229
7531
|
/** isorite's tools that change a note: their line opens it, where the agent's words are marked. */
|
|
7230
7532
|
const CHANGES = /* @__PURE__ */ new Set(["edit_note", "write_note"]);
|
|
7533
|
+
const isorite = (name) => LOOKUPS.has(name) || CHANGES.has(name);
|
|
7231
7534
|
/** What a tool call acted on, in a line. */
|
|
7232
7535
|
function titleOf(name, input = {}) {
|
|
7233
7536
|
const s = (k) => typeof input[k] === "string" ? input[k] : "";
|
|
@@ -7275,16 +7578,21 @@ function toTurns(messages, { live = /* @__PURE__ */ new Map(), ours = /* @__PURE
|
|
|
7275
7578
|
const input = c.state.input ?? {};
|
|
7276
7579
|
const output = (c.state.content ?? []).map((x) => x.text ?? "").join("").slice(0, OUTPUT);
|
|
7277
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
|
+
};
|
|
7278
7585
|
const item = {
|
|
7279
7586
|
kind: "tool",
|
|
7280
7587
|
id: c.id,
|
|
7281
7588
|
name: c.name,
|
|
7282
|
-
title: titleOf(c.name,
|
|
7589
|
+
title: (base ? `${base}: ` : "") + titleOf(c.name, args),
|
|
7283
7590
|
status,
|
|
7284
7591
|
output
|
|
7285
7592
|
};
|
|
7286
|
-
const named = LOOKUPS.has(c.name) ?
|
|
7593
|
+
const named = LOOKUPS.has(c.name) ? args.id : CHANGES.has(c.name) ? args.note : void 0;
|
|
7287
7594
|
if (typeof named === "string" && named) item.note = named.replace(/^\//, "").replace(/\.md$/, "");
|
|
7595
|
+
if (base) item.base = base;
|
|
7288
7596
|
const diff = diffOf(c.name, input);
|
|
7289
7597
|
if (diff) item.diff = diff;
|
|
7290
7598
|
turn.items.push(item);
|
|
@@ -7716,7 +8024,7 @@ const AgentStatus = z.object({
|
|
|
7716
8024
|
session: z.string().min(1).max(200),
|
|
7717
8025
|
busy: z.boolean()
|
|
7718
8026
|
}).strict();
|
|
7719
|
-
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 }) {
|
|
7720
8028
|
const app = new Hono();
|
|
7721
8029
|
const allowed = new Set([...allowHosts].map((h) => h.toLowerCase()));
|
|
7722
8030
|
const reply = (c, status, value) => c.body(JSON.stringify(value), status, {
|
|
@@ -7919,9 +8227,52 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal
|
|
|
7919
8227
|
post("/api/settings/provider/models", z.object({ draft: SetupDraft.nullable() }).strict(), async (c, b) => ({ models: await setup.models(local(c) ? b.draft : null) }));
|
|
7920
8228
|
post("/api/settings/report", z.object({}).strict(), async () => ({ text: await setup.report() }));
|
|
7921
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
|
+
}
|
|
7922
8269
|
app.get("/api/agent/tools", (c) => answer(c, () => api.agentTools()));
|
|
7923
8270
|
post("/api/agent/look", AgentLook, (_c, b) => api.agentLook(b));
|
|
7924
|
-
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
|
+
})));
|
|
7925
8276
|
post("/api/agent/write", AgentChange, (_c, b) => api.agentWrite(b));
|
|
7926
8277
|
post("/api/agent/send", z.object({
|
|
7927
8278
|
note: z.string().max(400),
|
|
@@ -8362,7 +8713,7 @@ var Setup = class {
|
|
|
8362
8713
|
};
|
|
8363
8714
|
//#endregion
|
|
8364
8715
|
//#region packages/server/src/version.ts
|
|
8365
|
-
const VERSION = "0.
|
|
8716
|
+
const VERSION = "0.8.0";
|
|
8366
8717
|
//#endregion
|
|
8367
8718
|
//#region packages/server/src/studybase.ts
|
|
8368
8719
|
init_src$1();
|
|
@@ -8433,15 +8784,16 @@ function newBase(path) {
|
|
|
8433
8784
|
makeOwn(at);
|
|
8434
8785
|
return at;
|
|
8435
8786
|
}
|
|
8436
|
-
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 = {}) {
|
|
8437
8788
|
const provider = providerConfig(config);
|
|
8438
8789
|
const own = makeOwn(notes);
|
|
8790
|
+
const embedder = extras.embedder !== void 0 ? extras.embedder : model ? nodeEmbedder(model) : null;
|
|
8439
8791
|
return Engine.open({
|
|
8440
8792
|
notes: new NodeFiles(notes),
|
|
8441
8793
|
own: new NodeFiles(own),
|
|
8442
8794
|
clock: systemClock,
|
|
8443
8795
|
device,
|
|
8444
|
-
name: basename(notes),
|
|
8796
|
+
name: extras.name ?? basename(notes),
|
|
8445
8797
|
http: http ?? gatewayHttp(provider, gatewayTls(config)),
|
|
8446
8798
|
secrets,
|
|
8447
8799
|
provider,
|
|
@@ -8449,13 +8801,14 @@ async function openEngine({ notes }, device = deviceId(), config = configPath(),
|
|
|
8449
8801
|
const dir = resolve(notes, path);
|
|
8450
8802
|
return existsSync(dir) && statSync(dir).isDirectory() ? new NodeFiles(dir) : null;
|
|
8451
8803
|
},
|
|
8452
|
-
...
|
|
8453
|
-
embedder
|
|
8804
|
+
...embedder ? {
|
|
8805
|
+
embedder,
|
|
8454
8806
|
cache: {
|
|
8455
8807
|
files: new NodeFiles(cacheDir()),
|
|
8456
8808
|
name: `vectors-${createHash("sha256").update(notes).digest("hex").slice(0, 16)}.json`
|
|
8457
8809
|
}
|
|
8458
|
-
} : {}
|
|
8810
|
+
} : {},
|
|
8811
|
+
...extras.elsewhere ? { elsewhere: extras.elsewhere } : {}
|
|
8459
8812
|
});
|
|
8460
8813
|
}
|
|
8461
8814
|
/** The guide's finder, as a provider: no key, no sign-in, and a name that says what it is. */
|
|
@@ -8480,16 +8833,49 @@ async function liveProvider(config, stand) {
|
|
|
8480
8833
|
}
|
|
8481
8834
|
/** A link most terminals let you click (OSC 8), when the output is a terminal; the address as it is otherwise. */
|
|
8482
8835
|
const link$1 = (url, tty = Boolean(process.stdout.isTTY)) => tty ? `\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\` : url;
|
|
8483
|
-
/**
|
|
8484
|
-
*
|
|
8485
|
-
|
|
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() } = {}) {
|
|
8486
8841
|
const opened = locate(path);
|
|
8487
|
-
const config = configPath();
|
|
8488
8842
|
const current = () => liveProvider(config, stand);
|
|
8489
8843
|
let live = await current();
|
|
8490
|
-
const secrets = new ConfigSecrets(config);
|
|
8491
|
-
const
|
|
8492
|
-
|
|
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
|
+
});
|
|
8493
8879
|
const setup = new Setup({
|
|
8494
8880
|
config,
|
|
8495
8881
|
notes: opened.notes,
|
|
@@ -8497,25 +8883,29 @@ async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [],
|
|
|
8497
8883
|
port,
|
|
8498
8884
|
kept: async () => {
|
|
8499
8885
|
live = await current();
|
|
8500
|
-
api.setProvider(live.provider, live.http);
|
|
8886
|
+
bases.each((api) => api.setProvider(live.provider, live.http));
|
|
8501
8887
|
}
|
|
8502
8888
|
});
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
const terminal = new Terminal();
|
|
8506
|
-
const app = createApp({
|
|
8507
|
-
api,
|
|
8508
|
-
token: randomBytes(24).toString("base64url"),
|
|
8509
|
-
loopback: LOOPBACK.has(host),
|
|
8510
|
-
web,
|
|
8511
|
-
allowHosts,
|
|
8889
|
+
await bases.start();
|
|
8890
|
+
return {
|
|
8512
8891
|
notes: opened.notes,
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
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() } : {}
|
|
8516
8906
|
});
|
|
8517
8907
|
const server = serve({
|
|
8518
|
-
fetch
|
|
8908
|
+
fetch,
|
|
8519
8909
|
hostname: host,
|
|
8520
8910
|
port
|
|
8521
8911
|
});
|
|
@@ -8523,11 +8913,13 @@ async function serve$1(path, { host = "127.0.0.1", port = 4767, allowHosts = [],
|
|
|
8523
8913
|
console.error(e.code === "EADDRINUSE" ? `port ${port} is in use (isorite serve --port <n>)` : e.message);
|
|
8524
8914
|
process.exit(1);
|
|
8525
8915
|
});
|
|
8526
|
-
console.log(`isorite: ${
|
|
8527
|
-
console.log(
|
|
8528
|
-
|
|
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/`);
|
|
8529
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()}`);
|
|
8530
|
-
|
|
8922
|
+
stop = () => {
|
|
8531
8923
|
server.close();
|
|
8532
8924
|
process.exit(0);
|
|
8533
8925
|
};
|
|
@@ -8563,49 +8955,77 @@ async function serving(url) {
|
|
|
8563
8955
|
return null;
|
|
8564
8956
|
}
|
|
8565
8957
|
}
|
|
8566
|
-
/** The isorite server on the port, and how to let it go: one already running for this base
|
|
8567
|
-
* 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. */
|
|
8568
8962
|
async function ensureServer(path, { port = 4767, env = process.env, wait = 12e4 } = {}) {
|
|
8569
|
-
const
|
|
8963
|
+
const root = `http://127.0.0.1:${port}`;
|
|
8570
8964
|
const opened = path === void 0 ? null : locate(path);
|
|
8571
|
-
|
|
8572
|
-
if (name
|
|
8573
|
-
if (opened
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
server.kill();
|
|
8601
|
-
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))]);
|
|
8602
8994
|
}
|
|
8603
|
-
|
|
8995
|
+
started.unref();
|
|
8604
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);
|
|
8605
9022
|
return {
|
|
8606
9023
|
url,
|
|
8607
9024
|
release: () => {
|
|
8608
|
-
|
|
9025
|
+
holding.abort();
|
|
9026
|
+
held.then((ok) => {
|
|
9027
|
+
if (!ok && started) started.kill();
|
|
9028
|
+
});
|
|
8609
9029
|
}
|
|
8610
9030
|
};
|
|
8611
9031
|
}
|
|
@@ -8626,7 +9046,7 @@ async function tell(url, path, body) {
|
|
|
8626
9046
|
headers: {
|
|
8627
9047
|
"Content-Type": "application/json",
|
|
8628
9048
|
"x-isorite-token": token,
|
|
8629
|
-
Origin: url
|
|
9049
|
+
Origin: new URL(url).origin
|
|
8630
9050
|
},
|
|
8631
9051
|
body: JSON.stringify(body)
|
|
8632
9052
|
});
|
|
@@ -8717,9 +9137,9 @@ async function modelsFor(env, server) {
|
|
|
8717
9137
|
}
|
|
8718
9138
|
/** What `isorite term` says before OpenCode takes the screen, and again when it gives it back: where isorite is in
|
|
8719
9139
|
* the browser, which models the terminal uses and where they come from, and, over SSH, how to reach the browser. */
|
|
8720
|
-
function where(port, models, env, tty) {
|
|
9140
|
+
function where(port, models, env, tty, page = "/") {
|
|
8721
9141
|
const names = models.gateway?.models.join(", ");
|
|
8722
|
-
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"];
|
|
8723
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()}`);
|
|
8724
9144
|
return lines;
|
|
8725
9145
|
}
|
|
@@ -8746,7 +9166,7 @@ async function term(path, { port = 4767, cwd = process.cwd(), env: given = proce
|
|
|
8746
9166
|
throw e;
|
|
8747
9167
|
}
|
|
8748
9168
|
const { gateway, env } = models;
|
|
8749
|
-
const said = where(port, models, given);
|
|
9169
|
+
const said = where(port, models, given, void 0, new URL(server.url).pathname.replace(/\/?$/, "/"));
|
|
8750
9170
|
for (const line of said) console.log(line);
|
|
8751
9171
|
const agents = path === void 0 ? null : join(locate(path).notes, AGENTS_FILE);
|
|
8752
9172
|
const home = writeHome(defaultHome(env), {
|
|
@@ -9219,7 +9639,7 @@ async function mcp(path, { port = 4767, agent = "mcp", quiet = QUIET_MS, input =
|
|
|
9219
9639
|
headers: {
|
|
9220
9640
|
"Content-Type": "application/json",
|
|
9221
9641
|
[TOKEN_HEADER]: await token,
|
|
9222
|
-
Origin: url
|
|
9642
|
+
Origin: new URL(url).origin
|
|
9223
9643
|
},
|
|
9224
9644
|
body: JSON.stringify(body)
|
|
9225
9645
|
});
|
|
@@ -9249,7 +9669,7 @@ async function mcp(path, { port = 4767, agent = "mcp", quiet = QUIET_MS, input =
|
|
|
9249
9669
|
name: "isorite",
|
|
9250
9670
|
version: "0.1.0"
|
|
9251
9671
|
},
|
|
9252
|
-
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."
|
|
9253
9673
|
};
|
|
9254
9674
|
if (rpc.method === "ping") return {};
|
|
9255
9675
|
if (rpc.method === "tools/list") return { tools: (await get("/api/agent/tools")).map(({ function: t }) => ({
|
|
@@ -10485,6 +10905,98 @@ async function writeGeometry(folder, now = Date.now()) {
|
|
|
10485
10905
|
return { notes };
|
|
10486
10906
|
}
|
|
10487
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
|
|
10488
11000
|
//#region packages/server/src/main.ts
|
|
10489
11001
|
init_src$1();
|
|
10490
11002
|
init_model();
|
|
@@ -10513,6 +11025,10 @@ const USAGE = `isorite: an agentic harness for understanding
|
|
|
10513
11025
|
provider (asked a few words, a few hundred tokens), the terminal and
|
|
10514
11026
|
the notes; --report gives it as one block, secrets hidden, to paste
|
|
10515
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
|
|
10516
11032
|
isorite help setup how isorite is set up, every setting and failure: written for an AI
|
|
10517
11033
|
helping you set it up (docs/setup.md is for people)
|
|
10518
11034
|
isorite guide [<folder>] [--port <n>] the guide: a knowledge base about isorite, with things to do in it,
|
|
@@ -10551,7 +11067,8 @@ function main(argv) {
|
|
|
10551
11067
|
const allowHosts = rest.flatMap((a, i) => a === "--allow-host" && rest[i + 1] ? [rest[i + 1]] : []);
|
|
10552
11068
|
return serve$1(rest[0], {
|
|
10553
11069
|
...port === void 0 ? {} : { port },
|
|
10554
|
-
allowHosts
|
|
11070
|
+
allowHosts,
|
|
11071
|
+
held: rest.includes("--held")
|
|
10555
11072
|
}).then(() => new Promise(() => void 0), (e) => {
|
|
10556
11073
|
if (e instanceof StudyError) {
|
|
10557
11074
|
console.error(e.message);
|
|
@@ -10607,6 +11124,8 @@ function main(argv) {
|
|
|
10607
11124
|
}
|
|
10608
11125
|
return modelCommand(rest.includes("--from") ? from : void 0);
|
|
10609
11126
|
}
|
|
11127
|
+
if (command === "memory") return memory(rest.find((a) => !a.startsWith("--")));
|
|
11128
|
+
if (command === "bases") return bases(rest);
|
|
10610
11129
|
if (command === "--version" || command === "-v") {
|
|
10611
11130
|
console.log(VERSION);
|
|
10612
11131
|
return 0;
|