@astralform/js 7.2.0 → 7.3.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.cjs CHANGED
@@ -35,10 +35,14 @@ __export(index_exports, {
35
35
  StreamAbortedError: () => StreamAbortedError,
36
36
  StreamManager: () => StreamManager,
37
37
  ToolRegistry: () => ToolRegistry,
38
+ VOICE_POLISH_MODES: () => VOICE_POLISH_MODES,
38
39
  generateId: () => generateId,
39
40
  isEmbeddedResource: () => isEmbeddedResource,
41
+ isVoiceLLMMode: () => isVoiceLLMMode,
42
+ isVoicePolishMode: () => isVoicePolishMode,
40
43
  mapSseToChat: () => mapSseToChat,
41
44
  parseEmbeddedResource: () => parseEmbeddedResource,
45
+ parseVoicePolishFrame: () => parseVoicePolishFrame,
42
46
  replayEvents: () => replayEvents,
43
47
  streamJobSSE: () => streamJobSSE,
44
48
  translateDelta: () => translateDelta
@@ -241,12 +245,13 @@ function createRateLimitErrorFromHttp(response, rawText) {
241
245
 
242
246
  // src/streaming.ts
243
247
  async function* streamJobSSE(options) {
244
- const { url, headers, signal, fetchFn } = options;
248
+ const { url, headers, signal, fetchFn, method = "GET", body } = options;
245
249
  let response;
246
250
  try {
247
251
  response = await fetchFn(url, {
248
- method: "GET",
252
+ method,
249
253
  headers,
254
+ body,
250
255
  signal
251
256
  });
252
257
  } catch (err) {
@@ -308,6 +313,57 @@ async function* streamJobSSE(options) {
308
313
  }
309
314
  }
310
315
 
316
+ // src/types.ts
317
+ var ChatEventType = {
318
+ // Connection lifecycle (SDK-local, not wire)
319
+ Connected: "connected",
320
+ Disconnected: "disconnected",
321
+ // Turn lifecycle
322
+ MessageStart: "message_start",
323
+ MessageStop: "message_stop",
324
+ // Block lifecycle
325
+ BlockStart: "block_start",
326
+ BlockDelta: "block_delta",
327
+ BlockStop: "block_stop",
328
+ // Reliability
329
+ Stall: "stall",
330
+ Retry: "retry",
331
+ Error: "error",
332
+ Keepalive: "keepalive",
333
+ // Conversation-level (typed custom events)
334
+ UserMessage: "user_message",
335
+ TitleGenerated: "title_generated",
336
+ TodoUpdate: "todo_update",
337
+ PlanUpdate: "plan_update",
338
+ NoteUpdate: "note_update",
339
+ ContextUpdate: "context_update",
340
+ SubagentStart: "subagent_start",
341
+ SubagentStop: "subagent_stop",
342
+ ContextWarning: "context_warning",
343
+ MemoryRecall: "memory_recall",
344
+ MemoryUpdate: "memory_update",
345
+ DesktopStream: "desktop_stream",
346
+ AttachmentStaged: "attachment_staged",
347
+ WorkspaceReady: "workspace_ready",
348
+ AssetCreated: "asset_created",
349
+ ToolApprovalRequested: "tool_approval_requested",
350
+ ToolApprovalGranted: "tool_approval_granted",
351
+ ToolPermissionDenied: "tool_permission_denied",
352
+ ToolHarnessWarning: "tool_harness_warning",
353
+ UserUnavailable: "user_unavailable",
354
+ PromptSuggestion: "prompt_suggestion",
355
+ StateChanged: "state_changed",
356
+ // Generic fallthrough for unknown custom events
357
+ Custom: "custom"
358
+ };
359
+ var VOICE_POLISH_MODES = ["raw", "light", "structured", "formal"];
360
+ function isVoicePolishMode(value) {
361
+ return typeof value === "string" && VOICE_POLISH_MODES.includes(value);
362
+ }
363
+ function isVoiceLLMMode(mode) {
364
+ return mode !== "raw";
365
+ }
366
+
311
367
  // src/client.ts
312
368
  var DEFAULT_BASE_URL = "https://api.astralform.ai";
313
369
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -736,6 +792,94 @@ var AstralformClient = class {
736
792
  const raw = await response.json();
737
793
  return this.mapAsset(raw);
738
794
  }
795
+ // --- Voice input ---
796
+ /** The agent's voice-input defaults (`GET /v1/voice/config`). */
797
+ async getVoiceConfig() {
798
+ const raw = await this.get("/v1/voice/config");
799
+ return {
800
+ enabled: Boolean(raw.enabled),
801
+ modes: raw.modes ?? [...VOICE_POLISH_MODES],
802
+ // A mode this SDK does not know must not reach a `switch` typed as
803
+ // `VoicePolishMode`; `structured` is the server's own default.
804
+ defaultMode: isVoicePolishMode(raw.default_mode) ? raw.default_mode : "structured",
805
+ silenceAutoStopSeconds: raw.silence_auto_stop_seconds ?? 2,
806
+ autoSend: raw.auto_send ?? true,
807
+ maxRecordingSeconds: raw.max_recording_seconds ?? 300,
808
+ supportsStreaming: Boolean(raw.supports_streaming),
809
+ hotwords: raw.hotwords ?? []
810
+ };
811
+ }
812
+ /**
813
+ * Transcribe one recording with the agent's configured speech-to-text
814
+ * provider (`POST /v1/voice/transcriptions`). 16 kHz mono 16-bit WAV is the
815
+ * reference format; anything the provider accepts works.
816
+ *
817
+ * Deliberately outside `withDeadline`: a recording can run to
818
+ * `VoiceConfig.maxRecordingSeconds`, so the 30 s default would cut real
819
+ * uploads off. Pass `options.signal` to give up on a stalled one; the
820
+ * promise then rejects with the abort reason — the runtime's `AbortError`,
821
+ * or whatever was passed to `abort(reason)`.
822
+ */
823
+ async transcribeVoice(audio, options = {}) {
824
+ const formData = new FormData();
825
+ formData.append("file", audio, options.filename ?? "recording.wav");
826
+ if (options.hotwords?.length) {
827
+ formData.append("hotwords", options.hotwords.join(", "));
828
+ }
829
+ if (options.language) {
830
+ formData.append("language", options.language);
831
+ }
832
+ const response = await this.fetchFn(`${this.baseURL}/v1/voice/transcriptions`, {
833
+ method: "POST",
834
+ headers: this.authHeaders,
835
+ body: formData,
836
+ signal: options.signal
837
+ }).catch((err) => {
838
+ if (options.signal?.aborted) {
839
+ throw err;
840
+ }
841
+ throw new ConnectionError(
842
+ err instanceof Error ? err.message : "Failed to connect"
843
+ );
844
+ });
845
+ await this.handleError(response);
846
+ const raw = await response.json();
847
+ return {
848
+ text: raw.text ?? "",
849
+ language: raw.language ?? null,
850
+ durationMs: raw.duration_ms ?? null,
851
+ asrMs: raw.asr_ms ?? 0
852
+ };
853
+ }
854
+ /**
855
+ * Stream the LLM rewrite of a transcript (`POST /v1/voice/polish`) as typed
856
+ * frames.
857
+ *
858
+ * Failures the server reports mid-stream arrive as an `error` frame, but
859
+ * the iteration itself can reject: aborting `signal` closes the connection
860
+ * (which cancels the model call upstream) and rejects with
861
+ * `StreamAbortedError`; a non-2xx open rejects with `AuthenticationError`,
862
+ * `RateLimitError` or `ServerError`; a network failure with
863
+ * `ConnectionError`. Wrap the `for await` accordingly.
864
+ */
865
+ async *streamVoicePolish(request, options = {}) {
866
+ const frames = streamJobSSE({
867
+ url: `${this.baseURL}/v1/voice/polish`,
868
+ headers: { ...this.headers, Accept: "text/event-stream" },
869
+ method: "POST",
870
+ body: JSON.stringify({
871
+ text: request.text,
872
+ mode: request.mode,
873
+ hotwords: request.hotwords ?? []
874
+ }),
875
+ signal: options.signal,
876
+ fetchFn: this.fetchFn
877
+ });
878
+ for await (const frame of frames) {
879
+ const event = parseVoicePolishFrame(frame);
880
+ if (event) yield event;
881
+ }
882
+ }
739
883
  async listUploads(conversationId) {
740
884
  const raw = await this.get(
741
885
  `/v1/conversations/${encodeURIComponent(conversationId)}/uploads`
@@ -822,6 +966,35 @@ var AstralformClient = class {
822
966
  }));
823
967
  }
824
968
  };
969
+ function parseVoicePolishFrame(frame) {
970
+ let payload;
971
+ try {
972
+ const parsed = JSON.parse(frame.data);
973
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
974
+ payload = parsed;
975
+ } catch {
976
+ return null;
977
+ }
978
+ switch (frame.event) {
979
+ case "delta":
980
+ return typeof payload.text === "string" ? { type: "delta", text: payload.text } : null;
981
+ case "done":
982
+ return typeof payload.text === "string" ? {
983
+ type: "done",
984
+ text: payload.text,
985
+ polishMs: payload.polish_ms ?? 0
986
+ } : null;
987
+ case "error":
988
+ return {
989
+ type: "error",
990
+ reason: payload.reason ?? "unknown",
991
+ partial: payload.partial ?? "",
992
+ ...typeof payload.detail === "string" ? { detail: payload.detail } : {}
993
+ };
994
+ default:
995
+ return null;
996
+ }
997
+ }
825
998
 
826
999
  // src/storage.ts
827
1000
  var InMemoryStorage = class {
@@ -2306,50 +2479,6 @@ function planRestore(args) {
2306
2479
  return steps;
2307
2480
  }
2308
2481
 
2309
- // src/types.ts
2310
- var ChatEventType = {
2311
- // Connection lifecycle (SDK-local, not wire)
2312
- Connected: "connected",
2313
- Disconnected: "disconnected",
2314
- // Turn lifecycle
2315
- MessageStart: "message_start",
2316
- MessageStop: "message_stop",
2317
- // Block lifecycle
2318
- BlockStart: "block_start",
2319
- BlockDelta: "block_delta",
2320
- BlockStop: "block_stop",
2321
- // Reliability
2322
- Stall: "stall",
2323
- Retry: "retry",
2324
- Error: "error",
2325
- Keepalive: "keepalive",
2326
- // Conversation-level (typed custom events)
2327
- UserMessage: "user_message",
2328
- TitleGenerated: "title_generated",
2329
- TodoUpdate: "todo_update",
2330
- PlanUpdate: "plan_update",
2331
- NoteUpdate: "note_update",
2332
- ContextUpdate: "context_update",
2333
- SubagentStart: "subagent_start",
2334
- SubagentStop: "subagent_stop",
2335
- ContextWarning: "context_warning",
2336
- MemoryRecall: "memory_recall",
2337
- MemoryUpdate: "memory_update",
2338
- DesktopStream: "desktop_stream",
2339
- AttachmentStaged: "attachment_staged",
2340
- WorkspaceReady: "workspace_ready",
2341
- AssetCreated: "asset_created",
2342
- ToolApprovalRequested: "tool_approval_requested",
2343
- ToolApprovalGranted: "tool_approval_granted",
2344
- ToolPermissionDenied: "tool_permission_denied",
2345
- ToolHarnessWarning: "tool_harness_warning",
2346
- UserUnavailable: "user_unavailable",
2347
- PromptSuggestion: "prompt_suggestion",
2348
- StateChanged: "state_changed",
2349
- // Generic fallthrough for unknown custom events
2350
- Custom: "custom"
2351
- };
2352
-
2353
2482
  // src/stream-manager.ts
2354
2483
  var StreamManager = class {
2355
2484
  constructor(session) {
@@ -3004,10 +3133,14 @@ function parseEmbeddedResource(value) {
3004
3133
  StreamAbortedError,
3005
3134
  StreamManager,
3006
3135
  ToolRegistry,
3136
+ VOICE_POLISH_MODES,
3007
3137
  generateId,
3008
3138
  isEmbeddedResource,
3139
+ isVoiceLLMMode,
3140
+ isVoicePolishMode,
3009
3141
  mapSseToChat,
3010
3142
  parseEmbeddedResource,
3143
+ parseVoicePolishFrame,
3011
3144
  replayEvents,
3012
3145
  streamJobSSE,
3013
3146
  translateDelta