@co0ontty/wand 1.3.6 → 1.5.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/dist/storage.js CHANGED
@@ -12,6 +12,17 @@ function parseStoredMessages(raw) {
12
12
  return undefined;
13
13
  }
14
14
  }
15
+ function parseStructuredState(raw) {
16
+ if (!raw) {
17
+ return undefined;
18
+ }
19
+ try {
20
+ return JSON.parse(raw);
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ }
15
26
  export const DEFAULT_DB_FILE = "wand.db";
16
27
  export function resolveDatabasePath(configPath) {
17
28
  return path.resolve(path.dirname(configPath), DEFAULT_DB_FILE);
@@ -34,7 +45,14 @@ const INIT_SQL = `
34
45
  output TEXT NOT NULL,
35
46
  archived INTEGER NOT NULL DEFAULT 0,
36
47
  archived_at TEXT,
37
- claude_session_id TEXT
48
+ claude_session_id TEXT,
49
+ session_kind TEXT NOT NULL DEFAULT 'pty',
50
+ runner TEXT,
51
+ messages TEXT,
52
+ structured_state TEXT,
53
+ resumed_from_session_id TEXT,
54
+ resumed_to_session_id TEXT,
55
+ auto_recovered INTEGER NOT NULL DEFAULT 0
38
56
  );
39
57
 
40
58
  CREATE TABLE IF NOT EXISTS app_config (
@@ -125,9 +143,9 @@ export class WandStorage {
125
143
  this.db
126
144
  .prepare(`INSERT INTO command_sessions (
127
145
  id, command, cwd, mode, status, exit_code, started_at, ended_at, output
128
- , archived, archived_at, claude_session_id, messages
146
+ , archived, archived_at, claude_session_id, session_kind, runner, messages, structured_state
129
147
  , resumed_from_session_id, resumed_to_session_id, auto_recovered
130
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
148
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
131
149
  ON CONFLICT(id) DO UPDATE SET
132
150
  command = excluded.command,
133
151
  cwd = excluded.cwd,
@@ -140,11 +158,14 @@ export class WandStorage {
140
158
  archived = excluded.archived,
141
159
  archived_at = excluded.archived_at,
142
160
  claude_session_id = excluded.claude_session_id,
161
+ session_kind = excluded.session_kind,
162
+ runner = excluded.runner,
143
163
  messages = excluded.messages,
164
+ structured_state = excluded.structured_state,
144
165
  resumed_from_session_id = excluded.resumed_from_session_id,
145
166
  resumed_to_session_id = excluded.resumed_to_session_id,
146
167
  auto_recovered = excluded.auto_recovered`)
147
- .run(snapshot.id, snapshot.command, snapshot.cwd, snapshot.mode, snapshot.status, snapshot.exitCode, snapshot.startedAt, snapshot.endedAt, snapshot.output, snapshot.archived ? 1 : 0, snapshot.archivedAt, snapshot.claudeSessionId, snapshot.messages ? JSON.stringify(snapshot.messages) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0);
168
+ .run(snapshot.id, snapshot.command, snapshot.cwd, snapshot.mode, snapshot.status, snapshot.exitCode, snapshot.startedAt, snapshot.endedAt, snapshot.output, snapshot.archived ? 1 : 0, snapshot.archivedAt, snapshot.claudeSessionId, snapshot.sessionKind ?? "pty", snapshot.runner ?? null, snapshot.messages ? JSON.stringify(snapshot.messages) : null, snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0);
148
169
  this.db.exec("COMMIT");
149
170
  }
150
171
  catch (error) {
@@ -163,13 +184,14 @@ export class WandStorage {
163
184
  command = ?, cwd = ?, mode = ?, status = ?, exit_code = ?,
164
185
  started_at = ?, ended_at = ?, output = ?,
165
186
  archived = ?, archived_at = ?, claude_session_id = ?,
187
+ session_kind = ?, runner = ?, structured_state = ?,
166
188
  resumed_from_session_id = ?, resumed_to_session_id = ?, auto_recovered = ?
167
189
  WHERE id = ?`)
168
- .run(snapshot.command, snapshot.cwd, snapshot.mode, snapshot.status, snapshot.exitCode, snapshot.startedAt, snapshot.endedAt, snapshot.output, snapshot.archived ? 1 : 0, snapshot.archivedAt, snapshot.claudeSessionId, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0, snapshot.id);
190
+ .run(snapshot.command, snapshot.cwd, snapshot.mode, snapshot.status, snapshot.exitCode, snapshot.startedAt, snapshot.endedAt, snapshot.output, snapshot.archived ? 1 : 0, snapshot.archivedAt, snapshot.claudeSessionId, snapshot.sessionKind ?? "pty", snapshot.runner ?? null, snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null, snapshot.resumedFromSessionId ?? null, snapshot.resumedToSessionId ?? null, snapshot.autoRecovered ? 1 : 0, snapshot.id);
169
191
  }
170
192
  getSession(id) {
171
193
  const row = this.db
172
- .prepare(`SELECT id, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages
194
+ .prepare(`SELECT id, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, structured_state
173
195
  , resumed_from_session_id, resumed_to_session_id, auto_recovered
174
196
  FROM command_sessions
175
197
  WHERE id = ?`)
@@ -178,7 +200,7 @@ export class WandStorage {
178
200
  }
179
201
  getLatestSessionByClaudeSessionId(claudeSessionId) {
180
202
  const row = this.db
181
- .prepare(`SELECT id, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages
203
+ .prepare(`SELECT id, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, structured_state
182
204
  , resumed_from_session_id, resumed_to_session_id, auto_recovered
183
205
  FROM command_sessions
184
206
  WHERE claude_session_id = ?
@@ -189,7 +211,7 @@ export class WandStorage {
189
211
  }
190
212
  loadSessions() {
191
213
  const rows = this.db
192
- .prepare(`SELECT id, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages
214
+ .prepare(`SELECT id, session_kind, runner, command, cwd, mode, status, exit_code, started_at, ended_at, output, archived, archived_at, claude_session_id, messages, structured_state
193
215
  , resumed_from_session_id, resumed_to_session_id, auto_recovered
194
216
  FROM command_sessions
195
217
  ORDER BY started_at DESC`)
@@ -199,6 +221,8 @@ export class WandStorage {
199
221
  mapSessionRow(row) {
200
222
  return {
201
223
  id: row.id,
224
+ sessionKind: row.session_kind ?? "pty",
225
+ runner: row.runner ?? undefined,
202
226
  command: row.command,
203
227
  cwd: row.cwd,
204
228
  mode: row.mode,
@@ -211,6 +235,7 @@ export class WandStorage {
211
235
  archivedAt: row.archived_at,
212
236
  claudeSessionId: row.claude_session_id,
213
237
  messages: parseStoredMessages(row.messages),
238
+ structuredState: parseStructuredState(row.structured_state),
214
239
  resumedFromSessionId: row.resumed_from_session_id ?? undefined,
215
240
  resumedToSessionId: row.resumed_to_session_id ?? undefined,
216
241
  autoRecovered: Boolean(row.auto_recovered)
@@ -232,9 +257,18 @@ function ensureCommandSessionSchema(db) {
232
257
  if (!names.has("claude_session_id")) {
233
258
  db.exec("ALTER TABLE command_sessions ADD COLUMN claude_session_id TEXT");
234
259
  }
260
+ if (!names.has("session_kind")) {
261
+ db.exec("ALTER TABLE command_sessions ADD COLUMN session_kind TEXT NOT NULL DEFAULT 'pty'");
262
+ }
263
+ if (!names.has("runner")) {
264
+ db.exec("ALTER TABLE command_sessions ADD COLUMN runner TEXT");
265
+ }
235
266
  if (!names.has("messages")) {
236
267
  db.exec("ALTER TABLE command_sessions ADD COLUMN messages TEXT");
237
268
  }
269
+ if (!names.has("structured_state")) {
270
+ db.exec("ALTER TABLE command_sessions ADD COLUMN structured_state TEXT");
271
+ }
238
272
  if (!names.has("resumed_from_session_id")) {
239
273
  db.exec("ALTER TABLE command_sessions ADD COLUMN resumed_from_session_id TEXT");
240
274
  }
@@ -0,0 +1,56 @@
1
+ import { WandStorage } from "./storage.js";
2
+ import { ExecutionMode, SessionRunner, SessionSnapshot, WandConfig } from "./types.js";
3
+ import { ProcessEvent } from "./ws-broadcast.js";
4
+ interface CreateStructuredSessionOptions {
5
+ cwd: string;
6
+ mode: ExecutionMode;
7
+ prompt?: string;
8
+ runner?: SessionRunner;
9
+ }
10
+ export declare class StructuredSessionManager {
11
+ private readonly storage;
12
+ private readonly config;
13
+ private readonly sessions;
14
+ private readonly pendingChildren;
15
+ private emitEvent;
16
+ constructor(storage: WandStorage, config: WandConfig);
17
+ setEventEmitter(emitEvent: (event: ProcessEvent) => void): void;
18
+ list(): SessionSnapshot[];
19
+ get(id: string): SessionSnapshot | null;
20
+ createSession(options: CreateStructuredSessionOptions): SessionSnapshot;
21
+ sendMessage(id: string, input: string): Promise<SessionSnapshot>;
22
+ /** Approve a pending permission request. */
23
+ approvePermission(sessionId: string): SessionSnapshot;
24
+ /** Deny a pending permission request. */
25
+ denyPermission(sessionId: string): SessionSnapshot;
26
+ /** Toggle auto-approve for the session. */
27
+ toggleAutoApprove(sessionId: string): SessionSnapshot;
28
+ /** Resolve a specific escalation by requestId. */
29
+ resolveEscalation(sessionId: string, requestId: string, resolution?: "approve_once" | "approve_turn" | "deny"): SessionSnapshot;
30
+ stop(id: string): SessionSnapshot;
31
+ delete(id: string): void;
32
+ private requireSession;
33
+ private emit;
34
+ private resolvePermission;
35
+ private incrementApprovalStats;
36
+ private buildPermissionArgs;
37
+ /**
38
+ * Spawn `claude -p --output-format stream-json` and parse NDJSON lines as
39
+ * they arrive, emitting incremental WebSocket events so the UI can render
40
+ * text / thinking / tool_use blocks in real-time.
41
+ *
42
+ * Permission handling:
43
+ * - Non-root + full-access/managed: --permission-mode bypassPermissions
44
+ * - Non-root + auto-edit: --permission-mode acceptEdits
45
+ * - Root: --permission-mode acceptEdits + --allowedTools (extends approval
46
+ * outside CWD). stdin is always "ignore" — no ACP bidirectional control.
47
+ */
48
+ private runClaudeStreaming;
49
+ private extractAssistantMessage;
50
+ private compactContentBlocks;
51
+ private normalizeToolInput;
52
+ private normalizeToolResultContent;
53
+ private extractModelName;
54
+ private extractUsage;
55
+ }
56
+ export {};