@h-rig/pi-rig 0.0.6-alpha.8 → 0.0.6-alpha.80

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.
@@ -0,0 +1,46 @@
1
+ import type { MinimalPiApi } from "./index";
2
+ /** What we use of pi-tui's Component contract. */
3
+ export type ComponentLike = {
4
+ render(width: number): string[];
5
+ };
6
+ type AssistantComponentLike = ComponentLike & {
7
+ updateContent(message: Record<string, unknown>): void;
8
+ };
9
+ type ToolComponentLike = ComponentLike & {
10
+ markExecutionStarted(): void;
11
+ updateArgs(args: unknown): void;
12
+ setArgsComplete(): void;
13
+ updateResult(result: {
14
+ content: Array<Record<string, unknown>>;
15
+ details?: unknown;
16
+ isError: boolean;
17
+ }, isPartial?: boolean): void;
18
+ };
19
+ type PiComponentModule = {
20
+ AssistantMessageComponent: new (message?: unknown) => AssistantComponentLike;
21
+ UserMessageComponent: new (text: string) => ComponentLike;
22
+ ToolExecutionComponent: new (toolName: string, toolCallId: string, args: unknown, options: Record<string, unknown>, toolDefinition: unknown, ui: unknown, cwd: string) => ToolComponentLike;
23
+ };
24
+ export type LiveMirrorModules = {
25
+ components: PiComponentModule;
26
+ };
27
+ export declare const DRONE_MESSAGE_TYPE = "rig-drone";
28
+ export declare const DRONE_USER_MESSAGE_TYPE = "rig-drone-user";
29
+ export type LiveMirror = {
30
+ /** Feed one worker `pi.event` frame's inner event into the mirror. */
31
+ handleWorkerEvent(event: Record<string, unknown>): void;
32
+ /** The setWidget factory used purely to capture the TUI handle. */
33
+ captureTui(tui: unknown): ComponentLike;
34
+ };
35
+ /**
36
+ * Wire the live mirror: registers the message renderers and returns the
37
+ * event feed. Call once per operator session, before events start flowing.
38
+ */
39
+ export declare function createLiveMirror(input: {
40
+ pi: MinimalPiApi;
41
+ /** Worker workspace path, for tool renderers (diff headers etc.). */
42
+ workerCwd?: string;
43
+ /** Test seam: fake Pi modules. Defaults to the real dynamic import. */
44
+ modules?: LiveMirrorModules;
45
+ }): Promise<LiveMirror>;
46
+ export {};
@@ -0,0 +1,223 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // packages/pi-rig/src/live-mirror.ts
5
+ async function loadPiModules() {
6
+ const components = await import("@earendil-works/pi-coding-agent");
7
+ return { components };
8
+ }
9
+ function createRootContainer() {
10
+ const children = [];
11
+ return {
12
+ addChild(child) {
13
+ children.push(child);
14
+ },
15
+ removeChild(child) {
16
+ const index = children.indexOf(child);
17
+ if (index >= 0)
18
+ children.splice(index, 1);
19
+ },
20
+ clear() {
21
+ children.length = 0;
22
+ },
23
+ render(width) {
24
+ return children.flatMap((child) => child.render(width));
25
+ }
26
+ };
27
+ }
28
+ var DRONE_MESSAGE_TYPE = "rig-drone";
29
+ var DRONE_USER_MESSAGE_TYPE = "rig-drone-user";
30
+ function recordOf(value) {
31
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
32
+ }
33
+ function messageRole(event) {
34
+ const message = recordOf(event.message);
35
+ return message && typeof message.role === "string" ? message.role : null;
36
+ }
37
+ function userMessageText(message) {
38
+ const content = message.content;
39
+ if (typeof content === "string")
40
+ return content;
41
+ if (Array.isArray(content)) {
42
+ return content.flatMap((block) => {
43
+ const record = recordOf(block);
44
+ return record && record.type === "text" && typeof record.text === "string" ? [record.text] : [];
45
+ }).join(`
46
+ `);
47
+ }
48
+ return "";
49
+ }
50
+ async function createLiveMirror(input) {
51
+ const { pi } = input;
52
+ const modules = input.modules ?? await loadPiModules();
53
+ const cwd = input.workerCwd ?? process.cwd();
54
+ const turns = new Map;
55
+ const toolOwners = new Map;
56
+ const userComponents = new Map;
57
+ let streamingTurn = null;
58
+ let sequence = 0;
59
+ let tui = null;
60
+ let renderTimer = null;
61
+ const requestRender = () => {
62
+ if (renderTimer)
63
+ return;
64
+ renderTimer = setTimeout(() => {
65
+ renderTimer = null;
66
+ tui?.requestRender?.();
67
+ }, 33);
68
+ renderTimer.unref?.();
69
+ };
70
+ pi.registerMessageRenderer?.(DRONE_MESSAGE_TYPE, (message) => {
71
+ const details = recordOf(recordOf(message)?.details);
72
+ const key = details && typeof details.key === "string" ? details.key : null;
73
+ return key ? turns.get(key)?.root : undefined;
74
+ });
75
+ pi.registerMessageRenderer?.(DRONE_USER_MESSAGE_TYPE, (message) => {
76
+ const details = recordOf(recordOf(message)?.details);
77
+ const key = details && typeof details.key === "string" ? details.key : null;
78
+ return key ? userComponents.get(key) : undefined;
79
+ });
80
+ const ensureToolComponent = (turn, toolCallId, toolName, args) => {
81
+ const existing = turn.tools.get(toolCallId);
82
+ if (existing)
83
+ return existing;
84
+ const component = new modules.components.ToolExecutionComponent(toolName, toolCallId, args, {}, undefined, tui, cwd);
85
+ turn.root.addChild(component);
86
+ turn.tools.set(toolCallId, component);
87
+ toolOwners.set(toolCallId, turn);
88
+ return component;
89
+ };
90
+ const startAssistantTurn = (message) => {
91
+ const key = `turn-${++sequence}`;
92
+ const root = createRootContainer();
93
+ const assistant = new modules.components.AssistantMessageComponent;
94
+ root.addChild(assistant);
95
+ const turn = { root, assistant, tools: new Map };
96
+ assistant.updateContent(message);
97
+ turns.set(key, turn);
98
+ streamingTurn = turn;
99
+ pi.sendMessage?.({ customType: DRONE_MESSAGE_TYPE, content: "drone turn", display: true, details: { key } }, { triggerTurn: false });
100
+ requestRender();
101
+ };
102
+ const updateAssistantTurn = (message) => {
103
+ const turn = streamingTurn;
104
+ if (!turn)
105
+ return;
106
+ turn.assistant.updateContent(message);
107
+ const content = Array.isArray(message.content) ? message.content : [];
108
+ for (const block of content) {
109
+ const record = recordOf(block);
110
+ if (!record || record.type !== "toolCall")
111
+ continue;
112
+ const id = typeof record.id === "string" ? record.id : null;
113
+ const name = typeof record.name === "string" ? record.name : "tool";
114
+ if (!id)
115
+ continue;
116
+ const component = ensureToolComponent(turn, id, name, record.arguments);
117
+ component.updateArgs(record.arguments);
118
+ }
119
+ requestRender();
120
+ };
121
+ const endAssistantTurn = (message) => {
122
+ const turn = streamingTurn;
123
+ if (!turn)
124
+ return;
125
+ turn.assistant.updateContent(message);
126
+ const stopReason = typeof message.stopReason === "string" ? message.stopReason : "stop";
127
+ if (stopReason === "aborted" || stopReason === "error") {
128
+ const errorText = typeof message.errorMessage === "string" && message.errorMessage ? message.errorMessage : stopReason === "aborted" ? "Operation aborted" : "Error";
129
+ for (const component of turn.tools.values()) {
130
+ component.updateResult({ content: [{ type: "text", text: errorText }], isError: true });
131
+ }
132
+ } else {
133
+ for (const component of turn.tools.values()) {
134
+ component.setArgsComplete();
135
+ }
136
+ }
137
+ streamingTurn = null;
138
+ requestRender();
139
+ };
140
+ const mirrorUserMessage = (message) => {
141
+ const text = userMessageText(message).trim();
142
+ if (!text)
143
+ return;
144
+ const key = `user-${++sequence}`;
145
+ userComponents.set(key, new modules.components.UserMessageComponent(text));
146
+ pi.sendMessage?.({ customType: DRONE_USER_MESSAGE_TYPE, content: text, display: true, details: { key } }, { triggerTurn: false });
147
+ requestRender();
148
+ };
149
+ return {
150
+ captureTui(capturedTui) {
151
+ tui = recordOf(capturedTui);
152
+ return { render: () => [] };
153
+ },
154
+ handleWorkerEvent(event) {
155
+ const type = typeof event.type === "string" ? event.type : null;
156
+ switch (type) {
157
+ case "message_start": {
158
+ const message = recordOf(event.message);
159
+ if (!message)
160
+ return;
161
+ if (messageRole(event) === "assistant")
162
+ startAssistantTurn(message);
163
+ else if (messageRole(event) === "user")
164
+ mirrorUserMessage(message);
165
+ return;
166
+ }
167
+ case "message_update": {
168
+ const message = recordOf(event.message);
169
+ if (message && messageRole(event) === "assistant")
170
+ updateAssistantTurn(message);
171
+ return;
172
+ }
173
+ case "message_end": {
174
+ const message = recordOf(event.message);
175
+ if (message && messageRole(event) === "assistant")
176
+ endAssistantTurn(message);
177
+ return;
178
+ }
179
+ case "tool_execution_start": {
180
+ const id = typeof event.toolCallId === "string" ? event.toolCallId : null;
181
+ const name = typeof event.toolName === "string" ? event.toolName : "tool";
182
+ if (!id)
183
+ return;
184
+ const turn = toolOwners.get(id) ?? streamingTurn;
185
+ if (!turn)
186
+ return;
187
+ ensureToolComponent(turn, id, name, event.args).markExecutionStarted();
188
+ requestRender();
189
+ return;
190
+ }
191
+ case "tool_execution_update": {
192
+ const id = typeof event.toolCallId === "string" ? event.toolCallId : null;
193
+ const component = id ? toolOwners.get(id)?.tools.get(id) : undefined;
194
+ const partial = recordOf(event.partialResult);
195
+ if (component && partial) {
196
+ const content = Array.isArray(partial.content) ? partial.content : [];
197
+ component.updateResult({ ...partial, content, isError: false }, true);
198
+ requestRender();
199
+ }
200
+ return;
201
+ }
202
+ case "tool_execution_end": {
203
+ const id = typeof event.toolCallId === "string" ? event.toolCallId : null;
204
+ const component = id ? toolOwners.get(id)?.tools.get(id) : undefined;
205
+ const result = recordOf(event.result);
206
+ if (component && result) {
207
+ const content = Array.isArray(result.content) ? result.content : [];
208
+ component.updateResult({ ...result, content, isError: event.isError === true });
209
+ requestRender();
210
+ }
211
+ return;
212
+ }
213
+ default:
214
+ return;
215
+ }
216
+ }
217
+ };
218
+ }
219
+ export {
220
+ createLiveMirror,
221
+ DRONE_USER_MESSAGE_TYPE,
222
+ DRONE_MESSAGE_TYPE
223
+ };
@@ -0,0 +1,19 @@
1
+ import type { RigBridgeClient, RigExtensionContext } from "./client";
2
+ export type RigToolResult = {
3
+ readonly content: Array<{
4
+ type: "text";
5
+ text: string;
6
+ }>;
7
+ readonly details?: Record<string, unknown>;
8
+ };
9
+ export type RigToolDefinition = {
10
+ readonly name: string;
11
+ readonly label: string;
12
+ readonly description: string;
13
+ readonly parameters: Record<string, unknown>;
14
+ readonly execute: (toolCallId: string, params: Record<string, unknown>) => Promise<RigToolResult>;
15
+ };
16
+ export declare function createRigTools(input: {
17
+ readonly context: RigExtensionContext;
18
+ readonly client: RigBridgeClient;
19
+ }): RigToolDefinition[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/pi-rig",
3
- "version": "0.0.6-alpha.8",
3
+ "version": "0.0.6-alpha.80",
4
4
  "type": "module",
5
5
  "description": "Rig package",
6
6
  "license": "UNLICENSED",
@@ -10,15 +10,19 @@
10
10
  ],
