@turri-dev/cli 0.1.21 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chats.d.ts +6 -1
- package/dist/extension.js +1 -1
- package/dist/sentinel.d.ts +5 -1
- package/dist/sentinel.js +26 -4
- package/docs/hosting.md +5 -1
- package/docs/sentinel.md +1 -1
- package/package.json +1 -1
package/dist/chats.d.ts
CHANGED
|
@@ -67,9 +67,14 @@ export interface TurriHost {
|
|
|
67
67
|
onNotice?(notice: Notice): void;
|
|
68
68
|
/** A notice is over - answered, dismissed, or superseded by a newer one about its module: the host takes its card down. */
|
|
69
69
|
onNoticeOver?(notice: Notice): void;
|
|
70
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* The sentinel's own tools beside turri's - a host's browser, say - and its surface: where the person watches it use
|
|
72
|
+
* them, in the host's words. Given, the sentinel tries a release with those tools, keeping its shell for what they
|
|
73
|
+
* cannot show; absent, the person does not watch it, and the tools are only to test with as well.
|
|
74
|
+
*/
|
|
71
75
|
sentinel?: {
|
|
72
76
|
tools?: ToolDefinition[];
|
|
77
|
+
surface?: string;
|
|
73
78
|
};
|
|
74
79
|
}
|
|
75
80
|
/** What a chat's extension does for its chat when the session asks. */
|
package/dist/extension.js
CHANGED
|
@@ -522,7 +522,7 @@ function turriExtension(api, session, link) {
|
|
|
522
522
|
}
|
|
523
523
|
}
|
|
524
524
|
if (isSentinel) {
|
|
525
|
-
const prompt = [event.systemPrompt, sentinelPrompt(cwd, { extraTools: hostSentinelTools.map((tool) => tool.name) }), projectConfigBlock(cwd), rulesBlock()].join("\n\n");
|
|
525
|
+
const prompt = [event.systemPrompt, sentinelPrompt(cwd, { extraTools: hostSentinelTools.map((tool) => tool.name), ...(host.sentinel?.surface !== undefined ? { surface: host.sentinel.surface } : {}) }), projectConfigBlock(cwd), rulesBlock()].join("\n\n");
|
|
526
526
|
return { systemPrompt: host.systemPrompt !== undefined ? host.systemPrompt(prompt) : prompt };
|
|
527
527
|
}
|
|
528
528
|
return {
|
package/dist/sentinel.d.ts
CHANGED
|
@@ -27,9 +27,13 @@ export declare function pulseEndsBlock(sentinel: boolean): string;
|
|
|
27
27
|
* does with it - and to tell the person so, and do nothing else about it.
|
|
28
28
|
*/
|
|
29
29
|
export declare function landedNote(job: Job, released: DeployedProcess[]): string;
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Its identity and its task, in its system prompt in place of the conversation's. A host's `surface` - where the person
|
|
32
|
+
* watches it, in the host's words - stands in place of their not watching, and has it try a release with the host's tools.
|
|
33
|
+
*/
|
|
31
34
|
export declare function sentinelPrompt(cwd: string, options?: {
|
|
32
35
|
extraTools?: string[];
|
|
36
|
+
surface?: string;
|
|
33
37
|
}): string;
|
|
34
38
|
/** What the sentinel is told of one finished pulse: the ledger's facts, the chats, and what was released. */
|
|
35
39
|
export declare function sentinelBrief(job: Job, context: {
|
package/dist/sentinel.js
CHANGED
|
@@ -98,15 +98,25 @@ export function landedNote(job, released) {
|
|
|
98
98
|
`Tell the person, in a line or two, what landed and that the sentinel is checking it${wasReleased ? " and testing the release" : ""}. Do nothing else about it: the pulse is the sentinel's now. Do not test it, release it, open it, run anything for it, or start other work because of it - whatever the project's guidance says to do when a pulse finishes.`,
|
|
99
99
|
].join("\n");
|
|
100
100
|
}
|
|
101
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* Its identity and its task, in its system prompt in place of the conversation's. A host's `surface` - where the person
|
|
103
|
+
* watches it, in the host's words - stands in place of their not watching, and has it try a release with the host's tools.
|
|
104
|
+
*/
|
|
102
105
|
export function sentinelPrompt(cwd, options = {}) {
|
|
103
106
|
const guidance = readGuidance(cwd);
|
|
107
|
+
const tools = options.extraTools ?? [];
|
|
108
|
+
const surface = options.surface?.trim() ?? "";
|
|
109
|
+
const theirs = tools.length > 0 ? tools.join(", ") : "the host's tools";
|
|
104
110
|
return [
|
|
105
111
|
"<sentinel>",
|
|
106
112
|
"You are turri's sentinel: a chat of turri's own, beside the chats the person talks to while they build their software",
|
|
107
113
|
"with turri. Every pulse that finishes comes to you, as a message turri writes - what the pulse was, how it ended,",
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
...(surface === ""
|
|
115
|
+
? ["its ***plain diff, its log, and what was released. The person does not watch you: they are in another chat, and", "hear from you only through notify_person."]
|
|
116
|
+
: [
|
|
117
|
+
"its ***plain diff, its log, and what was released. The person can watch you try a release, where the host shows it;",
|
|
118
|
+
"they talk to you in another chat, and hear what you find only through notify_person.",
|
|
119
|
+
]),
|
|
110
120
|
"",
|
|
111
121
|
"For each pulse:",
|
|
112
122
|
"1. Check how it went, from what turri tells you - and, when that is not enough, its log, the ledger (ledger, pulses),",
|
|
@@ -130,7 +140,19 @@ export function sentinelPrompt(cwd, options = {}) {
|
|
|
130
140
|
"",
|
|
131
141
|
"The person can talk to you here. When they tell you how to check - what to test, what to leave alone, where things",
|
|
132
142
|
"run - keep it with keep_guidance, in their words, so it holds from now on.",
|
|
133
|
-
...(
|
|
143
|
+
...(surface !== ""
|
|
144
|
+
? [
|
|
145
|
+
"",
|
|
146
|
+
"Where the person watches you, in the host's words:",
|
|
147
|
+
"<surface>",
|
|
148
|
+
surface,
|
|
149
|
+
"</surface>",
|
|
150
|
+
`Try a release with ${theirs}, where the person can watch. Keep your shell for what those cannot show - logs, exit`,
|
|
151
|
+
"codes, files.",
|
|
152
|
+
]
|
|
153
|
+
: tools.length > 0
|
|
154
|
+
? ["", `The host gives you ${theirs} to test with as well.`]
|
|
155
|
+
: []),
|
|
134
156
|
"</sentinel>",
|
|
135
157
|
...(guidance !== "" ? ["", "<guidance>", "What the person told you to keep in mind:", guidance, "</guidance>"] : []),
|
|
136
158
|
].join("\n");
|
package/docs/hosting.md
CHANGED
|
@@ -86,7 +86,10 @@ const turri = await createTurri({
|
|
|
86
86
|
onNotice: (notice) => {}, // the sentinel puts something to the person: draw it in notice.chat
|
|
87
87
|
onNoticeOver: (notice) => {}, // ...answered, dismissed or superseded: take its card down
|
|
88
88
|
onConcluded: (conclusion) => {}, // the sentinel is done with a pulse: holds or wrong, told to conclusion.chat
|
|
89
|
-
sentinel: {
|
|
89
|
+
sentinel: {
|
|
90
|
+
tools: [browserTool], // the sentinel's own tools, beside turri's: what it tests a release with
|
|
91
|
+
surface: "...", // where the person watches it use them, in the host's words
|
|
92
|
+
},
|
|
90
93
|
});
|
|
91
94
|
|
|
92
95
|
const chat = await turri.openChat(); // { id, title, status, shown, session }: a fresh chat, shown
|
|
@@ -123,6 +126,7 @@ The [sentinel](sentinel.md) is one of the host's chats: turri opens it with the
|
|
|
123
126
|
- **Conclusions.** As the sentinel is done with each pulse - all well or not - `onConcluded({ job, verdict: "holds" | "wrong", summary, module, pulse, chat })` comes, and the chat `chat` names - the one `focus` last named, as for a notice - shows it and its agent says so in a line.
|
|
124
127
|
- **Its model.** The `sentinel` role in `models:`, else the chat's model - `models.chat`, else `harness-model` - with its thinking, set as its chat opens: not pi's default model, which may not be one the project's provider has. What the person then picks in its chat stands. See [its model](sentinel.md#its-model).
|
|
125
128
|
- **Its tools.** `sentinel: { tools }` are [pi](https://pi.dev) tool definitions registered in the sentinel's chat alone and told to it in its prompt - the host's browser, say, for it to test a release with. The rest of the host's `extensions` are built into its chat too, but their tools are refused it: only its own read, and these.
|
|
129
|
+
- **Its surface.** `sentinel: { surface }` is where the person watches the sentinel use those tools, in the host's own words - the preview pane its browser and terminal show in, say. Given, the sentinel's prompt has it, as a `<surface>` block, in place of the person not watching it: it tries a release with the host's tools, where the person can watch, and keeps its own shell for what those 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. As for the conversation, `systemPrompt` has the last word on its prompt.
|
|
126
130
|
- **Its surface.** What it is told is its own prompt, not the conversation's: the host's `surface` is not in it; `systemPrompt` still has the last word.
|
|
127
131
|
|
|
128
132
|
A surface for a desktop app might read:
|
package/docs/sentinel.md
CHANGED
|
@@ -47,7 +47,7 @@ The `sentinel` role in `models:` - else the chat's model, `models.chat` else `ha
|
|
|
47
47
|
|
|
48
48
|
## In a host
|
|
49
49
|
|
|
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. `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. See [running turri in a host](hosting.md#the-sentinel).
|
|
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. `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
51
|
|
|
52
52
|
## In the ledger
|
|
53
53
|
|