xyne-plugin 1.2.22 → 1.2.26

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.22",
3
+ "version": "1.2.26",
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
@@ -127,6 +104,18 @@ export interface PluginAgentDef {
127
104
  topP?: number
128
105
  }
129
106
 
107
+ // ─── Provider context for chat hooks ────────────────────────────────────────
108
+
109
+ /** Provider metadata exposed to plugin hooks. */
110
+ export interface ProviderInfo {
111
+ id: string
112
+ name: string
113
+ source: CredentialSource
114
+ env: string[]
115
+ options: Record<string, unknown>
116
+ models: Record<string, { name: string; contextLength: number; maxOutputTokens: number; [key: string]: unknown }>
117
+ }
118
+
130
119
  // ─── Plugin function types ───────────────────────────────────────────────────
131
120
 
132
121
  export type PluginInput = {
@@ -134,11 +123,11 @@ export type PluginInput = {
134
123
  worktree: string
135
124
  appName: string
136
125
  /** SDK client — available for plugins that need session/message/tool APIs. */
137
- client?: unknown
126
+ client?: unknown | undefined
138
127
  /** Base URL for the local server (if running). */
139
- serverUrl?: URL
128
+ serverUrl?: URL | undefined
140
129
  /** Bun shell for running commands. */
141
- $?: unknown
130
+ $?: unknown | undefined
142
131
  }
143
132
 
144
133
  export type Plugin = (input: PluginInput) => Promise<Hooks>
@@ -206,7 +195,7 @@ export type AuthMethod =
206
195
  label: string
207
196
  /** Message shown in the TUI while authorize() runs (default: "Connecting..."). */
208
197
  pendingMessage?: string
209
- /** Hint shown below the pending message. */
198
+ /** Hint shown below the pending message (e.g. "Waiting for approval... · esc cancel"). */
210
199
  pendingHint?: string
211
200
  prompts?: AuthPrompt[]
212
201
  /** Plugin handles the entire auth flow. UI shows pendingMessage while this runs. */
@@ -218,6 +207,10 @@ export type AuthMethod =
218
207
 
219
208
  /**
220
209
  * Options returned by an auth loader to configure the provider SDK.
210
+ *
211
+ * Standard SDK options (baseURL, apiKey, fetch) are merged into the SDK factory call.
212
+ * The optional `getModel` callback lets the loader control model selection
213
+ * (e.g. routing gpt-5 to a responses API vs chat API).
221
214
  */
222
215
  export interface AuthLoaderResult {
223
216
  baseURL?: string
@@ -234,6 +227,7 @@ export type AuthHook = {
234
227
  * Called after auth succeeds to configure the provider SDK.
235
228
  * Receives a getter for the auth entry and the provider info.
236
229
  * Returns SDK options (baseURL, apiKey, fetch, getModel, etc.)
230
+ * that are merged into the provider's SDK configuration.
237
231
  */
238
232
  loader?: (auth: () => Promise<unknown>, provider: unknown) => Promise<AuthLoaderResult>
239
233
  methods: AuthMethod[]
@@ -279,13 +273,13 @@ export interface Hooks {
279
273
 
280
274
  /** Mutate LLM sampling parameters before a chat request. */
281
275
  "chat.params"?: (
282
- input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo; message?: MessageInfo },
276
+ input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo | undefined; message?: MessageInfo | undefined },
283
277
  output: { temperature?: number; topP?: number; topK?: number; options?: Record<string, unknown> },
284
278
  ) => Promise<void>
285
279
 
286
280
  /** Inject extra HTTP headers into the provider request. */
287
281
  "chat.headers"?: (
288
- input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo; message?: MessageInfo },
282
+ input: { sessionID: string; agent: string; model: ModelSpec; provider?: ProviderInfo | undefined; message?: MessageInfo | undefined },
289
283
  output: { headers: Record<string, string> },
290
284
  ) => Promise<void>
291
285
 
@@ -313,15 +307,20 @@ export interface Hooks {
313
307
  output: { parts: PartInfo[] },
314
308
  ) => Promise<void>
315
309
 
316
- /** Modify tool args before execution. */
310
+ /**
311
+ * Modify tool args before execution, or block the tool call entirely.
312
+ *
313
+ * To block: set `output.blocked = true` and optionally `output.reason = "why"`.
314
+ * The reason is returned to the LLM as the tool output so it can adapt.
315
+ */
317
316
  "tool.execute.before"?: (
318
- input: { tool: string; sessionID: string; callID: string },
319
- output: { args: unknown },
317
+ input: { tool: string; sessionID: string; callID: string; agent: string; isSubagent: boolean },
318
+ output: { args: unknown; blocked?: boolean; reason?: string },
320
319
  ) => Promise<void>
321
320
 
322
321
  /** Modify tool output after execution. */
323
322
  "tool.execute.after"?: (
324
- input: { tool: string; sessionID: string; callID: string; args: unknown },
323
+ input: { tool: string; sessionID: string; callID: string; agent: string; isSubagent: boolean; args: unknown },
325
324
  output: { title: string; output: string; metadata: unknown },
326
325
  ) => Promise<void>
327
326
 
@@ -340,7 +339,7 @@ export interface Hooks {
340
339
  /** Customise context preservation during session compaction. */
341
340
  "session.compacting"?: (
342
341
  input: { sessionID: string },
343
- output: { context: string[]; prompt?: string },
342
+ output: { context: string[]; prompt?: string | undefined },
344
343
  ) => Promise<void>
345
344
 
346
345
  /** Text completion hook — modify or provide text completions. */
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
- * xyne-plugin — SDK for writing Xyne plugins.
2
+ * @xyne/plugin — SDK for writing Xyne plugins.
3
3
  *
4
4
  * @example
5
5
  * ```ts
6
- * import { type Plugin, tool, z } from "xyne-plugin"
6
+ * import { type Plugin, tool, z } from "@xyne/plugin"
7
7
  *
8
8
  * const plugin: Plugin = async ({ directory }) => ({
9
9
  * tool: {
@@ -22,11 +22,11 @@
22
22
  */
23
23
 
24
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"
25
+ export type { PluginFn as Plugin, PluginInput, Hooks, PluginAgentDef, AuthMethod, AuthPrompt, AuthOAuthResult } from "@tui/agent-core"
26
26
 
27
27
  // Tool authoring
28
- export { tool } from "./tool"
29
- export type { ToolDefinition, ToolContext, ToolAuthConfig, ServiceCredentials, ToolAttachment, PluginToolResult } from "./tool"
28
+ export { tool } from "@tui/agent-core"
29
+ export type { ToolDefinition, ToolContext, ToolAuthConfig, ServiceCredentials, ToolAttachment, PluginToolResult } from "@tui/agent-core"
30
30
 
31
31
  // Zod re-export for convenience
32
32
  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
  * ```