@turri-dev/cli 0.1.25 → 0.1.26-next.f80f259
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/chat-records.d.ts +4 -0
- package/dist/chat-records.js +5 -0
- package/dist/chats.d.ts +25 -8
- package/dist/chats.js +280 -43
- package/dist/config-command.js +30 -0
- package/dist/config-edit.js +2 -2
- package/dist/config.d.ts +6 -3
- package/dist/config.js +2 -2
- package/dist/extension.js +12 -9
- package/dist/host-session.d.ts +4 -2
- package/dist/host.d.ts +1 -1
- package/dist/host.js +1 -1
- package/dist/init-config.d.ts +1 -1
- package/dist/init-config.js +3 -2
- package/dist/notice.d.ts +33 -2
- package/dist/notice.js +24 -1
- package/dist/regenerate/ledger.d.ts +4 -0
- package/dist/sentinel-chat.d.ts +5 -0
- package/dist/sentinel-chat.js +30 -3
- package/dist/sentinel.d.ts +8 -2
- package/dist/sentinel.js +49 -11
- package/docs/chats.md +2 -2
- package/docs/config.md +1 -1
- package/docs/hosting.md +7 -3
- package/docs/sentinel.md +28 -10
- package/docs/using.md +2 -1
- package/genome/rules/turri-regeneration.md +1 -1
- package/package.json +1 -1
package/docs/sentinel.md
CHANGED
|
@@ -2,22 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
The sentinel is a chat of turri's own, beside the chats you talk to. Every pulse that finishes goes to it: it checks how the pulse went - and, with `release-after-pulse` on, tests what was released - and when something needs you, it says so in the chat you are in, with what can be done about it. You go on talking about the software, and building it, while it watches what lands.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`sentinel:` in `config.turri.yaml` says whether there is one: `ask`, as it is unset - you are asked before it first opens in a session (below); `true` - it opens without asking; `false` - none. It is there in the terminal and in a host such as turri-desktop - wherever turri holds [chats](chats.md) - and not in pi's print, json or rpc modes, nor on the command line (`turri regenerate`, `turri change-plain`), which have no chat to tell: there a pulse is told as it is without it.
|
|
6
|
+
|
|
7
|
+
## Starting it
|
|
8
|
+
|
|
9
|
+
Under `sentinel: ask`, a tab that starts working on its own is not sprung on you. When the first pulse of a session finishes, turri asks first - a notice of its own in the chat you are in, before the sentinel opens, in one sentence: `app · regenerate pulse 3 finished - start the sentinel, a background tab that reviews each finished pulse and tries to use what was released?` - the release left out with `release-after-pulse` off. In the terminal it is a dialog once that chat is not mid-turn, with how to switch to the sentinel once it is open - `Use "/chat 2" to open the sentinel.`, the number the one it will have in the strip; in a host, a notice with `kind: "sentinel-start"` (see [in a host](#in-a-host)). You answer:
|
|
10
|
+
|
|
11
|
+
- **Start the sentinel** - it opens, and checks this pulse and each after it this session;
|
|
12
|
+
- **Start it, and don't ask again** - the same, and `sentinel: true` is written to `config.turri.yaml`;
|
|
13
|
+
- **Not this session**, or `esc` - no sentinel until turri starts again: each pulse is told to the chat that asked for it, as without one;
|
|
14
|
+
- **Turn it off for this project** - `sentinel: false` is written.
|
|
15
|
+
|
|
16
|
+
Nothing goes to the sentinel, or to your chat, until you answer: the pulses that finish meanwhile are held, and go to it - or to their chats - as you say. You are asked once a session: a sentinel closed and opened again by the next pulse is not asked for again, nor one `turri --continue` reopened. Close the chat it was asked in, and it is asked again in the one you look at.
|
|
17
|
+
|
|
18
|
+
`/sentinel` sets it from the chat - `/sentinel ask`, `/sentinel on`, `/sentinel off`, or `/sentinel` alone to pick - written to `config.turri.yaml` as `/turri-config sentinel` does.
|
|
6
19
|
|
|
7
20
|
## What you see
|
|
8
21
|
|
|
9
22
|
- **In the chat you are in**, a pulse that finishes shows as it always has - `✓ app · regenerate pulse 3 finished after 4m 10s`, and for one that went wrong, where to see why. A pulse that landed is told to the chat's agent, which tells you in a line or two what landed and what the sentinel is doing with it - and again, in a line, when the sentinel is done with it - checking it, testing what was released - and does nothing else about it: it does not test, release or open it, nor do what the project's guidance says to do once a pulse finishes. The pulse is the sentinel's. A pulse that went wrong is not told to it: the sentinel looks into it, and you hear of it as a notice.
|
|
10
|
-
- **The sentinel** opens with the first pulse that finishes - in the background: it never takes the screen, and you stay in your chat. It is a tab in the strip like any other chat - `sentinel` - marked while it works and when it has something new. Switch to it to read what it checked.
|
|
23
|
+
- **The sentinel** opens with the first pulse that finishes - once you said to start it, under `sentinel: ask` - in the background: it never takes the screen, and you stay in your chat. It is a tab in the strip like any other chat - `sentinel` - marked while it works and when it has something new. Switch to it to read what it checked: it shows the pulse it is on, or the last one it checked - each pulse has a conversation of its own (below).
|
|
11
24
|
- **A notice**, when something needs you: a card in the chat you are in - what is wrong, what shows it, and what can be done, numbered - and, in the terminal, once that chat is not mid-turn, a dialog on it:
|
|
12
25
|
- `enter` on an action accepts it: the chat's agent is started on it - a change to the \*\*\*plain, a pulse regenerated again, a credential to provide, a setting to change;
|
|
26
|
+
- **Ignore - leave it as the build has it**, on a notice the sentinel marked ignorable: nothing is done, and it is not raised again - in this session or the next (below);
|
|
13
27
|
- **Say what to do instead…** - your own words, and the agent does that;
|
|
14
|
-
- **Dismiss**, or `esc`: nothing is done.
|
|
28
|
+
- **Dismiss**, or `esc`: nothing is done, this once - the next pulse may raise it again.
|
|
15
29
|
|
|
16
30
|
Nothing is done without your answer. A notice goes to the chat you are in; while you are in the sentinel's, to the chat that asked for the pulse. There is one open notice a module: a newer one about the same module supersedes the older one, whose call to action would be stale, and the card says so.
|
|
17
31
|
|
|
18
32
|
## What it does
|
|
19
33
|
|
|
20
|
-
For each pulse that finishes, turri writes the sentinel what it was and how it ended - its stages, rounds and failures as the [ledger](using.md#the-ledger) has them, its \*\*\*plain diff, its log, the chat that asked for it, the chat you are in, and what was released and where it runs. Then it:
|
|
34
|
+
Each pulse it checks from scratch, in a conversation of its own: its prompt, your guidance (`.turri/sentinel.md`, below) and what turri writes it of that pulse - nothing of the pulses before it. Its chat stays the same tab; the conversation in it is replaced as it starts on the next pulse, and the one before is let go - deleted, outside [debug mode](config.md). For each pulse that finishes, turri writes the sentinel what it was and how it ended - its stages, rounds and failures as the [ledger](using.md#the-ledger) has them, its \*\*\*plain diff, its log, the chat that asked for it, the chat you are in, and what was released and where it runs. Then it:
|
|
21
35
|
|
|
22
36
|
1. **checks the pulse** - from what it is told, and when that is not enough, from the pulse's log, the ledger (`ledger`, `pulses`), `diagnose`, the code and the \*\*\*plain;
|
|
23
37
|
2. **tests the release**, with `release-after-pulse` on: what was released is running where it is told, and it tries it - in the terminal with its shell (`curl`, the program itself, the logs), in a host also with what the host gives it, a browser say. How far it tests is its own judgement, from what the pulse changed and what the \*\*\*plain says it now does. With `release-after-pulse` off nothing is running, and it checks the pulse alone;
|
|
@@ -25,7 +39,11 @@ For each pulse that finishes, turri writes the sentinel what it was and how it e
|
|
|
25
39
|
4. **notifies no one** when all is well;
|
|
26
40
|
5. **concludes**, last, whether all is well or not: what it checked and what it found, in a line or two. The chat you are in shows it - `✓ sentinel · app · pulse 3 · checked, it holds` - and its agent tells you so in a line, and does nothing more; when something was wrong, that its notice says what.
|
|
27
41
|
|
|
28
|
-
A pulse that
|
|
42
|
+
It checks one pulse at a time. A pulse that finishes while it works waits its turn, and is checked once it is done - from scratch, as every pulse is. A pulse that went well is checked until a newer pulse of its module finishes: its check is stopped, and the newer one is checked instead, at once - and a newer pulse of a module takes the place of one of it that went well and waits. A pulse that went wrong is looked into to its end.
|
|
43
|
+
|
|
44
|
+
A notice may be **ignorable**: the sentinel marks one so when you may reasonably leave it as it is - chiefly a gap in the \*\*\*plain the build settled with an assumption: *the \*\*\*plain does not say what happens to passwords over 72 bytes; the build refuses them with 422*. Never something broken, a pulse that went wrong, a release that did not come up, nor the question whether to start the sentinel. Ignore one and it is left as the build has it, for good: nothing is done in your chat, and a line is written to the sentinel's guidance (`.turri/sentinel.md`, as `keep_guidance` writes) saying what was left as it is - its module, the gap, what the build does - and that you chose to. Every conversation after has it, and does not raise it again - unless the build no longer does what you accepted. To have it raised again, take the line out of `.turri/sentinel.md`.
|
|
45
|
+
|
|
46
|
+
Two functional specs that contradict each other stop a pulse in a way only you can settle: turri puts that notice itself, with an action that starts your chat on working it out with you.
|
|
29
47
|
|
|
30
48
|
## What it may do
|
|
31
49
|
|
|
@@ -33,22 +51,22 @@ Nothing that changes the project. Its file tools - `read`, `ls`, `grep`, `find`
|
|
|
33
51
|
|
|
34
52
|
## Talking to it
|
|
35
53
|
|
|
36
|
-
Switch to its chat and say how to check your software - *test the sign-up flow at a phone's width*, *ignore the styling*, *the API runs on port 8080*. What should hold from now on it keeps with `keep_guidance`, in `.turri/sentinel.md`, and it is in its prompt from then on - after it is closed and opened again too.
|
|
54
|
+
Switch to its chat and say how to check your software - *test the sign-up flow at a phone's width*, *ignore the styling*, *the API runs on port 8080*. What should hold from now on it keeps with `keep_guidance`, in `.turri/sentinel.md`, and it is in its prompt from then on - for every pulse after, and after it is closed and opened again too. Nothing else you say carries over: the conversation you talk to it in is the pulse's, and the next pulse starts a fresh one. A pulse that finishes while it answers you waits until it has.
|
|
37
55
|
|
|
38
56
|
## Closing it
|
|
39
57
|
|
|
40
|
-
It closes like any chat (`/chat close`, `alt+w`, a host's `closeChat`), and that does not turn it off: the next pulse that finishes opens it again, with a fresh conversation - and your guidance. `turri --continue` reopens it with the other chats,
|
|
58
|
+
It closes like any chat (`/chat close`, `alt+w`, a host's `closeChat`), and that does not turn it off: what it was checking is stopped, the pulses that waited for it are let go, and the next pulse that finishes opens it again, with a fresh conversation - and your guidance. `turri --continue` reopens it with the other chats, with the conversation of the last pulse it was on; the next pulse starts a fresh one. `sentinel: false` - in `config.turri.yaml`, or with `/sentinel off` or `/turri-config` - turns it off: from the next pulse that finishes, a pulse is told to the chat that asked for it, whose agent reacts to it, as without one.
|
|
41
59
|
|
|
42
60
|
It has a slot of its own: with it on, you open up to eight chats, and the ninth is the sentinel's, open or not.
|
|
43
61
|
|
|
44
62
|
## Its model
|
|
45
63
|
|
|
46
|
-
The `sentinel` role in `models:` - else the chat's model, `models.chat` else `harness-model`, with its thinking - see [models](config.md#models). A `sentinel` entry that gives only `thinking` runs on the chat's model, thinking as it says. It is set as the sentinel's chat opens, in the terminal and in a host alike - never the model its session started on, which in a host is [pi](https://pi.dev)'s default and may not be one the project's provider has. What the person then picks in its chat stands. Judging behaviour against \*\*\*plain, and driving a browser, want a capable model; point it at a cheaper one if it costs too much for every pulse.
|
|
64
|
+
The `sentinel` role in `models:` - else the chat's model, `models.chat` else `harness-model`, with its thinking - see [models](config.md#models). A `sentinel` entry that gives only `thinking` runs on the chat's model, thinking as it says. It is set as the sentinel's chat opens, in the terminal and in a host alike - never the model its session started on, which in a host is [pi](https://pi.dev)'s default and may not be one the project's provider has. What the person then picks in its chat stands, through each pulse's fresh conversation, until it is closed. Judging behaviour against \*\*\*plain, and driving a browser, want a capable model; point it at a cheaper one if it costs too much for every pulse.
|
|
47
65
|
|
|
48
66
|
## In a host
|
|
49
67
|
|
|
50
|
-
A host sees the sentinel as one of its chats - `role: "sentinel"` in `chats()` and `onChats` - and never has focus moved to it. Each notice comes as `onNotice(notice)` - `{ id, chat, module, pulse, job, severity, summary, evidence, actions, state }`, `chat` the one to draw it in - and `onNoticeOver(notice)` when it is answered or superseded; `notices()` lists those open. `answer(id, { action } | { steer } | { dismiss: true })` answers one, as the terminal's dialog does
|
|
68
|
+
A host sees the sentinel as one of its chats - `role: "sentinel"` in `chats()` and `onChats` - and never has focus moved to it. Each pulse's fresh conversation is a new pi session in the same chat, under the same id: `onChatSession(chat, session)` comes before the pulse is put to it, and `session(id)` returns the new one. Under `sentinel: ask`, the question before it first opens comes as a notice like the others, with `kind: "sentinel-start"`, its actions the four answers: `answer(id, { action })` answers it - by index or label - and `{ dismiss: true }` is not this session. Each notice comes as `onNotice(notice)` - `{ id, chat, module, pulse, job, severity, summary, evidence, actions, state }`, `chat` the one to draw it in - and `onNoticeOver(notice)` when it is answered or superseded; `notices()` lists those open. `answer(id, { action } | { steer } | { dismiss: true } | { ignore: true })` answers one, as the terminal's dialog does - `{ ignore: true }` only on a notice with `ignorable: true`, `"refused"` on any other; it ends `state: "ignored"`. `onConcluded({ job, verdict: "holds" | "wrong", summary, module, pulse, chat })` comes as it is done with a pulse, all well or not - told to the chat `chat` too. The sentinel's own tools - a browser - are given with `createTurri({ sentinel: { tools } })`, and it is told it has them. `sentinel: { surface }` says, in the host's words, where the person watches it use them: then it tries a release with them, where the person can watch, and keeps its shell for what they cannot show - logs, exit codes, files. Without it, the person does not watch the sentinel, and the host's tools are only to test with as well. See [running turri in a host](hosting.md#the-sentinel).
|
|
51
69
|
|
|
52
70
|
## In the ledger
|
|
53
71
|
|
|
54
|
-
`chat.opened` carries `role: "sentinel"` for its chat; `notice.put` and `notice.over` record each notice - its severity and module, and how it ended: `accepted`, `steered`, `dismissed`, `superseded` - and `sentinel.concluded` each pulse it is done with, `holds` or `wrong`.
|
|
72
|
+
`chat.opened` carries `role: "sentinel"` for its chat; `sentinel.checking` each pulse it starts a conversation for - `replaced`, the pulse of the same module whose check it stopped; `notice.put` and `notice.over` record each notice - the question before it opens among them - - its severity and module, and how it ended: `accepted`, `steered`, `dismissed`, `ignored`, `superseded` - and `sentinel.concluded` each pulse it is done with, `holds` or `wrong`.
|
package/docs/using.md
CHANGED
|
@@ -81,6 +81,7 @@ A regeneration takes minutes, so in the TUI every tool that regenerates starts t
|
|
|
81
81
|
`ctrl+h` again, or `esc`, closes it and goes back to the chat. Beside the chat, one side has the keys and the other is drawn faint: `ctrl+h` switches sides - from the chat to the panel, from the panel to the chat, the panel staying and following the pulses - `esc` gives the chat the keys too, and a click gives them to the side clicked. `ctrl+c` closes the panel; so do `s`, over the whole screen, then `esc`. ([pi](https://pi.dev)'s regular screen, `--tui-mode regular`, reads no mouse: there it is the keys alone.) While the panel has the keys, every key is the panel's, whatever else happens: a question the chat model asks in the background (`ask_user`) waits in the chat, unanswered - an `enter` in the panel never answers it - and the panel's top line says "the chat is waiting for your answer". Back in the chat, the question is there, with the keys.
|
|
82
82
|
- `turri` runs [pi](https://pi.dev) in fullscreen mode, so the conversation stays where you scroll it while the pulses redraw (PgUp/PgDn, the wheel); `--tui-mode regular` goes back to the terminal's own scrollback.
|
|
83
83
|
- **`/turri-config`** changes a setting of the project's `config.turri.yaml` - the models, review, conformance tests, any key - and says when it takes effect: now, at your next message, or at the next pulse; never only after a restart. See [changing it from the chat](config.md#changing-it-from-the-chat).
|
|
84
|
+
- **`/sentinel`** says whether the [sentinel](sentinel.md) checks every pulse that finishes: `ask` - you are asked before it first opens in a session - `on`, or `off`; written to `config.turri.yaml`.
|
|
84
85
|
- **`/stack`** chooses the stack everything is built with from then on, in a project whose parts are on different stacks: a list of the project's stacks, or `/stack backend`, `/stack none`. The stack's `config.turri.<stack>.yaml` is laid over `config.turri.yaml` - its testing scripts, toolchains, models - for the session; a module already asked for keeps the stack it was asked for with. The chat model switches it too when you ask - and, to build one part, passes that part's stack to `regenerate` instead, leaving the session's as it is. See [stacks](config.md#stacks).
|
|
85
86
|
- **`/regenerate [module | file.plain] [functionality ...] [from scratch [whole chain]] [how to cut the work]`** regenerates a module from its \*\*\*plain as it is now, as the `regenerate` tool does: what is left to regenerate is planned into pulses - one functional spec at a time, a few together or the whole module in one, as the planner judges - and the first starts. Words after the module are guidance for that plan (`/regenerate app models first`). A module's name typed with spaces for its underscores is that module - `/regenerate d365_function executor` is `d365_function_executor`. With no module named: the one `module:` in `config.turri.yaml` names, else the folder's top modules, to pick from. Asked for from scratch - `from scratch`, `start over`, `delete the module` - it starts at once, with nothing to confirm: it deletes the module's code and conformance tests in every codebase where they live, committed so `/lineage` keeps them, and regenerates the whole module from its \*\*\*plain, built though nothing in it changed: `/regenerate shorty from scratch`. The modules below it keep their code, and the module starts again from a copy of the code below it. Asked for its chain too - `/regenerate shorty from scratch, the whole chain` - the code of every module of its requires chain is deleted, the base's included, and the whole chain is regenerated from the bottom: what to do after modules were split, merged or moved, when the code below was laid out for modules that are no more. The chat's `regenerate` does the same with `from_scratch: "module"` or `"chain"`, when you ask for it; `turri regenerate --from-scratch` or `--from-scratch-chain` on the command line, not with `--dry-run`. Functionalities named after the module - `/regenerate shorty 3 5` - are regenerated though their \*\*\*plain did not change, on the model chosen for functionalities: as `turri regenerate shorty.plain 3 5` does, and the chat's `regenerate` with `functionalities`.
|
|
86
87
|
- **`/halt <id>`** halts a pulse - a queued one never starts - **`/halt <module>`** (or a planned row's id) halts a module's planned pulses until you ask for it again, a \*\*\*plain change not starting it, and **`/halt activated <name>`** halts a server `activate` started; it lists all three when no id is given.
|
|
@@ -186,7 +187,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
|
|
|
186
187
|
| the healers | `healer.started`, `healer.ended` with what it found, `healer.failed` and why - the debugger, the diagnosis of a failed pulse |
|
|
187
188
|
| the history | `history.committed`, `history.failed`, `history.reset`, `history.rolledBack` |
|
|
188
189
|
| the environment | `environment.error` |
|
|
189
|
-
| chats and notices | `chat.opened` (`role: "sentinel"` for the [sentinel](sentinel.md)'s), `chat.closed`, `notice.put` (its severity and module), `notice.over` (accepted, steered, dismissed or superseded), `sentinel.concluded` (`holds` or `wrong`, a pulse the sentinel is done with) |
|
|
190
|
+
| chats and notices | `chat.opened` (`role: "sentinel"` for the [sentinel](sentinel.md)'s), `chat.closed`, `notice.put` (its severity and module), `notice.over` (accepted, steered, dismissed, ignored or superseded), `sentinel.checking` (a pulse the sentinel starts a conversation for - `replaced`, the one whose check it stopped), `sentinel.concluded` (`holds` or `wrong`, a pulse the sentinel is done with) |
|
|
190
191
|
|
|
191
192
|
Never in it: prompts, replies, thinking, tool arguments and results, file contents - the one body it keeps is each pulse's \*\*\*plain diff, so what an earlier session regenerated can be read back. What an event does carry - a stage, an error's first line - is masked as the logs are.
|
|
192
193
|
|
|
@@ -43,7 +43,7 @@ The other rules say what a spec means. This one says what turri does with a chan
|
|
|
43
43
|
- In order of cost: change the spec where it is wrong or silent; regenerate the functional specs whose code went bad again, by name; regenerate the module from scratch, or its whole chain - only when the person asks for it or agrees to it
|
|
44
44
|
- Pulses regenerate in the background: the spec can be changed while they run, and need not wait for them. A change inside what a running pulse regenerates steers it; the rest is planned again around it
|
|
45
45
|
- A pulse that does not land is rolled back whole - code, tests and record together - and its module waits until the spec changes or the module is asked for again. Running it again unchanged usually ends the same way: look at why it did not land first
|
|
46
|
-
- A finished pulse goes to the sentinel, a chat of turri's own - unless `sentinel: false
|
|
46
|
+
- A finished pulse goes to the sentinel, a chat of turri's own - unless `sentinel: false`; under `sentinel: ask`, the default, only once the person said to start it, asked by turri before it first opens in a session (declined, a pulse goes to the chat that asked for it, as with `false`): it checks the pulse, tests what was released, and puts what is wrong to the person as a notice with what can be done. A pulse that landed is told to the chat the person is in too, only to say it landed and what the sentinel does with it, and when the sentinel is done with a pulse, what it found - the chat does nothing else about either; a pulse that went wrong is not told to it - the chat's agent is given a notice's task once the person accepts it, and does that
|
|
47
47
|
|
|
48
48
|
## Where the cost is
|
|
49
49
|
|