@turri-dev/cli 0.1.16 → 0.1.17-next.63f454c
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/ask.d.ts +2 -0
- package/dist/ask.js +2 -0
- package/dist/chat-commands.d.ts +25 -0
- package/dist/chat-commands.js +162 -0
- package/dist/chat-records.d.ts +44 -0
- package/dist/chat-records.js +102 -0
- package/dist/chat-scope.d.ts +14 -0
- package/dist/chat-scope.js +15 -0
- package/dist/chat-terminal.d.ts +23 -0
- package/dist/chat-terminal.js +163 -0
- package/dist/chat-ui.d.ts +33 -0
- package/dist/chat-ui.js +192 -0
- package/dist/chats.d.ts +192 -0
- package/dist/chats.js +378 -0
- package/dist/cli.js +5 -7
- package/dist/config.d.ts +8 -2
- package/dist/config.js +18 -4
- package/dist/extension.d.ts +20 -33
- package/dist/extension.js +427 -272
- package/dist/genome.d.ts +13 -7
- package/dist/genome.js +13 -3
- package/dist/host-session.d.ts +58 -0
- package/dist/host-session.js +72 -0
- package/dist/host.d.ts +5 -4
- package/dist/host.js +6 -5
- package/dist/identity.js +5 -0
- package/dist/pi-launch.js +23 -1
- package/dist/question-dialog.d.ts +4 -0
- package/dist/question-dialog.js +13 -2
- package/dist/regenerate/activity.d.ts +8 -1
- package/dist/regenerate/activity.js +23 -2
- package/dist/regenerate/jobs.d.ts +2 -0
- package/dist/regenerate/jobs.js +3 -0
- package/dist/regenerate/ledger.d.ts +7 -0
- package/dist/regenerate/ledger.js +8 -1
- package/dist/regenerate/test-scripts.d.ts +1 -1
- package/dist/stacks.d.ts +2 -1
- package/dist/stacks.js +4 -3
- package/docs/README.md +4 -2
- package/docs/chats.md +75 -0
- package/docs/config.md +3 -3
- package/docs/development.md +4 -1
- package/docs/hosting.md +37 -28
- package/docs/skills-and-gates.md +2 -2
- package/docs/using.md +2 -2
- package/package.json +1 -1
package/dist/chat-ui.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/** Notices kept for a hidden chat: the most recent. */
|
|
2
|
+
const NOTICES_KEPT = 10;
|
|
3
|
+
export function createChatUi(options) {
|
|
4
|
+
let screen;
|
|
5
|
+
let lastScreen;
|
|
6
|
+
const waiting = [];
|
|
7
|
+
let asking = false;
|
|
8
|
+
let notices = [];
|
|
9
|
+
let draft = "";
|
|
10
|
+
// What the chat keeps on the screen, to put back each time it is shown.
|
|
11
|
+
const widgets = new Map();
|
|
12
|
+
const statuses = new Map();
|
|
13
|
+
const kept = new Map();
|
|
14
|
+
const inputHandlers = new Map();
|
|
15
|
+
const providers = [];
|
|
16
|
+
const any = () => screen ?? lastScreen ?? options.screen();
|
|
17
|
+
const call = (name, args) => screen[name](...args);
|
|
18
|
+
/** Its questions, asked one after another while it is shown. */
|
|
19
|
+
const askWaiting = async () => {
|
|
20
|
+
if (asking)
|
|
21
|
+
return;
|
|
22
|
+
asking = true;
|
|
23
|
+
try {
|
|
24
|
+
while (screen !== undefined && waiting.length > 0) {
|
|
25
|
+
const next = waiting.shift();
|
|
26
|
+
options.onChange?.();
|
|
27
|
+
try {
|
|
28
|
+
next.resolve(await next.ask(screen));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
next.reject(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
asking = false;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const dialog = (name, none) => (...args) => {
|
|
40
|
+
if (screen !== undefined && waiting.length === 0)
|
|
41
|
+
return call(name, args);
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
waiting.push({ ask: (shown) => shown[name](...args), resolve, reject, none });
|
|
44
|
+
options.onChange?.();
|
|
45
|
+
void askWaiting();
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
// What it keeps on the screen: written down, and drawn when it is shown.
|
|
49
|
+
const keep = (name) => (...args) => {
|
|
50
|
+
kept.set(name, args);
|
|
51
|
+
if (screen !== undefined)
|
|
52
|
+
call(name, args);
|
|
53
|
+
};
|
|
54
|
+
const overrides = {
|
|
55
|
+
select: dialog("select", undefined),
|
|
56
|
+
confirm: dialog("confirm", false),
|
|
57
|
+
input: dialog("input", undefined),
|
|
58
|
+
editor: dialog("editor", undefined),
|
|
59
|
+
custom: dialog("custom", undefined),
|
|
60
|
+
notify: (message, type) => {
|
|
61
|
+
if (screen !== undefined)
|
|
62
|
+
screen.notify(message, type);
|
|
63
|
+
else
|
|
64
|
+
notices = [...notices, [message, type]].slice(-NOTICES_KEPT);
|
|
65
|
+
},
|
|
66
|
+
setWidget: (key, ...rest) => {
|
|
67
|
+
if (rest[0] === undefined)
|
|
68
|
+
widgets.delete(key);
|
|
69
|
+
else
|
|
70
|
+
widgets.set(key, [key, ...rest]);
|
|
71
|
+
if (screen !== undefined)
|
|
72
|
+
call("setWidget", [key, ...rest]);
|
|
73
|
+
},
|
|
74
|
+
setStatus: (key, text) => {
|
|
75
|
+
if (text === undefined)
|
|
76
|
+
statuses.delete(key);
|
|
77
|
+
else
|
|
78
|
+
statuses.set(key, text);
|
|
79
|
+
if (screen !== undefined)
|
|
80
|
+
screen.setStatus(key, text);
|
|
81
|
+
},
|
|
82
|
+
setWorkingMessage: keep("setWorkingMessage"),
|
|
83
|
+
setWorkingVisible: keep("setWorkingVisible"),
|
|
84
|
+
setWorkingIndicator: keep("setWorkingIndicator"),
|
|
85
|
+
setHiddenThinkingLabel: keep("setHiddenThinkingLabel"),
|
|
86
|
+
setFooter: keep("setFooter"),
|
|
87
|
+
setHeader: keep("setHeader"),
|
|
88
|
+
setTitle: keep("setTitle"),
|
|
89
|
+
setEditorComponent: keep("setEditorComponent"),
|
|
90
|
+
onTerminalInput: (handler) => {
|
|
91
|
+
inputHandlers.set(handler, screen?.onTerminalInput(handler));
|
|
92
|
+
return () => {
|
|
93
|
+
inputHandlers.get(handler)?.();
|
|
94
|
+
inputHandlers.delete(handler);
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
addAutocompleteProvider: (factory) => {
|
|
98
|
+
providers.push(factory);
|
|
99
|
+
if (screen !== undefined)
|
|
100
|
+
call("addAutocompleteProvider", [factory]);
|
|
101
|
+
},
|
|
102
|
+
getEditorText: () => (screen !== undefined ? screen.getEditorText() : draft),
|
|
103
|
+
setEditorText: (text) => {
|
|
104
|
+
if (screen !== undefined)
|
|
105
|
+
screen.setEditorText(text);
|
|
106
|
+
else
|
|
107
|
+
draft = text;
|
|
108
|
+
},
|
|
109
|
+
pasteToEditor: (text) => {
|
|
110
|
+
if (screen !== undefined)
|
|
111
|
+
screen.pasteToEditor(text);
|
|
112
|
+
else
|
|
113
|
+
draft += text;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
// What the screen has besides, forwarded to the screen as it is when called: one function a name, kept.
|
|
117
|
+
const forwarders = new Map();
|
|
118
|
+
const read = (key) => {
|
|
119
|
+
if (typeof key === "string" && key in overrides)
|
|
120
|
+
return overrides[key];
|
|
121
|
+
const target = any();
|
|
122
|
+
if (target === undefined)
|
|
123
|
+
return undefined;
|
|
124
|
+
const value = Reflect.get(target, key);
|
|
125
|
+
if (typeof value !== "function")
|
|
126
|
+
return value;
|
|
127
|
+
let forward = forwarders.get(key);
|
|
128
|
+
if (forward === undefined) {
|
|
129
|
+
forward = (...args) => Reflect.get(any(), key).apply(any(), args);
|
|
130
|
+
forwarders.set(key, forward);
|
|
131
|
+
}
|
|
132
|
+
return forward;
|
|
133
|
+
};
|
|
134
|
+
const keys = () => [...new Set([...Object.keys(overrides), ...(any() !== undefined ? Reflect.ownKeys(any()) : [])])];
|
|
135
|
+
// pi copies the ui it is given - `{ ...ui }` - so every name is the stand-in's own, not only read through it.
|
|
136
|
+
const ui = new Proxy({}, {
|
|
137
|
+
get: (_target, key) => read(key),
|
|
138
|
+
has: (_target, key) => keys().includes(key),
|
|
139
|
+
ownKeys: () => keys().filter((key) => typeof key !== "number"),
|
|
140
|
+
getOwnPropertyDescriptor: (_target, key) => (keys().includes(key) ? { value: read(key), enumerable: true, configurable: true, writable: false } : undefined),
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
ui,
|
|
144
|
+
get shown() {
|
|
145
|
+
return screen !== undefined;
|
|
146
|
+
},
|
|
147
|
+
show(next) {
|
|
148
|
+
screen = next;
|
|
149
|
+
lastScreen = next;
|
|
150
|
+
for (const args of widgets.values())
|
|
151
|
+
call("setWidget", args);
|
|
152
|
+
for (const [key, text] of statuses)
|
|
153
|
+
next.setStatus(key, text);
|
|
154
|
+
for (const [name, args] of kept)
|
|
155
|
+
call(name, args);
|
|
156
|
+
for (const handler of inputHandlers.keys())
|
|
157
|
+
inputHandlers.set(handler, next.onTerminalInput(handler));
|
|
158
|
+
for (const factory of providers)
|
|
159
|
+
call("addAutocompleteProvider", [factory]);
|
|
160
|
+
if (draft !== "")
|
|
161
|
+
next.setEditorText(draft);
|
|
162
|
+
draft = "";
|
|
163
|
+
for (const [message, type] of notices)
|
|
164
|
+
next.notify(message, type);
|
|
165
|
+
notices = [];
|
|
166
|
+
options.onChange?.();
|
|
167
|
+
void askWaiting();
|
|
168
|
+
},
|
|
169
|
+
hide() {
|
|
170
|
+
if (screen === undefined)
|
|
171
|
+
return;
|
|
172
|
+
try {
|
|
173
|
+
draft = screen.getEditorText();
|
|
174
|
+
screen.setEditorText("");
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
draft = "";
|
|
178
|
+
}
|
|
179
|
+
// pi takes them off the screen as it shows another chat; they are put on again when this one is shown.
|
|
180
|
+
for (const handler of inputHandlers.keys())
|
|
181
|
+
inputHandlers.set(handler, undefined);
|
|
182
|
+
screen = undefined;
|
|
183
|
+
options.onChange?.();
|
|
184
|
+
},
|
|
185
|
+
waiting: () => waiting.length,
|
|
186
|
+
close() {
|
|
187
|
+
for (const next of waiting.splice(0))
|
|
188
|
+
next.resolve(next.none);
|
|
189
|
+
screen = undefined;
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
package/dist/chats.d.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { AgentSession, ExtensionAPI, ExtensionContext, ExtensionFactory, ExtensionUIContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type ChatScope } from "./chat-scope.ts";
|
|
3
|
+
import { type RunRecord } from "./chat-records.ts";
|
|
4
|
+
import { type ChatUi } from "./chat-ui.ts";
|
|
5
|
+
import type { AskUserIO } from "./ask.ts";
|
|
6
|
+
import type { Job, JobRegistry } from "./regenerate/jobs.ts";
|
|
7
|
+
/**
|
|
8
|
+
* The chats of a turri session.
|
|
9
|
+
*
|
|
10
|
+
* One turri process is one session on one project, and holds up to MAX_CHATS chats: conversations, each with its own
|
|
11
|
+
* history, model, thinking level, stack and questions, each working while another is shown. What they share is the
|
|
12
|
+
* session's: the one job registry and work loop the project's pulses run in - a pulse any chat starts is in every
|
|
13
|
+
* chat's side panel - the coding model, steering, releases and the ledger.
|
|
14
|
+
*
|
|
15
|
+
* A chat is a pi session with turri's extension in it. The extension, built once for each, links itself to its chat
|
|
16
|
+
* here (`attach`); its tools, commands and handlers then run inside the chat's scope (chat-scope.ts), so a pulse knows
|
|
17
|
+
* which chat asked for it, and is reported back there when it ends - to the chat shown, when that one is closed.
|
|
18
|
+
*
|
|
19
|
+
* Where the chats live is a driver's: the terminal's (chat-terminal.ts) swaps which one pi's screen shows; a host's
|
|
20
|
+
* (`createTurri`) makes a session for each and shows them in its own windows.
|
|
21
|
+
*/
|
|
22
|
+
/** The most chats a session has open at once. */
|
|
23
|
+
export declare const MAX_CHATS = 9;
|
|
24
|
+
/** What a chat is called until something is said in it. */
|
|
25
|
+
export declare const NEW_CHAT_TITLE = "new chat";
|
|
26
|
+
export type ChatStatus = "idle" | "working" | "question" | "unread";
|
|
27
|
+
export interface ChatInfo {
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
/** What it is doing: a turn running, a question waiting for it to be shown, something said since it was last shown. */
|
|
31
|
+
status: ChatStatus;
|
|
32
|
+
/** On the screen now - the terminal's, or the window the host said it shows (`focus`). */
|
|
33
|
+
shown: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** What turri tells and asks a host that runs its conversation itself. */
|
|
36
|
+
export interface TurriHost {
|
|
37
|
+
/** A job started, moved to another stage, did something, or ended. */
|
|
38
|
+
onJobChange?(job: Job): void;
|
|
39
|
+
/** What is planned changed with no job - a module being planned, or planned. */
|
|
40
|
+
onPlanChange?(): void;
|
|
41
|
+
/** The dialogs ask_user puts its questions in - the conversation's and a skill's interview - instead of the terminal's. */
|
|
42
|
+
dialogs?: AskUserIO;
|
|
43
|
+
/**
|
|
44
|
+
* The host's own UI, in its own words: the views it has in place of the terminal's, what its marks and buttons mean,
|
|
45
|
+
* where questions appear. Given, turri tells the model none of the terminal's - its keys, the side panel, regen,
|
|
46
|
+
* /pulses - and gives it this instead, as a `<surface>` block in turri's identity. Absent, the terminal is the surface.
|
|
47
|
+
*/
|
|
48
|
+
surface?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The last word on the whole system prompt, as turri has built it for this turn - after its own before_agent_start
|
|
51
|
+
* work: what it returns is what is sent. For a host that needs to change anything else.
|
|
52
|
+
*/
|
|
53
|
+
systemPrompt?(prompt: string): string;
|
|
54
|
+
}
|
|
55
|
+
/** What a chat's extension does for its chat when the session asks. */
|
|
56
|
+
export interface ChatInstance {
|
|
57
|
+
/** A pulse it asked for ended: said in the chat, and its agent told. */
|
|
58
|
+
announce(job: Job): void;
|
|
59
|
+
/** The jobs changed: what it draws of them is drawn again. */
|
|
60
|
+
jobsChanged(): void;
|
|
61
|
+
/** How hard the chat's model thinks now: sub-agents started from it think as hard. */
|
|
62
|
+
thinkingLevel(): string | undefined;
|
|
63
|
+
/** The chat's context, as its tools get it - for its model and its providers. */
|
|
64
|
+
context(): ExtensionContext | undefined;
|
|
65
|
+
}
|
|
66
|
+
/** A chat's extension, linked to its chat. */
|
|
67
|
+
export interface ChatLink {
|
|
68
|
+
readonly id: string;
|
|
69
|
+
readonly scope: ChatScope;
|
|
70
|
+
/** The host's dialogs, for this chat's questions - undefined in the terminal. */
|
|
71
|
+
readonly dialogs: AskUserIO | undefined;
|
|
72
|
+
/** The extension built for the chat - again, when pi rebuilds it - and what it does for it. */
|
|
73
|
+
live(instance: ChatInstance): void;
|
|
74
|
+
}
|
|
75
|
+
/** Where chats live: the terminal's screen, or a host's windows. */
|
|
76
|
+
export interface ChatDriver {
|
|
77
|
+
/** pi's terminal: the chats are switched between with its keys and /chat. */
|
|
78
|
+
terminal?: boolean;
|
|
79
|
+
/** A session for the chat - its saved one, when `sessionFile` - with the session's extension built for it. */
|
|
80
|
+
create(chat: ChatState, sessionFile: string | undefined): Promise<void>;
|
|
81
|
+
/** Put the chat on the screen. */
|
|
82
|
+
show?(chat: ChatState, from: ChatState | undefined): Promise<void>;
|
|
83
|
+
/** The chat is closed or the session ends: its session let go. */
|
|
84
|
+
dispose(chat: ChatState): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
/** A chat, as the session keeps it. */
|
|
87
|
+
export interface ChatState {
|
|
88
|
+
id: string;
|
|
89
|
+
scope: ChatScope;
|
|
90
|
+
/** Its title as recorded, until it says one of its own. */
|
|
91
|
+
recordedTitle?: string;
|
|
92
|
+
session?: AgentSession;
|
|
93
|
+
/** pi's services the session was made with - the terminal's driver hands them to pi with it. */
|
|
94
|
+
services?: unknown;
|
|
95
|
+
/** What it draws on, in the terminal. */
|
|
96
|
+
ui?: ChatUi;
|
|
97
|
+
instance?: ChatInstance;
|
|
98
|
+
dialogs?: AskUserIO;
|
|
99
|
+
working: boolean;
|
|
100
|
+
unread: boolean;
|
|
101
|
+
unsubscribe?: () => void;
|
|
102
|
+
}
|
|
103
|
+
export interface TurriCore {
|
|
104
|
+
readonly cwd: string;
|
|
105
|
+
readonly host: TurriHost;
|
|
106
|
+
readonly jobs: JobRegistry;
|
|
107
|
+
/** turri's extension, for pi to build into each chat's session. */
|
|
108
|
+
readonly extension: ExtensionFactory;
|
|
109
|
+
/** Whether chats can be opened beside the one there is - with a driver that has somewhere to put them. */
|
|
110
|
+
readonly canOpen: boolean;
|
|
111
|
+
/** Whether the chats are the terminal's, switched between with /chat and its keys. */
|
|
112
|
+
readonly terminal: boolean;
|
|
113
|
+
chats(): ChatInfo[];
|
|
114
|
+
/** The chat's pi session. */
|
|
115
|
+
session(id: string): AgentSession | undefined;
|
|
116
|
+
/** The chat shown now. */
|
|
117
|
+
shown(): string | undefined;
|
|
118
|
+
/** Open a chat - fresh, or the saved one `sessionFile` is - shown at once unless `hidden`. */
|
|
119
|
+
open(options?: {
|
|
120
|
+
sessionFile?: string;
|
|
121
|
+
hidden?: boolean;
|
|
122
|
+
title?: string;
|
|
123
|
+
}): Promise<ChatInfo>;
|
|
124
|
+
close(id: string): Promise<void>;
|
|
125
|
+
/** Show the chat - in the terminal, put it on the screen; for a host, the one its person looks at now. */
|
|
126
|
+
show(id: string): Promise<void>;
|
|
127
|
+
onChats(listener: () => void): () => void;
|
|
128
|
+
/** For a driver: something about the chats changed - a question waiting, one shown - and those listening are told. */
|
|
129
|
+
changed(): void;
|
|
130
|
+
/** Said in the chat shown - or the first one shown, when none is yet. */
|
|
131
|
+
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
132
|
+
/** Every chat let go, but the one pi is letting go itself. */
|
|
133
|
+
dispose(options?: {
|
|
134
|
+
except?: AgentSession;
|
|
135
|
+
}): Promise<void>;
|
|
136
|
+
/** For the extension: the chat a pi API is built for (see ChatLink). */
|
|
137
|
+
attach(pi: ExtensionAPI): ChatLink;
|
|
138
|
+
/** For a driver: the chat whose session this is, or none. */
|
|
139
|
+
chatOfSession(session: AgentSession): ChatState | undefined;
|
|
140
|
+
/** For a driver: the chat shown now, as the session keeps it. */
|
|
141
|
+
shownChat(): ChatState | undefined;
|
|
142
|
+
/** For the terminal's driver: the chat made by pi itself, its session now known - the first, or one pi rebuilt. */
|
|
143
|
+
adopt(session: AgentSession, services: unknown): ChatState;
|
|
144
|
+
/** For a driver: `chat` is on the screen now. */
|
|
145
|
+
showing(chat: ChatState): void;
|
|
146
|
+
/** For the extension: a pulse ended - told to the chat that asked for it. */
|
|
147
|
+
jobFinished(job: Job): void;
|
|
148
|
+
/** For the extension: the jobs changed - every chat draws them again. */
|
|
149
|
+
jobsChanged(): void;
|
|
150
|
+
/** The chat whose work this is, else the one shown: its instance. */
|
|
151
|
+
instanceInScope(): ChatInstance | undefined;
|
|
152
|
+
setDriver(driver: ChatDriver): void;
|
|
153
|
+
/** Reopen a run's chats - `--continue` - beside the first, which pi reopened itself. */
|
|
154
|
+
restore(run: RunRecord, options?: {
|
|
155
|
+
skipFirst?: boolean;
|
|
156
|
+
}): Promise<void>;
|
|
157
|
+
}
|
|
158
|
+
export interface CoreOptions {
|
|
159
|
+
cwd: string;
|
|
160
|
+
host: TurriHost;
|
|
161
|
+
jobs: JobRegistry;
|
|
162
|
+
/** The extension, as it builds itself into a chat. */
|
|
163
|
+
extension: (pi: ExtensionAPI, link: ChatLink) => void;
|
|
164
|
+
/** Record the open chats in .turri/chats.json - not for pi's print, json and rpc modes. */
|
|
165
|
+
persist?: boolean;
|
|
166
|
+
/** The host's dialogs, a chat's own. */
|
|
167
|
+
dialogs?: (chat: string) => AskUserIO | undefined;
|
|
168
|
+
}
|
|
169
|
+
/** A chat's session, as stopping what it does reaches it. */
|
|
170
|
+
export type Stoppable = Pick<AgentSession, "isStreaming" | "isCompacting" | "isRetrying" | "isBashRunning" | "abort" | "abortBash">;
|
|
171
|
+
/** A second interrupt this soon after one that stopped something stops nothing more and ends nothing: one pressed twice to be sure. */
|
|
172
|
+
export declare const INTERRUPT_SETTLE_MS = 500;
|
|
173
|
+
/**
|
|
174
|
+
* ctrl+c in a chat, the terminal's or a host's: what the chat is doing stopped - its turn, a skill or a tool in it, a
|
|
175
|
+
* `!` command, a compaction, a retry - and nothing in the other chats. `stopTurn` ends its turn: pi's own esc in the
|
|
176
|
+
* terminal. A chat doing nothing gets `idle`'s answer - the session ended - unless the last interrupt, at `lastStop`,
|
|
177
|
+
* stopped something a moment ago: then it is `ignored`.
|
|
178
|
+
*/
|
|
179
|
+
export declare function interruptChat<R>(session: Stoppable, asked: {
|
|
180
|
+
lastStop: number;
|
|
181
|
+
now?: number;
|
|
182
|
+
stopTurn?: () => void;
|
|
183
|
+
idle: () => R;
|
|
184
|
+
}): "stopped" | "ignored" | R;
|
|
185
|
+
export declare function createChats(options: CoreOptions): TurriCore;
|
|
186
|
+
/**
|
|
187
|
+
* pi's extension API for a chat: every tool, command, shortcut and handler the extension registers runs inside the
|
|
188
|
+
* chat's scope - its stack, and its id on what it starts.
|
|
189
|
+
*/
|
|
190
|
+
export declare function scopedToChat(pi: ExtensionAPI, scope: ChatScope): ExtensionAPI;
|
|
191
|
+
/** A chat's stand-in for the screen, for the terminal's driver. */
|
|
192
|
+
export declare function terminalUi(chat: ChatState, screen: () => ExtensionUIContext | undefined, onChange: () => void): ChatUi;
|