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 +1 -1
- package/src/hooks.ts +59 -60
- package/src/index.ts +5 -5
- package/src/tool.ts +51 -12
package/package.json
CHANGED
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
|
-
/**
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
structured?: unknown
|
|
74
|
-
summary?: string
|
|
58
|
+
cost?: number
|
|
59
|
+
[key: string]: unknown
|
|
75
60
|
}
|
|
76
61
|
|
|
77
|
-
/**
|
|
78
|
-
export
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
* - `
|
|
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
|
-
/**
|
|
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
|
|
2
|
+
* @xyne/plugin — SDK for writing Xyne plugins.
|
|
3
3
|
*
|
|
4
4
|
* @example
|
|
5
5
|
* ```ts
|
|
6
|
-
* import { type Plugin, tool, z } from "xyne
|
|
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,
|
|
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 "
|
|
29
|
-
export type { ToolDefinition, ToolContext, ToolAuthConfig, ServiceCredentials, ToolAttachment, PluginToolResult } from "
|
|
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.
|
|
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: "
|
|
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
|
-
*
|
|
161
|
+
* args.query // ← string
|
|
162
|
+
* return "result"
|
|
124
163
|
* },
|
|
125
164
|
* })
|
|
126
165
|
* ```
|