@tomradex/plugin-sdk 0.2.0 → 0.2.16
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 -1
- package/dist/cli-hooks.d.ts +8 -0
- package/dist/cli-hooks.js +27 -0
- package/dist/contract.d.ts +292 -9
- package/dist/exports.d.ts +10 -0
- package/dist/exports.js +73 -0
- package/dist/im-markdown.d.ts +10 -0
- package/dist/im-markdown.js +257 -0
- package/dist/im.d.ts +319 -0
- package/dist/im.js +55 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +9 -1
- package/dist/language.d.ts +48 -0
- package/dist/language.js +62 -0
- package/dist/manifest.d.ts +78 -10
- package/dist/manifest.js +80 -13
- package/dist/models.d.ts +219 -0
- package/dist/models.js +2 -0
- package/dist/terminals.d.ts +72 -0
- package/dist/terminals.js +2 -0
- package/dist/web-search.d.ts +54 -0
- package/dist/web-search.js +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -60,4 +60,4 @@ export default definePlugin({
|
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Declare what your code does beyond the SDK — the hosts it talks to, whether it touches the disk, whether it starts programs.
|
|
63
|
+
Declare what your code does beyond the SDK — the hosts it talks to, whether it touches the disk, whether it starts programs. Whoever installs it sees it beside what the analyzer finds in the code. Community lists open-source plugins only, published from your public GitHub repository; nobody reviews them, so say plainly what yours does.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** SDK 0.2.15: what a CLI agent's hook says happened, in one set of names whichever agent sent it. */
|
|
2
|
+
export type CliHookEvent = "session-start" | "prompt" | "pre-tool" | "post-tool" | "stop" | "session-end";
|
|
3
|
+
export declare const CLI_HOOK_EVENTS: readonly CliHookEvent[];
|
|
4
|
+
export declare const CLI_HOOK_COVERAGE: Readonly<Record<CliHookEvent, {
|
|
5
|
+
answers: readonly string[];
|
|
6
|
+
sees: readonly string[];
|
|
7
|
+
}>>;
|
|
8
|
+
//# sourceMappingURL=cli-hooks.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const CLI_HOOK_EVENTS = ["session-start", "prompt", "pre-tool", "post-tool", "stop", "session-end"];
|
|
2
|
+
/** The agents whose hooks reach Tomrade at all: in sessions it starts, or in repositories it connects. */
|
|
3
|
+
const REPORTING = ["claude-code", "workbuddy", "dsh", "pi", "codex", "cursor", "opencode", "hermes"];
|
|
4
|
+
const ANSWERS = {
|
|
5
|
+
"session-start": ["claude-code", "workbuddy", "dsh"],
|
|
6
|
+
prompt: ["claude-code", "workbuddy", "dsh"],
|
|
7
|
+
"pre-tool": ["claude-code", "workbuddy", "dsh", "pi"],
|
|
8
|
+
"post-tool": ["claude-code", "workbuddy", "dsh", "pi"],
|
|
9
|
+
stop: ["claude-code", "workbuddy", "dsh"],
|
|
10
|
+
"session-end": []
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Which agents a CLI hook reaches. `answers`: in a session Tomrade started, the
|
|
14
|
+
* agent acts on the answer. `sees`: the hook hears the event — a connected
|
|
15
|
+
* repository's, or one the agent cannot be answered on — and its answer goes
|
|
16
|
+
* nowhere. Gemini CLI and Antigravity report no hooks at all.
|
|
17
|
+
*/
|
|
18
|
+
const reach = (event) => ({ answers: ANSWERS[event], sees: REPORTING.filter((agent) => !ANSWERS[event].includes(agent)) });
|
|
19
|
+
export const CLI_HOOK_COVERAGE = {
|
|
20
|
+
"session-start": reach("session-start"),
|
|
21
|
+
prompt: reach("prompt"),
|
|
22
|
+
"pre-tool": reach("pre-tool"),
|
|
23
|
+
"post-tool": reach("post-tool"),
|
|
24
|
+
stop: reach("stop"),
|
|
25
|
+
"session-end": reach("session-end")
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=cli-hooks.js.map
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
+
import type { ImApi } from "./im.js";
|
|
3
|
+
import type { TerminalsApi } from "./terminals.js";
|
|
4
|
+
import type { WebSearchApi } from "./web-search.js";
|
|
5
|
+
import type { ModelsApi } from "./models.js";
|
|
2
6
|
import type { Author, Board, CreateItemInput, FieldDef, FieldOption, FieldType, FilterClause, Item, ListItemsFilter, OplogEntity, OplogEntry, OplogOp, Patch, Project, SortKey, View } from "@tomradex/domain";
|
|
3
7
|
/**
|
|
4
8
|
* The plugin contract. Everything a plugin reaches returns a Promise, and every
|
|
@@ -7,8 +11,24 @@ import type { Author, Board, CreateItemInput, FieldDef, FieldOption, FieldType,
|
|
|
7
11
|
* plain data — never a live object of the host's.
|
|
8
12
|
*/
|
|
9
13
|
export type Disposer = () => void;
|
|
14
|
+
/** A language the app may be speaking: a BCP 47 tag such as "en", "zh", "zh-Hant" or one a language pack adds ("pt-BR"). SDK 0.2.8 widened it from "en" | "zh". */
|
|
15
|
+
export type Locale = string;
|
|
16
|
+
/** Text in the app's language: a plain string, or one per language tag picked each time it is shown (English is the fallback). */
|
|
17
|
+
export type Localized = string | ({
|
|
18
|
+
en: string;
|
|
19
|
+
} & Partial<Record<Locale, string>>);
|
|
20
|
+
/** A settings section's text in one language; fields are keyed by schema property. */
|
|
21
|
+
export interface SectionWords {
|
|
22
|
+
title?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
fields?: Record<string, {
|
|
25
|
+
title?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
options?: Record<string, string>;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
10
30
|
/** The services a plugin names in `inject` (required) or `optional`. */
|
|
11
|
-
export type ServiceName = "items" | "fields" | "boards" | "views" | "projects" | "db" | "settings" | "tools" | "contributions" | "agent" | "surfaces" | "oplog";
|
|
31
|
+
export type ServiceName = "items" | "fields" | "boards" | "views" | "projects" | "db" | "settings" | "tools" | "contributions" | "agent" | "surfaces" | "alerts" | "oplog" | "im" | "webSearch" | "terminals" | "models";
|
|
12
32
|
export interface Logger {
|
|
13
33
|
debug(...args: unknown[]): void;
|
|
14
34
|
info(...args: unknown[]): void;
|
|
@@ -37,6 +57,30 @@ export interface ItemsApi {
|
|
|
37
57
|
addRelation(projectId: string, id: string, blockedId: string): Promise<void>;
|
|
38
58
|
removeRelation(projectId: string, id: string, blockedId: string): Promise<void>;
|
|
39
59
|
labels(projectId: string): Promise<string[]>;
|
|
60
|
+
/**
|
|
61
|
+
* SDK 0.2.12: a policy over every write before it lands — items, and the workstream and view
|
|
62
|
+
* writes that take the same path — whoever writes. Throw to refuse it. It is asked inside the
|
|
63
|
+
* write's transaction, so it must answer at once: a check that returns a Promise is not waited
|
|
64
|
+
* for, and the write goes on.
|
|
65
|
+
*/
|
|
66
|
+
gate(check: (write: PendingWrite) => void): Promise<Disposer>;
|
|
67
|
+
}
|
|
68
|
+
/** SDK 0.2.12: a write about to land, as a write gate sees it. A copy: changing it changes nothing. */
|
|
69
|
+
export interface PendingWrite<T = unknown> {
|
|
70
|
+
projectId: string;
|
|
71
|
+
entity: OplogEntity;
|
|
72
|
+
op: OplogOp;
|
|
73
|
+
id: string;
|
|
74
|
+
by: Author;
|
|
75
|
+
/** The extension making the write, when one is. */
|
|
76
|
+
via: string | null;
|
|
77
|
+
runId: string | null;
|
|
78
|
+
/** The version the writer read, when it asserts one. */
|
|
79
|
+
baseVersion: number | null;
|
|
80
|
+
before: T | null;
|
|
81
|
+
after: T | null;
|
|
82
|
+
/** For an update: only the fields the writer asked to change. */
|
|
83
|
+
patch: Record<string, unknown> | null;
|
|
40
84
|
}
|
|
41
85
|
export interface FieldsApi {
|
|
42
86
|
list(projectId: string): Promise<FieldDef[]>;
|
|
@@ -82,17 +126,21 @@ export interface TableSpec {
|
|
|
82
126
|
via: string;
|
|
83
127
|
table: string;
|
|
84
128
|
};
|
|
85
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Exported tables travel in the project's folder; `false` keeps them on this machine.
|
|
131
|
+
* `private` rows (conversations, say) never travel to a remote known to be public.
|
|
132
|
+
*/
|
|
86
133
|
export: {
|
|
87
134
|
order: string;
|
|
88
135
|
appendOnly?: boolean;
|
|
89
136
|
always?: boolean;
|
|
137
|
+
private?: boolean;
|
|
90
138
|
} | false;
|
|
91
139
|
}
|
|
92
140
|
/**
|
|
93
141
|
* Plain SQL against the caches. The namespace is the plugin's own: tables are
|
|
94
142
|
* `plugin_<namespace>_*`. Reads of the app's tables are allowed; writing them
|
|
95
|
-
* goes around history and undo, and
|
|
143
|
+
* goes around history and undo, and the install dialog says so.
|
|
96
144
|
*/
|
|
97
145
|
export interface DbApi {
|
|
98
146
|
migrate(steps: {
|
|
@@ -122,14 +170,23 @@ export interface SettingsSectionSpec<T> {
|
|
|
122
170
|
id?: string;
|
|
123
171
|
title: string;
|
|
124
172
|
description?: string;
|
|
173
|
+
/** Drawn from its JSON Schema; a field with `.meta({ advanced: true })` is folded under Advanced. */
|
|
125
174
|
schema: z.ZodType<T>;
|
|
126
175
|
defaults: T;
|
|
127
176
|
scope?: "machine" | "user";
|
|
177
|
+
/** What agents may do with it through get_settings / update_settings. Defaults to read; a section holding a password is none. */
|
|
178
|
+
agent?: "write" | "read" | "none";
|
|
179
|
+
/** The card in other languages; `title`, `description` and the schema's own labels are the English. */
|
|
180
|
+
words?: Partial<Record<Locale, SectionWords>>;
|
|
128
181
|
}
|
|
129
182
|
export interface SettingsApi {
|
|
130
183
|
register<T>(section: SettingsSectionSpec<T>): Promise<void>;
|
|
131
184
|
get<T = unknown>(id?: string): Promise<T>;
|
|
132
185
|
set<T = unknown>(value: T, id?: string): Promise<void>;
|
|
186
|
+
/** Opens the app's Settings on this plugin's section, bringing the window forward — for a click outside the window. */
|
|
187
|
+
show(id?: string): Promise<void>;
|
|
188
|
+
/** The app's language now. It is a setting: `settings.changed` fires when the person switches it. */
|
|
189
|
+
locale(): Promise<Locale>;
|
|
133
190
|
}
|
|
134
191
|
export interface ToolInvocation {
|
|
135
192
|
projectId?: string;
|
|
@@ -143,6 +200,8 @@ export interface ToolSpec<S extends z.ZodObject = z.ZodObject> {
|
|
|
143
200
|
title: string;
|
|
144
201
|
description: string;
|
|
145
202
|
input: S;
|
|
203
|
+
/** A kit few conversations need: the tool is left out of each request until the model loads the kit. Named like the tool. */
|
|
204
|
+
kit?: string;
|
|
146
205
|
execute(args: z.output<S>, invocation: ToolInvocation): unknown | Promise<unknown>;
|
|
147
206
|
}
|
|
148
207
|
export interface ToolCallInfo {
|
|
@@ -154,6 +213,8 @@ export interface ToolCallInfo {
|
|
|
154
213
|
}
|
|
155
214
|
export interface ToolsApi {
|
|
156
215
|
register<S extends z.ZodObject>(tool: ToolSpec<S>): Promise<Disposer>;
|
|
216
|
+
/** What a kit is for, in a few words the model reads before it loads the kit ("a focus timer"). */
|
|
217
|
+
kit(id: string, purpose: string): Promise<Disposer>;
|
|
157
218
|
list(): Promise<{
|
|
158
219
|
name: string;
|
|
159
220
|
title: string;
|
|
@@ -163,9 +224,18 @@ export interface ToolsApi {
|
|
|
163
224
|
call(name: string, args: unknown, opts?: {
|
|
164
225
|
projectId?: string;
|
|
165
226
|
}): Promise<unknown>;
|
|
166
|
-
/**
|
|
167
|
-
|
|
227
|
+
/**
|
|
228
|
+
* A policy over every tool call: throw to refuse it. SDK 0.2.12: or answer `{ args }` to go on with
|
|
229
|
+
* other arguments (checked against the tool's input again), or `{ result }` to answer in the tool's place.
|
|
230
|
+
*/
|
|
231
|
+
gate(check: (call: ToolCallInfo) => ToolGateAnswer | void | Promise<ToolGateAnswer | void>): Promise<Disposer>;
|
|
168
232
|
}
|
|
233
|
+
/** SDK 0.2.12: what a tool gate may answer besides letting the call through unchanged. `result` wins over `args`. */
|
|
234
|
+
export type ToolGateAnswer = {
|
|
235
|
+
args: unknown;
|
|
236
|
+
} | {
|
|
237
|
+
result: unknown;
|
|
238
|
+
};
|
|
169
239
|
export type BadgeTone = "faint" | "neutral" | "strong";
|
|
170
240
|
export interface Badge {
|
|
171
241
|
id: string;
|
|
@@ -189,6 +259,15 @@ export interface SectionRow {
|
|
|
189
259
|
href?: string;
|
|
190
260
|
command?: string;
|
|
191
261
|
}
|
|
262
|
+
export interface SidebarRow {
|
|
263
|
+
id: string;
|
|
264
|
+
label: string;
|
|
265
|
+
icon?: string;
|
|
266
|
+
detail?: string;
|
|
267
|
+
count?: number | string;
|
|
268
|
+
command?: string;
|
|
269
|
+
href?: string;
|
|
270
|
+
}
|
|
192
271
|
export interface GraphEdge {
|
|
193
272
|
from: string;
|
|
194
273
|
to: string;
|
|
@@ -200,8 +279,8 @@ type Maybe<T> = T | Promise<T>;
|
|
|
200
279
|
export interface ContributionsApi {
|
|
201
280
|
command(def: {
|
|
202
281
|
id: string;
|
|
203
|
-
title:
|
|
204
|
-
group?:
|
|
282
|
+
title: Localized;
|
|
283
|
+
group?: Localized;
|
|
205
284
|
shortcut?: string;
|
|
206
285
|
run(scope: CommandScope): Maybe<{
|
|
207
286
|
toast?: string;
|
|
@@ -213,15 +292,36 @@ export interface ContributionsApi {
|
|
|
213
292
|
projectId: string;
|
|
214
293
|
}): Maybe<Record<string, Badge[]>>;
|
|
215
294
|
}): Promise<Disposer>;
|
|
295
|
+
/**
|
|
296
|
+
* A status bar item. `command` (in the plugin's namespace) runs on click, or `menu` lists actions the click
|
|
297
|
+
* opens instead; `until` returns an epoch ms the renderer counts down to after the text, without polling.
|
|
298
|
+
* Everything is re-read when `refresh()` is called.
|
|
299
|
+
*/
|
|
216
300
|
statusItem(def: {
|
|
217
301
|
id: string;
|
|
218
302
|
text(scope: {
|
|
219
303
|
projectId?: string;
|
|
220
304
|
}): Maybe<string | null>;
|
|
305
|
+
title?: Localized;
|
|
306
|
+
icon?: string;
|
|
307
|
+
align?: "start" | "end";
|
|
308
|
+
command?: string;
|
|
309
|
+
until?(scope: {
|
|
310
|
+
projectId?: string;
|
|
311
|
+
}): Maybe<number | null>;
|
|
312
|
+
menu?(scope: {
|
|
313
|
+
projectId?: string;
|
|
314
|
+
}): Maybe<{
|
|
315
|
+
title: string;
|
|
316
|
+
command: string;
|
|
317
|
+
icon?: string;
|
|
318
|
+
}[] | null>;
|
|
221
319
|
}): Promise<Disposer>;
|
|
320
|
+
/** Every window re-reads this plugin's contributions — for state that changed outside a command run. */
|
|
321
|
+
refresh(): Promise<void>;
|
|
222
322
|
section(def: {
|
|
223
323
|
id: string;
|
|
224
|
-
title:
|
|
324
|
+
title: Localized;
|
|
225
325
|
render(item: Item): Maybe<{
|
|
226
326
|
rows?: SectionRow[];
|
|
227
327
|
text?: string;
|
|
@@ -236,6 +336,16 @@ export interface ContributionsApi {
|
|
|
236
336
|
projectId: string;
|
|
237
337
|
}): Maybe<GraphEdge[]>;
|
|
238
338
|
}): Promise<Disposer>;
|
|
339
|
+
/** A module in the project sidebar. Without `rows` it is a single door; rows returning null hide it in that project. */
|
|
340
|
+
sidebarModule(def: {
|
|
341
|
+
id: string;
|
|
342
|
+
title: Localized;
|
|
343
|
+
icon?: string;
|
|
344
|
+
command?: string;
|
|
345
|
+
rows?(scope: {
|
|
346
|
+
projectId: string;
|
|
347
|
+
}): Maybe<SidebarRow[] | null>;
|
|
348
|
+
}): Promise<Disposer>;
|
|
239
349
|
}
|
|
240
350
|
export interface PlaybookSpec {
|
|
241
351
|
/** Must start with the namespace. */
|
|
@@ -246,26 +356,173 @@ export interface PlaybookSpec {
|
|
|
246
356
|
body: string;
|
|
247
357
|
tools?: string[];
|
|
248
358
|
}
|
|
359
|
+
/** SDK 0.2.14: one block of a model message, as an in-app conversation sends it. */
|
|
360
|
+
export type ModelContent = {
|
|
361
|
+
type: "text";
|
|
362
|
+
text: string;
|
|
363
|
+
} | {
|
|
364
|
+
type: "media";
|
|
365
|
+
media: {
|
|
366
|
+
mime: string;
|
|
367
|
+
base64: string;
|
|
368
|
+
name: string;
|
|
369
|
+
};
|
|
370
|
+
} | {
|
|
371
|
+
type: "tool_call";
|
|
372
|
+
id: string;
|
|
373
|
+
name: string;
|
|
374
|
+
args: unknown;
|
|
375
|
+
} | {
|
|
376
|
+
type: "tool_result";
|
|
377
|
+
id: string;
|
|
378
|
+
content: string;
|
|
379
|
+
isError?: boolean;
|
|
380
|
+
} | {
|
|
381
|
+
type: "native";
|
|
382
|
+
provider: string;
|
|
383
|
+
data: unknown;
|
|
384
|
+
};
|
|
385
|
+
export interface ModelMessage {
|
|
386
|
+
role: "user" | "assistant";
|
|
387
|
+
content: ModelContent[];
|
|
388
|
+
}
|
|
389
|
+
/** A tool as the model is offered it; `inputSchema` is JSON Schema. */
|
|
390
|
+
export interface ModelTool {
|
|
391
|
+
name: string;
|
|
392
|
+
description: string;
|
|
393
|
+
inputSchema: Record<string, unknown>;
|
|
394
|
+
}
|
|
395
|
+
/** Which turn, step and model profile a request belongs to. */
|
|
396
|
+
export interface TurnInfo {
|
|
397
|
+
runId: string;
|
|
398
|
+
threadId: string;
|
|
399
|
+
projectId: string;
|
|
400
|
+
purpose: string;
|
|
401
|
+
profile: string;
|
|
402
|
+
step: number;
|
|
403
|
+
}
|
|
404
|
+
/** SDK 0.2.14: one model request an in-app conversation is about to send. A copy: what the hook answers is what changes. */
|
|
405
|
+
export interface ModelRequest {
|
|
406
|
+
system: string;
|
|
407
|
+
tools: ModelTool[];
|
|
408
|
+
messages: ModelMessage[];
|
|
409
|
+
turn: TurnInfo;
|
|
410
|
+
}
|
|
411
|
+
export type ModelRequestChange = Partial<Pick<ModelRequest, "system" | "tools" | "messages">>;
|
|
412
|
+
export interface ModelRequestFailure {
|
|
413
|
+
/** What the provider or the network said. */
|
|
414
|
+
message: string;
|
|
415
|
+
request: ModelRequest;
|
|
416
|
+
/** How many times this step was already sent again on a hook's answer. */
|
|
417
|
+
attempt: number;
|
|
418
|
+
}
|
|
419
|
+
/** Send the request again: changed, on another model profile (an id from the person's profiles), or both. */
|
|
420
|
+
export interface ModelRetry {
|
|
421
|
+
request?: ModelRequestChange;
|
|
422
|
+
profile?: string;
|
|
423
|
+
}
|
|
424
|
+
export interface TurnEnding {
|
|
425
|
+
/** What the model said last. */
|
|
426
|
+
text: string;
|
|
427
|
+
turn: TurnInfo;
|
|
428
|
+
/** How many times this turn was already kept going. */
|
|
429
|
+
continued: number;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* The agent's in-app conversations. The three hooks (SDK 0.2.14) reach the
|
|
433
|
+
* conversation Tomrade runs itself (`engine-loop`); CLI agents — Claude Code,
|
|
434
|
+
* Codex, dsh and the rest — run their own loop and send nothing through them.
|
|
435
|
+
*/
|
|
249
436
|
export interface AgentApi {
|
|
250
437
|
playbook(spec: PlaybookSpec): Promise<Disposer>;
|
|
438
|
+
/** Before each model request: answer the parts to send instead, or nothing to leave it as it is. */
|
|
439
|
+
request(hook: (request: ModelRequest) => Maybe<ModelRequestChange | void>): Promise<Disposer>;
|
|
440
|
+
/** A model request failed: answer what to try instead, or nothing to let the failure stand. The first plugin to answer decides. */
|
|
441
|
+
requestError(hook: (failure: ModelRequestFailure) => Maybe<ModelRetry | void>): Promise<Disposer>;
|
|
442
|
+
/** A turn is about to end: answer `{ continue }` with a message to the model to keep it going. Bounded, so it cannot run forever. */
|
|
443
|
+
turnEnding(hook: (ending: TurnEnding) => Maybe<{
|
|
444
|
+
continue: string;
|
|
445
|
+
} | void>): Promise<Disposer>;
|
|
446
|
+
/**
|
|
447
|
+
* SDK 0.2.15: a hook in the CLI agents Tomrade works with — Claude Code, Codex, dsh and the rest. It
|
|
448
|
+
* hears their sessions and, in one Tomrade started, may answer: see `CLI_HOOK_COVERAGE` for which.
|
|
449
|
+
*/
|
|
450
|
+
cliHook(spec: CliHookSpec): Promise<Disposer>;
|
|
451
|
+
}
|
|
452
|
+
export type { CliHookEvent } from "./cli-hooks.js";
|
|
453
|
+
/** SDK 0.2.15: what a CLI agent's hook says happened, whichever agent sent it. */
|
|
454
|
+
export interface CliHookCall {
|
|
455
|
+
event: import("./cli-hooks.js").CliHookEvent;
|
|
456
|
+
/** `claude-code`, `codex`, `dsh`, `pi`, `workbuddy`, `cursor`, `opencode`, `hermes`… */
|
|
457
|
+
agent: string;
|
|
458
|
+
/** Whether an answer reaches the agent: a session Tomrade started, by an agent that acts on this event. */
|
|
459
|
+
answerable: boolean;
|
|
460
|
+
projectId: string;
|
|
461
|
+
/** Tomrade's code session the hook belongs to. */
|
|
462
|
+
sessionId: string;
|
|
463
|
+
/** The ticket the session works, when it has one. */
|
|
464
|
+
ticket: string | null;
|
|
465
|
+
cwd: string | null;
|
|
466
|
+
tool?: {
|
|
467
|
+
name: string;
|
|
468
|
+
input: unknown;
|
|
469
|
+
output?: string;
|
|
470
|
+
error?: string;
|
|
471
|
+
};
|
|
472
|
+
/** What the model said last, at `stop`. */
|
|
473
|
+
text?: string;
|
|
474
|
+
/** The hook's own payload, as the agent sent it. */
|
|
475
|
+
raw: Record<string, unknown>;
|
|
476
|
+
}
|
|
477
|
+
/** Refuse the tool (`pre-tool`), add context for the model (`session-start`, `prompt`, `post-tool`), or keep the turn going (`stop`). */
|
|
478
|
+
export interface CliHookAnswer {
|
|
479
|
+
deny?: string;
|
|
480
|
+
context?: string;
|
|
481
|
+
continue?: string;
|
|
482
|
+
}
|
|
483
|
+
export interface CliHookSpec {
|
|
484
|
+
/** The events it hears; all of them when left out. */
|
|
485
|
+
events?: import("./cli-hooks.js").CliHookEvent[];
|
|
486
|
+
/** A regular expression the tool's name must match, for `pre-tool` and `post-tool`. */
|
|
487
|
+
tools?: string;
|
|
488
|
+
/** The agents it hears; all of them when left out. */
|
|
489
|
+
agents?: string[];
|
|
490
|
+
handle(call: CliHookCall): Maybe<CliHookAnswer | void>;
|
|
251
491
|
}
|
|
252
492
|
export interface SurfaceSpec {
|
|
253
493
|
id: string;
|
|
254
494
|
title: string;
|
|
495
|
+
/**
|
|
496
|
+
* SDK 0.2.11: `"window"` is a working window — title bar, resizable, closable, in the Dock or
|
|
497
|
+
* taskbar, its bounds remembered — and needs `permissions.window`. The default `"widget"` is
|
|
498
|
+
* the fixed, frameless surface it always was. A window's title carries the plugin's own.
|
|
499
|
+
*/
|
|
500
|
+
kind?: "widget" | "window";
|
|
501
|
+
/** SDK 0.2.11, windows only: how small the person may make it. */
|
|
502
|
+
minWidth?: number;
|
|
503
|
+
minHeight?: number;
|
|
255
504
|
/** A document inside the plugin's package, relative to its root. Remote pages are refused. */
|
|
256
505
|
document: string;
|
|
257
506
|
width: number;
|
|
258
507
|
height: number;
|
|
259
508
|
transparent?: boolean;
|
|
260
509
|
alwaysOnTop?: boolean;
|
|
510
|
+
/** Allow keyboard input; defaults to false for passive widgets. */
|
|
511
|
+
focusable?: boolean;
|
|
261
512
|
clickThrough?: boolean;
|
|
513
|
+
/** First placed on top of an app element by name — `"sidebar-foot"`: just above the sidebar's Project settings. */
|
|
514
|
+
anchor?: string;
|
|
262
515
|
initialState?: unknown;
|
|
263
516
|
/** Messages the document sends; a person is driving, so writes made here are theirs. */
|
|
264
517
|
onMessage?(message: unknown): Maybe<void>;
|
|
518
|
+
/** Once, when the window is gone — the person closed it, `close()` did, or the plugin unloaded. The handle's other calls then reject. */
|
|
519
|
+
onClose?(): Maybe<void>;
|
|
265
520
|
}
|
|
266
521
|
export interface SurfaceHandle {
|
|
267
522
|
readonly id: string;
|
|
268
523
|
setState(state: unknown): Promise<void>;
|
|
524
|
+
/** SDK 0.2.11: one message to the document's `tomradeSurface.onMessage`, for frequent small updates; `setState` replaces the whole state. */
|
|
525
|
+
post(message: unknown): Promise<void>;
|
|
269
526
|
setPosition(x: number, y: number): Promise<void>;
|
|
270
527
|
bounds(): Promise<{
|
|
271
528
|
x: number;
|
|
@@ -284,6 +541,25 @@ export interface SurfaceHandle {
|
|
|
284
541
|
export interface SurfacesApi {
|
|
285
542
|
open(spec: SurfaceSpec): Promise<SurfaceHandle>;
|
|
286
543
|
}
|
|
544
|
+
/** A sound the machine offers: `system:<name>`. A file the person chose is played by its absolute path. */
|
|
545
|
+
export interface AlertSound {
|
|
546
|
+
id: string;
|
|
547
|
+
title: string;
|
|
548
|
+
}
|
|
549
|
+
/** Alerts the system raises for the plugin. The plugin chooses when; it never reads the sound file itself. */
|
|
550
|
+
export interface AlertsApi {
|
|
551
|
+
/** The system sounds on this machine, the system's own alert sound first. */
|
|
552
|
+
sounds(): Promise<AlertSound[]>;
|
|
553
|
+
/** Plays a system sound or an audio file once; volume runs 0–1. */
|
|
554
|
+
play(sound: string, opts?: {
|
|
555
|
+
volume?: number;
|
|
556
|
+
}): Promise<void>;
|
|
557
|
+
/** A silent system notification; clicking it brings Tomrade forward. */
|
|
558
|
+
notify(notice: {
|
|
559
|
+
title: string;
|
|
560
|
+
body?: string;
|
|
561
|
+
}): Promise<void>;
|
|
562
|
+
}
|
|
287
563
|
export interface OplogApi {
|
|
288
564
|
recent(projectId: string, limit?: number): Promise<OplogEntry[]>;
|
|
289
565
|
history(projectId: string, entity: OplogEntity, id: string, limit?: number): Promise<OplogEntry[]>;
|
|
@@ -325,8 +601,16 @@ export interface PluginContext {
|
|
|
325
601
|
readonly tools: ToolsApi;
|
|
326
602
|
readonly contributions: ContributionsApi;
|
|
327
603
|
readonly agent: AgentApi;
|
|
604
|
+
readonly im: ImApi;
|
|
328
605
|
readonly surfaces: SurfacesApi;
|
|
606
|
+
readonly alerts: AlertsApi;
|
|
329
607
|
readonly oplog: OplogApi;
|
|
608
|
+
/** SDK 0.2.9: answers the agent's web search, as the app's own providers do. */
|
|
609
|
+
readonly webSearch: WebSearchApi;
|
|
610
|
+
/** SDK 0.2.10: live terminals of the herdr panes code sessions run in. */
|
|
611
|
+
readonly terminals: TerminalsApi;
|
|
612
|
+
/** SDK 0.2.16: model providers, as the app's own are. */
|
|
613
|
+
readonly models: ModelsApi;
|
|
330
614
|
on<K extends keyof Events>(event: K, listener: Events[K]): Disposer;
|
|
331
615
|
/** Runs `fn` while an optional service is present; what it returns is disposed when the service goes. */
|
|
332
616
|
when(name: ServiceName, fn: (ctx: PluginContext) => Maybe<Disposer | void>): Disposer;
|
|
@@ -337,5 +621,4 @@ export interface PluginContext {
|
|
|
337
621
|
/** A timer cleared with the plugin. */
|
|
338
622
|
interval(fn: () => Maybe<void>, ms: number): Disposer;
|
|
339
623
|
}
|
|
340
|
-
export {};
|
|
341
624
|
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract each value the SDK exports first shipped in. A plugin imports
|
|
3
|
+
* the SDK from the Tomrade it runs in, not from its own bundle, so a name the
|
|
4
|
+
* oldest Tomrade its `engines.sdk` accepts does not have fails the plugin as it
|
|
5
|
+
* loads. A new export is listed here under the contract it ships in.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SDK_EXPORTS: Readonly<Record<string, string>>;
|
|
8
|
+
/** The oldest contract `range` accepts, up to this SDK's own; null when it accepts none of them. */
|
|
9
|
+
export declare function lowestContract(range: string): string | null;
|
|
10
|
+
//# sourceMappingURL=exports.d.ts.map
|
package/dist/exports.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SDK_CONTRACT } from "./manifest.js";
|
|
2
|
+
import { compareVersions, parseVersion, satisfies } from "./semver.js";
|
|
3
|
+
/**
|
|
4
|
+
* The contract each value the SDK exports first shipped in. A plugin imports
|
|
5
|
+
* the SDK from the Tomrade it runs in, not from its own bundle, so a name the
|
|
6
|
+
* oldest Tomrade its `engines.sdk` accepts does not have fails the plugin as it
|
|
7
|
+
* loads. A new export is listed here under the contract it ships in.
|
|
8
|
+
*/
|
|
9
|
+
export const SDK_EXPORTS = {
|
|
10
|
+
ContributesSchema: "0.2.0",
|
|
11
|
+
MANIFEST_FILE: "0.2.0",
|
|
12
|
+
ManifestSchema: "0.2.0",
|
|
13
|
+
NAMESPACE: "0.2.0",
|
|
14
|
+
OFFICIAL_PUBLISHER: "0.2.0",
|
|
15
|
+
OSI_LICENSES: "0.2.0",
|
|
16
|
+
PLUGIN_ID: "0.2.0",
|
|
17
|
+
PermissionsSchema: "0.2.0",
|
|
18
|
+
RESERVED_NAMESPACES: "0.2.0",
|
|
19
|
+
SDK_CONTRACT: "0.2.0",
|
|
20
|
+
SDK_PLUGIN: "0.2.0",
|
|
21
|
+
ToolInputError: "0.2.0",
|
|
22
|
+
assertOwned: "0.2.0",
|
|
23
|
+
compareVersions: "0.2.0",
|
|
24
|
+
definePlugin: "0.2.0",
|
|
25
|
+
defineTool: "0.2.0",
|
|
26
|
+
isOpenSource: "0.2.0",
|
|
27
|
+
isSdkPlugin: "0.2.0",
|
|
28
|
+
isVersion: "0.2.0",
|
|
29
|
+
nameOf: "0.2.0",
|
|
30
|
+
namespaceOf: "0.2.0",
|
|
31
|
+
ownsId: "0.2.0",
|
|
32
|
+
parseManifest: "0.2.0",
|
|
33
|
+
parseVersion: "0.2.0",
|
|
34
|
+
publisherOf: "0.2.0",
|
|
35
|
+
requireProjectId: "0.2.0",
|
|
36
|
+
riskOf: "0.2.0",
|
|
37
|
+
satisfies: "0.2.0",
|
|
38
|
+
ImDeliveryError: "0.2.1",
|
|
39
|
+
IM_CAPABILITIES_V2: "0.2.5",
|
|
40
|
+
IM_MARKDOWN_DIALECTS: "0.2.5",
|
|
41
|
+
ImCapabilitiesSchema: "0.2.5",
|
|
42
|
+
resolveImCapabilities: "0.2.5",
|
|
43
|
+
splitImText: "0.2.5",
|
|
44
|
+
toImDialect: "0.2.5",
|
|
45
|
+
isRange: "0.2.7",
|
|
46
|
+
LANGUAGE_PACK_LIMITS: "0.2.8",
|
|
47
|
+
LanguageEntrySchema: "0.2.8",
|
|
48
|
+
LanguagePackFileSchema: "0.2.8",
|
|
49
|
+
languageCoverage: "0.2.8",
|
|
50
|
+
parseLanguagePack: "0.2.8",
|
|
51
|
+
slotsOf: "0.2.8",
|
|
52
|
+
SDK_EXPORTS: "0.2.9",
|
|
53
|
+
lowestContract: "0.2.9",
|
|
54
|
+
pickLanguage: "0.2.9",
|
|
55
|
+
wordsFor: "0.2.9",
|
|
56
|
+
RowSchema: "0.2.13",
|
|
57
|
+
CLI_HOOK_COVERAGE: "0.2.15",
|
|
58
|
+
CLI_HOOK_EVENTS: "0.2.15"
|
|
59
|
+
};
|
|
60
|
+
/** The oldest contract `range` accepts, up to this SDK's own; null when it accepts none of them. */
|
|
61
|
+
export function lowestContract(range) {
|
|
62
|
+
const known = [...Object.values(SDK_EXPORTS), SDK_CONTRACT];
|
|
63
|
+
// Every patch of each minor the SDK has shipped, since a contract that added no export is still one a Tomrade may run.
|
|
64
|
+
const lines = new Map();
|
|
65
|
+
for (const v of known) {
|
|
66
|
+
const p = parseVersion(v);
|
|
67
|
+
if (p)
|
|
68
|
+
lines.set(`${p[0]}.${p[1]}`, Math.max(lines.get(`${p[0]}.${p[1]}`) ?? 0, p[2]));
|
|
69
|
+
}
|
|
70
|
+
const candidates = [...lines].flatMap(([line, last]) => Array.from({ length: last + 1 }, (_, patch) => `${line}.${patch}`)).sort(compareVersions);
|
|
71
|
+
return candidates.find((v) => compareVersions(v, SDK_CONTRACT) <= 0 && satisfies(v, range)) ?? null;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=exports.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImMarkdownDialect } from "./im.js";
|
|
2
|
+
/** Converts the common layer's Markdown into an adapter's dialect. */
|
|
3
|
+
export declare function toImDialect(markdown: string, dialect: ImMarkdownDialect): string;
|
|
4
|
+
/**
|
|
5
|
+
* Splits Markdown into messages that each fit `max` once converted, breaking
|
|
6
|
+
* between lines. A code block stays whole when it fits a message; a longer one
|
|
7
|
+
* is closed and reopened with its language rather than cut mid-fence.
|
|
8
|
+
*/
|
|
9
|
+
export declare function splitImText(markdown: string, max: number, dialect?: ImMarkdownDialect): string[];
|
|
10
|
+
//# sourceMappingURL=im-markdown.d.ts.map
|