@mirasoth/soothe-client 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/index.js ADDED
@@ -0,0 +1,383 @@
1
+ import {
2
+ Client,
3
+ decodeMessage,
4
+ defaultConfig,
5
+ encodeMessage,
6
+ extractSootheLoopID,
7
+ loadConfigFromEnv,
8
+ newLoopInputMessage,
9
+ newLoopNewMessage,
10
+ newLoopSubscribeMessage,
11
+ newRequestID,
12
+ splitWirePayload
13
+ } from "./chunk-OMAC7LA7.js";
14
+
15
+ // src/errors.ts
16
+ var ConnectionError = class extends Error {
17
+ url;
18
+ attempt;
19
+ cause;
20
+ constructor(url, attempt, cause) {
21
+ super(`connection error to ${url} (attempt ${attempt}): ${cause.message}`);
22
+ this.name = "ConnectionError";
23
+ this.url = url;
24
+ this.attempt = attempt;
25
+ this.cause = cause;
26
+ }
27
+ };
28
+ var DaemonError = class extends Error {
29
+ code;
30
+ /** The daemon's error message text. */
31
+ daemonMessage;
32
+ constructor(code, message) {
33
+ super(`daemon error [${code}]: ${message}`);
34
+ this.name = "DaemonError";
35
+ this.code = code;
36
+ this.daemonMessage = message;
37
+ }
38
+ };
39
+ var TimeoutError = class extends Error {
40
+ operation;
41
+ duration;
42
+ constructor(operation, duration) {
43
+ super(`timeout after ${duration} waiting for ${operation}`);
44
+ this.name = "TimeoutError";
45
+ this.operation = operation;
46
+ this.duration = duration;
47
+ }
48
+ };
49
+
50
+ // src/verbosity.ts
51
+ var VerbosityTier = /* @__PURE__ */ ((VerbosityTier2) => {
52
+ VerbosityTier2[VerbosityTier2["Quiet"] = 0] = "Quiet";
53
+ VerbosityTier2[VerbosityTier2["Normal"] = 1] = "Normal";
54
+ VerbosityTier2[VerbosityTier2["Detailed"] = 2] = "Detailed";
55
+ VerbosityTier2[VerbosityTier2["Debug"] = 3] = "Debug";
56
+ VerbosityTier2[VerbosityTier2["Internal"] = 99] = "Internal";
57
+ return VerbosityTier2;
58
+ })(VerbosityTier || {});
59
+ var verbosityLevelValues = {
60
+ quiet: 0,
61
+ normal: 1,
62
+ debug: 3
63
+ };
64
+ function shouldShow(tier, verbosity) {
65
+ if (tier === 99 /* Internal */) {
66
+ return false;
67
+ }
68
+ const level = verbosityLevelValues[verbosity] ?? 1;
69
+ return tier <= level;
70
+ }
71
+ function isValidVerbosityLevel(s) {
72
+ return s in verbosityLevelValues;
73
+ }
74
+
75
+ // src/events.ts
76
+ var EventPlanCreated = "soothe.cognition.plan.created";
77
+ var EventExploreStarted = "soothe.subagent.explore.started";
78
+ var EventExploreMilestone = "soothe.subagent.explore.milestone";
79
+ var EventExploreStepCompleted = "soothe.subagent.explore.step.completed";
80
+ var EventExploreCompleted = "soothe.subagent.explore.completed";
81
+ var EventTacitusStarted = "soothe.subagent.tacitus.started";
82
+ var EventTacitusGatherSummary = "soothe.subagent.tacitus.gather.summary";
83
+ var EventTacitusCompleted = "soothe.subagent.tacitus.completed";
84
+ var EventReplayComplete = "replay_complete";
85
+ var EventLoopReattachedWire = "loop_reattached";
86
+ var EventToolStarted = "soothe.tool.execution.started";
87
+ var EventToolCompleted = "soothe.tool.execution.completed";
88
+ var EventToolError = "soothe.tool.execution.error";
89
+ var EventStreamToolCallUpdate = "soothe.stream.tool_call.update";
90
+ var EventToolCallUpdatesBatch = "tool_call_updates_batch";
91
+ var EventAgentLoopStarted = "soothe.cognition.agent_loop.started";
92
+ var EventAgentLoopIterated = "soothe.cognition.agent_loop.iterated";
93
+ var EventAgentLoopCompleted = "soothe.cognition.agent_loop.completed";
94
+ var EventAgentLoopReasoned = "soothe.cognition.agent_loop.reasoned";
95
+ var EventMessageReceived = "soothe.protocol.message.received";
96
+ var EventMessageSent = "soothe.protocol.message.sent";
97
+ var EventFinalReport = "soothe.output.autonomous.final_report.reported";
98
+ var EventGeneralFailed = "soothe.error.general.failed";
99
+ function parseNamespace(ns) {
100
+ const parts = splitNamespace(ns);
101
+ if (parts.length < 4 || parts[0] !== "soothe") {
102
+ return null;
103
+ }
104
+ if (parts[1] === "internal") {
105
+ return null;
106
+ }
107
+ return { domain: parts[1], component: parts[2], action: parts[3] };
108
+ }
109
+ function splitNamespace(ns) {
110
+ const parts = [];
111
+ let start = 0;
112
+ for (let i = 0; i < ns.length; i++) {
113
+ if (ns[i] === ".") {
114
+ parts.push(ns.slice(start, i));
115
+ start = i + 1;
116
+ }
117
+ }
118
+ parts.push(ns.slice(start));
119
+ return parts;
120
+ }
121
+ function classifyEventVerbosity(eventTypeOrNamespace) {
122
+ const parsed = parseNamespace(eventTypeOrNamespace);
123
+ if (!parsed) {
124
+ return classifyByEventTypeString(eventTypeOrNamespace);
125
+ }
126
+ return classifyByDomainAndComponent(parsed.domain, parsed.component, eventTypeOrNamespace);
127
+ }
128
+ function classifyByDomainAndComponent(domain, _component, full) {
129
+ switch (domain) {
130
+ case "cognition":
131
+ return 1 /* Normal */;
132
+ case "protocol":
133
+ return 2 /* Detailed */;
134
+ case "tool":
135
+ return 99 /* Internal */;
136
+ case "subagent":
137
+ return classifySubagentEvent(full);
138
+ case "output":
139
+ case "error":
140
+ return 0 /* Quiet */;
141
+ default:
142
+ return 1 /* Normal */;
143
+ }
144
+ }
145
+ function classifySubagentEvent(full) {
146
+ const parsed = parseNamespace(full);
147
+ if (!parsed) return 1 /* Normal */;
148
+ switch (parsed.action) {
149
+ case "started":
150
+ case "completed":
151
+ return 1 /* Normal */;
152
+ default:
153
+ return 2 /* Detailed */;
154
+ }
155
+ }
156
+ function classifyByEventTypeString(eventType) {
157
+ if (eventType === EventFinalReport || eventType === EventGeneralFailed) {
158
+ return 0 /* Quiet */;
159
+ }
160
+ if (eventType === EventToolStarted) {
161
+ return 99 /* Internal */;
162
+ }
163
+ return 1 /* Normal */;
164
+ }
165
+ function isCompletionEvent(eventType) {
166
+ return eventType.endsWith(".completed") || eventType.endsWith(".failed") || eventType === EventGeneralFailed;
167
+ }
168
+ function isSubagentProgressEvent(eventType) {
169
+ const parsed = parseNamespace(eventType);
170
+ if (!parsed || parsed.domain !== "subagent") {
171
+ return false;
172
+ }
173
+ return parsed.action === "started" || parsed.action === "completed";
174
+ }
175
+ var ESSENTIAL_EVENT_TYPES = /* @__PURE__ */ new Set([
176
+ EventAgentLoopStarted,
177
+ EventAgentLoopCompleted,
178
+ EventAgentLoopReasoned,
179
+ EventPlanCreated,
180
+ EventExploreStarted,
181
+ EventExploreCompleted,
182
+ EventTacitusStarted,
183
+ EventTacitusCompleted,
184
+ EventGeneralFailed
185
+ ]);
186
+
187
+ // src/helpers.ts
188
+ async function checkDaemonStatus(client, timeout) {
189
+ return client.requestResponse({ type: "daemon_status" }, "daemon_status_response", timeout ?? 5e3);
190
+ }
191
+ async function isDaemonLive(wsURL, timeout) {
192
+ const { Client: Client2 } = await import("./client-QS23U6WX.js");
193
+ const t = timeout ?? 5e3;
194
+ const client = new Client2(wsURL, defaultConfig());
195
+ try {
196
+ await client.connect();
197
+ } catch {
198
+ return false;
199
+ }
200
+ try {
201
+ await checkDaemonStatus(client, t);
202
+ return true;
203
+ } catch {
204
+ return false;
205
+ } finally {
206
+ client.close();
207
+ }
208
+ }
209
+ async function requestDaemonShutdown(client, timeout) {
210
+ const resp = await client.requestResponse({ type: "daemon_shutdown" }, "shutdown_ack", timeout ?? 1e4);
211
+ if (resp.status !== "acknowledged") {
212
+ throw new Error(`shutdown not acknowledged: ${JSON.stringify(resp)}`);
213
+ }
214
+ }
215
+ async function fetchSkillsCatalog(client, timeout) {
216
+ const resp = await client.requestResponse({ type: "skills_list" }, "skills_list_response", timeout ?? 15e3);
217
+ const skillsRaw = resp.skills;
218
+ if (!skillsRaw || !Array.isArray(skillsRaw)) return [];
219
+ return skillsRaw.filter((s) => typeof s === "object" && s !== null);
220
+ }
221
+ async function fetchConfigSection(client, section, timeout) {
222
+ const resp = await client.requestResponse({ type: "config_get", section }, "config_get_response", timeout ?? 5e3);
223
+ const sec = resp[section];
224
+ if (sec && typeof sec === "object") {
225
+ return sec;
226
+ }
227
+ return resp;
228
+ }
229
+
230
+ // src/session.ts
231
+ async function bootstrapLoopSession(client, resumeLoopId, config, loopNew) {
232
+ const cfg = config ?? defaultConfig();
233
+ await client.sendMessage({ type: "daemon_ready" });
234
+ await waitDaemonReady(client, cfg.daemonReadyTimeout);
235
+ let loopId = (resumeLoopId ?? "").trim();
236
+ if (!loopId) {
237
+ const newResp = await client.requestResponse(
238
+ newLoopNewMessage(loopNew),
239
+ "loop_new_response",
240
+ cfg.loopStatusTimeout
241
+ );
242
+ loopId = String(newResp.loop_id ?? "").trim();
243
+ if (!loopId) {
244
+ throw new Error("loop_new_response missing loop_id");
245
+ }
246
+ }
247
+ const subResp = await client.requestResponse(
248
+ { type: "loop_subscribe", loop_id: loopId, verbosity: cfg.verbosityLevel },
249
+ "loop_subscribe_response",
250
+ cfg.subscriptionTimeout
251
+ );
252
+ if (subResp.success === false) {
253
+ throw new Error(String(subResp.message ?? "loop_subscribe failed"));
254
+ }
255
+ return loopId;
256
+ }
257
+ async function waitDaemonReady(client, timeout) {
258
+ const deadline = Date.now() + timeout;
259
+ while (Date.now() < deadline) {
260
+ const remaining = deadline - Date.now();
261
+ if (remaining <= 0) break;
262
+ const ev = await client.readEventWithTimeout(remaining);
263
+ if (ev === null) break;
264
+ if (ev.type === "daemon_ready") {
265
+ if (ev.state === "ready") return;
266
+ throw new Error(
267
+ `daemon not ready: state=${JSON.stringify(ev.state)} message=${JSON.stringify(ev.message ?? "")}`
268
+ );
269
+ }
270
+ }
271
+ throw new Error(`timeout after ${timeout}ms waiting for daemon_ready (state=ready)`);
272
+ }
273
+ async function waitLoopStatusWithID(client, timeout) {
274
+ const deadline = Date.now() + timeout;
275
+ while (Date.now() < deadline) {
276
+ const remaining = deadline - Date.now();
277
+ if (remaining <= 0) break;
278
+ const ev = await client.readEventWithTimeout(remaining);
279
+ if (ev === null) break;
280
+ if (ev.type === "error") {
281
+ const errResp = ev;
282
+ throw new Error(`daemon error: ${errResp.code}: ${errResp.message}`);
283
+ }
284
+ if (ev.type === "status") {
285
+ const status = ev;
286
+ const lid = status.loop_id;
287
+ if (lid && lid !== "") {
288
+ return status;
289
+ }
290
+ }
291
+ }
292
+ throw new Error(`timeout after ${timeout}ms waiting for status with loop_id`);
293
+ }
294
+ async function waitSubscriptionConfirmed(client, wantLoopID, _wantVerbosity, timeout) {
295
+ const deadline = Date.now() + timeout;
296
+ while (Date.now() < deadline) {
297
+ const remaining = deadline - Date.now();
298
+ if (remaining <= 0) break;
299
+ const ev = await client.readEventWithTimeout(remaining);
300
+ if (ev === null) break;
301
+ if (ev.type === "loop_subscribe_response" && ev.success === true) {
302
+ if (String(ev.loop_id ?? "") === wantLoopID) return;
303
+ }
304
+ if (ev.type === "subscription_confirmed") {
305
+ const lid = String(ev.loop_id ?? "");
306
+ if (lid === wantLoopID) return;
307
+ }
308
+ }
309
+ throw new Error(`timeout after ${timeout}ms waiting for subscription_confirmed`);
310
+ }
311
+ async function connectWithRetries(client, maxRetries, retryDelay) {
312
+ const retries = maxRetries && maxRetries > 0 ? maxRetries : 40;
313
+ const delay = retryDelay && retryDelay > 0 ? retryDelay : 250;
314
+ let lastErr = null;
315
+ for (let attempt = 0; attempt < retries; attempt++) {
316
+ try {
317
+ await client.connect();
318
+ return;
319
+ } catch (err) {
320
+ lastErr = err;
321
+ }
322
+ await new Promise((resolve) => setTimeout(resolve, delay));
323
+ }
324
+ throw new Error(`failed to connect after ${retries} attempts: ${lastErr?.message ?? "unknown error"}`);
325
+ }
326
+ export {
327
+ Client,
328
+ ConnectionError,
329
+ DaemonError,
330
+ ESSENTIAL_EVENT_TYPES,
331
+ EventAgentLoopCompleted,
332
+ EventAgentLoopIterated,
333
+ EventAgentLoopReasoned,
334
+ EventAgentLoopStarted,
335
+ EventExploreCompleted,
336
+ EventExploreMilestone,
337
+ EventExploreStarted,
338
+ EventExploreStepCompleted,
339
+ EventFinalReport,
340
+ EventGeneralFailed,
341
+ EventLoopReattachedWire,
342
+ EventMessageReceived,
343
+ EventMessageSent,
344
+ EventPlanCreated,
345
+ EventReplayComplete,
346
+ EventStreamToolCallUpdate,
347
+ EventTacitusCompleted,
348
+ EventTacitusGatherSummary,
349
+ EventTacitusStarted,
350
+ EventToolCallUpdatesBatch,
351
+ EventToolCompleted,
352
+ EventToolError,
353
+ EventToolStarted,
354
+ TimeoutError,
355
+ VerbosityTier,
356
+ bootstrapLoopSession,
357
+ checkDaemonStatus,
358
+ classifyEventVerbosity,
359
+ connectWithRetries,
360
+ decodeMessage,
361
+ defaultConfig,
362
+ encodeMessage,
363
+ extractSootheLoopID,
364
+ fetchConfigSection,
365
+ fetchSkillsCatalog,
366
+ isCompletionEvent,
367
+ isDaemonLive,
368
+ isSubagentProgressEvent,
369
+ isValidVerbosityLevel,
370
+ loadConfigFromEnv,
371
+ newLoopInputMessage,
372
+ newLoopNewMessage,
373
+ newLoopSubscribeMessage,
374
+ newRequestID,
375
+ parseNamespace,
376
+ requestDaemonShutdown,
377
+ shouldShow,
378
+ splitWirePayload,
379
+ waitDaemonReady,
380
+ waitLoopStatusWithID,
381
+ waitSubscriptionConfirmed
382
+ };
383
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/verbosity.ts","../src/events.ts","../src/helpers.ts","../src/session.ts"],"sourcesContent":["/**\n * Custom error types for the Soothe client.\n */\n\n/** Represents a WebSocket connection failure. */\nexport class ConnectionError extends Error {\n readonly url: string;\n readonly attempt: number;\n readonly cause: Error;\n\n constructor(url: string, attempt: number, cause: Error) {\n super(`connection error to ${url} (attempt ${attempt}): ${cause.message}`);\n this.name = 'ConnectionError';\n this.url = url;\n this.attempt = attempt;\n this.cause = cause;\n }\n}\n\n/** Represents an error reported by the Soothe daemon. */\nexport class DaemonError extends Error {\n readonly code: string;\n /** The daemon's error message text. */\n readonly daemonMessage: string;\n\n constructor(code: string, message: string) {\n super(`daemon error [${code}]: ${message}`);\n this.name = 'DaemonError';\n this.code = code;\n this.daemonMessage = message;\n }\n}\n\n/** Represents a timeout waiting for a daemon response. */\nexport class TimeoutError extends Error {\n readonly operation: string;\n readonly duration: string;\n\n constructor(operation: string, duration: string) {\n super(`timeout after ${duration} waiting for ${operation}`);\n this.name = 'TimeoutError';\n this.operation = operation;\n this.duration = duration;\n }\n}\n","/**\n * Verbosity levels and tiers for event filtering.\n */\n\n/** User-configurable verbosity setting. */\nexport type VerbosityLevel = 'quiet' | 'normal' | 'debug';\n\n/** Minimum verbosity level at which content is visible. */\nexport enum VerbosityTier {\n /** Always visible (errors, assistant text, final reports) */\n Quiet = 0,\n /** Standard progress (plan updates, milestones, agentic loop) */\n Normal = 1,\n /** Detailed internals (protocol events, tool calls, subagent activity) */\n Detailed = 2,\n /** Everything including internals (thinking, heartbeats) */\n Debug = 3,\n /** Never shown at any level (implementation details) */\n Internal = 99,\n}\n\nconst verbosityLevelValues: Record<VerbosityLevel, number> = {\n quiet: 0,\n normal: 1,\n debug: 3,\n};\n\n/** Returns true if content at the given tier is visible at the given verbosity. */\nexport function shouldShow(tier: VerbosityTier, verbosity: VerbosityLevel): boolean {\n if (tier === VerbosityTier.Internal) {\n return false;\n }\n const level = verbosityLevelValues[verbosity] ?? 1; // default to normal\n return tier <= level;\n}\n\n/** Checks whether a string is a valid verbosity level. */\nexport function isValidVerbosityLevel(s: string): s is VerbosityLevel {\n return s in verbosityLevelValues;\n}\n","/**\n * Client-facing event namespace constants for the Soothe daemon wire protocol.\n *\n * Internal catalog types (`soothe.internal.*`) are server-only and are never\n * broadcast to WebSocket clients. Do not add them here.\n *\n * Format: soothe.<domain>.<component>.<action>\n */\n\nimport { VerbosityTier } from './verbosity.js';\n\n// Plan events (client UX)\nexport const EventPlanCreated = 'soothe.cognition.plan.created';\n\n// Explore subagent events (built-in wire, IG-339)\nexport const EventExploreStarted = 'soothe.subagent.explore.started';\nexport const EventExploreMilestone = 'soothe.subagent.explore.milestone';\nexport const EventExploreStepCompleted = 'soothe.subagent.explore.step.completed';\nexport const EventExploreCompleted = 'soothe.subagent.explore.completed';\n\n// Tacitus subagent events (built-in wire, IG-339)\nexport const EventTacitusStarted = 'soothe.subagent.tacitus.started';\nexport const EventTacitusGatherSummary = 'soothe.subagent.tacitus.gather.summary';\nexport const EventTacitusCompleted = 'soothe.subagent.tacitus.completed';\n\n// Control-plane wire envelopes (not soothe.* catalog events)\nexport const EventReplayComplete = 'replay_complete';\nexport const EventLoopReattachedWire = 'loop_reattached';\n\n// Tool events\nexport const EventToolStarted = 'soothe.tool.execution.started';\nexport const EventToolCompleted = 'soothe.tool.execution.completed';\nexport const EventToolError = 'soothe.tool.execution.error';\n\n// Stream tool call events (RFC-450, IG-416)\nexport const EventStreamToolCallUpdate = 'soothe.stream.tool_call.update';\nexport const EventToolCallUpdatesBatch = 'tool_call_updates_batch';\n\n// Agent loop events (cognition domain)\nexport const EventAgentLoopStarted = 'soothe.cognition.agent_loop.started';\nexport const EventAgentLoopIterated = 'soothe.cognition.agent_loop.iterated';\nexport const EventAgentLoopCompleted = 'soothe.cognition.agent_loop.completed';\nexport const EventAgentLoopReasoned = 'soothe.cognition.agent_loop.reasoned';\n\n// Message protocol events (client stream metadata)\nexport const EventMessageReceived = 'soothe.protocol.message.received';\nexport const EventMessageSent = 'soothe.protocol.message.sent';\n\n// Output events\nexport const EventFinalReport = 'soothe.output.autonomous.final_report.reported';\n\n// Error events\nexport const EventGeneralFailed = 'soothe.error.general.failed';\n\n// ---------------------------------------------------------------------------\n// Namespace parsing\n// ---------------------------------------------------------------------------\n\n/** Splits a 4-segment event namespace into domain, component, and action. */\nexport function parseNamespace(ns: string): { domain: string; component: string; action: string } | null {\n const parts = splitNamespace(ns);\n if (parts.length < 4 || parts[0] !== 'soothe') {\n return null;\n }\n if (parts[1] === 'internal') {\n return null;\n }\n return { domain: parts[1], component: parts[2], action: parts[3] };\n}\n\nfunction splitNamespace(ns: string): string[] {\n const parts: string[] = [];\n let start = 0;\n for (let i = 0; i < ns.length; i++) {\n if (ns[i] === '.') {\n parts.push(ns.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(ns.slice(start));\n return parts;\n}\n\n// ---------------------------------------------------------------------------\n// Classification\n// ---------------------------------------------------------------------------\n\n/** Returns the VerbosityTier for a given event type string. */\nexport function classifyEventVerbosity(eventTypeOrNamespace: string): VerbosityTier {\n const parsed = parseNamespace(eventTypeOrNamespace);\n if (!parsed) {\n return classifyByEventTypeString(eventTypeOrNamespace);\n }\n return classifyByDomainAndComponent(parsed.domain, parsed.component, eventTypeOrNamespace);\n}\n\nfunction classifyByDomainAndComponent(domain: string, _component: string, full: string): VerbosityTier {\n switch (domain) {\n case 'cognition':\n return VerbosityTier.Normal;\n case 'protocol':\n return VerbosityTier.Detailed;\n case 'tool':\n return VerbosityTier.Internal;\n case 'subagent':\n return classifySubagentEvent(full);\n case 'output':\n case 'error':\n return VerbosityTier.Quiet;\n default:\n return VerbosityTier.Normal;\n }\n}\n\nfunction classifySubagentEvent(full: string): VerbosityTier {\n const parsed = parseNamespace(full);\n if (!parsed) return VerbosityTier.Normal;\n switch (parsed.action) {\n case 'started':\n case 'completed':\n return VerbosityTier.Normal;\n default:\n return VerbosityTier.Detailed;\n }\n}\n\nfunction classifyByEventTypeString(eventType: string): VerbosityTier {\n if (eventType === EventFinalReport || eventType === EventGeneralFailed) {\n return VerbosityTier.Quiet;\n }\n if (eventType === EventToolStarted) {\n return VerbosityTier.Internal;\n }\n return VerbosityTier.Normal;\n}\n\n// ---------------------------------------------------------------------------\n// Event classification helpers\n// ---------------------------------------------------------------------------\n\n/** Event types that represent completion milestones. */\nexport function isCompletionEvent(eventType: string): boolean {\n return (\n eventType.endsWith('.completed') ||\n eventType.endsWith('.failed') ||\n eventType === EventGeneralFailed\n );\n}\n\n/** Lifecycle subagent events (started/completed) for progress UI. */\nexport function isSubagentProgressEvent(eventType: string): boolean {\n const parsed = parseNamespace(eventType);\n if (!parsed || parsed.domain !== 'subagent') {\n return false;\n }\n return parsed.action === 'started' || parsed.action === 'completed';\n}\n\n/** Essential progress event types for minimal UI surfaces. */\nexport const ESSENTIAL_EVENT_TYPES: ReadonlySet<string> = new Set([\n EventAgentLoopStarted,\n EventAgentLoopCompleted,\n EventAgentLoopReasoned,\n EventPlanCreated,\n EventExploreStarted,\n EventExploreCompleted,\n EventTacitusStarted,\n EventTacitusCompleted,\n EventGeneralFailed,\n]);\n","/**\n * Convenience RPC helper functions for the Soothe client.\n */\n\nimport type { Client } from './client.js';\nimport { defaultConfig } from './config.js';\n\n/** Checks daemon status via RPC. */\nexport async function checkDaemonStatus(client: Client, timeout?: number): Promise<Record<string, unknown>> {\n return client.requestResponse({ type: 'daemon_status' }, 'daemon_status_response', timeout ?? 5_000);\n}\n\n/** Performs a composite health check: connect + status RPC. */\nexport async function isDaemonLive(wsURL: string, timeout?: number): Promise<boolean> {\n const { Client } = await import('./client.js');\n const t = timeout ?? 5_000;\n const client = new Client(wsURL, defaultConfig());\n\n try {\n await client.connect();\n } catch {\n return false;\n }\n\n try {\n await checkDaemonStatus(client, t);\n return true;\n } catch {\n return false;\n } finally {\n client.close();\n }\n}\n\n/** Requests daemon shutdown via RPC. */\nexport async function requestDaemonShutdown(client: Client, timeout?: number): Promise<void> {\n const resp = await client.requestResponse({ type: 'daemon_shutdown' }, 'shutdown_ack', timeout ?? 10_000);\n if (resp.status !== 'acknowledged') {\n throw new Error(`shutdown not acknowledged: ${JSON.stringify(resp)}`);\n }\n}\n\n/** Fetches the skills catalog via RPC. */\nexport async function fetchSkillsCatalog(client: Client, timeout?: number): Promise<Record<string, unknown>[]> {\n const resp = await client.requestResponse({ type: 'skills_list' }, 'skills_list_response', timeout ?? 15_000);\n const skillsRaw = resp.skills;\n if (!skillsRaw || !Array.isArray(skillsRaw)) return [];\n return skillsRaw.filter((s): s is Record<string, unknown> => typeof s === 'object' && s !== null);\n}\n\n/** Fetches a daemon config section via RPC. */\nexport async function fetchConfigSection(client: Client, section: string, timeout?: number): Promise<Record<string, unknown>> {\n const resp = await client.requestResponse({ type: 'config_get', section }, 'config_get_response', timeout ?? 5_000);\n const sec = resp[section];\n if (sec && typeof sec === 'object') {\n return sec as Record<string, unknown>;\n }\n return resp;\n}\n","/**\n * Session bootstrap flows, wait helpers, and connect-with-retries.\n */\n\nimport type { Client } from './client.js';\nimport type { Config } from './config.js';\nimport { defaultConfig } from './config.js';\nimport type { DecodedMessage, LoopNewOptions, StatusResponse, ErrorResponse } from './protocol.js';\nimport { newLoopNewMessage } from './protocol.js';\n\n// ---------------------------------------------------------------------------\n// Bootstrap flows (loop-first, RFC-503)\n// ---------------------------------------------------------------------------\n\n/** Daemon ready → loop_new (or reuse id) → loop_subscribe; returns loop id. */\nexport async function bootstrapLoopSession(\n client: Client,\n resumeLoopId: string | null | undefined,\n config?: Config,\n loopNew?: LoopNewOptions,\n): Promise<string> {\n const cfg = config ?? defaultConfig();\n\n await client.sendMessage({ type: 'daemon_ready' });\n await waitDaemonReady(client, cfg.daemonReadyTimeout);\n\n let loopId = (resumeLoopId ?? '').trim();\n if (!loopId) {\n const newResp = await client.requestResponse(\n newLoopNewMessage(loopNew) as unknown as Record<string, unknown>,\n 'loop_new_response',\n cfg.loopStatusTimeout,\n );\n loopId = String(newResp.loop_id ?? '').trim();\n if (!loopId) {\n throw new Error('loop_new_response missing loop_id');\n }\n }\n\n const subResp = await client.requestResponse(\n { type: 'loop_subscribe', loop_id: loopId, verbosity: cfg.verbosityLevel },\n 'loop_subscribe_response',\n cfg.subscriptionTimeout,\n );\n if (subResp.success === false) {\n throw new Error(String(subResp.message ?? 'loop_subscribe failed'));\n }\n\n return loopId;\n}\n\n// ---------------------------------------------------------------------------\n// Wait helpers (use client's readEventWithTimeout internally)\n// ---------------------------------------------------------------------------\n\n/** Blocks until a daemon_ready message with state == \"ready\". */\nexport async function waitDaemonReady(\n client: Client,\n timeout: number,\n): Promise<void> {\n const deadline = Date.now() + timeout;\n while (Date.now() < deadline) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n const ev = (await client.readEventWithTimeout(remaining)) as Record<string, unknown> | null;\n if (ev === null) break;\n if (ev.type === 'daemon_ready') {\n if (ev.state === 'ready') return;\n throw new Error(\n `daemon not ready: state=${JSON.stringify(ev.state)} message=${JSON.stringify(ev.message ?? '')}`,\n );\n }\n }\n throw new Error(`timeout after ${timeout}ms waiting for daemon_ready (state=ready)`);\n}\n\n/** Waits for daemon_ready using messages from an ``AsyncIterable`` (e.g. ``receiveMessages()``). */\nexport async function waitDaemonReadyFromStream(\n eventStream: AsyncIterable<DecodedMessage>,\n timeout: number,\n): Promise<void> {\n const deadline = Date.now() + timeout;\n for await (const msg of eventStream) {\n if (msg && typeof msg === 'object') {\n const m = msg as Record<string, unknown>;\n if (m.type === 'daemon_ready') {\n if (m.state === 'ready') return;\n throw new Error(\n `daemon not ready: state=${JSON.stringify(m.state)} message=${JSON.stringify(m.message ?? '')}`,\n );\n }\n }\n if (Date.now() >= deadline) break;\n }\n throw new Error(`timeout after ${timeout}ms waiting for daemon_ready (state=ready)`);\n}\n\n/** Waits for a status message with a non-empty ``loop_id``. */\nexport async function waitLoopStatusWithID(\n client: Client,\n timeout: number,\n): Promise<StatusResponse> {\n const deadline = Date.now() + timeout;\n while (Date.now() < deadline) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n const ev = (await client.readEventWithTimeout(remaining)) as Record<string, unknown> | null;\n if (ev === null) break;\n\n if (ev.type === 'error') {\n const errResp = ev as unknown as ErrorResponse;\n throw new Error(`daemon error: ${errResp.code}: ${errResp.message}`);\n }\n\n if (ev.type === 'status') {\n const status = ev as unknown as StatusResponse;\n const lid = status.loop_id;\n if (lid && lid !== '') {\n return status;\n }\n }\n }\n throw new Error(`timeout after ${timeout}ms waiting for status with loop_id`);\n}\n\n/** Waits for subscription_confirmed or loop_subscribe_response matching loop id. */\nexport async function waitSubscriptionConfirmed(\n client: Client,\n wantLoopID: string,\n _wantVerbosity: string,\n timeout: number,\n): Promise<void> {\n const deadline = Date.now() + timeout;\n while (Date.now() < deadline) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n const ev = (await client.readEventWithTimeout(remaining)) as Record<string, unknown> | null;\n if (ev === null) break;\n if (ev.type === 'loop_subscribe_response' && ev.success === true) {\n if (String(ev.loop_id ?? '') === wantLoopID) return;\n }\n if (ev.type === 'subscription_confirmed') {\n const lid = String((ev as { loop_id?: string }).loop_id ?? '');\n if (lid === wantLoopID) return;\n }\n }\n throw new Error(`timeout after ${timeout}ms waiting for subscription_confirmed`);\n}\n\n// ---------------------------------------------------------------------------\n// Connect with retries\n// ---------------------------------------------------------------------------\n\n/** Attempts to connect to the Soothe daemon with bounded retries. */\nexport async function connectWithRetries(\n client: Client,\n maxRetries?: number,\n retryDelay?: number,\n): Promise<void> {\n const retries = maxRetries && maxRetries > 0 ? maxRetries : 40;\n const delay = retryDelay && retryDelay > 0 ? retryDelay : 250;\n\n let lastErr: Error | null = null;\n for (let attempt = 0; attempt < retries; attempt++) {\n try {\n await client.connect();\n return;\n } catch (err) {\n lastErr = err as Error;\n }\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n throw new Error(`failed to connect after ${retries} attempts: ${lastErr?.message ?? 'unknown error'}`);\n}"],"mappings":"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","names":["VerbosityTier","Client"]}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@mirasoth/soothe-client",
3
+ "version": "0.1.0",
4
+ "description": "WebSocket client in TypeScript for soothe-daemon",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "dist/index.cjs",
9
+ "module": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
14
+ "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "clean": "rm -rf dist",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "test:integration": "SOOTHE_INTEGRATION=1 vitest run test/integration.test.ts",
23
+ "typecheck": "tsc --noEmit"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/OpenSoothe/soothe-client-typescript.git"
28
+ },
29
+ "keywords": ["soothe", "websocket", "client", "daemon", "typescript"],
30
+ "author": "OpenSoothe",
31
+ "license": "MIT",
32
+ "type": "module",
33
+ "files": ["dist/**/*", "README.md", "LICENSE"],
34
+ "engines": { "node": ">=19.0.0" },
35
+ "dependencies": {
36
+ "ws": "^8.20.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.0.0",
40
+ "@types/ws": "^8.18.1",
41
+ "tsup": "^8.0.0",
42
+ "typescript": "^5.7.0",
43
+ "vitest": "^3.0.0"
44
+ }
45
+ }