@tomradex/plugin-sdk 0.2.16 → 0.2.19

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.
@@ -3,6 +3,7 @@ import type { ImApi } from "./im.js";
3
3
  import type { TerminalsApi } from "./terminals.js";
4
4
  import type { WebSearchApi } from "./web-search.js";
5
5
  import type { ModelsApi } from "./models.js";
6
+ import type { LocalAgentOptions, LocalAgentSpec } from "./local-agents.js";
6
7
  import type { Author, Board, CreateItemInput, FieldDef, FieldOption, FieldType, FilterClause, Item, ListItemsFilter, OplogEntity, OplogEntry, OplogOp, Patch, Project, SortKey, View } from "@tomradex/domain";
7
8
  /**
8
9
  * The plugin contract. Everything a plugin reaches returns a Promise, and every
@@ -448,6 +449,12 @@ export interface AgentApi {
448
449
  * hears their sessions and, in one Tomrade started, may answer: see `CLI_HOOK_COVERAGE` for which.
449
450
  */
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>;
451
458
  }
452
459
  export type { CliHookEvent } from "./cli-hooks.js";
453
460
  /** SDK 0.2.15: what a CLI agent's hook says happened, whichever agent sent it. */
package/dist/exports.js CHANGED
@@ -55,7 +55,9 @@ export const SDK_EXPORTS = {
55
55
  wordsFor: "0.2.9",
56
56
  RowSchema: "0.2.13",
57
57
  CLI_HOOK_COVERAGE: "0.2.15",
58
- CLI_HOOK_EVENTS: "0.2.15"
58
+ CLI_HOOK_EVENTS: "0.2.15",
59
+ NotSignedIn: "0.2.17",
60
+ PERMISSION_CHOICES: "0.2.17"
59
61
  };
60
62
  /** The oldest contract `range` accepts, up to this SDK's own; null when it accepts none of them. */
61
63
  export function lowestContract(range) {
package/dist/im.d.ts CHANGED
@@ -185,6 +185,28 @@ export interface ImAdapterSession {
185
185
  id: string;
186
186
  name?: string;
187
187
  };
188
+ /**
189
+ * SDK 0.2.19. Topics the bot opens itself, when `topics`: one per ticket
190
+ * being worked in a group conversation. Messages people write in one arrive
191
+ * with its `topicId`.
192
+ */
193
+ topics?: {
194
+ open(command: {
195
+ operationId: string;
196
+ address: ImAddress;
197
+ title: string;
198
+ }): Promise<{
199
+ topicId: string;
200
+ }>;
201
+ /** Renames it, and closes it out of the way or opens it again. */
202
+ update(command: {
203
+ address: ImAddress & {
204
+ topicId: string;
205
+ };
206
+ title: string;
207
+ closed: boolean;
208
+ }): Promise<void>;
209
+ };
188
210
  close(): void | Promise<void>;
189
211
  }
190
212
  /** SDK 0.2.5. One value a person copies from the platform's developer console. */
