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