isorite 0.3.1 → 0.6.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 +3 -3
- package/dist/core.js +4 -2
- package/dist/main.js +4290 -3960
- package/docs/setup-for-agents.md +7 -2
- package/docs/setup.md +23 -5
- package/docs/terminal.md +9 -1
- package/package.json +1 -1
- package/plugin/tui.tsx +3 -77
- package/web/_app/immutable/assets/{0.IOi79DXH.css → 0.LEIHnkPh.css} +1 -1
- package/web/_app/immutable/chunks/{8IM6BNk6.js → 2DHY0Dzh.js} +21 -21
- package/web/_app/immutable/chunks/BQRMn_tl.js +1 -0
- package/web/_app/immutable/chunks/C-352d8F.js +1 -0
- package/web/_app/immutable/chunks/{B_AYGp9o.js → CHxNLteG.js} +1 -1
- package/web/_app/immutable/chunks/Cf6h5DpS.js +20 -0
- package/web/_app/immutable/chunks/{CMp5T_-1.js → D4A7-5k0.js} +1 -1
- package/web/_app/immutable/chunks/Dijhflxl.js +1 -0
- package/web/_app/immutable/chunks/Ps0IPbdL.js +1 -0
- package/web/_app/immutable/chunks/{BHPhFydX.js → ZRnI3PpK.js} +1 -1
- package/web/_app/immutable/chunks/nhmVyQdD.js +3 -0
- package/web/_app/immutable/chunks/rTqzqD6j.js +1 -0
- package/web/_app/immutable/chunks/y24aTX4O.js +1 -0
- package/web/_app/immutable/entry/app.BQ0r6jSu.js +2 -0
- package/web/_app/immutable/entry/start.CIQiYrSU.js +1 -0
- package/web/_app/immutable/nodes/0.zPrOkn79.js +26 -0
- package/web/_app/immutable/nodes/1.DfBkOAo2.js +1 -0
- package/web/_app/immutable/nodes/2.fp8Y5joG.js +1 -0
- package/web/_app/immutable/nodes/3.Do9cwxjS.js +1 -0
- package/web/_app/immutable/nodes/4.CGd6gzGJ.js +2 -0
- package/web/_app/immutable/nodes/5.Dtac-HK8.js +5 -0
- package/web/_app/immutable/nodes/6._-kZkWYe.js +1 -0
- package/web/_app/immutable/nodes/7.BdTE6U4H.js +1 -0
- package/web/_app/version.json +1 -1
- package/web/index.html +17 -20
- package/dist/tutorial/barnes_hut.py +0 -46
- package/dist/tutorial/sim.py +0 -29
- package/plugin/tour.ts +0 -74
- package/web/_app/immutable/assets/8.CFA9_nYZ.css +0 -1
- package/web/_app/immutable/assets/Tutorial.DOfsM49T.css +0 -1
- package/web/_app/immutable/chunks/Bze-M_kr.js +0 -3
- package/web/_app/immutable/chunks/CFA70_0c.js +0 -20
- package/web/_app/immutable/chunks/CGgq5NWd.js +0 -1
- package/web/_app/immutable/chunks/CHlFgDNB.js +0 -1
- package/web/_app/immutable/chunks/CRzLMJPS.js +0 -1
- package/web/_app/immutable/chunks/D89YN-Dr.js +0 -1
- package/web/_app/immutable/chunks/DF3lgcLW.js +0 -1
- package/web/_app/immutable/chunks/DGVcYHaJ.js +0 -43
- package/web/_app/immutable/chunks/DHMLhlug.js +0 -1
- package/web/_app/immutable/chunks/DmgpLvxp.js +0 -1
- package/web/_app/immutable/chunks/DrsVvzvg.js +0 -1
- package/web/_app/immutable/entry/app.hlk6OrE8.js +0 -2
- package/web/_app/immutable/entry/start.BE1F0HZV.js +0 -1
- package/web/_app/immutable/nodes/0.DMGdYhkH.js +0 -15
- package/web/_app/immutable/nodes/1.Dn_0fgt7.js +0 -1
- package/web/_app/immutable/nodes/2.Y1Dd5PBI.js +0 -1
- package/web/_app/immutable/nodes/3.BlxuRbLf.js +0 -1
- package/web/_app/immutable/nodes/4.Bt_H0MbN.js +0 -2
- package/web/_app/immutable/nodes/5.BJ-EVWbz.js +0 -5
- package/web/_app/immutable/nodes/6.DDIiJnwi.js +0 -1
- package/web/_app/immutable/nodes/7.DmPPLDvb.js +0 -1
- package/web/_app/immutable/nodes/8.CwMTBog4.js +0 -486
package/docs/setup-for-agents.md
CHANGED
|
@@ -19,7 +19,7 @@ turn off certificate checking (`NODE_TLS_REJECT_UNAUTHORIZED=0`).
|
|
|
19
19
|
| `isorite serve <notes> [--port <n>]` | The browser app at `http://localhost:4767/`, on 127.0.0.1 only. |
|
|
20
20
|
| `isorite term [<notes>] [--port <n>]` | OpenCode 2 in this terminal, in the current folder (the code), with the notes beside it. Starts the isorite server if none answers on the port, and stops it on exit. |
|
|
21
21
|
| `isorite check <notes>` | Lints the notes: frontmatter, links, cycles. |
|
|
22
|
-
| `isorite
|
|
22
|
+
| `isorite guide [<folder>]` | The guide, a knowledge base about isorite, written into `~/isorite-guide` (or the folder named) and served. With no provider set up, Iso is the guide's finder: no key or network needed. Good to check the install alone. `isorite --tutorial` is another name for it. |
|
|
23
23
|
| `isorite model [--from <folder>]` | Optional: the model for search by meaning. Says which is installed and where, else fetches it (53 MB) from npm, a GitHub release, then Hugging Face, saying why each failed. `--from`: granite's files, or any compatible model (below). |
|
|
24
24
|
| `isorite help setup` | Prints this. |
|
|
25
25
|
|
|
@@ -30,6 +30,7 @@ turn off certificate checking (`NODE_TLS_REJECT_UNAUTHORIZED=0`).
|
|
|
30
30
|
| `~/.config/isorite/config.toml` (or `$XDG_CONFIG_HOME/isorite/`) | isorite's settings for this machine: `device`, `[provider]`, `[tiers]`, `[prices]`. Mode 600. Holds the key. |
|
|
31
31
|
| `~/.config/isorite/opencode/` | OpenCode's settings as isorite writes them at each `isorite term` start (`opencode.json`, theme, plugin link). Rewritten; don't edit. |
|
|
32
32
|
| `~/.config/isorite/opencode/local.json` | The person's own OpenCode settings, laid over isorite's. Never rewritten. |
|
|
33
|
+
| `<notes>/AGENTS.md` | The base's own instructions (notation, level, how notes are named), sent to Iso with every turn and named in OpenCode's `instructions`. Not a note: not on the Atlas, not linted. |
|
|
33
34
|
| `<notes>/.isorite/` | isorite's own folder in the notes: journals of every change, `settings.toml` (`repository = "<path>"`, where code links point). Made on first open; commit it with the notes. |
|
|
34
35
|
| `~/.local/share/isorite/models/` (or `$XDG_DATA_HOME/isorite/models/`) | The models for search by meaning, a folder each: `granite-embedding-small-english-r2/`, and any other installed with `--from`. `use` names the one used, if not granite. |
|
|
35
36
|
| `~/.cache/isorite/vectors-<hash>.json` | Each base's vectors, made by the model; made again when the model changes. |
|
|
@@ -40,11 +41,15 @@ isorite never reads or changes the person's own OpenCode settings (`~/.config/op
|
|
|
40
41
|
|
|
41
42
|
## config.toml
|
|
42
43
|
|
|
44
|
+
The person can set all of this from Iso's settings in the browser (M18: on `localhost` only; checked as `doctor`
|
|
45
|
+
checks it, kept into use without a restart). The page writes the same fields; it drops comments, keeping the file
|
|
46
|
+
as it was once as `config.toml.bak`. Writing it by hand needs the server restarted.
|
|
47
|
+
|
|
43
48
|
```toml
|
|
44
49
|
[provider]
|
|
45
50
|
name = "work" # optional; what isorite calls the provider in messages
|
|
46
51
|
url = "https://…/v1" # OpenAI-compatible; /chat/completions and /models are added. Absent: OpenRouter
|
|
47
|
-
key = "…" # the key; also settable in
|
|
52
|
+
key = "…" # the key; also settable in Iso's settings in the browser
|
|
48
53
|
key_header = "api-key" # send the key in this header, as it is, instead of Authorization: Bearer
|
|
49
54
|
stream_usage = false # don't send stream_options (some gateways answer 400 to it)
|
|
50
55
|
max_tokens = 16000 # the least output budget any call gets, for models that think at length
|
package/docs/setup.md
CHANGED
|
@@ -9,23 +9,37 @@ run `isorite doctor`: it checks each step and says what to change.
|
|
|
9
9
|
- **isorite**: `npm i -g isorite`.
|
|
10
10
|
- For the terminal, **OpenCode 2**: `npm i -g @opencode/cli`.
|
|
11
11
|
|
|
12
|
-
`isorite
|
|
12
|
+
`isorite guide` now opens the guide, a knowledge base about isorite with things to do in it. Until a provider is set up, Iso there is the guide's finder: no key, nothing sent anywhere.
|
|
13
13
|
|
|
14
14
|
## 2. A model provider
|
|
15
15
|
|
|
16
16
|
Iso and the terminal need an OpenAI-compatible provider. isorite's settings are in `~/.config/isorite/config.toml`
|
|
17
17
|
(or `$XDG_CONFIG_HOME/isorite/config.toml`), a file only you can read.
|
|
18
18
|
|
|
19
|
-
**OpenRouter** is the default. Open isorite in the browser,
|
|
20
|
-
OpenRouter, which makes one
|
|
19
|
+
**OpenRouter** is the default. Open isorite in the browser, open Iso's settings (the icon at the right of Iso's
|
|
20
|
+
pane head), and paste a key there, or sign in with OpenRouter, which makes one. Nothing else is needed.
|
|
21
21
|
|
|
22
|
-
**Any other provider or gateway** is
|
|
22
|
+
**Any other provider or gateway** is set up in the same place: choose *a gateway of your own*, give its address and
|
|
23
|
+
key, and open *what else it asks for* for its key's header, headers, query values, client certificate, and whether
|
|
24
|
+
it reports usage. Each tier's model is picked from the gateway's own list. The pulse icon checks the setup before
|
|
25
|
+
it is kept, as `isorite doctor` would, and says what to change; the tick keeps it, in use at once, without a
|
|
26
|
+
restart. The clipboard icon copies `isorite doctor --report` for someone helping.
|
|
27
|
+
|
|
28
|
+
- The setup is changed only from this machine (`localhost`). From a phone over a tailnet it is shown and checked,
|
|
29
|
+
and the key can be changed, but not the address it goes to.
|
|
30
|
+
- Keeping a new address forgets the key held for the old one, unless a key is given with it.
|
|
31
|
+
- Secrets (the key, the `.pfx`'s password, a header whose name looks secret) are written and never shown again.
|
|
32
|
+
- The page writes `config.toml` again without its comments; the first time, the file as it was is kept beside it
|
|
33
|
+
as `config.toml.bak`.
|
|
34
|
+
- A running `isorite term` keeps the models it started with, but asks through the new address at once.
|
|
35
|
+
|
|
36
|
+
The same setup can be written by hand in `config.toml`, under `[provider]`, with the models under `[tiers]`:
|
|
23
37
|
|
|
24
38
|
```toml
|
|
25
39
|
[provider]
|
|
26
40
|
name = "work" # what isorite calls it
|
|
27
41
|
url = "https://llm.example.com/v1" # /chat/completions is added to it
|
|
28
|
-
key = "…" # or set it
|
|
42
|
+
key = "…" # or set it in Iso's settings in the browser
|
|
29
43
|
|
|
30
44
|
[tiers] # the provider's own names for its models
|
|
31
45
|
low = "small-model" # quick replies
|
|
@@ -88,6 +102,10 @@ isorite serve ~/notes/my-project
|
|
|
88
102
|
`isorite new` makes the folder, empty but for isorite's own `.isorite/`. A folder of notes you have already is
|
|
89
103
|
opened as it is, with `isorite serve`.
|
|
90
104
|
|
|
105
|
+
How you want the base kept (your notation, the level, the language, how notes are named) goes in `AGENTS.md` at
|
|
106
|
+
the top of the notes, which Iso and the terminal's agent are both given. Edit it in Iso's settings, the icon at the
|
|
107
|
+
right of its pane's header, or in any editor. Iso may offer a line for it when you say how you want things done.
|
|
108
|
+
|
|
91
109
|
It prints a link to click: `http://localhost:4767/`. isorite keeps what it needs in the folder's `.isorite/`.
|
|
92
110
|
|
|
93
111
|
## 4. The terminal
|
package/docs/terminal.md
CHANGED
|
@@ -72,7 +72,12 @@ terminal's session:
|
|
|
72
72
|
- **Notes the agent changes** are written at once, under its name. Turn on provenance in the Note pane's header to
|
|
73
73
|
see the agent's words marked, and the timeline to put a note back.
|
|
74
74
|
|
|
75
|
-
With both off, the pane is Iso's own again.
|
|
75
|
+
With both off, the pane is Iso's own again. The ≡ in its header, or a click on its gauge, opens what Iso is sent:
|
|
76
|
+
a row for each part, with its size. These are the notes you gave and the notes Iso read, the conversation's turns,
|
|
77
|
+
and what is always sent. Iso keeps a note it read in hand, and sets it aside after three turns unused, as a line it
|
|
78
|
+
can read again. A click switches a row on or off. At the head of the conversation, one icon sums the earlier turns
|
|
79
|
+
up into a briefing sent instead of them, and another starts a new conversation from that briefing. Iso sums up on
|
|
80
|
+
its own only when a turn would be more than the model takes.
|
|
76
81
|
|
|
77
82
|
## The models
|
|
78
83
|
|
|
@@ -89,6 +94,9 @@ OpenCode doesn't know a gateway's prices, so Iso's pane shows the session's toke
|
|
|
89
94
|
|
|
90
95
|
## OpenCode's settings
|
|
91
96
|
|
|
97
|
+
OpenCode is given the base's `AGENTS.md` (in its `instructions`), when `isorite term` is named the notes, and the
|
|
98
|
+
knowledge base's ways through `brief_base`: how a note is written and linked, and how the base is kept.
|
|
99
|
+
|
|
92
100
|
isorite keeps OpenCode's settings in `~/.config/isorite/opencode/`, rewritten at each start, so your own OpenCode
|
|
93
101
|
settings are never read or changed. Anything of your own goes in `~/.config/isorite/opencode/local.json`, which is
|
|
94
102
|
never rewritten, and is laid over what isorite writes. For example, to have OpenCode ask before every command (by
|
package/package.json
CHANGED
package/plugin/tui.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { BoxRenderable, fg, ImageRenderable, RGBA, StyledText, TextAttributes, TextRenderable, type Renderable, type TextChunk } from "@opentui/core";
|
|
7
7
|
import { Plugin } from "@opencode/plugin/tui";
|
|
8
|
-
import { createEffect, createResource, createSignal, For, onCleanup, Show
|
|
8
|
+
import { createEffect, createResource, createSignal, For, onCleanup, Show } from "solid-js";
|
|
9
9
|
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
10
10
|
import { ago, anchorOf, blocks, describe, momentKey, moments, reach, type Entry, type MapModel } from "../dist/core.js";
|
|
11
11
|
import { drawAtlas, runsOf, type Role } from "./oneatlas.ts";
|
|
@@ -15,7 +15,6 @@ import * as Isorite from "./isorite.ts";
|
|
|
15
15
|
import * as Logo from "./logo.ts";
|
|
16
16
|
import { lapis, quartz, routeColours } from "./palette.ts";
|
|
17
17
|
import { Play, type Step } from "./play.ts";
|
|
18
|
-
import { TOUR, type TourState } from "./tour.ts";
|
|
19
18
|
import { parseSources } from "./sources.ts";
|
|
20
19
|
import * as Wordmark from "./wordmark.ts";
|
|
21
20
|
|
|
@@ -265,9 +264,6 @@ export default Plugin.define({
|
|
|
265
264
|
</text>
|
|
266
265
|
),
|
|
267
266
|
});
|
|
268
|
-
// What the tour (isorite --tutorial) waits on that nothing else counts: the agent's turns ended, Iso's answers,
|
|
269
|
-
// and notes sent from here.
|
|
270
|
-
const [turns, setTurns] = createSignal(0), [isoAnswers, setIsoAnswers] = createSignal(0), [sent, setSent] = createSignal(0);
|
|
271
267
|
|
|
272
268
|
/** [s]: a note, or a passage of it, sent to the agent from the terminal, as from the browser: under "from you". */
|
|
273
269
|
const sendToAgent = async (note: string, passage?: string) => {
|
|
@@ -275,7 +271,7 @@ export default Plugin.define({
|
|
|
275
271
|
const said = await ctx.ui.dialog.prompt({ title: "Send to the agent", description: passage ? "the passage the cursor is on" : `the note “${(titles().get(note) ?? note).toLowerCase()}”`, placeholder: "what you want of it (optional)" });
|
|
276
272
|
if (said === undefined) return;
|
|
277
273
|
await Isorite.post("/api/agent/send", { note, ...(passage ? { passage } : {}), said: said.trim() })
|
|
278
|
-
.
|
|
274
|
+
.catch((e: Error) => ctx.ui.toast?.show?.({ message: e.message, variant: "error" }));
|
|
279
275
|
};
|
|
280
276
|
|
|
281
277
|
const stop = new AbortController();
|
|
@@ -293,8 +289,7 @@ export default Plugin.define({
|
|
|
293
289
|
if (sessionID) void deliver(sessionID);
|
|
294
290
|
return;
|
|
295
291
|
}
|
|
296
|
-
if (type === "agent-done" && typeof data.session === "string") { reading.delete(data.session); hear((n) => n + 1);
|
|
297
|
-
if (type === "iso-done") setIsoAnswers((n) => n + 1);
|
|
292
|
+
if (type === "agent-done" && typeof data.session === "string") { reading.delete(data.session); hear((n) => n + 1); }
|
|
298
293
|
if (type === "note-changed") bumpMap((n) => n + 1);
|
|
299
294
|
if (type !== "agent-step" || data.agent !== Isorite.AGENT || typeof data.session !== "string") return;
|
|
300
295
|
const play = plays.get(data.session) ?? new Play();
|
|
@@ -324,75 +319,6 @@ export default Plugin.define({
|
|
|
324
319
|
return new TextRenderable(ctx.renderer, { content: new StyledText(chunks) });
|
|
325
320
|
});
|
|
326
321
|
|
|
327
|
-
// ------------------------------------------------------------ the tour (isorite --tutorial)
|
|
328
|
-
const TUTORIAL = process.env.ISORITE_TUTORIAL === "1";
|
|
329
|
-
const [stepAt, setStepAt] = createSignal(TUTORIAL ? 0 : TOUR.length);
|
|
330
|
-
const [cardShown, setCardShown] = createSignal(true);
|
|
331
|
-
const tourState = (): TourState => {
|
|
332
|
-
heard();
|
|
333
|
-
const route = ctx.ui.router.current(), shown = note();
|
|
334
|
-
return {
|
|
335
|
-
page: route.type === "plugin" ? (route.name === SPLASH ? "splash" : route.name === NOTE ? "note" : route.name) : route.type,
|
|
336
|
-
turns: turns(),
|
|
337
|
-
inPlay: route.type === "session" ? plays.get(route.sessionID)?.size ?? 0 : 0,
|
|
338
|
-
atlasOpen: ctx.ui.panel.current()?.name === ATLAS,
|
|
339
|
-
note: shownNote(),
|
|
340
|
-
// Not known until the note is read: no step moves on for a note still opening.
|
|
341
|
-
waiting: shown && shown.id === shownNote() ? shown.items.filter((i) => i.type === "change").length : -1,
|
|
342
|
-
past: moment() !== null,
|
|
343
|
-
isoAnswers: isoAnswers(),
|
|
344
|
-
sent: sent(),
|
|
345
|
-
};
|
|
346
|
-
};
|
|
347
|
-
/** Puts the step's prompt in the prompt being typed in, once it has focus and while it is empty. */
|
|
348
|
-
const fillPrompt = (text: string) => {
|
|
349
|
-
let tries = 0;
|
|
350
|
-
const timer = setInterval(() => {
|
|
351
|
-
const editor = ctx.renderer.currentFocusedEditor;
|
|
352
|
-
if (editor && !editor.plainText.trim()) editor.insertText(text);
|
|
353
|
-
if (editor || ++tries > 50) clearInterval(timer);
|
|
354
|
-
}, 100);
|
|
355
|
-
};
|
|
356
|
-
ctx.ui.slot({
|
|
357
|
-
append: "app",
|
|
358
|
-
render() {
|
|
359
|
-
if (!TUTORIAL) return null;
|
|
360
|
-
// A step moves on when what it waits for holds, judged against what held when it began.
|
|
361
|
-
let at = -1, began = untrack(tourState);
|
|
362
|
-
createEffect(() => {
|
|
363
|
-
const i = stepAt(), now = tourState(), step = TOUR[i];
|
|
364
|
-
if (i !== at) { at = i; began = now; if (step?.fill) fillPrompt(step.fill); return; }
|
|
365
|
-
if (step?.until?.(now, began)) setStepAt(i + 1);
|
|
366
|
-
});
|
|
367
|
-
ctx.keymap.layer(() => ({
|
|
368
|
-
mode: "global",
|
|
369
|
-
commands: [
|
|
370
|
-
{ id: "isorite.tour.next", title: "Tour: the next step", palette: true, slash: { name: "next" }, run: () => { setStepAt((i) => Math.min(i + 1, TOUR.length)); } },
|
|
371
|
-
{ id: "isorite.tour.card", title: "Tour: hide or show the card", palette: true, slash: { name: "tour" }, run: () => { setCardShown((v) => !v); } },
|
|
372
|
-
],
|
|
373
|
-
}));
|
|
374
|
-
return (
|
|
375
|
-
<Show when={cardShown() && TOUR[stepAt()]}>
|
|
376
|
-
{(step) => (
|
|
377
|
-
<box position="absolute" right={2} bottom={4} width={48} zIndex={3000} flexDirection="column" paddingLeft={1} paddingRight={1}
|
|
378
|
-
border borderStyle="rounded" borderColor={ground().growth} backgroundColor={ground().g0} title={` tour · ${stepAt() + 1} of ${TOUR.length} `}>
|
|
379
|
-
<text fg={ground().text} wrapMode="word"><b>{step().title}</b></text>
|
|
380
|
-
<text fg={ground().soft} wrapMode="word">{step().text}</text>
|
|
381
|
-
<text> </text>
|
|
382
|
-
<box flexDirection="row">
|
|
383
|
-
<text wrapMode="none">
|
|
384
|
-
<For each={TOUR}>{(_, k) => <span style={{ fg: k() <= stepAt() ? ground().growth : ground().line }}>{k() <= stepAt() ? "■" : "□"}</span>}</For>
|
|
385
|
-
</text>
|
|
386
|
-
<box flexGrow={1} />
|
|
387
|
-
<text fg={ground().faint} wrapMode="none">{step().until ? "/tour hides" : "/next /tour hides"}</text>
|
|
388
|
-
</box>
|
|
389
|
-
</box>
|
|
390
|
-
)}
|
|
391
|
-
</Show>
|
|
392
|
-
);
|
|
393
|
-
},
|
|
394
|
-
});
|
|
395
|
-
|
|
396
322
|
ctx.ui.slot({
|
|
397
323
|
append: "sidebar.content",
|
|
398
324
|
render: (input) => {
|