@getfamiliar/shared 0.1.2
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/build/AgentRun.d.ts +187 -0
- package/build/AgentRun.js +1 -0
- package/build/AgentRunBus.d.ts +147 -0
- package/build/AgentRunBus.js +362 -0
- package/build/Calendar.d.ts +450 -0
- package/build/Calendar.js +37 -0
- package/build/ChatMessage.d.ts +53 -0
- package/build/ChatMessage.js +11 -0
- package/build/ChatMessageBus.d.ts +110 -0
- package/build/ChatMessageBus.js +257 -0
- package/build/Config.d.ts +83 -0
- package/build/Config.js +1 -0
- package/build/ContainerTool.d.ts +34 -0
- package/build/ContainerTool.js +1 -0
- package/build/CronExpression.d.ts +53 -0
- package/build/CronExpression.js +77 -0
- package/build/Event.d.ts +280 -0
- package/build/Event.js +14 -0
- package/build/EventBus.d.ts +198 -0
- package/build/EventBus.js +482 -0
- package/build/HandlerCatalog.d.ts +75 -0
- package/build/HandlerCatalog.js +165 -0
- package/build/InferenceEvent.d.ts +30 -0
- package/build/InferenceEvent.js +1 -0
- package/build/InferenceEventBus.d.ts +39 -0
- package/build/InferenceEventBus.js +80 -0
- package/build/Mail.d.ts +250 -0
- package/build/Mail.js +73 -0
- package/build/MailStyleTemplate.d.ts +71 -0
- package/build/MailStyleTemplate.js +47 -0
- package/build/ModelMetaData.d.ts +63 -0
- package/build/ModelMetaData.js +12 -0
- package/build/PathGlob.d.ts +20 -0
- package/build/PathGlob.js +33 -0
- package/build/Plugin.d.ts +709 -0
- package/build/Plugin.js +29 -0
- package/build/PostgresConnection.d.ts +75 -0
- package/build/PostgresConnection.js +162 -0
- package/build/ScheduledSubagent.d.ts +49 -0
- package/build/ScheduledSubagent.js +1 -0
- package/build/ScheduledSubagentBus.d.ts +94 -0
- package/build/ScheduledSubagentBus.js +187 -0
- package/build/Schema.d.ts +84 -0
- package/build/Schema.js +570 -0
- package/build/StepResult.d.ts +103 -0
- package/build/StepResult.js +1 -0
- package/build/StepResultBus.d.ts +64 -0
- package/build/StepResultBus.js +169 -0
- package/build/Timezone.d.ts +58 -0
- package/build/Timezone.js +84 -0
- package/build/ToolCall.d.ts +25 -0
- package/build/ToolCall.js +1 -0
- package/build/ToolCallBus.d.ts +65 -0
- package/build/ToolCallBus.js +110 -0
- package/build/ToolLevel.d.ts +20 -0
- package/build/ToolLevel.js +2 -0
- package/build/ToolRunner.d.ts +118 -0
- package/build/ToolRunner.js +192 -0
- package/build/ToolsExpressionUtils.d.ts +80 -0
- package/build/ToolsExpressionUtils.js +102 -0
- package/build/WorkspaceFile.d.ts +104 -0
- package/build/WorkspaceFile.js +7 -0
- package/build/estimateTokens.d.ts +16 -0
- package/build/estimateTokens.js +18 -0
- package/build/index.d.ts +37 -0
- package/build/index.js +26 -0
- package/build/logging/Logger.d.ts +66 -0
- package/build/logging/Logger.js +90 -0
- package/build/markdownHtml.d.ts +14 -0
- package/build/markdownHtml.js +21 -0
- package/build/markdownTerminal.d.ts +14 -0
- package/build/markdownTerminal.js +376 -0
- package/build/testing/MockAgentRunBus.d.ts +52 -0
- package/build/testing/MockAgentRunBus.js +175 -0
- package/build/testing/MockBusStore.d.ts +22 -0
- package/build/testing/MockBusStore.js +22 -0
- package/build/testing/MockEventBus.d.ts +20 -0
- package/build/testing/MockEventBus.js +57 -0
- package/build/testing/MockLogger.d.ts +52 -0
- package/build/testing/MockLogger.js +65 -0
- package/build/testing/index.d.ts +4 -0
- package/build/testing/index.js +4 -0
- package/package.json +63 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import type { CommandDef } from "citty";
|
|
3
|
+
import type { AgentRunRow } from "./AgentRun.js";
|
|
4
|
+
import type { CalendarApi } from "./Calendar.js";
|
|
5
|
+
import type { ChatFilter } from "./ChatMessage.js";
|
|
6
|
+
import type { ChatHandler, ChatUnsubscribe } from "./ChatMessageBus.js";
|
|
7
|
+
import type { ConfigService } from "./Config.js";
|
|
8
|
+
import type { EventFile, EventRow, NewEvent } from "./Event.js";
|
|
9
|
+
import type { Logger } from "./logging/Logger.js";
|
|
10
|
+
import type { MailApi } from "./Mail.js";
|
|
11
|
+
import type { MailStyleTemplate } from "./MailStyleTemplate.js";
|
|
12
|
+
import type { ModelMetaData, ModelProviderDescriptor } from "./ModelMetaData.js";
|
|
13
|
+
import type { StepResultRow } from "./StepResult.js";
|
|
14
|
+
import type { ToolLevel } from "./ToolLevel.js";
|
|
15
|
+
import type { ToolRunContext } from "./ToolRunner.js";
|
|
16
|
+
import type { WorkspaceWatcherApi } from "./WorkspaceFile.js";
|
|
17
|
+
export type { ChatHandler, Client as McpClient };
|
|
18
|
+
/**
|
|
19
|
+
* Citty's `CommandDef` is generic over its `ArgsDef`, and that
|
|
20
|
+
* generic appears in contravariant position on `run`. As a result a
|
|
21
|
+
* `CommandDef<{ message: ... }>` is not assignable to the default
|
|
22
|
+
* `CommandDef<ArgsDef>`. Citty's own `SubCommandsDef` works around
|
|
23
|
+
* this with `CommandDef<any>`; we mirror that pattern in the plugin
|
|
24
|
+
* manifest so plugins can return well-typed commands without
|
|
25
|
+
* casting at the boundary.
|
|
26
|
+
*/
|
|
27
|
+
export type AnyCommandDef = CommandDef<any>;
|
|
28
|
+
/**
|
|
29
|
+
* Capabilities the host exposes to plugins. Passed into every plugin
|
|
30
|
+
* lifecycle method (`start`, `commands`, `cronjobs`).
|
|
31
|
+
*
|
|
32
|
+
* **Plugins must reach host capabilities only through this object.**
|
|
33
|
+
* Direct imports of `EventBus`, `PostgresConnection`, or other host
|
|
34
|
+
* internals are not allowed in plugin code — the host owns those
|
|
35
|
+
* lifecycles, and routing through ctx is what lets the host enforce
|
|
36
|
+
* policy (later: assert a plugin only emits topics it declared, scope
|
|
37
|
+
* logs by plugin id, gate writes through approval, etc.) and what
|
|
38
|
+
* makes plugins unit-testable with a fake ctx.
|
|
39
|
+
*
|
|
40
|
+
* The contract is intentionally minimal today: only what the first
|
|
41
|
+
* plugin (cli-chat) needs. New capabilities are added on demand as
|
|
42
|
+
* plugins surface them.
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* Async function a plugin registers via
|
|
46
|
+
* `ctx.events.registerContextProvider(fn)` to contribute per-event
|
|
47
|
+
* situational knowledge to the handler that is about to run. Receives
|
|
48
|
+
* the agentrun about to execute and its parent event; returns the
|
|
49
|
+
* markdown body injected into the current-run user message, right after
|
|
50
|
+
* the `# Runtime` block (see `buildRuntimeContextBlock`). The system
|
|
51
|
+
* prompt itself stays static so it remains a cacheable prefix.
|
|
52
|
+
* Returning `null`, `undefined`, or an empty/whitespace string
|
|
53
|
+
* contributes nothing — useful for "only speak up when relevant"
|
|
54
|
+
* providers.
|
|
55
|
+
*
|
|
56
|
+
* The function is called once per `buildRuntimeContextBlock` (i.e. once
|
|
57
|
+
* per agentrun start). It runs in the host process — providers may close
|
|
58
|
+
* over plugin-local services (DB handles, caches) without going through
|
|
59
|
+
* the bastion. The PromptBuilder reaches the registry through the
|
|
60
|
+
* bastion's `/event-context/` gateway, which fans out to every
|
|
61
|
+
* registered provider in parallel; one bad provider must not poison
|
|
62
|
+
* the prompt, so the gateway isolates rejections and per-call timeouts.
|
|
63
|
+
*/
|
|
64
|
+
export type EventContextProvider = (agentrun: AgentRunRow, event: EventRow) => Promise<string | null | undefined>;
|
|
65
|
+
/**
|
|
66
|
+
* Optional callbacks passed alongside `ctx.events.emit`. Bundled into
|
|
67
|
+
* an options object so additional per-emit hooks (e.g. cancellation,
|
|
68
|
+
* progress) can be added without breaking the call signature.
|
|
69
|
+
*/
|
|
70
|
+
export interface EmitOptions {
|
|
71
|
+
/**
|
|
72
|
+
* Fired for every step row inserted into `stepresults` for this
|
|
73
|
+
* event, in INSERT order. Errors thrown by the callback are caught
|
|
74
|
+
* and logged so a buggy subscriber can't break the emit. When
|
|
75
|
+
* omitted, no `stepresults_new` LISTEN is registered (zero
|
|
76
|
+
* overhead).
|
|
77
|
+
*/
|
|
78
|
+
readonly onStep?: (step: StepResultRow) => void | Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Fired whenever an `agentruns` row tied to this event is inserted
|
|
81
|
+
* or transitions state. Receives the current {@link AgentRunRow} —
|
|
82
|
+
* callers that only care about a specific transition must filter
|
|
83
|
+
* on `row.state` themselves. Errors thrown by the callback are
|
|
84
|
+
* caught and logged so a buggy subscriber can't break the emit.
|
|
85
|
+
* When omitted, no `agentruns_changed` LISTEN is registered (zero
|
|
86
|
+
* overhead).
|
|
87
|
+
*/
|
|
88
|
+
readonly onAgentRun?: (row: AgentRunRow) => void | Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Handle returned by `ctx.events.emit` once the event row has been
|
|
92
|
+
* persisted. The two halves let callers act on the assigned id
|
|
93
|
+
* immediately (e.g. registering it in an in-flight tracker) without
|
|
94
|
+
* blocking on agent execution.
|
|
95
|
+
*/
|
|
96
|
+
export interface EmitHandle {
|
|
97
|
+
/** Database id of the inserted event row, available immediately. */
|
|
98
|
+
readonly id: string;
|
|
99
|
+
/**
|
|
100
|
+
* Resolves with the `result_text` of the last-settled agentrun
|
|
101
|
+
* for the event (empty string if the handler produced no text).
|
|
102
|
+
* Rejects with an `Error` carrying the failed agentrun's `error`
|
|
103
|
+
* message when the event terminates in `failed`.
|
|
104
|
+
*
|
|
105
|
+
* Consumers MUST attach a `.then`/`.catch` (or `await`) to this
|
|
106
|
+
* promise — otherwise a `failed` event surfaces as an unhandled
|
|
107
|
+
* rejection.
|
|
108
|
+
*/
|
|
109
|
+
readonly settled: Promise<string>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Metadata projection of one MCP entry as `ctx.mcp.getList()` exposes
|
|
113
|
+
* it to plugins. Combines the `mcp.yml` key with the (non-sensitive)
|
|
114
|
+
* identifying string of whatever provides the MCP: image for
|
|
115
|
+
* `docker-mcp-registry`, package name for `npm` / `pypi`, URL for
|
|
116
|
+
* `external`.
|
|
117
|
+
*/
|
|
118
|
+
export interface McpInfo {
|
|
119
|
+
/** The top-level key in `mcp.yml`. Plugin-stable identifier. */
|
|
120
|
+
readonly key: string;
|
|
121
|
+
/** Source classifier; matches the `source` value in `mcp.yml`. */
|
|
122
|
+
readonly source: string;
|
|
123
|
+
/**
|
|
124
|
+
* The image (docker-mcp-registry), bare package name (npm / pypi),
|
|
125
|
+
* or URL (external) — whichever fits the source. One field so
|
|
126
|
+
* callers can grep across sources uniformly; the source tag tells
|
|
127
|
+
* them how to interpret it. Always non-empty.
|
|
128
|
+
*/
|
|
129
|
+
readonly package: string;
|
|
130
|
+
}
|
|
131
|
+
export interface HostContext {
|
|
132
|
+
/**
|
|
133
|
+
* Emit an event into the bus. The returned outer promise resolves
|
|
134
|
+
* once the row has been inserted (so the assigned id is known); the
|
|
135
|
+
* `settled` field on the resolved handle reaches its terminal
|
|
136
|
+
* state when the event finishes processing.
|
|
137
|
+
*
|
|
138
|
+
* Implementation subscribes to `events_state` before inserting to
|
|
139
|
+
* avoid missing the terminal NOTIFY on a fast-processed event, and
|
|
140
|
+
* tears the subscription down when `settled` resolves or rejects.
|
|
141
|
+
*
|
|
142
|
+
* Rejects with {@link DuplicateIdempotencyKeyError} when the event's
|
|
143
|
+
* `idempotencyKey` collides with one already in the bus. Emitters
|
|
144
|
+
* that re-derive a stable key (delta-poll re-walks, chat compaction)
|
|
145
|
+
* should catch it and treat the collision as a no-op; the event
|
|
146
|
+
* already exists.
|
|
147
|
+
*/
|
|
148
|
+
readonly events: {
|
|
149
|
+
emit(event: NewEvent, options?: EmitOptions): Promise<EmitHandle>;
|
|
150
|
+
/**
|
|
151
|
+
* Register an async function called once per agentrun start with
|
|
152
|
+
* the agentrun row and its parent event. Its returned string is
|
|
153
|
+
* injected below the `# Available tools` section in the system
|
|
154
|
+
* prompt the handler runs against. Use this to teach the agent
|
|
155
|
+
* situational facts a plugin knows — e.g. "the sender of this
|
|
156
|
+
* mail is currently in a meeting", "this chat thread has 14
|
|
157
|
+
* unread mails attached to it".
|
|
158
|
+
*
|
|
159
|
+
* Providers must be cheap to run: the PromptBuilder fans out to
|
|
160
|
+
* every registered provider in parallel via the bastion's
|
|
161
|
+
* `/event-context/` gateway and waits on them before assembling
|
|
162
|
+
* the prompt. Slow providers delay the start of every agentrun.
|
|
163
|
+
*
|
|
164
|
+
* A provider that returns `null`, `undefined`, or whitespace
|
|
165
|
+
* contributes nothing — typical for "only speak up when
|
|
166
|
+
* relevant" cases. Exceptions thrown inside the provider are
|
|
167
|
+
* isolated by the gateway and logged with the plugin id; the
|
|
168
|
+
* prompt still assembles with the remaining providers' output.
|
|
169
|
+
*
|
|
170
|
+
* Plugins typically call this from their `start(ctx)` hook.
|
|
171
|
+
*/
|
|
172
|
+
registerContextProvider(fn: EventContextProvider): void;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Chat capabilities.
|
|
176
|
+
*
|
|
177
|
+
* `subscribe` taps the host's chatmessages NOTIFY stream for live
|
|
178
|
+
* delivery. Delivery is at-least-once: when no listener returned
|
|
179
|
+
* `true` during a message's lifetime, a future subscriber matching
|
|
180
|
+
* the filter will receive it on registration via the bus's replay
|
|
181
|
+
* pass. Listeners must therefore tolerate being called for an
|
|
182
|
+
* already-displayed message; the typical pattern is to render and
|
|
183
|
+
* return `true` unconditionally.
|
|
184
|
+
*
|
|
185
|
+
* `appendAssistantMessage` is a **history maintenance** helper,
|
|
186
|
+
* not a parallel send API. The user → agent direction still flows
|
|
187
|
+
* exclusively through events. Use it for recording an agent reply
|
|
188
|
+
* that wasn't produced by the AgentRunner's `outputChat` path or
|
|
189
|
+
* by `send_chat` — e.g. the result text of a direct cli-chat
|
|
190
|
+
* handler call after `handle.settled` resolves.
|
|
191
|
+
*
|
|
192
|
+
* User-side messages have **no** equivalent post-emit helper: doing
|
|
193
|
+
* a chatmessages INSERT after `events.emit` returns can race the
|
|
194
|
+
* input-event watcher's claim (FK row-lock vs.
|
|
195
|
+
* `FOR UPDATE SKIP LOCKED`) and strand the event in `pending`.
|
|
196
|
+
* Use {@link NewEvent.userChatMessage} for atomic insertion of a
|
|
197
|
+
* user chatmessage at emit time instead.
|
|
198
|
+
*/
|
|
199
|
+
readonly chat: {
|
|
200
|
+
subscribe(filter: ChatFilter, handler: ChatHandler): Promise<ChatUnsubscribe>;
|
|
201
|
+
/**
|
|
202
|
+
* Insert a `role='assistant'` chatmessage attached to `eventId`.
|
|
203
|
+
* Channel is resolved via the event's
|
|
204
|
+
* `preferred_chat_channel_id`. Safe to call only after the
|
|
205
|
+
* event has settled (agentruns done / failed) — earlier calls
|
|
206
|
+
* can deadlock the input-event watcher's claim.
|
|
207
|
+
*/
|
|
208
|
+
appendAssistantMessage(eventId: string, text: string): Promise<void>;
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Per-event scratch directory operations. Each event the bus
|
|
212
|
+
* persists has a private scratch dir at `/scratch/<event-id>/`
|
|
213
|
+
* (bind-mounted into the agent container and every MCP container).
|
|
214
|
+
* Files staged via `NewEvent.files` land there at emit time; this
|
|
215
|
+
* surface lets plugin code *add* more files later, while an
|
|
216
|
+
* agentrun for that event is still running — typically from inside
|
|
217
|
+
* a {@link PluginTool} that fetches bytes on demand.
|
|
218
|
+
*
|
|
219
|
+
* Path resolution is host-side: callers pass the event id, the
|
|
220
|
+
* host writes under its own `scratchDir`. The directory is
|
|
221
|
+
* created if it doesn't exist yet (defensive — should always exist
|
|
222
|
+
* for events that were emitted with `files`).
|
|
223
|
+
*/
|
|
224
|
+
readonly scratch: {
|
|
225
|
+
/**
|
|
226
|
+
* Stage one or more files into `/scratch/<eventId>/`. Same
|
|
227
|
+
* `EventFile` shape as `NewEvent.files`, same basename
|
|
228
|
+
* validation, same per-file atomic write. Returns the absolute
|
|
229
|
+
* agent-visible paths the files were written to (i.e.
|
|
230
|
+
* `/scratch/<eventId>/<name>`), in input order.
|
|
231
|
+
*
|
|
232
|
+
* Throws if a `name` fails validation (path separator, `..`,
|
|
233
|
+
* empty). Existing files with the same name are overwritten —
|
|
234
|
+
* caller is responsible for unique names.
|
|
235
|
+
*/
|
|
236
|
+
addFiles(eventId: string, files: readonly EventFile[]): Promise<readonly string[]>;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* The pino-style {@link Logger} for the plugin. Use `.info` for the
|
|
240
|
+
* common case and `.debug` / `.warn` / `.error` for leveled output,
|
|
241
|
+
* plus per-call structured fields. Already scoped to the plugin by
|
|
242
|
+
* the host, so records carry a stable `component` tag without the
|
|
243
|
+
* plugin doing anything.
|
|
244
|
+
*/
|
|
245
|
+
readonly logger: Logger;
|
|
246
|
+
/**
|
|
247
|
+
* Absolute host path of the project's `data/` directory.
|
|
248
|
+
*
|
|
249
|
+
* Plugins that need to persist host-side state (auth tokens that
|
|
250
|
+
* must not cross into the container, on-disk caches, etc.) should
|
|
251
|
+
* scope their files under `<dataDir>/<plugin-id>/...` to avoid
|
|
252
|
+
* collisions. Anything that needs to be visible to the agent or to
|
|
253
|
+
* other plugins should go through the bus or workspace instead —
|
|
254
|
+
* `dataDir` is for plugin-private host-only state.
|
|
255
|
+
*/
|
|
256
|
+
readonly dataDir: string;
|
|
257
|
+
/**
|
|
258
|
+
* Absolute host path of the project's `tmp/` directory — the
|
|
259
|
+
* gitignored, safe-to-wipe scratch root (it also holds the daemon
|
|
260
|
+
* pidfile, the postgres port file, and per-event scratch dirs).
|
|
261
|
+
*
|
|
262
|
+
* Use this for ephemeral on-disk caches that can be regenerated at
|
|
263
|
+
* any time (e.g. a refetchable model catalogue). Plugin-private
|
|
264
|
+
* state that must survive a `tmp/` wipe belongs under
|
|
265
|
+
* {@link dataDir} instead.
|
|
266
|
+
*/
|
|
267
|
+
readonly tmpDir: string;
|
|
268
|
+
/**
|
|
269
|
+
* Cheap synchronous probe for whether the host daemon (`./cli.sh
|
|
270
|
+
* start`) is currently running on this machine. Inspects the
|
|
271
|
+
* daemon's pidfile under `<tmpDir>/.daemon.pid` and confirms the
|
|
272
|
+
* recorded pid is still alive — no network calls, no MCP traffic.
|
|
273
|
+
*
|
|
274
|
+
* One-shot CLI commands that need to reach MCPs (i.e. anything
|
|
275
|
+
* routed through `ctx.mcp`) should call this up front and exit
|
|
276
|
+
* early when it returns `false`: the bastion that fronts MCPs
|
|
277
|
+
* lives inside the daemon, so attempting an MCP call without it
|
|
278
|
+
* fails with a generic `fetch failed` that misleads users into
|
|
279
|
+
* chasing login state instead of the missing daemon.
|
|
280
|
+
*/
|
|
281
|
+
readonly isDaemonRunning: () => boolean;
|
|
282
|
+
/**
|
|
283
|
+
* Read/write access to the host's YAML config (`config/config.yml`).
|
|
284
|
+
*
|
|
285
|
+
* Plugins read their own subtree (e.g. `telegram.botToken`) via
|
|
286
|
+
* `ctx.config.getString(...)` rather than touching `process.env`
|
|
287
|
+
* or parsing the file themselves. Required-vs-optional is decided
|
|
288
|
+
* per call: omit the default to throw on missing, or pass `null`
|
|
289
|
+
* to get a `string | null` back and self-disable on absence.
|
|
290
|
+
*/
|
|
291
|
+
readonly config: ConfigService;
|
|
292
|
+
/**
|
|
293
|
+
* Inference-provider resolution. Lets a host-side plugin turn a
|
|
294
|
+
* provider key (as configured under `inference.apiKeys.<key>`) into
|
|
295
|
+
* the concrete `{ apiKey, npmPackage, apiEndpoint? }` it needs to
|
|
296
|
+
* talk to the provider directly — without re-implementing the
|
|
297
|
+
* models.dev + plugin-descriptor lookup the platform already owns.
|
|
298
|
+
*
|
|
299
|
+
* The motivating consumer is the memory plugin, which builds an
|
|
300
|
+
* embedding client against the real upstream (it does not go through
|
|
301
|
+
* the bastion). `resolveProvider` resolves `npmPackage` / `apiEndpoint`
|
|
302
|
+
* from the models.dev catalogue or a plugin's
|
|
303
|
+
* {@link PluginHostManifest.getModelProviders} descriptor, and reads
|
|
304
|
+
* `apiKey` from config. Returns `undefined` when the key isn't
|
|
305
|
+
* configured or doesn't resolve to a known provider.
|
|
306
|
+
*/
|
|
307
|
+
readonly inference: {
|
|
308
|
+
resolveProvider(key: string): Promise<{
|
|
309
|
+
apiKey: string;
|
|
310
|
+
npmPackage: string;
|
|
311
|
+
apiEndpoint?: string;
|
|
312
|
+
} | undefined>;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Access to MCPs declared in `config/mcp.yml`. `getList()` returns
|
|
316
|
+
* metadata only (no connections opened). The two getters return the
|
|
317
|
+
* official `@modelcontextprotocol/sdk` `Client` ready to use —
|
|
318
|
+
* `client.callTool({ name, arguments })`, `client.listTools()`, etc.
|
|
319
|
+
*
|
|
320
|
+
* Connections are lazy and cached: returning a client from
|
|
321
|
+
* `getByKey` does not open one; the first method invocation on the
|
|
322
|
+
* client opens a connection to the bastion's MCP gateway and that
|
|
323
|
+
* connection is reused thereafter. The host closes every cached
|
|
324
|
+
* client on daemon shutdown.
|
|
325
|
+
*
|
|
326
|
+
* First-call latency note: stdio-transport MCPs (npm / pypi /
|
|
327
|
+
* docker-mcp-registry) are cold-spawned on the bastion's first
|
|
328
|
+
* request and idle-reaped after `idleTimeoutSeconds` (per-entry in
|
|
329
|
+
* `mcp.yml`, default 30 min). The first `callTool` / `listTools`
|
|
330
|
+
* after the daemon starts can therefore take seconds; subsequent
|
|
331
|
+
* calls within the idle window are milliseconds.
|
|
332
|
+
*/
|
|
333
|
+
/**
|
|
334
|
+
* Shared calendar data layer. Plugins that act as calendar
|
|
335
|
+
* providers register a {@link CalendarProvider} during `start()`
|
|
336
|
+
* via `ctx.calendar.registerProvider(provider)` and then feed the
|
|
337
|
+
* cache through `upsertCalendar` / `addEvent`. Plugins that just
|
|
338
|
+
* read calendars (notifications, summaries) use the read side
|
|
339
|
+
* (`findEvents`, `getEvent`, `resolveDefaultCalendar`). The core
|
|
340
|
+
* `cal_*` agent tools dispatch through the same interface.
|
|
341
|
+
*/
|
|
342
|
+
readonly calendar: CalendarApi;
|
|
343
|
+
/**
|
|
344
|
+
* Shared mail dispatch layer. Plugins that act as mail providers
|
|
345
|
+
* register a {@link MailProvider} during `start()` via
|
|
346
|
+
* `ctx.mail.registerProvider(provider)`. The core `mail_*` agent
|
|
347
|
+
* tools route through the registered provider by parsing the
|
|
348
|
+
* `<pluginId>:` prefix on every mail id.
|
|
349
|
+
*
|
|
350
|
+
* Unlike `calendar`, there is no read-side surface here — the
|
|
351
|
+
* core does not cache mail bodies or metadata. Pollers emit
|
|
352
|
+
* `mail:<plugin>` events with a prefixed `mail_id` in the payload;
|
|
353
|
+
* the agent reaches the body via `mail_fetch_body` on demand.
|
|
354
|
+
*/
|
|
355
|
+
readonly mail: MailApi;
|
|
356
|
+
/**
|
|
357
|
+
* Read the per-mailbox style template the user (or the
|
|
358
|
+
* extract-style handler) wrote at `data/mail/templates/<mailbox>/<name>.json`.
|
|
359
|
+
* Used by mail provider implementations at send time to inject the
|
|
360
|
+
* user's signature + CSS into outgoing mail. Returns `undefined`
|
|
361
|
+
* when no template exists yet for the (mailbox, name) pair — the
|
|
362
|
+
* caller should fall back to sending bare HTML.
|
|
363
|
+
*
|
|
364
|
+
* `name` defaults to `"default"`. Writes flow through the core
|
|
365
|
+
* `mailstyle_*` agent tools, not directly through this surface —
|
|
366
|
+
* `ctx` exposes only the read path because that's what plugins
|
|
367
|
+
* (mail providers) need.
|
|
368
|
+
*/
|
|
369
|
+
readonly getMailStyleTemplate: (mailbox: string, name?: string) => Promise<MailStyleTemplate | undefined>;
|
|
370
|
+
/**
|
|
371
|
+
* Observe markdown files in the workspace. The snapshot-then-diff
|
|
372
|
+
* pattern: `ctx.workspace.listMarkdownFiles(filter)` for the
|
|
373
|
+
* baseline, `ctx.workspace.onMarkdownFileUpdate(filter, cb)` for
|
|
374
|
+
* live transitions (added / changed / removed under the filter
|
|
375
|
+
* matched against frontmatter and/or workspace-relative path). See
|
|
376
|
+
* {@link WorkspaceWatcherApi} for the full contract.
|
|
377
|
+
*
|
|
378
|
+
* Only available inside the daemon: one-shot CLI invocations
|
|
379
|
+
* (`./cli.sh <plugin> …`) do not spin up the watcher, so calling
|
|
380
|
+
* either method from a CLI command throws synchronously. Daemon
|
|
381
|
+
* plugins are the intended consumer.
|
|
382
|
+
*/
|
|
383
|
+
readonly workspace: WorkspaceWatcherApi;
|
|
384
|
+
readonly mcp: {
|
|
385
|
+
/**
|
|
386
|
+
* Snapshot of every MCP declared in `mcp.yml` as `{ key, source,
|
|
387
|
+
* package }`. Does not open any connections.
|
|
388
|
+
*/
|
|
389
|
+
getList(): readonly McpInfo[];
|
|
390
|
+
/**
|
|
391
|
+
* Return the MCP SDK client for the entry with this yml key.
|
|
392
|
+
* Throws if the key is unknown. The returned client opens its
|
|
393
|
+
* connection lazily on its first method call.
|
|
394
|
+
*/
|
|
395
|
+
getByKey(key: string): Client;
|
|
396
|
+
/**
|
|
397
|
+
* Return the MCP SDK client whose combined `package` field
|
|
398
|
+
* (image / package / url, per source) exactly matches `pkg`.
|
|
399
|
+
* The optional `source` arg narrows the search first. Throws
|
|
400
|
+
* if zero or multiple entries match.
|
|
401
|
+
*/
|
|
402
|
+
getByPackage(pkg: string, source?: string): Client;
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* Aborts when the host daemon is no longer running — i.e. when the
|
|
406
|
+
* `./cli.sh start` process backing this context has stopped or
|
|
407
|
+
* crashed. Always present; consumers don't need to null-check.
|
|
408
|
+
*
|
|
409
|
+
* Intended use: one-shot CLI commands (`./cli.sh <plugin> ...`,
|
|
410
|
+
* `./cli.sh cli-chat`, etc.) race long waits (`emit().settled`,
|
|
411
|
+
* polling loops, interactive prompts) against this signal and exit
|
|
412
|
+
* cleanly when it fires instead of hanging on a postgres connection
|
|
413
|
+
* the dying daemon took with it.
|
|
414
|
+
*
|
|
415
|
+
* For daemon-internal contexts (cron, scheduled handlers, plugin
|
|
416
|
+
* `start`/tool contexts that live inside the daemon process itself)
|
|
417
|
+
* this signal never fires under normal operation — there is no
|
|
418
|
+
* "other daemon" to watch; the daemon owns its own shutdown.
|
|
419
|
+
*
|
|
420
|
+
* When fired by the CLI-side watcher, the abort reason is the
|
|
421
|
+
* string `"daemon-stopped"`. Consumers that need to distinguish
|
|
422
|
+
* daemon-down from other aborts can check {@link DaemonStoppedError}
|
|
423
|
+
* on rejected promises from `ctx.events.emit().settled`.
|
|
424
|
+
*/
|
|
425
|
+
readonly daemonDownSignal: AbortSignal;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Thrown by `ctx.events.emit().settled` when the host daemon stops
|
|
429
|
+
* (or crashes) while a one-shot CLI command was waiting for the event
|
|
430
|
+
* to reach a terminal state. Catch and exit cleanly — there is no
|
|
431
|
+
* postgres on the other side to recover state from.
|
|
432
|
+
*/
|
|
433
|
+
export declare class DaemonStoppedError extends Error {
|
|
434
|
+
constructor(message?: string);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* One tool a plugin contributes to the agent. The agent reaches it
|
|
438
|
+
* via the bastion's `/plugin-tools/` gateway: the container fetches
|
|
439
|
+
* the catalog (name, description, schema), and any tool call routes
|
|
440
|
+
* back to the host where {@link execute} runs **inside the plugin's
|
|
441
|
+
* own Node process** with access to the plugin's deps.
|
|
442
|
+
*
|
|
443
|
+
* Naming: `name` is the bare tool name (e.g. `draft_response`); the
|
|
444
|
+
* gateway registers it under `${pluginId}_${name}` so a handler's
|
|
445
|
+
* `tools:` can address each plugin's tools as a group. Keep `name`
|
|
446
|
+
* lowercase alnum + `_` to avoid the sanitization fold the gateway
|
|
447
|
+
* applies on the key.
|
|
448
|
+
*
|
|
449
|
+
* `inputSchema` is raw JSON Schema — the same shape MCP tools return
|
|
450
|
+
* from `listTools()`. The container converts it via the AI SDK's
|
|
451
|
+
* `jsonSchema()` helper before handing the tool to the model.
|
|
452
|
+
*
|
|
453
|
+
* Result is returned to the agent **unwrapped**: whatever {@link
|
|
454
|
+
* execute} resolves with goes straight back to the model. The
|
|
455
|
+
* `{ ok, result | error }` envelope on the wire is purely the
|
|
456
|
+
* HTTP-layer error channel; agent-facing semantics match how MCP
|
|
457
|
+
* tools and the built-in subagent tools (`schedule_subagent`,
|
|
458
|
+
* `start_subagent`) feel.
|
|
459
|
+
*/
|
|
460
|
+
export interface PluginTool<TInput = unknown, TOutput = unknown> {
|
|
461
|
+
/**
|
|
462
|
+
* Bare tool name (no plugin prefix). The gateway namespaces it as
|
|
463
|
+
* `${pluginId}_${name}` for registration, so this stays short and
|
|
464
|
+
* action-oriented.
|
|
465
|
+
*/
|
|
466
|
+
readonly name: string;
|
|
467
|
+
/** Plain-English description the model sees on its tool list. */
|
|
468
|
+
readonly description: string;
|
|
469
|
+
/** Raw JSON Schema describing the `execute` args. */
|
|
470
|
+
readonly inputSchema: object;
|
|
471
|
+
/**
|
|
472
|
+
* Run the tool. Receives the parsed args and the call context the
|
|
473
|
+
* gateway resolves per invocation (full event + agentrun rows,
|
|
474
|
+
* plugin-scoped HostContext, scoped logger). Throw or reject to
|
|
475
|
+
* surface a tool error to the agent.
|
|
476
|
+
*/
|
|
477
|
+
execute(args: TInput, ctx: PluginToolCallContext): Promise<TOutput>;
|
|
478
|
+
/**
|
|
479
|
+
* Curated tool groups this tool joins, in addition to its
|
|
480
|
+
* identity-derived auto-group (the plugin id). Every name in the
|
|
481
|
+
* array becomes a group that resolves to a union of every tool
|
|
482
|
+
* declaring it — built-in container tools, host-side core tools,
|
|
483
|
+
* and plugin tools all contribute through the same mechanism.
|
|
484
|
+
*
|
|
485
|
+
* Conventional names today:
|
|
486
|
+
*
|
|
487
|
+
* - `core` — the implicit default tool set every handler that
|
|
488
|
+
* omits `tools:` receives. Reserve this for genuinely ambient
|
|
489
|
+
* capabilities (long-term memory, basic chat reply, file read)
|
|
490
|
+
* whose `description` reads cleanly out of any context.
|
|
491
|
+
* - `fs`, `reflection`, … — curated bundles a handler opts into
|
|
492
|
+
* via `tools: fs` or `tools: core, reflection`. New names can
|
|
493
|
+
* be coined freely.
|
|
494
|
+
*
|
|
495
|
+
* Each name must match {@link IDENT_PATTERN} and must not be one
|
|
496
|
+
* of the three reserved names (`all`, `none`, `mcp`). A
|
|
497
|
+
* plugin tool cannot list its own plugin id (the auto-group
|
|
498
|
+
* already covers that). Registration fails loudly on either
|
|
499
|
+
* violation.
|
|
500
|
+
*/
|
|
501
|
+
readonly groups?: readonly string[];
|
|
502
|
+
/**
|
|
503
|
+
* Security classification gating who may invoke this tool. Omitted
|
|
504
|
+
* ⇒ {@link DEFAULT_TOOL_LEVEL} (`default`, anyone). Set `approval`
|
|
505
|
+
* for external mutations (sending mail, …) and `privileged` for
|
|
506
|
+
* capabilities restricted to trusted-input runs. Enforced
|
|
507
|
+
* container-side in `ToolsFactory`. See {@link ToolLevel}.
|
|
508
|
+
*/
|
|
509
|
+
readonly level?: ToolLevel;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Per-invocation context the host gateway resolves for every plugin
|
|
513
|
+
* tool call. The container POSTs `{ args, eventId, agentrunId }`; the
|
|
514
|
+
* gateway loads the full rows and the plugin's scoped {@link
|
|
515
|
+
* HostContext} before calling {@link PluginTool.execute}.
|
|
516
|
+
*/
|
|
517
|
+
export interface PluginToolCallContext {
|
|
518
|
+
/**
|
|
519
|
+
* The originating event row. Tools dig into `payload` here for
|
|
520
|
+
* the entity they're acting on (e.g. mail id for `draft_response`).
|
|
521
|
+
*/
|
|
522
|
+
readonly event: EventRow;
|
|
523
|
+
/** The agentrun row that issued the tool call. */
|
|
524
|
+
readonly agentrun: AgentRunRow;
|
|
525
|
+
/**
|
|
526
|
+
* The plugin's own {@link HostContext} — same instance the
|
|
527
|
+
* plugin's `start`/`tools` saw. Lets the tool emit follow-up
|
|
528
|
+
* events, reach MCPs, read config, etc.
|
|
529
|
+
*/
|
|
530
|
+
readonly host: HostContext;
|
|
531
|
+
/** Logger child pre-scoped to `{ plugin, tool, eventId, agentrunId }`. */
|
|
532
|
+
readonly log: Logger;
|
|
533
|
+
/**
|
|
534
|
+
* Per-call runner context: byte budget plus a `spill` callback that
|
|
535
|
+
* writes oversized results into the calling event's scratch dir.
|
|
536
|
+
* Pass this into {@link import("./ToolRunner.js").runJsonTool} (or
|
|
537
|
+
* its `JsonLines`/`Text` siblings) so the runner can decide between
|
|
538
|
+
* inline and offload-to-scratch consistently across host and
|
|
539
|
+
* container.
|
|
540
|
+
*/
|
|
541
|
+
readonly toolRunContext: ToolRunContext;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Host-side surface a plugin may declare. All fields optional; a
|
|
545
|
+
* plugin that's pure workspace template (no host code) leaves `host`
|
|
546
|
+
* out of its manifest entirely.
|
|
547
|
+
*
|
|
548
|
+
* Each function receives the {@link HostContext} so handlers close
|
|
549
|
+
* over `ctx` and never need to look up host services another way.
|
|
550
|
+
*/
|
|
551
|
+
export interface PluginHostManifest {
|
|
552
|
+
/**
|
|
553
|
+
* Synchronous, side-effect-light setup. Runs once per process,
|
|
554
|
+
* **before any plugin's `start`** and before any plugin one-shot
|
|
555
|
+
* CLI command's `run`. Use this for module-level state that other
|
|
556
|
+
* plugins consume — e.g. populating an API-key constant the
|
|
557
|
+
* plugin's library exports — so the order in which siblings call
|
|
558
|
+
* each other doesn't depend on `start` finishing first.
|
|
559
|
+
*
|
|
560
|
+
* Hard rules: no async, no network, no DB, no daemons, no
|
|
561
|
+
* subscriptions. If you need any of those, do them in `start`.
|
|
562
|
+
*
|
|
563
|
+
* Skipped for introspective paths (`--help`, `config lint`) so a
|
|
564
|
+
* misconfigured file still lets the user inspect / lint.
|
|
565
|
+
*/
|
|
566
|
+
prepare?(ctx: HostContext): void;
|
|
567
|
+
/**
|
|
568
|
+
* Long-running daemon (sockets, listeners, etc.). Awaited at host
|
|
569
|
+
* startup; should resolve once setup is done. Long-lived work
|
|
570
|
+
* lives on background promises the daemon owns.
|
|
571
|
+
*
|
|
572
|
+
* Runs after every plugin's {@link prepare} has completed, so
|
|
573
|
+
* cross-plugin module state populated in `prepare` is reliably
|
|
574
|
+
* visible by the time any `start` body runs.
|
|
575
|
+
*/
|
|
576
|
+
start?(ctx: HostContext): Promise<void>;
|
|
577
|
+
/**
|
|
578
|
+
* Drain hook called during daemon shutdown. Awaited in reverse
|
|
579
|
+
* registration order (LIFO of `start`), wrapped in a `safeStop`
|
|
580
|
+
* so a throw is logged but does not abort the rest of the drain.
|
|
581
|
+
*
|
|
582
|
+
* Use this for plugins that own dirty in-memory state which must
|
|
583
|
+
* be flushed to disk before the process exits — the memory
|
|
584
|
+
* plugin's Orama index is the motivating case.
|
|
585
|
+
*
|
|
586
|
+
* Hard rules: must resolve within the daemon-wide drain budget
|
|
587
|
+
* (see `DRAINING_DEADLINE_MS` in `host/src/commands/Start.ts`).
|
|
588
|
+
* The shared budget covers every plugin's stop plus the postgres
|
|
589
|
+
* + bastion teardown, so individual stops should keep their work
|
|
590
|
+
* tight — a single bad flush should not eat the whole budget.
|
|
591
|
+
*
|
|
592
|
+
* Not called for one-shot CLI command runs — only on daemon
|
|
593
|
+
* shutdown via `PluginHost.close()`.
|
|
594
|
+
*/
|
|
595
|
+
stop?(ctx: HostContext): Promise<void>;
|
|
596
|
+
/**
|
|
597
|
+
* Declarative list of {@link PluginTool}s this plugin contributes
|
|
598
|
+
* to the agent. Collected once, **after** the plugin's `start`
|
|
599
|
+
* resolves, so closures over `start`-time state are safe.
|
|
600
|
+
*
|
|
601
|
+
* Hard rules: synchronous, side-effect-free — just build and
|
|
602
|
+
* return the tool list. The host registers each tool under
|
|
603
|
+
* `${plugin.id}_${tool.name}` (after sanitization) on the bastion's
|
|
604
|
+
* `/plugin-tools/` gateway, and the container's tools client
|
|
605
|
+
* discovers them per agentrun.
|
|
606
|
+
*
|
|
607
|
+
* A plugin id used for tools must match {@link IDENT_PATTERN} and
|
|
608
|
+
* not collide with an MCP id — the registry enforces both at
|
|
609
|
+
* register time so the failure surfaces at startup, not at first
|
|
610
|
+
* call.
|
|
611
|
+
*/
|
|
612
|
+
tools?(ctx: HostContext): readonly PluginTool[];
|
|
613
|
+
/**
|
|
614
|
+
* Supply {@link ModelMetaData} for a model the default (models.dev)
|
|
615
|
+
* database does not cover. The host calls this on every plugin that
|
|
616
|
+
* declares it — in registration order — only when a `(provider,
|
|
617
|
+
* model)` lookup misses the built-in database, and takes the first
|
|
618
|
+
* non-`undefined` result.
|
|
619
|
+
*
|
|
620
|
+
* - Return `undefined` to defer (this plugin doesn't know the model,
|
|
621
|
+
* or its own data isn't ready yet).
|
|
622
|
+
* - Throw {@link ModelNotSupported} when the plugin authoritatively
|
|
623
|
+
* owns `provider` and knows `model` is not supported there — the
|
|
624
|
+
* host treats that as a definitive "no" and stops the lookup
|
|
625
|
+
* without consulting further plugins.
|
|
626
|
+
*
|
|
627
|
+
* Other thrown errors are logged and the lookup continues with the
|
|
628
|
+
* next plugin, so a transient read failure doesn't poison results
|
|
629
|
+
* another plugin could still provide.
|
|
630
|
+
*/
|
|
631
|
+
getModelMetaData?(ctx: HostContext, provider: string, model: string): Promise<ModelMetaData | undefined>;
|
|
632
|
+
/**
|
|
633
|
+
* Declare the inference provider(s) this plugin owns — providers the
|
|
634
|
+
* models.dev database doesn't cover (e.g. `featherless`). Returned
|
|
635
|
+
* synchronously from constants (no fetch); the host calls it during
|
|
636
|
+
* provider resolution to learn the provider's `npmPackage` (which
|
|
637
|
+
* `create*` to use, how to inject auth) and `apiEndpoint` (where the
|
|
638
|
+
* reverse proxy forwards). The declared `key` is what the operator
|
|
639
|
+
* puts under `inference.apiKeys.<key>`.
|
|
640
|
+
*
|
|
641
|
+
* On a key collision with models.dev, the models.dev entry wins; on a
|
|
642
|
+
* collision between two plugins, the first registered wins (logged).
|
|
643
|
+
*/
|
|
644
|
+
getModelProviders?(ctx: HostContext): readonly ModelProviderDescriptor[];
|
|
645
|
+
/**
|
|
646
|
+
* Default command for the plugin's CLI root. When set, invoking
|
|
647
|
+
* the plugin id with no subcommand (e.g. `cli.sh cli-chat`) runs
|
|
648
|
+
* this command's `run`. Its `args` are also lifted onto the root.
|
|
649
|
+
* Useful for plugins whose primary mode is interactive (REPL-like)
|
|
650
|
+
* — the user shouldn't have to remember a subcommand name.
|
|
651
|
+
*
|
|
652
|
+
* Subcommands declared in {@link commands} are still available
|
|
653
|
+
* under the same root, so `cli.sh cli-chat send "hi"` keeps
|
|
654
|
+
* working alongside the bare-root invocation.
|
|
655
|
+
*/
|
|
656
|
+
main?(ctx: HostContext): AnyCommandDef;
|
|
657
|
+
/**
|
|
658
|
+
* Citty commands contributed by this plugin. They are mounted
|
|
659
|
+
* under `subCommands[<plugin.id>]` in the root CLI by default
|
|
660
|
+
* (e.g. `cli.sh cli-chat send "hi"`).
|
|
661
|
+
*/
|
|
662
|
+
commands?(ctx: HostContext): readonly AnyCommandDef[];
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* The full plugin manifest shape returned by {@link definePlugin}.
|
|
666
|
+
*
|
|
667
|
+
* A minimal plugin needs only `id`. Any combination of `host` and
|
|
668
|
+
* `workspaceTemplate` may be added — they are independent concerns.
|
|
669
|
+
* Plugin code never ships into the container: tools the agent can
|
|
670
|
+
* call are declared on {@link PluginHostManifest.tools} and execute
|
|
671
|
+
* host-side inside the plugin's own Node process via the bastion.
|
|
672
|
+
*/
|
|
673
|
+
export interface PluginManifest {
|
|
674
|
+
/**
|
|
675
|
+
* Plugin id, matching `[a-z0-9-]+`. Used to namespace the
|
|
676
|
+
* plugin's CLI commands (`cli.sh <id> <subcommand>`). A plugin
|
|
677
|
+
* that contributes {@link PluginHostManifest.tools | tools} must
|
|
678
|
+
* additionally satisfy {@link IDENT_PATTERN} so the id is usable
|
|
679
|
+
* as a tool group name; the tools registry enforces that at
|
|
680
|
+
* register time.
|
|
681
|
+
*/
|
|
682
|
+
readonly id: string;
|
|
683
|
+
/** Host-side surface (CLI commands, daemons, cronjobs, tools). */
|
|
684
|
+
readonly host?: PluginHostManifest;
|
|
685
|
+
/**
|
|
686
|
+
* Absolute path to a directory of files copied into
|
|
687
|
+
* `data/workspace/` on first install. Existing files are left
|
|
688
|
+
* alone — diff/merge on plugin updates is a separate concern.
|
|
689
|
+
* Plugins typically derive this with
|
|
690
|
+
* `path.join(__dirname, "..", "workspace-template")` (or the ESM
|
|
691
|
+
* `import.meta.url` equivalent).
|
|
692
|
+
*/
|
|
693
|
+
readonly workspaceTemplate?: string;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Identity function used by plugins to declare their manifest:
|
|
697
|
+
*
|
|
698
|
+
* ```ts
|
|
699
|
+
* export default definePlugin({
|
|
700
|
+
* id: "cli-chat",
|
|
701
|
+
* host: { commands: (ctx) => [sendCommand(ctx)] },
|
|
702
|
+
* });
|
|
703
|
+
* ```
|
|
704
|
+
*
|
|
705
|
+
* Exists purely so TypeScript narrows literal types correctly without
|
|
706
|
+
* the caller writing a `satisfies PluginManifest` clause. Same pattern
|
|
707
|
+
* as Vite's `defineConfig` / Astro's `defineConfig`.
|
|
708
|
+
*/
|
|
709
|
+
export declare function definePlugin<T extends PluginManifest>(plugin: T): T;
|