@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 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. The store's review compares it with the code. Community lists open-source plugins only, built from your public GitHub repository and reviewed before they are listed.
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
@@ -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
- /** Exported tables travel in the project's folder; `false` keeps them on this machine. */
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 review says so.
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
- /** A policy over every tool call: throw to refuse it. */
167
- gate(check: (call: ToolCallInfo) => void | Promise<void>): Promise<Disposer>;
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: string;
204
- group?: string;
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: string;
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
@@ -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