@co0ontty/wand 1.39.1 → 1.40.1

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": "@co0ontty/wand",
3
- "version": "1.39.1",
3
+ "version": "1.40.1",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,67 +0,0 @@
1
- /**
2
- * ACP (Agent Communication Protocol) implementation
3
- * Inspired by Happy's unified agent communication format
4
- */
5
- import type { ACPMessage, ACPTextMessage, ACPReasoningMessage, ACPToolCallMessage, ACPToolResultMessage, ACPFileEditMessage, ACPTerminalOutputMessage, ACPPermissionRequestMessage, ACPSessionStartMessage, ACPSessionEndMessage, ACPErrorMessage, ConversationTurn } from "./types.js";
6
- /**
7
- * Create a text message
8
- */
9
- export declare function createTextMessage(sessionId: string, role: "user" | "assistant" | "system", content: string): ACPTextMessage;
10
- /**
11
- * Create a reasoning message
12
- */
13
- export declare function createReasoningMessage(sessionId: string, content: string, isStreaming?: boolean): ACPReasoningMessage;
14
- /**
15
- * Create a tool call message
16
- */
17
- export declare function createToolCallMessage(sessionId: string, toolCallId: string, name: string, input: Record<string, unknown>, description?: string): ACPToolCallMessage;
18
- /**
19
- * Create a tool result message
20
- */
21
- export declare function createToolResultMessage(sessionId: string, toolCallId: string, output: string | Array<{
22
- type: string;
23
- [key: string]: unknown;
24
- }>, isError?: boolean): ACPToolResultMessage;
25
- /**
26
- * Create a file edit message
27
- */
28
- export declare function createFileEditMessage(sessionId: string, filePath: string, description: string, options?: {
29
- oldContent?: string;
30
- newContent?: string;
31
- diff?: string;
32
- }): ACPFileEditMessage;
33
- /**
34
- * Create a terminal output message
35
- */
36
- export declare function createTerminalOutputMessage(sessionId: string, data: string, toolCallId?: string): ACPTerminalOutputMessage;
37
- /**
38
- * Create a permission request message
39
- */
40
- export declare function createPermissionRequestMessage(sessionId: string, permissionId: string, toolName: string, description: string): ACPPermissionRequestMessage;
41
- /**
42
- * Create a session start message
43
- */
44
- export declare function createSessionStartMessage(sessionId: string, workingDirectory: string, agent?: string): ACPSessionStartMessage;
45
- /**
46
- * Create a session end message
47
- */
48
- export declare function createSessionEndMessage(sessionId: string, exitCode: number | null): ACPSessionEndMessage;
49
- /**
50
- * Create an error message
51
- */
52
- export declare function createErrorMessage(sessionId: string, error: string, details?: string): ACPErrorMessage;
53
- /**
54
- * Convert ConversationTurn to ACP messages
55
- */
56
- export declare function conversationTurnToACP(sessionId: string, turn: ConversationTurn): ACPMessage[];
57
- /**
58
- * Convert ACP messages to ConversationTurn
59
- */
60
- export declare function acpToConversationTurns(messages: ACPMessage[]): ConversationTurn[];
61
- /**
62
- * Parse Claude stream-json event to ACP message
63
- */
64
- export declare function parseClaudeStreamEvent(sessionId: string, event: {
65
- type?: string;
66
- [key: string]: unknown;
67
- }): ACPMessage | null;
@@ -1,291 +0,0 @@
1
- /**
2
- * ACP (Agent Communication Protocol) implementation
3
- * Inspired by Happy's unified agent communication format
4
- */
5
- /**
6
- * Generate a unique message ID
7
- */
8
- function generateId() {
9
- return `acp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
10
- }
11
- /**
12
- * Create a text message
13
- */
14
- export function createTextMessage(sessionId, role, content) {
15
- return {
16
- id: generateId(),
17
- type: "message",
18
- timestamp: Date.now(),
19
- sessionId,
20
- role,
21
- content,
22
- };
23
- }
24
- /**
25
- * Create a reasoning message
26
- */
27
- export function createReasoningMessage(sessionId, content, isStreaming = false) {
28
- return {
29
- id: generateId(),
30
- type: "reasoning",
31
- timestamp: Date.now(),
32
- sessionId,
33
- content,
34
- isStreaming,
35
- };
36
- }
37
- /**
38
- * Create a tool call message
39
- */
40
- export function createToolCallMessage(sessionId, toolCallId, name, input, description) {
41
- return {
42
- id: generateId(),
43
- type: "tool-call",
44
- timestamp: Date.now(),
45
- sessionId,
46
- toolCallId,
47
- name,
48
- input,
49
- description,
50
- };
51
- }
52
- /**
53
- * Create a tool result message
54
- */
55
- export function createToolResultMessage(sessionId, toolCallId, output, isError = false) {
56
- return {
57
- id: generateId(),
58
- type: "tool-result",
59
- timestamp: Date.now(),
60
- sessionId,
61
- toolCallId,
62
- output,
63
- isError,
64
- };
65
- }
66
- /**
67
- * Create a file edit message
68
- */
69
- export function createFileEditMessage(sessionId, filePath, description, options = {}) {
70
- return {
71
- id: generateId(),
72
- type: "file-edit",
73
- timestamp: Date.now(),
74
- sessionId,
75
- filePath,
76
- description,
77
- ...options,
78
- };
79
- }
80
- /**
81
- * Create a terminal output message
82
- */
83
- export function createTerminalOutputMessage(sessionId, data, toolCallId) {
84
- return {
85
- id: generateId(),
86
- type: "terminal-output",
87
- timestamp: Date.now(),
88
- sessionId,
89
- data,
90
- toolCallId,
91
- };
92
- }
93
- /**
94
- * Create a permission request message
95
- */
96
- export function createPermissionRequestMessage(sessionId, permissionId, toolName, description) {
97
- return {
98
- id: generateId(),
99
- type: "permission-request",
100
- timestamp: Date.now(),
101
- sessionId,
102
- permissionId,
103
- toolName,
104
- description,
105
- };
106
- }
107
- /**
108
- * Create a session start message
109
- */
110
- export function createSessionStartMessage(sessionId, workingDirectory, agent) {
111
- return {
112
- id: generateId(),
113
- type: "session-start",
114
- timestamp: Date.now(),
115
- sessionId,
116
- workingDirectory,
117
- agent,
118
- };
119
- }
120
- /**
121
- * Create a session end message
122
- */
123
- export function createSessionEndMessage(sessionId, exitCode) {
124
- return {
125
- id: generateId(),
126
- type: "session-end",
127
- timestamp: Date.now(),
128
- sessionId,
129
- exitCode,
130
- };
131
- }
132
- /**
133
- * Create an error message
134
- */
135
- export function createErrorMessage(sessionId, error, details) {
136
- return {
137
- id: generateId(),
138
- type: "error",
139
- timestamp: Date.now(),
140
- sessionId,
141
- error,
142
- details,
143
- };
144
- }
145
- /**
146
- * Convert ConversationTurn to ACP messages
147
- */
148
- export function conversationTurnToACP(sessionId, turn) {
149
- const messages = [];
150
- for (const block of turn.content) {
151
- switch (block.type) {
152
- case "text":
153
- messages.push(createTextMessage(sessionId, turn.role, block.text));
154
- break;
155
- case "thinking":
156
- messages.push(createReasoningMessage(sessionId, block.thinking));
157
- break;
158
- case "tool_use":
159
- messages.push(createToolCallMessage(sessionId, block.id, block.name, block.input, block.description));
160
- break;
161
- case "tool_result":
162
- messages.push(createToolResultMessage(sessionId, block.tool_use_id, block.content, block.is_error ?? false));
163
- break;
164
- }
165
- }
166
- return messages;
167
- }
168
- /**
169
- * Convert ACP messages to ConversationTurn
170
- */
171
- export function acpToConversationTurns(messages) {
172
- const turns = [];
173
- let currentTurn = null;
174
- for (const msg of messages) {
175
- switch (msg.type) {
176
- case "message":
177
- if (msg.role === "user") {
178
- // Start a new user turn
179
- if (currentTurn) {
180
- turns.push(currentTurn);
181
- }
182
- currentTurn = {
183
- role: "user",
184
- content: [{ type: "text", text: msg.content }],
185
- };
186
- }
187
- else if (msg.role === "assistant") {
188
- if (currentTurn?.role === "assistant") {
189
- // Add to existing assistant turn
190
- currentTurn.content.push({ type: "text", text: msg.content });
191
- }
192
- else {
193
- // Start new assistant turn
194
- if (currentTurn) {
195
- turns.push(currentTurn);
196
- }
197
- currentTurn = {
198
- role: "assistant",
199
- content: [{ type: "text", text: msg.content }],
200
- };
201
- }
202
- }
203
- break;
204
- case "reasoning":
205
- if (currentTurn?.role === "assistant") {
206
- currentTurn.content.push({ type: "thinking", thinking: msg.content });
207
- }
208
- break;
209
- case "tool-call":
210
- if (currentTurn?.role === "assistant") {
211
- currentTurn.content.push({
212
- type: "tool_use",
213
- id: msg.toolCallId,
214
- name: msg.name,
215
- input: msg.input,
216
- description: msg.description,
217
- });
218
- }
219
- break;
220
- case "tool-result":
221
- if (currentTurn?.role === "assistant") {
222
- currentTurn.content.push({
223
- type: "tool_result",
224
- tool_use_id: msg.toolCallId,
225
- content: msg.output,
226
- is_error: msg.isError,
227
- });
228
- }
229
- break;
230
- }
231
- }
232
- if (currentTurn) {
233
- turns.push(currentTurn);
234
- }
235
- return turns;
236
- }
237
- /**
238
- * Parse Claude stream-json event to ACP message
239
- */
240
- export function parseClaudeStreamEvent(sessionId, event) {
241
- switch (event.type) {
242
- case "content_block_start": {
243
- const block = event.content_block;
244
- if (!block)
245
- return null;
246
- if (block.type === "text") {
247
- return createTextMessage(sessionId, "assistant", "");
248
- }
249
- if (block.type === "thinking") {
250
- return createReasoningMessage(sessionId, "", true);
251
- }
252
- if (block.type === "tool_use") {
253
- return createToolCallMessage(sessionId, block.id ?? "", block.name ?? "", block.input ?? {});
254
- }
255
- return null;
256
- }
257
- case "content_block_delta": {
258
- const delta = event.delta;
259
- if (!delta)
260
- return null;
261
- if (delta.type === "text_delta" && delta.text) {
262
- return createTextMessage(sessionId, "assistant", delta.text);
263
- }
264
- if (delta.type === "thinking_delta" && delta.thinking) {
265
- return createReasoningMessage(sessionId, delta.thinking, true);
266
- }
267
- return null;
268
- }
269
- case "content_block_stop": {
270
- // Block completed - could emit a completion event
271
- return null;
272
- }
273
- case "message_start": {
274
- const message = event.message;
275
- if (message?.role === "assistant") {
276
- return createTextMessage(sessionId, "assistant", "");
277
- }
278
- return null;
279
- }
280
- case "message_stop": {
281
- // Message completed
282
- return null;
283
- }
284
- case "error": {
285
- const error = event.error;
286
- return createErrorMessage(sessionId, error?.message ?? "Unknown error");
287
- }
288
- default:
289
- return null;
290
- }
291
- }
@@ -1,35 +0,0 @@
1
- import type { ContentBlock } from "./types.js";
2
- export interface ClaudeStreamAdapterState {
3
- blocks: ContentBlock[];
4
- usage?: {
5
- inputTokens?: number;
6
- outputTokens?: number;
7
- cacheReadInputTokens?: number;
8
- cacheCreationInputTokens?: number;
9
- totalCostUsd?: number;
10
- };
11
- sessionId: string | null;
12
- }
13
- type ClaudeStreamEvent = {
14
- type?: string;
15
- session_id?: string;
16
- message?: {
17
- role?: string;
18
- content?: unknown[];
19
- };
20
- content_block?: {
21
- type?: string;
22
- [key: string]: unknown;
23
- };
24
- delta?: {
25
- type?: string;
26
- text?: string;
27
- thinking?: string;
28
- partial_json?: string;
29
- };
30
- result?: unknown;
31
- usage?: Record<string, unknown>;
32
- total_cost_usd?: number;
33
- };
34
- export declare function updateClaudeStreamState(state: ClaudeStreamAdapterState, event: ClaudeStreamEvent): ClaudeStreamAdapterState;
35
- export {};
@@ -1,153 +0,0 @@
1
- function normalizeToolInput(block) {
2
- if (typeof block._partialJson === "string" && block._partialJson.length > 0) {
3
- try {
4
- const parsed = JSON.parse(block._partialJson);
5
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
6
- return parsed;
7
- }
8
- }
9
- catch {
10
- // Keep original input if partial JSON is incomplete.
11
- }
12
- }
13
- if (block.input && typeof block.input === "object" && !Array.isArray(block.input)) {
14
- return block.input;
15
- }
16
- return {};
17
- }
18
- function appendAssistantBlock(target, block) {
19
- if (!block.type) {
20
- return;
21
- }
22
- if (block.type === "text") {
23
- target.push({ type: "text", text: typeof block.text === "string" ? block.text : "" });
24
- return;
25
- }
26
- if (block.type === "thinking") {
27
- target.push({ type: "thinking", thinking: typeof block.thinking === "string" ? block.thinking : "" });
28
- return;
29
- }
30
- if (block.type === "tool_use") {
31
- target.push({
32
- type: "tool_use",
33
- id: typeof block.id === "string" ? block.id : "",
34
- name: typeof block.name === "string" ? block.name : "",
35
- description: typeof block.description === "string" ? block.description : undefined,
36
- input: normalizeToolInput({ input: block.input, _partialJson: block._partialJson }),
37
- });
38
- return;
39
- }
40
- if (block.type === "tool_result") {
41
- const content = typeof block.content === "string"
42
- ? block.content
43
- : Array.isArray(block.content)
44
- ? block.content
45
- : "";
46
- target.push({
47
- type: "tool_result",
48
- tool_use_id: typeof block.tool_use_id === "string" ? block.tool_use_id : "",
49
- content,
50
- is_error: block.is_error === true,
51
- });
52
- }
53
- }
54
- function buildUsage(event) {
55
- if (!event.usage && event.total_cost_usd === undefined) {
56
- return undefined;
57
- }
58
- return {
59
- inputTokens: typeof event.usage?.input_tokens === "number" ? event.usage.input_tokens : undefined,
60
- outputTokens: typeof event.usage?.output_tokens === "number" ? event.usage.output_tokens : undefined,
61
- cacheReadInputTokens: typeof event.usage?.cache_read_input_tokens === "number"
62
- ? event.usage.cache_read_input_tokens
63
- : undefined,
64
- cacheCreationInputTokens: typeof event.usage?.cache_creation_input_tokens === "number"
65
- ? event.usage.cache_creation_input_tokens
66
- : undefined,
67
- totalCostUsd: typeof event.total_cost_usd === "number" ? event.total_cost_usd : undefined,
68
- };
69
- }
70
- export function updateClaudeStreamState(state, event) {
71
- if (typeof event.session_id === "string" && event.session_id.length > 0) {
72
- state.sessionId = event.session_id;
73
- }
74
- switch (event.type) {
75
- case "assistant": {
76
- const content = Array.isArray(event.message?.content) ? event.message?.content : [];
77
- for (const block of content) {
78
- if (block && typeof block === "object" && "type" in block) {
79
- appendAssistantBlock(state.blocks, block);
80
- }
81
- }
82
- break;
83
- }
84
- case "content_block_start": {
85
- if (event.content_block && typeof event.content_block === "object") {
86
- appendAssistantBlock(state.blocks, event.content_block);
87
- }
88
- break;
89
- }
90
- case "content_block_delta": {
91
- const lastBlock = state.blocks[state.blocks.length - 1];
92
- if (!lastBlock || !event.delta) {
93
- break;
94
- }
95
- if (lastBlock.type === "text" && event.delta.type === "text_delta" && typeof event.delta.text === "string") {
96
- lastBlock.text += event.delta.text;
97
- }
98
- else if (lastBlock.type === "thinking" &&
99
- event.delta.type === "thinking_delta" &&
100
- typeof event.delta.thinking === "string") {
101
- lastBlock.thinking += event.delta.thinking;
102
- }
103
- else if (lastBlock.type === "tool_use" &&
104
- typeof event.delta.partial_json === "string") {
105
- const nextPartial = (lastBlock._partialJson ?? "") + event.delta.partial_json;
106
- lastBlock._partialJson = nextPartial;
107
- lastBlock.input = normalizeToolInput({
108
- input: lastBlock.input,
109
- _partialJson: nextPartial,
110
- });
111
- }
112
- break;
113
- }
114
- case "user": {
115
- const content = Array.isArray(event.message?.content) ? event.message?.content : [];
116
- for (const block of content) {
117
- if (block &&
118
- typeof block === "object" &&
119
- "type" in block &&
120
- block.type === "tool_result") {
121
- appendAssistantBlock(state.blocks, block);
122
- }
123
- }
124
- break;
125
- }
126
- case "result": {
127
- const usage = buildUsage(event);
128
- if (usage) {
129
- state.usage = usage;
130
- }
131
- if (typeof event.result === "string") {
132
- const hasAssistantText = state.blocks.some((block) => block.type === "text");
133
- if (!hasAssistantText && event.result.trim().length > 0) {
134
- state.blocks.push({ type: "text", text: event.result });
135
- }
136
- }
137
- else if (event.result && typeof event.result === "object") {
138
- const resultContent = event.result.content;
139
- if (Array.isArray(resultContent)) {
140
- for (const block of resultContent) {
141
- if (block && typeof block === "object" && "type" in block) {
142
- appendAssistantBlock(state.blocks, block);
143
- }
144
- }
145
- }
146
- }
147
- break;
148
- }
149
- default:
150
- break;
151
- }
152
- return state;
153
- }
@@ -1,27 +0,0 @@
1
- import { ChildProcess } from "node:child_process";
2
- import type { ContentBlock, ConversationTurn } from "./types.js";
3
- export interface ClaudeStructuredRunnerCallbacks {
4
- onOutput: (text: string) => void;
5
- onBlocks: (blocks: ContentBlock[], usage?: ConversationTurn["usage"]) => void;
6
- onSessionId: (sessionId: string) => void;
7
- onClose: (exitCode: number | null, state: ClaudeStructuredRunnerState) => void;
8
- onError: (error: Error) => void;
9
- }
10
- export interface ClaudeStructuredRunnerState {
11
- stdoutBuffer: string;
12
- assistantBlocks: ContentBlock[];
13
- usage?: ConversationTurn["usage"];
14
- sessionId: string | null;
15
- }
16
- export interface StartClaudeStructuredRunnerOptions {
17
- command: string;
18
- cwd: string;
19
- env?: NodeJS.ProcessEnv;
20
- callbacks: ClaudeStructuredRunnerCallbacks;
21
- }
22
- export interface ClaudeStructuredRunnerHandle {
23
- child: ChildProcess;
24
- getState: () => ClaudeStructuredRunnerState;
25
- kill: () => void;
26
- }
27
- export declare function startClaudeStructuredRunner(options: StartClaudeStructuredRunnerOptions): ClaudeStructuredRunnerHandle;
@@ -1,106 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { updateClaudeStreamState } from "./claude-stream-adapter.js";
3
- function toContentBlocks(blocks) {
4
- return blocks.map((block) => {
5
- switch (block.type) {
6
- case "text":
7
- return { type: "text", text: typeof block.text === "string" ? block.text : "" };
8
- case "thinking":
9
- return { type: "thinking", thinking: typeof block.thinking === "string" ? block.thinking : "" };
10
- case "tool_use":
11
- return {
12
- type: "tool_use",
13
- id: typeof block.id === "string" ? block.id : "",
14
- name: typeof block.name === "string" ? block.name : "",
15
- description: typeof block.description === "string" ? block.description : undefined,
16
- input: block.input && typeof block.input === "object" && !Array.isArray(block.input)
17
- ? block.input
18
- : {},
19
- };
20
- case "tool_result":
21
- return {
22
- type: "tool_result",
23
- tool_use_id: typeof block.tool_use_id === "string" ? block.tool_use_id : "",
24
- content: typeof block.content === "string"
25
- ? block.content
26
- : Array.isArray(block.content)
27
- ? block.content
28
- : "",
29
- is_error: block.is_error === true,
30
- };
31
- default:
32
- return { type: "text", text: JSON.stringify(block) };
33
- }
34
- });
35
- }
36
- export function startClaudeStructuredRunner(options) {
37
- const state = {
38
- stdoutBuffer: "",
39
- assistantBlocks: [],
40
- usage: undefined,
41
- sessionId: null,
42
- };
43
- const child = spawn(options.command, [], {
44
- cwd: options.cwd,
45
- env: options.env,
46
- shell: true,
47
- stdio: ["ignore", "pipe", "pipe"],
48
- });
49
- child.stdout?.on("data", (chunk) => {
50
- state.stdoutBuffer += chunk.toString();
51
- const lines = state.stdoutBuffer.split("\n");
52
- state.stdoutBuffer = lines.pop() || "";
53
- for (const line of lines) {
54
- const trimmed = line.trim();
55
- if (!trimmed) {
56
- continue;
57
- }
58
- try {
59
- const event = JSON.parse(trimmed);
60
- const nextState = updateClaudeStreamState({
61
- blocks: state.assistantBlocks,
62
- usage: state.usage,
63
- sessionId: state.sessionId,
64
- }, event);
65
- state.assistantBlocks = nextState.blocks.map((block) => ({ ...block }));
66
- state.usage = nextState.usage;
67
- if (nextState.sessionId && nextState.sessionId !== state.sessionId) {
68
- state.sessionId = nextState.sessionId;
69
- options.callbacks.onSessionId(nextState.sessionId);
70
- }
71
- options.callbacks.onBlocks(state.assistantBlocks, state.usage);
72
- }
73
- catch {
74
- options.callbacks.onOutput(trimmed + "\n");
75
- }
76
- }
77
- });
78
- child.stderr?.on("data", (chunk) => {
79
- options.callbacks.onOutput(chunk.toString());
80
- });
81
- child.on("close", (code) => {
82
- options.callbacks.onClose(code, {
83
- stdoutBuffer: state.stdoutBuffer,
84
- assistantBlocks: [...state.assistantBlocks],
85
- usage: state.usage,
86
- sessionId: state.sessionId,
87
- });
88
- });
89
- child.on("error", (error) => {
90
- options.callbacks.onError(error);
91
- });
92
- return {
93
- child,
94
- getState: () => ({
95
- stdoutBuffer: state.stdoutBuffer,
96
- assistantBlocks: [...state.assistantBlocks],
97
- usage: state.usage,
98
- sessionId: state.sessionId,
99
- }),
100
- kill: () => {
101
- if (!child.killed) {
102
- child.kill();
103
- }
104
- },
105
- };
106
- }
@@ -1,2 +0,0 @@
1
- import type { ChatMessage } from "./types.js";
2
- export declare function parseMessages(output: string, command?: string): ChatMessage[];