@vibearound/plugin-channel-sdk 0.1.0
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/dist/connection.d.ts +62 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +89 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer.d.ts +154 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +317 -0
- package/dist/renderer.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -0
- package/src/connection.ts +147 -0
- package/src/index.ts +78 -0
- package/src/renderer.ts +395 -0
- package/src/types.ts +87 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP stdio connection helpers.
|
|
3
|
+
*
|
|
4
|
+
* Handles the boilerplate of wiring Node.js stdio streams to an ACP
|
|
5
|
+
* ClientSideConnection, performing the initialize handshake, and extracting
|
|
6
|
+
* plugin config from the host's _meta response.
|
|
7
|
+
*/
|
|
8
|
+
import { type Agent, type Client } from "@agentclientprotocol/sdk";
|
|
9
|
+
import type { PluginInitMeta } from "./types.js";
|
|
10
|
+
export interface PluginInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AgentInfo {
|
|
15
|
+
name?: string;
|
|
16
|
+
version?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ConnectResult {
|
|
19
|
+
/** ACP agent reference — call agent.prompt() to send messages. */
|
|
20
|
+
agent: Agent;
|
|
21
|
+
/** Plugin config and cache dir provided by the host at startup. */
|
|
22
|
+
meta: PluginInitMeta;
|
|
23
|
+
/** Info about the host agent (name, version) reported during initialize. */
|
|
24
|
+
agentInfo: AgentInfo;
|
|
25
|
+
/**
|
|
26
|
+
* The ACP connection. Await `conn.closed` to keep the process alive until
|
|
27
|
+
* the host disconnects.
|
|
28
|
+
*/
|
|
29
|
+
conn: {
|
|
30
|
+
readonly closed: Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Connect to the VibeAround host via stdio ACP.
|
|
35
|
+
*
|
|
36
|
+
* Sets up Node.js stdio streams → ACP transport, calls `initialize`, and
|
|
37
|
+
* returns the agent reference plus plugin config/meta from the host.
|
|
38
|
+
*
|
|
39
|
+
* @param pluginInfo - Name and version reported to the host
|
|
40
|
+
* @param makeClient - Factory called with the Agent; returns the Client
|
|
41
|
+
* implementation (sessionUpdate, requestPermission, extNotification).
|
|
42
|
+
* Capture the agent argument here if you need it before this function resolves.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* let agent!: Agent;
|
|
47
|
+
* const { meta, conn } = await connectToHost(
|
|
48
|
+
* { name: "vibearound-mybot", version: "0.1.0" },
|
|
49
|
+
* (a) => { agent = a; return myClient; },
|
|
50
|
+
* );
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function connectToHost(pluginInfo: PluginInfo, makeClient: (agent: Agent) => Client): Promise<ConnectResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Normalize ACP ext notification method names.
|
|
56
|
+
*
|
|
57
|
+
* The ACP SDK prepends "_" to ext method names (e.g. "_channel/system_text").
|
|
58
|
+
* Strip the prefix to get the canonical method name used in plugin.json.
|
|
59
|
+
*/
|
|
60
|
+
export declare function normalizeExtMethod(method: string): string;
|
|
61
|
+
export declare function redirectConsoleToStderr(): void;
|
|
62
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAIL,KAAK,KAAK,EACV,KAAK,MAAM,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,KAAK,EAAE,KAAK,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,cAAc,CAAC;IACrB,4EAA4E;IAC5E,SAAS,EAAE,SAAS,CAAC;IACrB;;;OAGG;IACH,IAAI,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GACnC,OAAO,CAAC,aAAa,CAAC,CAqCxB;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAWD,wBAAgB,uBAAuB,IAAI,IAAI,CAe9C"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP stdio connection helpers.
|
|
3
|
+
*
|
|
4
|
+
* Handles the boilerplate of wiring Node.js stdio streams to an ACP
|
|
5
|
+
* ClientSideConnection, performing the initialize handshake, and extracting
|
|
6
|
+
* plugin config from the host's _meta response.
|
|
7
|
+
*/
|
|
8
|
+
import { Readable, Writable } from "node:stream";
|
|
9
|
+
import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, } from "@agentclientprotocol/sdk";
|
|
10
|
+
/**
|
|
11
|
+
* Connect to the VibeAround host via stdio ACP.
|
|
12
|
+
*
|
|
13
|
+
* Sets up Node.js stdio streams → ACP transport, calls `initialize`, and
|
|
14
|
+
* returns the agent reference plus plugin config/meta from the host.
|
|
15
|
+
*
|
|
16
|
+
* @param pluginInfo - Name and version reported to the host
|
|
17
|
+
* @param makeClient - Factory called with the Agent; returns the Client
|
|
18
|
+
* implementation (sessionUpdate, requestPermission, extNotification).
|
|
19
|
+
* Capture the agent argument here if you need it before this function resolves.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* let agent!: Agent;
|
|
24
|
+
* const { meta, conn } = await connectToHost(
|
|
25
|
+
* { name: "vibearound-mybot", version: "0.1.0" },
|
|
26
|
+
* (a) => { agent = a; return myClient; },
|
|
27
|
+
* );
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export async function connectToHost(pluginInfo, makeClient) {
|
|
31
|
+
// Keep stdout clean for JSON-RPC — redirect all console output to stderr
|
|
32
|
+
redirectConsoleToStderr();
|
|
33
|
+
const inputStream = Readable.toWeb(process.stdin);
|
|
34
|
+
const outputStream = Writable.toWeb(process.stdout);
|
|
35
|
+
const stream = ndJsonStream(outputStream, inputStream);
|
|
36
|
+
let capturedAgent;
|
|
37
|
+
const wrappedMakeClient = (a) => {
|
|
38
|
+
capturedAgent = a;
|
|
39
|
+
return makeClient(a);
|
|
40
|
+
};
|
|
41
|
+
const conn = new ClientSideConnection(wrappedMakeClient, stream);
|
|
42
|
+
const initResponse = await conn.initialize({
|
|
43
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
44
|
+
clientInfo: { name: pluginInfo.name, version: pluginInfo.version },
|
|
45
|
+
capabilities: {},
|
|
46
|
+
});
|
|
47
|
+
const rawMeta = initResponse._meta;
|
|
48
|
+
const meta = {
|
|
49
|
+
config: (rawMeta?.config ?? {}),
|
|
50
|
+
cacheDir: rawMeta?.cacheDir,
|
|
51
|
+
};
|
|
52
|
+
const agentInfo = {
|
|
53
|
+
name: initResponse.agentInfo?.name,
|
|
54
|
+
version: initResponse.agentInfo?.version,
|
|
55
|
+
};
|
|
56
|
+
return { agent: capturedAgent, meta, agentInfo, conn };
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Utilities
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
/**
|
|
62
|
+
* Normalize ACP ext notification method names.
|
|
63
|
+
*
|
|
64
|
+
* The ACP SDK prepends "_" to ext method names (e.g. "_channel/system_text").
|
|
65
|
+
* Strip the prefix to get the canonical method name used in plugin.json.
|
|
66
|
+
*/
|
|
67
|
+
export function normalizeExtMethod(method) {
|
|
68
|
+
return method.startsWith("_") ? method.slice(1) : method;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Redirect all console.* output to stderr.
|
|
72
|
+
*
|
|
73
|
+
* ACP uses stdout for JSON-RPC framing — any stray console output corrupts
|
|
74
|
+
* the protocol. Call this once at plugin startup (connectToHost does this
|
|
75
|
+
* automatically).
|
|
76
|
+
*/
|
|
77
|
+
let _consoleRedirected = false;
|
|
78
|
+
export function redirectConsoleToStderr() {
|
|
79
|
+
if (_consoleRedirected)
|
|
80
|
+
return;
|
|
81
|
+
_consoleRedirected = true;
|
|
82
|
+
const toStderr = (...args) => process.stderr.write(args.map(String).join(" ") + "\n");
|
|
83
|
+
console.log = toStderr;
|
|
84
|
+
console.info = toStderr;
|
|
85
|
+
console.warn = (...args) => process.stderr.write("[warn] " + args.map(String).join(" ") + "\n");
|
|
86
|
+
console.error = (...args) => process.stderr.write("[error] " + args.map(String).join(" ") + "\n");
|
|
87
|
+
console.debug = (...args) => process.stderr.write("[debug] " + args.map(String).join(" ") + "\n");
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vibearound/plugin-channel-sdk
|
|
3
|
+
*
|
|
4
|
+
* Base classes and utilities for building VibeAround channel plugins.
|
|
5
|
+
*
|
|
6
|
+
* ## Quick start
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import {
|
|
10
|
+
* connectToHost,
|
|
11
|
+
* BlockRenderer,
|
|
12
|
+
* normalizeExtMethod,
|
|
13
|
+
* type Agent,
|
|
14
|
+
* type SessionNotification,
|
|
15
|
+
* } from "@vibearound/plugin-channel-sdk";
|
|
16
|
+
*
|
|
17
|
+
* class MyRenderer extends BlockRenderer<string> {
|
|
18
|
+
* protected async sendBlock(channelId, kind, content) {
|
|
19
|
+
* const msg = await myPlatform.send(channelId, content);
|
|
20
|
+
* return msg.id;
|
|
21
|
+
* }
|
|
22
|
+
* protected async editBlock(channelId, ref, kind, content, sealed) {
|
|
23
|
+
* await myPlatform.edit(ref, content);
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* let agent!: Agent;
|
|
28
|
+
* const renderer = new MyRenderer();
|
|
29
|
+
*
|
|
30
|
+
* const { meta, conn } = await connectToHost(
|
|
31
|
+
* { name: "vibearound-mybot", version: "0.1.0" },
|
|
32
|
+
* (a) => {
|
|
33
|
+
* agent = a;
|
|
34
|
+
* return {
|
|
35
|
+
* sessionUpdate: async (n) => renderer.onSessionUpdate(n),
|
|
36
|
+
* requestPermission: async (p) => ({
|
|
37
|
+
* outcome: { outcome: "selected", optionId: p.options![0].optionId },
|
|
38
|
+
* }),
|
|
39
|
+
* extNotification: async (method, params) => {
|
|
40
|
+
* switch (normalizeExtMethod(method)) {
|
|
41
|
+
* case "channel/system_text":
|
|
42
|
+
* await myPlatform.send(params.channelId as string, params.text as string);
|
|
43
|
+
* break;
|
|
44
|
+
* }
|
|
45
|
+
* },
|
|
46
|
+
* };
|
|
47
|
+
* },
|
|
48
|
+
* );
|
|
49
|
+
*
|
|
50
|
+
* const botToken = meta.config.bot_token as string;
|
|
51
|
+
* // … start your platform bot …
|
|
52
|
+
* await conn.closed;
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export { connectToHost, normalizeExtMethod, redirectConsoleToStderr } from "./connection.js";
|
|
56
|
+
export type { PluginInfo, ConnectResult, AgentInfo } from "./connection.js";
|
|
57
|
+
export { BlockRenderer } from "./renderer.js";
|
|
58
|
+
export type { Agent, Client, SessionNotification, RequestPermissionRequest, RequestPermissionResponse, BlockKind, VerboseConfig, BlockRendererOptions, PluginCapabilities, PluginManifest, PluginInitMeta, } from "./types.js";
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAGH,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC7F,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,YAAY,EAEV,KAAK,EACL,MAAM,EACN,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EAEzB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vibearound/plugin-channel-sdk
|
|
3
|
+
*
|
|
4
|
+
* Base classes and utilities for building VibeAround channel plugins.
|
|
5
|
+
*
|
|
6
|
+
* ## Quick start
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import {
|
|
10
|
+
* connectToHost,
|
|
11
|
+
* BlockRenderer,
|
|
12
|
+
* normalizeExtMethod,
|
|
13
|
+
* type Agent,
|
|
14
|
+
* type SessionNotification,
|
|
15
|
+
* } from "@vibearound/plugin-channel-sdk";
|
|
16
|
+
*
|
|
17
|
+
* class MyRenderer extends BlockRenderer<string> {
|
|
18
|
+
* protected async sendBlock(channelId, kind, content) {
|
|
19
|
+
* const msg = await myPlatform.send(channelId, content);
|
|
20
|
+
* return msg.id;
|
|
21
|
+
* }
|
|
22
|
+
* protected async editBlock(channelId, ref, kind, content, sealed) {
|
|
23
|
+
* await myPlatform.edit(ref, content);
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* let agent!: Agent;
|
|
28
|
+
* const renderer = new MyRenderer();
|
|
29
|
+
*
|
|
30
|
+
* const { meta, conn } = await connectToHost(
|
|
31
|
+
* { name: "vibearound-mybot", version: "0.1.0" },
|
|
32
|
+
* (a) => {
|
|
33
|
+
* agent = a;
|
|
34
|
+
* return {
|
|
35
|
+
* sessionUpdate: async (n) => renderer.onSessionUpdate(n),
|
|
36
|
+
* requestPermission: async (p) => ({
|
|
37
|
+
* outcome: { outcome: "selected", optionId: p.options![0].optionId },
|
|
38
|
+
* }),
|
|
39
|
+
* extNotification: async (method, params) => {
|
|
40
|
+
* switch (normalizeExtMethod(method)) {
|
|
41
|
+
* case "channel/system_text":
|
|
42
|
+
* await myPlatform.send(params.channelId as string, params.text as string);
|
|
43
|
+
* break;
|
|
44
|
+
* }
|
|
45
|
+
* },
|
|
46
|
+
* };
|
|
47
|
+
* },
|
|
48
|
+
* );
|
|
49
|
+
*
|
|
50
|
+
* const botToken = meta.config.bot_token as string;
|
|
51
|
+
* // … start your platform bot …
|
|
52
|
+
* await conn.closed;
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
// Connection helpers
|
|
56
|
+
export { connectToHost, normalizeExtMethod, redirectConsoleToStderr } from "./connection.js";
|
|
57
|
+
// Block renderer
|
|
58
|
+
export { BlockRenderer } from "./renderer.js";
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,qBAAqB;AACrB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAG7F,iBAAiB;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlockRenderer — abstract base class for block-based message rendering.
|
|
3
|
+
*
|
|
4
|
+
* ## How it works
|
|
5
|
+
*
|
|
6
|
+
* ACP streams agent responses as a sequence of typed events:
|
|
7
|
+
* text chunk, text chunk, tool call, tool update, text chunk, …
|
|
8
|
+
*
|
|
9
|
+
* Each contiguous run of the **same kind** (text / thinking / tool) is grouped
|
|
10
|
+
* into one "block". When the kind changes, the current block is **sealed**
|
|
11
|
+
* (no more edits) and a new block starts.
|
|
12
|
+
*
|
|
13
|
+
* Blocks are rendered to the platform by subclass-implemented `sendBlock` and
|
|
14
|
+
* `editBlock`. The renderer handles:
|
|
15
|
+
*
|
|
16
|
+
* - **Debounced flushing** — batches rapid deltas before sending (avoids
|
|
17
|
+
* excessive API calls during fast streaming).
|
|
18
|
+
* - **Edit throttling** — enforces a minimum interval between edits to
|
|
19
|
+
* respect platform rate limits.
|
|
20
|
+
* - **Ordered delivery** — a `sendChain` Promise serializes all send/edit
|
|
21
|
+
* calls so messages always arrive in the correct order.
|
|
22
|
+
* - **Sentinel guard** — prevents concurrent creates for the same block.
|
|
23
|
+
* - **Verbose filtering** — thinking / tool blocks can be suppressed without
|
|
24
|
+
* creating phantom block boundaries.
|
|
25
|
+
*
|
|
26
|
+
* ## Usage
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* class MyRenderer extends BlockRenderer<string> {
|
|
30
|
+
* protected async sendBlock(channelId, kind, content) {
|
|
31
|
+
* const msg = await myApi.sendMessage(channelId, content);
|
|
32
|
+
* return msg.id;
|
|
33
|
+
* }
|
|
34
|
+
* protected async editBlock(channelId, ref, kind, content, sealed) {
|
|
35
|
+
* await myApi.editMessage(ref, content);
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* // In main.ts:
|
|
40
|
+
* const renderer = new MyRenderer({ verbose: { showThinking: false } });
|
|
41
|
+
*
|
|
42
|
+
* // When user sends a message:
|
|
43
|
+
* renderer.onPromptSent(channelId);
|
|
44
|
+
* try {
|
|
45
|
+
* await agent.prompt({ sessionId, content });
|
|
46
|
+
* await renderer.onTurnEnd(channelId);
|
|
47
|
+
* } catch (e) {
|
|
48
|
+
* await renderer.onTurnError(channelId, String(e));
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* // In the ACP client's sessionUpdate handler:
|
|
52
|
+
* renderer.onSessionUpdate(notification);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
import type { SessionNotification } from "@agentclientprotocol/sdk";
|
|
56
|
+
import type { BlockKind, BlockRendererOptions, VerboseConfig } from "./types.js";
|
|
57
|
+
/**
|
|
58
|
+
* Abstract base class for block-based rendering of ACP session streams.
|
|
59
|
+
*
|
|
60
|
+
* @typeParam TRef - Platform-specific message reference type (e.g. `number`
|
|
61
|
+
* for Telegram message IDs, `string` for Feishu message IDs). Used as the
|
|
62
|
+
* return type of `sendBlock` and the first argument of `editBlock`.
|
|
63
|
+
*/
|
|
64
|
+
export declare abstract class BlockRenderer<TRef = string> {
|
|
65
|
+
protected readonly flushIntervalMs: number;
|
|
66
|
+
protected readonly minEditIntervalMs: number;
|
|
67
|
+
protected readonly verbose: VerboseConfig;
|
|
68
|
+
private states;
|
|
69
|
+
constructor(options?: BlockRendererOptions);
|
|
70
|
+
/**
|
|
71
|
+
* Send a new block message to the platform.
|
|
72
|
+
*
|
|
73
|
+
* Return the platform message reference that will be passed to future
|
|
74
|
+
* `editBlock` calls. Return `null` if editing is not supported.
|
|
75
|
+
*/
|
|
76
|
+
protected abstract sendBlock(channelId: string, kind: BlockKind, content: string): Promise<TRef | null>;
|
|
77
|
+
/**
|
|
78
|
+
* Edit an existing block message in-place.
|
|
79
|
+
*
|
|
80
|
+
* Optional — if not implemented, blocks are never edited (send-only mode,
|
|
81
|
+
* suitable for platforms like WeChat that don't support message editing).
|
|
82
|
+
*
|
|
83
|
+
* @param sealed - `true` when this is the final edit (block done streaming).
|
|
84
|
+
* Use to switch from a "streaming" card format to a finalized one.
|
|
85
|
+
*/
|
|
86
|
+
protected editBlock?(channelId: string, ref: TRef, kind: BlockKind, content: string, sealed: boolean): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Format block content before sending or editing.
|
|
89
|
+
*
|
|
90
|
+
* Default applies standard emoji prefixes:
|
|
91
|
+
* - `thinking` → `💭 <content>`
|
|
92
|
+
* - `tool` → trimmed content
|
|
93
|
+
* - `text` → content as-is
|
|
94
|
+
*
|
|
95
|
+
* Override to apply platform-specific formatting (e.g. markdown escaping).
|
|
96
|
+
*/
|
|
97
|
+
protected formatContent(kind: BlockKind, content: string, _sealed: boolean): string;
|
|
98
|
+
/**
|
|
99
|
+
* Called after the last block has been flushed and the turn is complete.
|
|
100
|
+
* Override to perform cleanup (e.g. remove a "typing" indicator, a
|
|
101
|
+
* processing reaction, etc.).
|
|
102
|
+
*/
|
|
103
|
+
protected onAfterTurnEnd(_channelId: string): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Called after a turn error, once state has been cleaned up.
|
|
106
|
+
* Override to send an error message to the user.
|
|
107
|
+
*/
|
|
108
|
+
protected onAfterTurnError(_channelId: string, _error: string): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Map an ACP `sessionId` to the channel ID used internally.
|
|
111
|
+
*
|
|
112
|
+
* Default: identity (sessionId === channelId).
|
|
113
|
+
*
|
|
114
|
+
* Override if your plugin namespaces channel IDs (e.g. Feishu uses
|
|
115
|
+
* `"feishu:<sessionId>"`, WeChat uses `"weixin-openclaw-bridge:<sessionId>"`).
|
|
116
|
+
*/
|
|
117
|
+
protected sessionIdToChannelId(sessionId: string): string;
|
|
118
|
+
/**
|
|
119
|
+
* Process an ACP `sessionUpdate` notification from the host.
|
|
120
|
+
*
|
|
121
|
+
* Routes the event to the correct block based on its variant, appending
|
|
122
|
+
* deltas to the current block or starting a new one when the kind changes.
|
|
123
|
+
*
|
|
124
|
+
* Call this from the ACP `Client.sessionUpdate` handler.
|
|
125
|
+
*/
|
|
126
|
+
onSessionUpdate(notification: SessionNotification): void;
|
|
127
|
+
/**
|
|
128
|
+
* Call this before sending a prompt to the agent.
|
|
129
|
+
*
|
|
130
|
+
* Clears any leftover state from a previous turn so the new turn starts
|
|
131
|
+
* with a clean slate.
|
|
132
|
+
*/
|
|
133
|
+
onPromptSent(channelId: string): void;
|
|
134
|
+
/**
|
|
135
|
+
* Call this after `agent.prompt()` resolves (turn complete).
|
|
136
|
+
*
|
|
137
|
+
* Seals and flushes the last block, then waits for all pending sends/edits
|
|
138
|
+
* to complete before calling `onAfterTurnEnd`.
|
|
139
|
+
*/
|
|
140
|
+
onTurnEnd(channelId: string): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Call this when `agent.prompt()` throws (turn error).
|
|
143
|
+
*
|
|
144
|
+
* Discards pending state and calls `onAfterTurnError` so the subclass can
|
|
145
|
+
* send an error message to the user.
|
|
146
|
+
*/
|
|
147
|
+
onTurnError(channelId: string, error: string): Promise<void>;
|
|
148
|
+
private appendToBlock;
|
|
149
|
+
private scheduleFlush;
|
|
150
|
+
private flush;
|
|
151
|
+
private enqueueFlush;
|
|
152
|
+
private flushBlock;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAuCjF;;;;;;GAMG;AACH,8BAAsB,aAAa,CAAC,IAAI,GAAG,MAAM;IAC/C,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAC3C,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAE1C,OAAO,CAAC,MAAM,CAAyC;gBAE3C,OAAO,GAAE,oBAAyB;IAa9C;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,SAAS,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAEvB;;;;;;;;OAQG;IACH,SAAS,CAAC,SAAS,CAAC,CAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,IAAI,EACT,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC;IAEhB;;;;;;;;;OASG;IACH,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM;IAQnF;;;;OAIG;IACH,SAAS,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;;OAGG;IACH,SAAS,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;;;;;;OAOG;IACH,SAAS,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAQzD;;;;;;;OAOG;IACH,eAAe,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI;IAqCxD;;;;;OAKG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAWrC;;;;;OAKG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBjD;;;;;OAKG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlE,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,KAAK;IAoBb,OAAO,CAAC,YAAY;YAMN,UAAU;CAqBzB"}
|