11
11
  "exports": {
12
12
  ".": {
13
+ "types": "./dist/src/index.d.ts",
13
14
  "import": "./dist/src/index.js"
14
15
  },
15
16
  "./client": {
17
+ "types": "./dist/src/client.d.ts",
16
18
  "import": "./dist/src/client.js"
17
19
  },
18
20
  "./commands": {
21
+ "types": "./dist/src/commands.d.ts",
19
22
  "import": "./dist/src/commands.js"
20
23
  },
21
24
  "./tools": {
25
+ "types": "./dist/src/tools.d.ts",
22
26
  "import": "./dist/src/tools.js"
23
27
  }
24
28
  },
@@ -27,13 +31,17 @@
27
31
  },
28
32
  "main": "./dist/src/index.js",
29
33
  "module": "./dist/src/index.js",
34
+ "types": "./dist/src/index.d.ts",
30
35
  "pi": {
31
36
  "extensions": [
32
37
  "./dist/src/index.js"
33
38
  ]
34
39
  },
40
+ "dependencies": {
41
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.80"
42
+ },
35
43
  "peerDependencies": {
36
- "@earendil-works/pi-coding-agent": "*",
44
+ "@earendil-works/pi-coding-agent": ">=0.79.0",
37
45
  "typebox": "*"
38
46
  }
39
47
  }