@openvole/paw-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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenVole
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,144 @@
1
+ import { ZodSchema } from 'zod';
2
+ export { z } from 'zod';
3
+
4
+ /** Agent context passed to hooks and the Brain */
5
+ interface AgentContext {
6
+ taskId: string;
7
+ messages: AgentMessage[];
8
+ availableTools: ToolSummary[];
9
+ activeSkills: ActiveSkill[];
10
+ metadata: Record<string, unknown>;
11
+ iteration: number;
12
+ maxIterations: number;
13
+ }
14
+ interface AgentMessage {
15
+ role: 'user' | 'brain' | 'tool_result' | 'error';
16
+ content: string;
17
+ toolCall?: {
18
+ name: string;
19
+ params: unknown;
20
+ };
21
+ timestamp: number;
22
+ }
23
+ interface ToolSummary {
24
+ name: string;
25
+ description: string;
26
+ pawName: string;
27
+ }
28
+ interface ActiveSkill {
29
+ name: string;
30
+ description: string;
31
+ satisfiedBy: string[];
32
+ }
33
+ /** Result of a tool execution */
34
+ interface ActionResult {
35
+ toolName: string;
36
+ pawName: string;
37
+ success: boolean;
38
+ output?: unknown;
39
+ error?: {
40
+ code: string;
41
+ message: string;
42
+ details?: unknown;
43
+ };
44
+ durationMs: number;
45
+ }
46
+ /** Plan returned by a Brain Paw */
47
+ interface AgentPlan {
48
+ actions: PlannedAction[];
49
+ execution?: 'parallel' | 'sequential';
50
+ response?: string;
51
+ done?: boolean;
52
+ }
53
+ interface PlannedAction {
54
+ tool: string;
55
+ params: unknown;
56
+ }
57
+ /** Tool definition within a Paw */
58
+ interface ToolDefinition {
59
+ name: string;
60
+ description: string;
61
+ parameters: ZodSchema;
62
+ execute: (params: unknown) => Promise<unknown>;
63
+ }
64
+ /** Bootstrap hook — called once when a task starts */
65
+ type BootstrapHook = (context: AgentContext) => Promise<AgentContext>;
66
+ /** Perceive hook — called before Think to enrich context */
67
+ type PerceiveHook = (context: AgentContext) => Promise<AgentContext>;
68
+ /** Observe hook — called after Act, fire-and-forget side effect */
69
+ type ObserveHook = (result: ActionResult) => Promise<void>;
70
+ /** Compact hook — called when context exceeds size threshold */
71
+ type CompactHook = (context: AgentContext) => Promise<AgentContext>;
72
+ /** Schedule hook */
73
+ interface ScheduleHook {
74
+ cron: string;
75
+ handler: () => Promise<{
76
+ input: string;
77
+ source?: 'schedule' | 'paw';
78
+ }>;
79
+ }
80
+ /** VoleIO interface for Paws that want to provide I/O */
81
+ interface VoleIO {
82
+ confirm(message: string): Promise<boolean>;
83
+ prompt(message: string): Promise<string>;
84
+ notify(message: string): void;
85
+ }
86
+ /** The full Paw definition */
87
+ interface PawDefinition {
88
+ name: string;
89
+ version: string;
90
+ description: string;
91
+ brain?: boolean;
92
+ inProcess?: boolean;
93
+ config?: ZodSchema;
94
+ hooks?: {
95
+ onBootstrap?: BootstrapHook;
96
+ onPerceive?: PerceiveHook;
97
+ onObserve?: ObserveHook;
98
+ onCompact?: CompactHook;
99
+ onSchedule?: ScheduleHook[];
100
+ };
101
+ tools?: ToolDefinition[];
102
+ think?: (context: AgentContext) => Promise<AgentPlan>;
103
+ io?: VoleIO;
104
+ onLoad?: (config: unknown) => Promise<void>;
105
+ onUnload?: () => Promise<void>;
106
+ }
107
+
108
+ /**
109
+ * definePaw — the primary export for Paw developers.
110
+ *
111
+ * For in-process Paws, this simply returns the definition (loaded directly by core).
112
+ * For subprocess Paws, this sets up the IPC transport and registers handlers.
113
+ */
114
+ declare function definePaw(definition: PawDefinition): PawDefinition;
115
+
116
+ type RequestHandler = (params: unknown) => Promise<unknown>;
117
+ /** Create an IPC transport for Node.js Paw subprocesses */
118
+ declare function createIpcTransport(): {
119
+ /** Register a handler for a method called by the core */
120
+ onRequest(method: string, handler: RequestHandler): void;
121
+ /** Send a notification to the core (no response expected) */
122
+ send(method: string, params?: unknown): void;
123
+ /** Send a request to the core and wait for a response */
124
+ request(method: string, params?: unknown): Promise<unknown>;
125
+ /** Subscribe to bus events from the core */
126
+ subscribe(events: string[]): void;
127
+ /** Query core state (tools, paws, skills, tasks) */
128
+ query(type: "tools" | "paws" | "skills" | "tasks"): Promise<unknown>;
129
+ /** Register a handler for forwarded bus events */
130
+ onBusEvent(handler: (event: string, data: unknown) => void): void;
131
+ /** Create a task in the core's task queue (for channel Paws that receive inbound messages) */
132
+ createTask(input: string, metadata?: Record<string, unknown> & {
133
+ sessionId?: string;
134
+ }): Promise<{
135
+ taskId: string;
136
+ }>;
137
+ };
138
+ /** Create a stdio transport for non-Node Paw processes (LSP-style framing) */
139
+ declare function createStdioTransport(): {
140
+ onRequest(method: string, handler: RequestHandler): void;
141
+ send(method: string, params?: unknown): void;
142
+ };
143
+
144
+ export { type ActionResult, type ActiveSkill, type AgentContext, type AgentMessage, type AgentPlan, type BootstrapHook, type CompactHook, type ObserveHook, type PawDefinition, type PerceiveHook, type PlannedAction, type ScheduleHook, type ToolDefinition, type ToolSummary, type VoleIO, createIpcTransport, createStdioTransport, definePaw };
package/dist/index.js ADDED
@@ -0,0 +1,280 @@
1
+ // src/transport.ts
2
+ function createIpcTransport() {
3
+ const handlers = /* @__PURE__ */ new Map();
4
+ process.on("message", async (msg) => {
5
+ if (!msg.method) return;
6
+ const handler = handlers.get(msg.method);
7
+ if (handler) {
8
+ try {
9
+ const result = await handler(msg.params);
10
+ if (msg.id) {
11
+ process.send({ jsonrpc: "2.0", id: msg.id, result });
12
+ }
13
+ } catch (err) {
14
+ const message = err instanceof Error ? err.message : String(err);
15
+ if (msg.id) {
16
+ process.send({
17
+ jsonrpc: "2.0",
18
+ id: msg.id,
19
+ error: { code: -32603, message }
20
+ });
21
+ }
22
+ }
23
+ }
24
+ });
25
+ return {
26
+ /** Register a handler for a method called by the core */
27
+ onRequest(method, handler) {
28
+ handlers.set(method, handler);
29
+ },
30
+ /** Send a notification to the core (no response expected) */
31
+ send(method, params) {
32
+ process.send({ jsonrpc: "2.0", method, params });
33
+ },
34
+ /** Send a request to the core and wait for a response */
35
+ async request(method, params) {
36
+ const id = crypto.randomUUID();
37
+ const message = { jsonrpc: "2.0", id, method, params };
38
+ return new Promise((resolve, reject) => {
39
+ const timeout = setTimeout(() => {
40
+ reject(new Error(`Request "${method}" timed out`));
41
+ }, 3e4);
42
+ const listener = (msg) => {
43
+ if (msg.id === id) {
44
+ clearTimeout(timeout);
45
+ process.off("message", listener);
46
+ if (msg.error) {
47
+ reject(new Error(msg.error.message));
48
+ } else {
49
+ resolve(msg.result);
50
+ }
51
+ }
52
+ };
53
+ process.on("message", listener);
54
+ process.send(message);
55
+ });
56
+ },
57
+ /** Subscribe to bus events from the core */
58
+ subscribe(events) {
59
+ process.send({ jsonrpc: "2.0", method: "subscribe", params: { events } });
60
+ },
61
+ /** Query core state (tools, paws, skills, tasks) */
62
+ async query(type) {
63
+ const id = crypto.randomUUID();
64
+ const message = { jsonrpc: "2.0", id, method: "query", params: { type } };
65
+ return new Promise((resolve, reject) => {
66
+ const timeout = setTimeout(() => {
67
+ reject(new Error(`Query "${type}" timed out`));
68
+ }, 1e4);
69
+ const listener = (msg) => {
70
+ if (msg.id === id) {
71
+ clearTimeout(timeout);
72
+ process.off("message", listener);
73
+ if (msg.error) {
74
+ reject(new Error(msg.error.message));
75
+ } else {
76
+ resolve(msg.result);
77
+ }
78
+ }
79
+ };
80
+ process.on("message", listener);
81
+ process.send(message);
82
+ });
83
+ },
84
+ /** Register a handler for forwarded bus events */
85
+ onBusEvent(handler) {
86
+ handlers.set("bus_event", async (params) => {
87
+ const { event, data } = params;
88
+ handler(event, data);
89
+ return { ok: true };
90
+ });
91
+ },
92
+ /** Create a task in the core's task queue (for channel Paws that receive inbound messages) */
93
+ async createTask(input, metadata) {
94
+ const { sessionId, ...rest } = metadata ?? {};
95
+ const id = crypto.randomUUID();
96
+ const message = {
97
+ jsonrpc: "2.0",
98
+ id,
99
+ method: "create_task",
100
+ params: { input, source: "paw", sessionId, metadata: rest }
101
+ };
102
+ return new Promise((resolve, reject) => {
103
+ const timeout = setTimeout(() => {
104
+ reject(new Error("createTask timed out"));
105
+ }, 1e4);
106
+ const listener = (msg) => {
107
+ if (msg.id === id) {
108
+ clearTimeout(timeout);
109
+ process.off("message", listener);
110
+ if (msg.error) {
111
+ reject(new Error(msg.error.message));
112
+ } else {
113
+ resolve(msg.result);
114
+ }
115
+ }
116
+ };
117
+ process.on("message", listener);
118
+ process.send(message);
119
+ });
120
+ }
121
+ };
122
+ }
123
+ function createStdioTransport() {
124
+ const handlers = /* @__PURE__ */ new Map();
125
+ let buffer = "";
126
+ process.stdin.setEncoding("utf-8");
127
+ process.stdin.on("data", (chunk) => {
128
+ buffer += chunk;
129
+ processBuffer();
130
+ });
131
+ async function processBuffer() {
132
+ while (buffer.length > 0) {
133
+ const headerEnd = buffer.indexOf("\r\n\r\n");
134
+ if (headerEnd === -1) break;
135
+ const header = buffer.substring(0, headerEnd);
136
+ const match = header.match(/Content-Length:\s*(\d+)/i);
137
+ if (!match) {
138
+ buffer = buffer.substring(headerEnd + 4);
139
+ continue;
140
+ }
141
+ const contentLength = Number.parseInt(match[1], 10);
142
+ const bodyStart = headerEnd + 4;
143
+ if (buffer.length < bodyStart + contentLength) break;
144
+ const body = buffer.substring(bodyStart, bodyStart + contentLength);
145
+ buffer = buffer.substring(bodyStart + contentLength);
146
+ try {
147
+ const msg = JSON.parse(body);
148
+ await handleMessage(msg);
149
+ } catch {
150
+ }
151
+ }
152
+ }
153
+ async function handleMessage(msg) {
154
+ if (!msg.method) return;
155
+ const handler = handlers.get(msg.method);
156
+ if (handler) {
157
+ try {
158
+ const result = await handler(msg.params);
159
+ if (msg.id) {
160
+ send({ jsonrpc: "2.0", id: msg.id, result });
161
+ }
162
+ } catch (err) {
163
+ const message = err instanceof Error ? err.message : String(err);
164
+ if (msg.id) {
165
+ send({
166
+ jsonrpc: "2.0",
167
+ id: msg.id,
168
+ error: { code: -32603, message }
169
+ });
170
+ }
171
+ }
172
+ }
173
+ }
174
+ function send(message) {
175
+ const json = JSON.stringify(message);
176
+ const header = `Content-Length: ${Buffer.byteLength(json)}\r
177
+ \r
178
+ `;
179
+ process.stdout.write(header + json);
180
+ }
181
+ return {
182
+ onRequest(method, handler) {
183
+ handlers.set(method, handler);
184
+ },
185
+ send(method, params) {
186
+ send({ jsonrpc: "2.0", method, params });
187
+ }
188
+ };
189
+ }
190
+
191
+ // src/define.ts
192
+ function definePaw(definition) {
193
+ if (definition.inProcess || !isSubprocess()) {
194
+ return definition;
195
+ }
196
+ setupSubprocessPaw(definition);
197
+ return definition;
198
+ }
199
+ function isSubprocess() {
200
+ return typeof process.send === "function";
201
+ }
202
+ function setupSubprocessPaw(definition) {
203
+ const transport = createIpcTransport();
204
+ transport.send("register", {
205
+ name: definition.name,
206
+ version: definition.version,
207
+ brain: definition.brain ?? false,
208
+ tools: (definition.tools ?? []).map((t) => ({
209
+ name: t.name,
210
+ description: t.description
211
+ })),
212
+ hooks: {
213
+ bootstrap: !!definition.hooks?.onBootstrap,
214
+ perceive: !!definition.hooks?.onPerceive,
215
+ observe: !!definition.hooks?.onObserve,
216
+ compact: !!definition.hooks?.onCompact,
217
+ schedule: (definition.hooks?.onSchedule ?? []).map((s) => s.cron)
218
+ }
219
+ });
220
+ if (definition.hooks?.onBootstrap) {
221
+ transport.onRequest("bootstrap", async (params) => {
222
+ return definition.hooks.onBootstrap(params);
223
+ });
224
+ }
225
+ if (definition.hooks?.onPerceive) {
226
+ transport.onRequest("perceive", async (params) => {
227
+ return definition.hooks.onPerceive(params);
228
+ });
229
+ }
230
+ if (definition.think) {
231
+ transport.onRequest("think", async (params) => {
232
+ return definition.think(params);
233
+ });
234
+ }
235
+ if (definition.tools && definition.tools.length > 0) {
236
+ const toolMap = new Map(definition.tools.map((t) => [t.name, t]));
237
+ transport.onRequest("execute_tool", async (params) => {
238
+ const { toolName, params: toolParams } = params;
239
+ const tool = toolMap.get(toolName);
240
+ if (!tool) {
241
+ throw new Error(`Tool "${toolName}" not found in this Paw`);
242
+ }
243
+ const validated = tool.parameters.parse(toolParams);
244
+ return tool.execute(validated);
245
+ });
246
+ }
247
+ if (definition.hooks?.onObserve) {
248
+ transport.onRequest("observe", async (params) => {
249
+ await definition.hooks.onObserve(params);
250
+ return { ok: true };
251
+ });
252
+ }
253
+ if (definition.hooks?.onCompact) {
254
+ transport.onRequest("compact", async (params) => {
255
+ return definition.hooks.onCompact(params);
256
+ });
257
+ }
258
+ transport.onRequest("shutdown", async () => {
259
+ if (definition.onUnload) {
260
+ await definition.onUnload();
261
+ }
262
+ process.exit(0);
263
+ });
264
+ if (definition.onLoad) {
265
+ definition.onLoad({}).catch((err) => {
266
+ console.error(`[${definition.name}] onLoad failed:`, err);
267
+ process.exit(1);
268
+ });
269
+ }
270
+ }
271
+
272
+ // src/index.ts
273
+ import { z } from "zod";
274
+ export {
275
+ createIpcTransport,
276
+ createStdioTransport,
277
+ definePaw,
278
+ z
279
+ };
280
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transport.ts","../src/define.ts","../src/index.ts"],"sourcesContent":["/** JSON-RPC 2.0 message */\ninterface IpcMessage {\n\tjsonrpc: '2.0'\n\tid?: string\n\tmethod?: string\n\tparams?: unknown\n\tresult?: unknown\n\terror?: { code: number; message: string; data?: unknown }\n}\n\ntype RequestHandler = (params: unknown) => Promise<unknown>\n\n/** Create an IPC transport for Node.js Paw subprocesses */\nexport function createIpcTransport() {\n\tconst handlers = new Map<string, RequestHandler>()\n\n\t// Listen for incoming messages from the core\n\tprocess.on('message', async (msg: IpcMessage) => {\n\t\tif (!msg.method) return\n\n\t\tconst handler = handlers.get(msg.method)\n\t\tif (handler) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler(msg.params)\n\t\t\t\tif (msg.id) {\n\t\t\t\t\tprocess.send!({ jsonrpc: '2.0', id: msg.id, result })\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err)\n\t\t\t\tif (msg.id) {\n\t\t\t\t\tprocess.send!({\n\t\t\t\t\t\tjsonrpc: '2.0',\n\t\t\t\t\t\tid: msg.id,\n\t\t\t\t\t\terror: { code: -32603, message },\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\n\treturn {\n\t\t/** Register a handler for a method called by the core */\n\t\tonRequest(method: string, handler: RequestHandler): void {\n\t\t\thandlers.set(method, handler)\n\t\t},\n\n\t\t/** Send a notification to the core (no response expected) */\n\t\tsend(method: string, params?: unknown): void {\n\t\t\tprocess.send!({ jsonrpc: '2.0', method, params })\n\t\t},\n\n\t\t/** Send a request to the core and wait for a response */\n\t\tasync request(method: string, params?: unknown): Promise<unknown> {\n\t\t\tconst id = crypto.randomUUID()\n\t\t\tconst message: IpcMessage = { jsonrpc: '2.0', id, method, params }\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\treject(new Error(`Request \"${method}\" timed out`))\n\t\t\t\t}, 30_000)\n\n\t\t\t\tconst listener = (msg: IpcMessage) => {\n\t\t\t\t\tif (msg.id === id) {\n\t\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\t\tprocess.off('message', listener)\n\t\t\t\t\t\tif (msg.error) {\n\t\t\t\t\t\t\treject(new Error(msg.error.message))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(msg.result)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprocess.on('message', listener)\n\t\t\t\tprocess.send!(message)\n\t\t\t})\n\t\t},\n\n\t\t/** Subscribe to bus events from the core */\n\t\tsubscribe(events: string[]): void {\n\t\t\tprocess.send!({ jsonrpc: '2.0', method: 'subscribe', params: { events } })\n\t\t},\n\n\t\t/** Query core state (tools, paws, skills, tasks) */\n\t\tasync query(type: 'tools' | 'paws' | 'skills' | 'tasks'): Promise<unknown> {\n\t\t\tconst id = crypto.randomUUID()\n\t\t\tconst message: IpcMessage = { jsonrpc: '2.0', id, method: 'query', params: { type } }\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\treject(new Error(`Query \"${type}\" timed out`))\n\t\t\t\t}, 10_000)\n\n\t\t\t\tconst listener = (msg: IpcMessage) => {\n\t\t\t\t\tif (msg.id === id) {\n\t\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\t\tprocess.off('message', listener)\n\t\t\t\t\t\tif (msg.error) {\n\t\t\t\t\t\t\treject(new Error(msg.error.message))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(msg.result)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprocess.on('message', listener)\n\t\t\t\tprocess.send!(message)\n\t\t\t})\n\t\t},\n\n\t\t/** Register a handler for forwarded bus events */\n\t\tonBusEvent(handler: (event: string, data: unknown) => void): void {\n\t\t\thandlers.set('bus_event', async (params) => {\n\t\t\t\tconst { event, data } = params as { event: string; data: unknown }\n\t\t\t\thandler(event, data)\n\t\t\t\treturn { ok: true }\n\t\t\t})\n\t\t},\n\n\t\t/** Create a task in the core's task queue (for channel Paws that receive inbound messages) */\n\t\tasync createTask(input: string, metadata?: Record<string, unknown> & { sessionId?: string }): Promise<{ taskId: string }> {\n\t\t\tconst { sessionId, ...rest } = metadata ?? {}\n\t\t\tconst id = crypto.randomUUID()\n\t\t\tconst message: IpcMessage = {\n\t\t\t\tjsonrpc: '2.0',\n\t\t\t\tid,\n\t\t\t\tmethod: 'create_task',\n\t\t\t\tparams: { input, source: 'paw', sessionId, metadata: rest },\n\t\t\t}\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\treject(new Error('createTask timed out'))\n\t\t\t\t}, 10_000)\n\n\t\t\t\tconst listener = (msg: IpcMessage) => {\n\t\t\t\t\tif (msg.id === id) {\n\t\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\t\tprocess.off('message', listener)\n\t\t\t\t\t\tif (msg.error) {\n\t\t\t\t\t\t\treject(new Error(msg.error.message))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(msg.result as { taskId: string })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprocess.on('message', listener)\n\t\t\t\tprocess.send!(message)\n\t\t\t})\n\t\t},\n\t}\n}\n\n/** Create a stdio transport for non-Node Paw processes (LSP-style framing) */\nexport function createStdioTransport() {\n\tconst handlers = new Map<string, RequestHandler>()\n\tlet buffer = ''\n\n\tprocess.stdin.setEncoding('utf-8')\n\tprocess.stdin.on('data', (chunk: string) => {\n\t\tbuffer += chunk\n\t\tprocessBuffer()\n\t})\n\n\tasync function processBuffer(): Promise<void> {\n\t\twhile (buffer.length > 0) {\n\t\t\tconst headerEnd = buffer.indexOf('\\r\\n\\r\\n')\n\t\t\tif (headerEnd === -1) break\n\n\t\t\tconst header = buffer.substring(0, headerEnd)\n\t\t\tconst match = header.match(/Content-Length:\\s*(\\d+)/i)\n\t\t\tif (!match) {\n\t\t\t\tbuffer = buffer.substring(headerEnd + 4)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst contentLength = Number.parseInt(match[1], 10)\n\t\t\tconst bodyStart = headerEnd + 4\n\t\t\tif (buffer.length < bodyStart + contentLength) break\n\n\t\t\tconst body = buffer.substring(bodyStart, bodyStart + contentLength)\n\t\t\tbuffer = buffer.substring(bodyStart + contentLength)\n\n\t\t\ttry {\n\t\t\t\tconst msg = JSON.parse(body) as IpcMessage\n\t\t\t\tawait handleMessage(msg)\n\t\t\t} catch {\n\t\t\t\t// Skip malformed messages\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function handleMessage(msg: IpcMessage): Promise<void> {\n\t\tif (!msg.method) return\n\n\t\tconst handler = handlers.get(msg.method)\n\t\tif (handler) {\n\t\t\ttry {\n\t\t\t\tconst result = await handler(msg.params)\n\t\t\t\tif (msg.id) {\n\t\t\t\t\tsend({ jsonrpc: '2.0', id: msg.id, result })\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err)\n\t\t\t\tif (msg.id) {\n\t\t\t\t\tsend({\n\t\t\t\t\t\tjsonrpc: '2.0',\n\t\t\t\t\t\tid: msg.id,\n\t\t\t\t\t\terror: { code: -32603, message },\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction send(message: IpcMessage): void {\n\t\tconst json = JSON.stringify(message)\n\t\tconst header = `Content-Length: ${Buffer.byteLength(json)}\\r\\n\\r\\n`\n\t\tprocess.stdout.write(header + json)\n\t}\n\n\treturn {\n\t\tonRequest(method: string, handler: RequestHandler): void {\n\t\t\thandlers.set(method, handler)\n\t\t},\n\n\t\tsend(method: string, params?: unknown): void {\n\t\t\tsend({ jsonrpc: '2.0', method, params })\n\t\t},\n\t}\n}\n","import type { PawDefinition, AgentContext, ActionResult } from './types.js'\nimport { createIpcTransport } from './transport.js'\n\n/**\n * definePaw — the primary export for Paw developers.\n *\n * For in-process Paws, this simply returns the definition (loaded directly by core).\n * For subprocess Paws, this sets up the IPC transport and registers handlers.\n */\nexport function definePaw(definition: PawDefinition): PawDefinition {\n\t// If this module is being imported by the core (in-process), just return the definition\n\tif (definition.inProcess || !isSubprocess()) {\n\t\treturn definition\n\t}\n\n\t// Running as a subprocess — wire up IPC transport\n\tsetupSubprocessPaw(definition)\n\treturn definition\n}\n\n/** Check if we're running as a subprocess (have IPC channel) */\nfunction isSubprocess(): boolean {\n\treturn typeof process.send === 'function'\n}\n\n/** Set up IPC handlers for a subprocess Paw */\nfunction setupSubprocessPaw(definition: PawDefinition): void {\n\tconst transport = createIpcTransport()\n\n\t// Register with the core\n\ttransport.send('register', {\n\t\tname: definition.name,\n\t\tversion: definition.version,\n\t\tbrain: definition.brain ?? false,\n\t\ttools: (definition.tools ?? []).map((t) => ({\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t})),\n\t\thooks: {\n\t\t\tbootstrap: !!definition.hooks?.onBootstrap,\n\t\t\tperceive: !!definition.hooks?.onPerceive,\n\t\t\tobserve: !!definition.hooks?.onObserve,\n\t\t\tcompact: !!definition.hooks?.onCompact,\n\t\t\tschedule: (definition.hooks?.onSchedule ?? []).map((s) => s.cron),\n\t\t},\n\t})\n\n\t// Handle bootstrap — called once at task start\n\tif (definition.hooks?.onBootstrap) {\n\t\ttransport.onRequest('bootstrap', async (params) => {\n\t\t\treturn definition.hooks!.onBootstrap!(params as AgentContext)\n\t\t})\n\t}\n\n\t// Handle perceive\n\tif (definition.hooks?.onPerceive) {\n\t\ttransport.onRequest('perceive', async (params) => {\n\t\t\treturn definition.hooks!.onPerceive!(params as AgentContext)\n\t\t})\n\t}\n\n\t// Handle think (Brain Paw)\n\tif (definition.think) {\n\t\ttransport.onRequest('think', async (params) => {\n\t\t\treturn definition.think!(params as AgentContext)\n\t\t})\n\t}\n\n\t// Handle tool execution\n\tif (definition.tools && definition.tools.length > 0) {\n\t\tconst toolMap = new Map(definition.tools.map((t) => [t.name, t]))\n\n\t\ttransport.onRequest('execute_tool', async (params) => {\n\t\t\tconst { toolName, params: toolParams } = params as {\n\t\t\t\ttoolName: string\n\t\t\t\tparams: unknown\n\t\t\t}\n\n\t\t\tconst tool = toolMap.get(toolName)\n\t\t\tif (!tool) {\n\t\t\t\tthrow new Error(`Tool \"${toolName}\" not found in this Paw`)\n\t\t\t}\n\n\t\t\t// Validate parameters\n\t\t\tconst validated = tool.parameters.parse(toolParams)\n\t\t\treturn tool.execute(validated)\n\t\t})\n\t}\n\n\t// Handle observe\n\tif (definition.hooks?.onObserve) {\n\t\ttransport.onRequest('observe', async (params) => {\n\t\t\tawait definition.hooks!.onObserve!(params as ActionResult)\n\t\t\treturn { ok: true }\n\t\t})\n\t}\n\n\t// Handle compact — called when context exceeds threshold\n\tif (definition.hooks?.onCompact) {\n\t\ttransport.onRequest('compact', async (params) => {\n\t\t\treturn definition.hooks!.onCompact!(params as AgentContext)\n\t\t})\n\t}\n\n\t// Handle shutdown\n\ttransport.onRequest('shutdown', async () => {\n\t\tif (definition.onUnload) {\n\t\t\tawait definition.onUnload()\n\t\t}\n\t\tprocess.exit(0)\n\t})\n\n\t// Run onLoad\n\tif (definition.onLoad) {\n\t\tdefinition.onLoad({}).catch((err) => {\n\t\t\tconsole.error(`[${definition.name}] onLoad failed:`, err)\n\t\t\tprocess.exit(1)\n\t\t})\n\t}\n}\n","export { definePaw } from './define.js'\nexport { createIpcTransport, createStdioTransport } from './transport.js'\nexport { z } from 'zod'\nexport type {\n\tPawDefinition,\n\tToolDefinition,\n\tAgentContext,\n\tAgentMessage,\n\tAgentPlan,\n\tPlannedAction,\n\tActionResult,\n\tActiveSkill,\n\tToolSummary,\n\tBootstrapHook,\n\tPerceiveHook,\n\tObserveHook,\n\tCompactHook,\n\tScheduleHook,\n\tVoleIO,\n} from './types.js'\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@openvole/paw-sdk",
3
+ "version": "0.1.0",
4
+ "description": "SDK for building OpenVole Paws — tool providers for the agent loop",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "zod": "^3.23.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^22.0.0",
19
+ "tsup": "^8.3.0",
20
+ "typescript": "^5.6.0",
21
+ "vitest": "^2.1.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=20.0.0"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/openvole/openvole",
33
+ "directory": "src/paw-sdk"
34
+ },
35
+ "keywords": [
36
+ "openvole",
37
+ "paw",
38
+ "sdk",
39
+ "agent",
40
+ "tools"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "typecheck": "tsc --noEmit"
47
+ }
48
+ }