@@ -221,6 +243,8 @@ export interface ImCapabilities {
221
243
  maxFileBytes: number;
222
244
  /** SDK 0.2.5. Replies can go into a thread under the triggering message. */
223
245
  threads?: boolean;
246
+ /** SDK 0.2.19. The bot opens a topic per ticket in a group, through `session.topics`. */
247
+ topics?: boolean;
224
248
  reactions?: boolean;
225
249
  typing?: boolean;
226
250
  /** `native`: session.stream. `edit`: throttled edits of one message. `none`: only the final message. */
@@ -254,6 +278,7 @@ export declare const ImCapabilitiesSchema: z.ZodObject<{
254
278
  maxTextLength: z.ZodNumber;
255
279
  maxFileBytes: z.ZodNumber;
256
280
  threads: z.ZodOptional<z.ZodBoolean>;
281
+ topics: z.ZodOptional<z.ZodBoolean>;
257
282
  reactions: z.ZodOptional<z.ZodBoolean>;
258
283
  typing: z.ZodOptional<z.ZodBoolean>;
259
284
  stream: z.ZodOptional<z.ZodEnum<{
package/dist/im.js CHANGED
@@ -10,6 +10,7 @@ export const ImCapabilitiesSchema = z.object({
10
10
  maxTextLength: z.number().int().min(128),
11
11
  maxFileBytes: z.number().int().min(0),
12
12
  threads: z.boolean().optional(),
13
+ topics: z.boolean().optional(),
13
14
  reactions: z.boolean().optional(),
14
15
  typing: z.boolean().optional(),
15
16
  stream: z.enum(["native", "edit", "none"]).optional(),
@@ -28,6 +29,7 @@ export function resolveImCapabilities(caps) {
28
29
  return {
29
30
  ...parsed,
30
31
  threads: parsed.threads ?? false,
32
+ topics: parsed.topics ?? false,
31
33
  reactions: parsed.reactions ?? false,
32
34
  typing: parsed.typing ?? false,
33
35
  stream: parsed.stream ?? "none",
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./im.js";
9
9
  export * from "./web-search.js";
10
10
  export * from "./models.js";
11
11
  export * from "./terminals.js";
12
+ export * from "./local-agents.js";
12
13
  export { CLI_HOOK_COVERAGE, CLI_HOOK_EVENTS } from "./cli-hooks.js";
13
14
  export * from "./im-markdown.js";
14
15
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export * from "./im.js";
8
8
  export * from "./web-search.js";
9
9
  export * from "./models.js";
10
10
  export * from "./terminals.js";
11
+ export * from "./local-agents.js";
11
12
  export { CLI_HOOK_COVERAGE, CLI_HOOK_EVENTS } from "./cli-hooks.js";
12
13
  export * from "./im-markdown.js";
13
14
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,282 @@
1
+ import type { z } from "zod";
2
+ type Maybe<T> = T | Promise<T>;
3
+ /** SDK 0.2.17. How much an agent may do without asking, as Tomrade settles it for one turn. */
4
+ export type LocalAgentPolicy = "full-access" | "ask" | "read-only";
5
+ /** SDK 0.2.17. What a "not ready" means: what the person has to do about it. */
6
+ export type LocalAgentNeed = "install" | "repair" | "sign-in" | "model" | "service";
7
+ /** SDK 0.2.17. The permission modes Tomrade knows, for `acp.permissionModes`. */
8
+ export declare const PERMISSION_CHOICES: {
9
+ readonly "full-access": {
10
+ readonly value: "full-access";
11
+ readonly label: "Full access";
12
+ };
13
+ readonly ask: {
14
+ readonly value: "ask";
15
+ readonly label: "Ask permissions";
16
+ };
17
+ readonly "read-only": {
18
+ readonly value: "read-only";
19
+ readonly label: "Read only";
20
+ };
21
+ readonly local: {
22
+ readonly value: "local";
23
+ readonly label: "Local rules";
24
+ };
25
+ };
26
+ /** SDK 0.2.17. Thrown from `lend.ask` when the CLI said, in its own words, that it is not signed in. */
27
+ export declare class NotSignedIn extends Error {
28
+ constructor(message: string);
29
+ }
30
+ export interface LocalAgentChoice {
31
+ value: string;
32
+ label?: string;
33
+ }
34
+ /** What the agent offered the last time a session opened, for the Model and Effort picks. Kept across restarts. */
35
+ export interface LocalAgentCatalog {
36
+ models: LocalAgentChoice[];
37
+ efforts: LocalAgentChoice[];
38
+ }
39
+ export interface LocalAgentPick {
40
+ key: string;
41
+ title: string;
42
+ choices: LocalAgentChoice[];
43
+ /** Choices are suggestions only: settings may hold any string. */
44
+ freeText?: boolean;
45
+ }
46
+ /**
47
+ * SDK 0.2.18. An installer script the app runs for the person: `script` (a
48
+ * POSIX shell installer) as `curl -fsSL <url> | bash`, `powershell` as
49
+ * `irm <url> | iex`. Plugin code names only the address.
50
+ */
51
+ export type LocalAgentInstaller = {
52
+ script: string;
53
+ } | {
54
+ powershell: string;
55
+ };
56
+ /** How the agent is installed and signed in, for setup screens. */
57
+ export interface LocalAgentSetup {
58
+ /** Install commands, the recommended one first; an installer script by its address (SDK 0.2.18). */
59
+ install: (string | LocalAgentInstaller)[];
60
+ signIn?: string;
61
+ /** Signing in starts the agent itself, which then asks; otherwise the command signs in and returns. */
62
+ signInRuns?: boolean;
63
+ docs?: string;
64
+ suggested?: boolean;
65
+ }
66
+ /** The machine a CLI is looked for on, for `cli.bundled`. */
67
+ export interface LocalCliHost {
68
+ platform: string;
69
+ env: Record<string, string | undefined>;
70
+ home: string;
71
+ /** Where Windows' installed-apps list says apps whose display name matches were installed; none elsewhere. */
72
+ installs(displayName: RegExp): Promise<string[]>;
73
+ }
74
+ /** A copy of the CLI inside a desktop app. */
75
+ export interface BundledCli {
76
+ file: string;
77
+ /** The app's binary that runs `file` as Node; absent when `file` is the app's own launcher, run as it is. */
78
+ runtime?: string;
79
+ }
80
+ /** A desktop app of the same agent that lacks the CLI, named when the CLI is missing but the app is there. */
81
+ export interface LocalAgentDesktopApp {
82
+ title: string;
83
+ /** macOS bundle names, looked for in /Applications and ~/Applications. */
84
+ mac?: string[];
85
+ /** Windows install folders, relative to %LOCALAPPDATA% or Program Files. */
86
+ windows?: string[];
87
+ paths?: string[];
88
+ }
89
+ export interface LocalCliRunOptions {
90
+ cwd?: string;
91
+ /** Laid over Tomrade's own environment; `undefined` removes a variable. */
92
+ env?: Record<string, string | undefined>;
93
+ stdin?: string;
94
+ signal?: AbortSignal;
95
+ /** Stopped after this long, and answered with `error.killed`. */
96
+ timeoutMs?: number;
97
+ }
98
+ export interface LocalCliResult {
99
+ code: number | null;
100
+ stdout: string;
101
+ /** Its last few thousand characters. */
102
+ stderr: string;
103
+ /** Set when there was no exit to report: it did not start, or it was stopped. */
104
+ error?: {
105
+ message: string;
106
+ killed?: boolean;
107
+ code?: string | number | null;
108
+ };
109
+ }
110
+ /**
111
+ * SDK 0.2.17. The agent's CLI as Tomrade found it — the path the person chose
112
+ * in Settings, else PATH, a login shell's PATH, npm's prefix, WSL or a bundled
113
+ * app — started the way Tomrade starts its own agents (no console window, its
114
+ * process tree stopped on Windows).
115
+ */
116
+ export interface LocalCli {
117
+ readonly name: string;
118
+ /** Runs it to its end: all of stdout, the tail of stderr. */
119
+ run(args: string[], opts?: LocalCliRunOptions): Promise<LocalCliResult>;
120
+ /** Runs it with stdout read as JSON lines, each line that parses handed to `onLine`. */
121
+ jsonl(args: string[], opts: LocalCliRunOptions & {
122
+ onLine(line: Record<string, unknown>): void;
123
+ }): Promise<{
124
+ code: number | null;
125
+ stderr: string;
126
+ }>;
127
+ }
128
+ /** Tomrade's MCP server, for the agent's own configuration; null while it is not running. */
129
+ export interface LocalAgentMcp {
130
+ url: string | null;
131
+ /** Where the agent's hooks post its session's events. */
132
+ eventUrl: string | null;
133
+ }
134
+ export interface LocalAgentIo<O> {
135
+ cli: LocalCli;
136
+ mcp: LocalAgentMcp;
137
+ /** The person's settings for this agent (`engines.<id>`), read through `options`. */
138
+ options: O;
139
+ }
140
+ /** How a connection to Tomrade looks from the agent's side, as the Assistant settings show it. */
141
+ export interface McpLinkState {
142
+ /** `stale`: a `tomrade` server is configured, but at another address. */
143
+ state: "connected" | "missing" | "stale" | "unavailable";
144
+ url: string | null;
145
+ configuredUrl: string | null;
146
+ file: string | null;
147
+ /** Commands that do the same by hand. */
148
+ manual: {
149
+ connect: string;
150
+ disconnect: string;
151
+ };
152
+ error?: string;
153
+ }
154
+ /**
155
+ * The agent's own user-level connection to Tomrade, so sessions a person
156
+ * starts reach it too: managed by the plugin, or by the agent's own
157
+ * `mcp add` / `mcp remove`, which Tomrade runs.
158
+ */
159
+ export type LocalAgentMcpLink<O> = {
160
+ status(io: LocalAgentIo<O>): Maybe<McpLinkState>;
161
+ connect(io: LocalAgentIo<O>): Maybe<McpLinkState>;
162
+ disconnect(io: LocalAgentIo<O>): Maybe<McpLinkState>;
163
+ } | {
164
+ cli: {
165
+ /** Where the agent keeps it. */
166
+ file(): string;
167
+ /** The address its `tomrade` entry names; null when there is none; throws when the file cannot be read. */
168
+ configuredUrl(): string | null;
169
+ add(url: string): string[];
170
+ remove(): string[];
171
+ };
172
+ };
173
+ /** One worker call: the instructions and the conversation as text. */
174
+ export interface LentRequest {
175
+ system: string;
176
+ transcript: string;
177
+ }
178
+ export interface LentAnswer {
179
+ text: string;
180
+ /** All of the input, what the cache read included. */
181
+ inputTokens: number;
182
+ cacheReadTokens?: number;
183
+ outputTokens: number;
184
+ }
185
+ export interface LocalAgentOptions {
186
+ command?: string;
187
+ model?: string;
188
+ effort?: string;
189
+ }
190
+ /** How Tomrade talks to an agent that speaks the Agent Client Protocol on stdio. */
191
+ export interface LocalAgentAcp<O> {
192
+ permissionModes: {
193
+ default: string;
194
+ choices: {
195
+ value: string;
196
+ label: string;
197
+ }[];
198
+ };
199
+ /** What starts its ACP server on stdio — for a turn, and for the short session that reads its model list. */
200
+ args(options: O): string[];
201
+ env?(options: O): Record<string, string>;
202
+ /** The agent's own session mode for a policy; Tomrade answers whatever it still asks. */
203
+ mode?(policy: LocalAgentPolicy): string | undefined;
204
+ /** "always": it registers HTTP MCP servers without advertising that it can. */
205
+ httpMcp?: "advertised" | "always";
206
+ /** How a stored session reopens; "never": a cold turn opens a new session with the conversation recapped. */
207
+ reopen?: "resume" | "load" | "never";
208
+ /** A tool call's own name, for an agent whose updates carry none but put it in the call's id. */
209
+ callName?(toolCallId: string): string | undefined;
210
+ /** Where tokens are reported: the prompt's answer for the turn, a running session total, or per request in a usage update. */
211
+ usage?: "turn" | "session" | ((update: Record<string, any>) => {
212
+ input: number;
213
+ output: number;
214
+ cacheRead?: number;
215
+ cacheWrite?: number;
216
+ } | null);
217
+ /** The prompt's `inputTokens` leaves out what `cachedReadTokens` and `cachedWriteTokens` count. */
218
+ cacheApart?: boolean;
219
+ /** How it takes a message sent while a turn runs: its own `session/steer` method, or a `/steer` prompt alongside. */
220
+ steer?: "method" | "slash";
221
+ /** Effort levels to offer before a session has advertised its own. */
222
+ efforts?: string[];
223
+ /** The agent's own words for a turn it ended on "refusal". */
224
+ failure?(response: Record<string, any>): string | undefined;
225
+ /** Its CLI can also be handed a ticket in a terminal. */
226
+ terminal?: boolean;
227
+ /** In a terminal it reaches Tomrade only through its user-level connection (`mcpLink`). */
228
+ terminalViaLink?: boolean;
229
+ }
230
+ /**
231
+ * SDK 0.2.17. A coding agent installed on this machine, run by Tomrade: over
232
+ * ACP inside the app when `acp` is given, else only in a terminal the person
233
+ * sees (handed the ticket, heard over MCP). Settings live under `engines.<id>`.
234
+ */
235
+ export interface LocalAgentSpec<O extends LocalAgentOptions = LocalAgentOptions> {
236
+ /** The engine's id, carrying the namespace. */
237
+ id: string;
238
+ title: string;
239
+ cli: {
240
+ name: string;
241
+ /** Copies inside desktop apps, tried after PATH. */
242
+ bundled?(host: LocalCliHost): Maybe<BundledCli[]>;
243
+ desktopApp?: LocalAgentDesktopApp;
244
+ };
245
+ /** The English tail of "not found — …"; `{install}` stands for the first of `setup.install` (SDK 0.2.18). */
246
+ install: string;
247
+ setup?: LocalAgentSetup;
248
+ /** The agent's settings (`engines.<id>`), drawn by the settings UI; an ACP agent needs one with `command`. */
249
+ options?: z.ZodType<O>;
250
+ /** What gets a signed-out agent going, said after its own error. */
251
+ signIn?: string;
252
+ /** A run that tells whether the agent can work yet; it may also fill in the catalog's models. */
253
+ probe?: {
254
+ args: string[];
255
+ says(output: string, catalog: LocalAgentCatalog): boolean | null;
256
+ reason: string;
257
+ need?: LocalAgentNeed;
258
+ };
259
+ /** Whether it is signed in, read without running it; null: no clear answer, taken as yes. */
260
+ signedIn?: {
261
+ ask(): Maybe<boolean | null>;
262
+ reason: string;
263
+ };
264
+ /** The agent's own logs and setup facts, for the troubleshooter. */
265
+ diagnostics?: {
266
+ logs?(): string[];
267
+ facts?(catalog: LocalAgentCatalog): Record<string, string>;
268
+ };
269
+ acp?: LocalAgentAcp<O>;
270
+ /** A terminal-only agent's picks (an ACP agent's come from its sessions). */
271
+ picks?: LocalAgentPick[];
272
+ mcpLink?: LocalAgentMcpLink<O>;
273
+ /** Its sign-in lent to Tomrade's worker for one-shot calls, in Automatic order by `rank` (lowest first). */
274
+ lend?: {
275
+ rank?: number;
276
+ ask(request: LentRequest, io: LocalAgentIo<O> & {
277
+ signal?: AbortSignal;
278
+ }): Promise<LentAnswer>;
279
+ };
280
+ }
281
+ export {};
282
+ //# sourceMappingURL=local-agents.d.ts.map
@@ -0,0 +1,15 @@
1
+ /** SDK 0.2.17. The permission modes Tomrade knows, for `acp.permissionModes`. */
2
+ export const PERMISSION_CHOICES = {
3
+ "full-access": { value: "full-access", label: "Full access" },
4
+ ask: { value: "ask", label: "Ask permissions" },
5
+ "read-only": { value: "read-only", label: "Read only" },
6
+ local: { value: "local", label: "Local rules" }
7
+ };
8
+ /** SDK 0.2.17. Thrown from `lend.ask` when the CLI said, in its own words, that it is not signed in. */
9
+ export class NotSignedIn extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "NotSignedIn";
13
+ }
14
+ }
15
+ //# sourceMappingURL=local-agents.js.map
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  /** The contract this SDK describes. A plugin's `engines.sdk` range is checked against the host's. */
3
- export declare const SDK_CONTRACT = "0.2.16";
3
+ export declare const SDK_CONTRACT = "0.2.19";
4
4
  export declare const PLUGIN_ID: RegExp;
5
5
  export declare const NAMESPACE: RegExp;
6
6
  /** The app's own names, and the official publisher's. */
@@ -40,6 +40,7 @@ export declare const ContributesSchema: z.ZodObject<{
40
40
  webSearchProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
41
41
  agentHooks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
42
42
  modelProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
43
+ localAgents: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
43
44
  }, z.core.$strip>;
44
45
  /**
45
46
  * One row a full-mode plugin lays over Tomrade's composition, merged by id like a
@@ -162,6 +163,7 @@ export declare const ManifestSchema: z.ZodObject<{
162
163
  webSearchProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
163
164
  agentHooks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
164
165
  modelProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
166
+ localAgents: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
165
167
  }, z.core.$strip>>;
166
168
  authoredBy: z.ZodDefault<z.ZodEnum<{
167
169
  agent: "agent";
package/dist/manifest.js CHANGED
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { LANGUAGE_PACK_LIMITS, LanguageEntrySchema } from "./language.js";
3
3
  import { isRange, isVersion } from "./semver.js";
4
4
  /** The contract this SDK describes. A plugin's `engines.sdk` range is checked against the host's. */
5
- export const SDK_CONTRACT = "0.2.16";
5
+ export const SDK_CONTRACT = "0.2.19";
6
6
  export const PLUGIN_ID = /^[a-z0-9][a-z0-9-]{0,38}\/[a-z0-9][a-z0-9-]{0,63}$/;
7
7
  export const NAMESPACE = /^[a-z][a-z0-9_]{1,31}$/;
8
8
  /** The app's own names, and the official publisher's. */
@@ -44,7 +44,9 @@ export const ContributesSchema = z
44
44
  /** SDK 0.2.15: the CLI agent hook events it hears. */
45
45
  agentHooks: z.array(z.string()).default([]),
46
46
  /** SDK 0.2.16: the model providers it registers. */
47
- modelProviders: z.array(z.string()).default([])
47
+ modelProviders: z.array(z.string()).default([]),
48
+ /** SDK 0.2.17: the local agents it registers. */
49
+ localAgents: z.array(z.string()).default([])
48
50
  })
49
51
  .partial();
50
52
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomradex/plugin-sdk",
3
- "version": "0.2.16",
3
+ "version": "0.2.19",
4
4
  "type": "module",
5
5
  "author": "Haoyuan Tang",
6
6
  "engines": {
@@ -29,7 +29,7 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@tomradex/domain": "0.2.16",
32
+ "@tomradex/domain": "0.2.19",
33
33
  "zod": "^4.4.3"
34
34
  }
35
35
  }