xyne-plugin 1.2.24 → 1.2.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xyne-plugin",
3
- "version": "1.2.24",
3
+ "version": "1.2.27",
4
4
  "description": "SDK for writing Xyne plugins — define custom tools, hooks, and extensions",
5
5
  "type": "module",
6
6
  "exports": {
package/src/hooks.ts CHANGED
@@ -1,16 +1,20 @@
1
+ /**
2
+ * Plugin hook type definitions.
3
+ *
4
+ * A plugin is a function that receives context about the current workspace
5
+ * and returns a bag of hooks the host can call at various extension points.
6
+ *
7
+ * Pattern: `Plugin = (input) => Promise<Hooks>`
8
+ */
9
+
1
10
  import type { ToolDefinition } from "./tool"
2
11
 
3
12
  // ─── Simplified types for hook signatures ───────────────────────────────────
4
- // These are minimal versions of internal types — just enough for plugin authors
5
- // to work with hook parameters.
6
13
 
7
14
  /** Model descriptor passed to chat hooks. */
8
15
  export interface ModelSpec {
9
- /** Model identifier, e.g. "claude-opus-4-6" */
10
16
  id: string
11
- /** Provider identifier, e.g. "anthropic" */
12
17
  providerID: string
13
- /** Human-readable name */
14
18
  name: string
15
19
  [key: string]: unknown
16
20
  }
@@ -31,8 +35,6 @@ export interface ProviderInfo {
31
35
  models: Record<string, { name: string; contextLength: number; maxOutputTokens: number; [key: string]: unknown }>
32
36
  }
33
37
 
34
- // ─── Message & Part types for hook signatures ────────────────────────────────
35
-
36
38
  /** Token usage breakdown. */
37
39
  export interface TokenInfo {
38
40
  input: number
@@ -42,58 +44,29 @@ export interface TokenInfo {
42
44
  reasoning: number
43
45
  }
44
46
 
45
- /** Error descriptor attached to failed messages. */
46
- export type MessageError =
47
- | { name: "AuthError"; providerID: string; message: string }
48
- | { name: "APIError"; message: string; statusCode?: number; isRetryable: boolean }
49
- | { name: "ContextOverflowError"; message: string }
50
- | { name: "AbortedError"; message: string }
51
- | { name: "OutputLengthError" }
52
- | { name: "StructuredOutputError"; message: string; retries: number }
53
- | { name: "Unknown"; message: string }
54
-
55
- /** A message in the session (user or assistant turn). */
47
+ /** Message info passed to hooks. */
56
48
  export interface MessageInfo {
57
49
  id: string
58
50
  sessionID: string
59
51
  role: "user" | "assistant"
60
52
  created: number
61
53
  completed?: number
62
- parentID?: string
63
54
  agentID?: string
64
55
  modelID?: string
65
56
  providerID?: string
66
- path?: { cwd: string; root: string }
67
- finish?: string
68
- error?: MessageError
69
- cost?: number
70
57
  tokens?: TokenInfo
71
- requestedModel?: { providerID: string; modelID: string }
72
- format?: { type: string; [key: string]: unknown }
73
- structured?: unknown
74
- summary?: string
58
+ cost?: number
59
+ [key: string]: unknown
75
60
  }
76
61
 
77
- /** Tool call state. */
78
- export type ToolState =
79
- | { status: "pending" }
80
- | { status: "running"; input: unknown; startTime: number; title?: string; metadata?: unknown }
81
- | { status: "completed"; input: unknown; output: string; title: string; metadata: unknown; startTime: number; endTime: number; attachments?: Array<{ type: string; url: string; mime: string }>; compacted?: number }
82
- | { status: "error"; input: unknown; error: string; startTime: number; endTime: number }
83
-
84
- /** Discriminated union of all part types. */
85
- export type PartInfo =
86
- | { type: "text"; id: string; messageID: string; sessionID: string; text: string; startTime: number; endTime?: number; metadata?: Record<string, unknown>; synthetic?: boolean; ignored?: boolean }
87
- | { type: "reasoning"; id: string; messageID: string; sessionID: string; text: string; startTime: number; endTime?: number; metadata?: Record<string, unknown> }
88
- | { type: "tool"; id: string; messageID: string; sessionID: string; callID: string; tool: string; state: ToolState }
89
- | { type: "step-start"; id: string; messageID: string; sessionID: string }
90
- | { type: "step-finish"; id: string; messageID: string; sessionID: string; reason: string; tokens?: TokenInfo; cost?: number }
91
- | { type: "retry"; id: string; messageID: string; sessionID: string; attempt: number; error: MessageError; time: { created: number } }
92
- | { type: "compaction"; id: string; messageID: string; sessionID: string; auto: boolean; overflow?: boolean }
93
- | { type: "subtask"; id: string; messageID: string; sessionID: string; prompt: string; description: string; agent: string; model?: { providerID: string; modelID: string }; command?: string }
94
- | { type: "patch"; id: string; messageID: string; sessionID: string; hash: string; files: string[] }
95
- | { type: "media"; id: string; messageID: string; sessionID: string; mime: string; filename?: string; url: string }
96
- | { type: "agent"; id: string; messageID: string; sessionID: string; name: string }
62
+ /** Part info passed to hooks. */
63
+ export interface PartInfo {
64
+ id: string
65
+ messageID: string
66
+ sessionID: string
67
+ type: string
68
+ [key: string]: unknown
69
+ }
97
70
 
98
71
  // ─── Plugin agent types ─────────────────────────────────────────────────────
99
72
 
@@ -102,7 +75,9 @@ export type PartInfo =
102
75
  *
103
76
  * Plugin agents follow the same Agent.Info shape but use a reduced surface:
104
77
  * - `name` is derived from the record key
105
- * - `tools` is a record of ToolDefinitions that are converted to Tool.Info[] at registration time
78
+ * - `native` is always false
79
+ * - `tools` is a record of ToolDefinitions (same format as plugin tools)
80
+ * that are converted to Tool.Info[] at registration time
106
81
  * - `permission` accepts a simple string map instead of a raw Permission.Ruleset
107
82
  */
108
83
  export interface PluginAgentDef {
@@ -114,12 +89,14 @@ export interface PluginAgentDef {
114
89
  /** Tools scoped to this agent. These are added on top of session tools. */
115
90
  tools?: Record<string, ToolDefinition>
116
91
  /**
117
- * If true, this agent ONLY has access to its own `tools` (built-in session tools are excluded).
92
+ * If true, this agent ONLY has access to its own `tools` (plus built-in session tools are excluded).
118
93
  * If false (default), the agent's tools are merged with the session's tool set.
119
94
  */
120
95
  isolatedTools?: boolean
121
96
  /** Permission overrides. Keys are tool IDs or "*", values are "allow" | "deny" | "ask". */
122
97
  permission?: Record<string, "allow" | "deny" | "ask" | Record<string, "allow" | "deny" | "ask">>
98
+ /** Subagent names this agent can spawn via the Task tool. If unset, all subagents are available. */
99
+ agents?: string[]
123
100
  maxSteps?: number
124
101
  color?: string
125
102
  hidden?: boolean
@@ -134,11 +111,11 @@ export type PluginInput = {
134
111
  worktree: string
135
112
  appName: string
136
113
  /** SDK client — available for plugins that need session/message/tool APIs. */
137
- client?: unknown
114
+ client?: unknown | undefined
138
115
  /** Base URL for the local server (if running). */
139
- serverUrl?: URL
116
+ serverUrl?: URL | undefined
140
117
  /** Bun shell for running commands. */
141
- $?: unknown
118
+ $?: unknown | undefined
142
119
  }
143
120
 
144
121
  export type Plugin = (input: PluginInput) => Promise<Hooks>
@@ -206,7 +183,7 @@ export type AuthMethod =
206
183
  label: string
207
184
  /** Message shown in the TUI while authorize() runs (default: "Connecting..."). */
208
185
  pendingMessage?: string
209
- /** Hint shown below the pending message. */
186
+ /** Hint shown below the pending message (e.g. "Waiting for approval... · esc cancel"). */
210
187
  pendingHint?: string
211
188
  prompts?: AuthPrompt[]
212
189
  /** Plugin handles the entire auth flow. UI shows pendingMessage while this runs. */
@@ -218,6 +195,10 @@ export type AuthMethod =
218
195
 
219
196
  /**
220
197
  * Options returned by an auth loader to configure the provider SDK.
198
+ *
199
+ * Standard SDK options (baseURL, apiKey, fetch) are merged into the SDK factory call.
200
+ * The optional `getModel` callback lets the loader control model selection
201
+ * (e.g. routing gpt-5 to a responses API vs chat API).
221
202
  */
222
203
  export interface AuthLoaderResult {
223
204
  baseURL?: string
@@ -234,6 +215,7 @@ export type AuthHook = {
234
215
  * Called after auth succeeds to configure the provider SDK.
235
216
  * Receives a getter for the auth entry and the provider info.
236
217
  * Returns SDK options (baseURL, apiKey, fetch, getModel, etc.)
218
+ * that are merged into the provider's SDK configuration.
237
219
  */
238
220
  loader?: (auth: () => Promise<unknown>, provider: unknown) => Promise<AuthLoaderResult>
239
221
  methods: AuthMethod[]
@@ -279,13 +261,13 @@ export interface Hooks {
279
261
 
280
262
  /** Mutate LLM sampling parameters before a chat request. */
281
263
  "chat.params"?: (
282
- input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo; message?: MessageInfo },
264
+ input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo | undefined; message?: MessageInfo | undefined },
283
265
  output: { temperature?: number; topP?: number; topK?: number; options?: Record<string, unknown> },
284
266
  ) => Promise<void>
285
267
 
286
268
  /** Inject extra HTTP headers into the provider request. */
287
269
  "chat.headers"?: (
288
- input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo; message?: MessageInfo },
270
+ input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo | undefined; message?: MessageInfo | undefined },
289
271
  output: { headers: Record<string, string> },
290
272
  ) => Promise<void>
291
273
 
@@ -313,15 +295,20 @@ export interface Hooks {
313
295
  output: { parts: PartInfo[] },
314
296
  ) => Promise<void>
315
297
 
316
- /** Modify tool args before execution. */
298
+ /**
299
+ * Modify tool args before execution, or block the tool call entirely.
300
+ *
301
+ * To block: set `output.blocked = true` and optionally `output.reason = "why"`.
302
+ * The reason is returned to the LLM as the tool output so it can adapt.
303
+ */
317
304
  "tool.execute.before"?: (
318
- input: { tool: string; sessionID: string; callID: string },
319
- output: { args: unknown },
305
+ input: { tool: string; sessionID: string; callID: string; agent: string; isSubagent: boolean },
306
+ output: { args: unknown; blocked?: boolean; reason?: string },
320
307
  ) => Promise<void>
321
308
 
322
309
  /** Modify tool output after execution. */
323
310
  "tool.execute.after"?: (
324
- input: { tool: string; sessionID: string; callID: string; args: unknown },
311
+ input: { tool: string; sessionID: string; callID: string; agent: string; isSubagent: boolean; args: unknown },
325
312
  output: { title: string; output: string; metadata: unknown },
326
313
  ) => Promise<void>
327
314
 
@@ -340,7 +327,7 @@ export interface Hooks {
340
327
  /** Customise context preservation during session compaction. */
341
328
  "session.compacting"?: (
342
329
  input: { sessionID: string },
343
- output: { context: string[]; prompt?: string },
330
+ output: { context: string[]; prompt?: string | undefined },
344
331
  ) => Promise<void>
345
332
 
346
333
  /** Text completion hook — modify or provide text completions. */
package/src/index.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  /**
2
- * xyne-plugin — SDK for writing Xyne plugins.
2
+ * @xyne/plugin — SDK for writing Xyne plugins.
3
+ *
4
+ * Self-contained — no @tui/agent-core dependency.
5
+ * All types are defined locally in hooks.ts and tool.ts.
3
6
  *
4
7
  * @example
5
8
  * ```ts
6
- * import { type Plugin, tool, z } from "xyne-plugin"
9
+ * import { type Plugin, tool, z } from "@xyne/plugin"
7
10
  *
8
11
  * const plugin: Plugin = async ({ directory }) => ({
9
12
  * tool: {
@@ -21,12 +24,36 @@
21
24
  * ```
22
25
  */
23
26
 
24
- // Plugin types
25
- export type { Plugin, PluginInput, Hooks, PluginAgentDef, AuthHook, AuthMethod, AuthPrompt, AuthOAuthResult, AuthLoaderResult, ModelSpec, ProviderInfo, CredentialSource, MessageInfo, PartInfo, ToolState, TokenInfo, MessageError } from "./hooks"
27
+ // Plugin types (from hooks.ts — self-contained, no workspace imports)
28
+ export type {
29
+ Plugin,
30
+ PluginInput,
31
+ Hooks,
32
+ PluginAgentDef,
33
+ AuthMethod,
34
+ AuthPrompt,
35
+ AuthOAuthResult,
36
+ AuthHook,
37
+ AuthLoaderResult,
38
+ ModelSpec,
39
+ Message,
40
+ CredentialSource,
41
+ ProviderInfo,
42
+ TokenInfo,
43
+ MessageInfo,
44
+ PartInfo,
45
+ } from "./hooks"
26
46
 
27
- // Tool authoring
47
+ // Tool authoring (from tool.ts — self-contained)
28
48
  export { tool } from "./tool"
29
- export type { ToolDefinition, ToolContext, ToolAuthConfig, ServiceCredentials, ToolAttachment, PluginToolResult } from "./tool"
49
+ export type {
50
+ ToolDefinition,
51
+ ToolContext,
52
+ ToolAuthConfig,
53
+ ServiceCredentials,
54
+ ToolAttachment,
55
+ PluginToolResult,
56
+ } from "./tool"
30
57
 
31
58
  // Zod re-export for convenience
32
59
  export { z } from "zod"
package/src/tool.ts CHANGED
@@ -63,6 +63,13 @@ export type ToolContext = {
63
63
  abort: AbortSignal
64
64
  metadata(input: { title?: string; metadata?: { [key: string]: unknown } }): void
65
65
  ask(input: AskInput): Promise<void>
66
+ /**
67
+ * Ask the user a free-text question and wait for their reply.
68
+ * Pauses the agent loop until the user submits an answer.
69
+ * Returns a string[][] — one string[] per question containing the selected/typed answers.
70
+ * Pass options: [] to render a plain free-text input with no predefined choices.
71
+ */
72
+ askQuestion(input: AskQuestionInput): Promise<string[][]>
66
73
  /**
67
74
  * Service credentials for this tool's declared `auth` service.
68
75
  * Populated automatically before execute() when the tool has a `ToolAuthConfig`
@@ -85,8 +92,46 @@ type AskInput = {
85
92
  metadata: { [key: string]: unknown }
86
93
  }
87
94
 
95
+ type QuestionOption = {
96
+ label: string
97
+ description: string
98
+ }
99
+
100
+ type AskQuestionInput = {
101
+ questions: Array<{
102
+ question: string
103
+ /** Very short label shown in the tab bar (max 30 chars). Defaults to the first 30 chars of question. */
104
+ header?: string
105
+ /** Predefined choices. Pass [] or omit for a plain free-text input. */
106
+ options?: QuestionOption[]
107
+ multiple?: boolean
108
+ }>
109
+ }
110
+
88
111
  // ─── Tool definition ────────────────────────────────────────────────────────
89
112
 
113
+ /**
114
+ * Define a custom tool. Use this in .agent/tool/*.ts files or in plugin packages.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * import { tool } from "@tui/agent-core/plugin"
119
+ *
120
+ * export default tool({
121
+ * description: "Does something useful",
122
+ * args: {
123
+ * query: tool.schema.string().describe("Search query"),
124
+ * },
125
+ * async execute(args, ctx) {
126
+ * return "result"
127
+ * },
128
+ * })
129
+ * ```
130
+ */
131
+ /**
132
+ * The wide storage type used in Hooks.tool and internal registries.
133
+ * Plugin authors don't construct this directly — use the `tool()` factory.
134
+ */
90
135
  export interface ToolAttachment {
91
136
  type: "file"
92
137
  mime: string
@@ -96,10 +141,6 @@ export interface ToolAttachment {
96
141
 
97
142
  export type PluginToolResult = string | { text: string; attachments: ToolAttachment[] }
98
143
 
99
- /**
100
- * The wide storage type used in Hooks.tool and internal registries.
101
- * Plugin authors don't construct this directly — use the `tool()` factory.
102
- */
103
144
  export interface ToolDefinition {
104
145
  description: string
105
146
  args: Record<string, z.ZodType>
@@ -108,19 +149,17 @@ export interface ToolDefinition {
108
149
  }
109
150
 
110
151
  /**
111
- * Define a custom tool. Use this in plugin packages or local tool files.
152
+ * Define a custom tool. Args are inferred from the zod schema so
153
+ * `execute` gets strongly-typed parameters.
112
154
  *
113
155
  * @example
114
156
  * ```ts
115
- * import { tool, z } from "xyne-plugin"
116
- *
117
157
  * export default tool({
118
- * description: "Search the web",
119
- * args: {
120
- * query: z.string().describe("Search query"),
121
- * },
158
+ * description: "Does something useful",
159
+ * args: { query: tool.schema.string().describe("Search query") },
122
160
  * async execute(args, ctx) {
123
- * return `Results for ${args.query}`
161
+ * args.query // string
162
+ * return "result"
124
163
  * },
125
164
  * })
126
165
  * ```