@astralform/js 9.3.0 → 9.4.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/index.cjs CHANGED
@@ -500,6 +500,79 @@ var AstralformClient = class {
500
500
  `/v1/code/projects/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`
501
501
  );
502
502
  }
503
+ },
504
+ /**
505
+ * Code groups: a user-named container under one repository, for a feature
506
+ * whose work outlives a single session. Its sessions share one plan and one
507
+ * set of notes, so what one writes the others read.
508
+ *
509
+ * A group is a LABEL, not a binding — unlike a task's project, which is
510
+ * written once on the first turn. A task may join, move between, or leave
511
+ * groups at any time, and a deleted group's sessions survive as ungrouped.
512
+ *
513
+ * The server enforces three things worth knowing before you render a
514
+ * failure: a group that is not this user's answers `404` (never `403`, so a
515
+ * group id cannot be probed); filing a task into a group under a DIFFERENT
516
+ * repository than the task's answers `409`; and the group's plan and notes
517
+ * are deleted with the group, while each session keeps its own.
518
+ */
519
+ groups: {
520
+ /**
521
+ * This user's groups, oldest first. Pass a repository to narrow to one
522
+ * repository's — the composer's picker does; the sidebar asks once and
523
+ * nests them itself.
524
+ */
525
+ list: async (repository) => {
526
+ const query = repository ? `?repository=${encodeURIComponent(repository)}` : "";
527
+ const raw = await this.get(`/v1/code/groups${query}`);
528
+ return raw.map((g) => camelizeKeys(g));
529
+ },
530
+ /**
531
+ * Create a group under a repository. The repository goes through the same
532
+ * normaliser the Projects surface uses, so a pasted GitHub URL is accepted
533
+ * and anything that is not `owner/repo` is refused.
534
+ */
535
+ create: async (repository, title) => {
536
+ const raw = await this.post("/v1/code/groups", { repository, title });
537
+ return camelizeKeys(raw);
538
+ },
539
+ /** Rename a group. A group that is not this user's answers `404`. */
540
+ rename: async (groupId, title) => {
541
+ const raw = await this.patch(`/v1/code/groups/${encodeURIComponent(groupId)}`, { title });
542
+ return camelizeKeys(raw);
543
+ },
544
+ /**
545
+ * Delete a group. Its tasks survive and become ungrouped, each keeping its
546
+ * own plan and notes; the group's SHARED documents are deleted with it.
547
+ */
548
+ remove: async (groupId) => {
549
+ await this.del(`/v1/code/groups/${encodeURIComponent(groupId)}`);
550
+ },
551
+ /**
552
+ * File a task into a group. Idempotent — filing an already-filed task
553
+ * again is the same state, not an error. Refused with `409` when the task
554
+ * belongs to a different repository than the group, because it would
555
+ * render nowhere in the sidebar.
556
+ */
557
+ assign: async (groupId, conversationId) => {
558
+ const raw = await this.post(
559
+ `/v1/code/groups/${encodeURIComponent(groupId)}/tasks/${encodeURIComponent(conversationId)}`,
560
+ void 0
561
+ );
562
+ return camelizeKeys(raw);
563
+ },
564
+ /**
565
+ * Take a task out of its group. The task and its documents are untouched.
566
+ *
567
+ * Deliberately not strict about WHICH group the task is in: a task that is
568
+ * in a different one (or in none) already satisfies "this task should not
569
+ * be grouped", so the call clears it rather than refusing.
570
+ */
571
+ unassign: async (groupId, conversationId) => {
572
+ await this.del(
573
+ `/v1/code/groups/${encodeURIComponent(groupId)}/tasks/${encodeURIComponent(conversationId)}`
574
+ );
575
+ }
503
576
  }
504
577
  };
505
578
  if (isApiKeyConfig(config)) {
@@ -1061,7 +1134,11 @@ var AstralformClient = class {
1061
1134
  // A mode this SDK does not know must not reach a `switch` typed as
1062
1135
  // `VoicePolishMode`; `structured` is the server's own default.
1063
1136
  defaultMode: isVoicePolishMode(raw.default_mode) ? raw.default_mode : "structured",
1064
- silenceAutoStopSeconds: raw.silence_auto_stop_seconds ?? 2,
1137
+ // Deliberately not read from the wire. The field was retired from the
1138
+ // platform (2026-10-09): no shipped client implements pause detection
1139
+ // and the server no longer sends it, so an older server's stored value
1140
+ // must not resurrect it.
1141
+ silenceAutoStopSeconds: 2,
1065
1142
  // Deliberately not read from the wire. Auto-send was removed from the
1066
1143
  // platform (2026-09-14) and the server pins `auto_send` to false; an
1067
1144
  // older server still sending `true` must not make a client auto-send, so
@@ -1718,22 +1795,24 @@ function translateWireEvent(wire) {
1718
1795
  status: wire.status,
1719
1796
  final: wire.final
1720
1797
  };
1721
- case "message_stop":
1798
+ case "message_stop": {
1799
+ const usage = wire.usage ?? {};
1722
1800
  return {
1723
1801
  type: "message_stop",
1724
1802
  turnId: wire.turn_id,
1725
1803
  jobId: wire.job_id,
1726
1804
  stopReason: wire.stop_reason,
1727
1805
  usage: {
1728
- inputTokens: wire.usage.input_tokens ?? 0,
1729
- outputTokens: wire.usage.output_tokens ?? 0,
1730
- cachedTokens: wire.usage.cached_tokens ?? 0,
1731
- cacheCreationTokens: wire.usage.cache_creation_tokens ?? 0
1806
+ inputTokens: usage.input_tokens ?? 0,
1807
+ outputTokens: usage.output_tokens ?? 0,
1808
+ cachedTokens: usage.cached_tokens ?? 0,
1809
+ cacheCreationTokens: usage.cache_creation_tokens ?? 0
1732
1810
  },
1733
1811
  ttfbMs: wire.ttfb_ms,
1734
1812
  totalMs: wire.total_ms,
1735
1813
  stallCount: wire.stall_count
1736
1814
  };
1815
+ }
1737
1816
  case "stall":
1738
1817
  return {
1739
1818
  type: "stall",
@@ -2085,7 +2164,7 @@ var ChatSession = class {
2085
2164
  if (!(err instanceof DOMException && err.name === "AbortError")) {
2086
2165
  this.emit({
2087
2166
  type: "error",
2088
- code: "connection_error",
2167
+ code: err instanceof AstralformError ? err.code : "connection_error",
2089
2168
  message: err instanceof Error ? err.message : String(err),
2090
2169
  blockPath: null
2091
2170
  });
@@ -2237,6 +2316,9 @@ var ChatSession = class {
2237
2316
  if (typeof data?.seq === "number") {
2238
2317
  this.lastSeq = data.seq;
2239
2318
  }
2319
+ if (typeof data === "object" && data !== null && "data" in data && data.data === "[DONE]") {
2320
+ sawTerminal = true;
2321
+ }
2240
2322
  continue;
2241
2323
  }
2242
2324
  parsed = data;
@@ -2366,7 +2448,7 @@ var ChatSession = class {
2366
2448
  }
2367
2449
  return;
2368
2450
  case "block_delta":
2369
- if (ownsTurnState && wire.delta.channel === "text" && this.currentTextPath !== null && pathEquals(this.currentTextPath, wire.path)) {
2451
+ if (ownsTurnState && wire.delta?.channel === "text" && this.currentTextPath !== null && pathEquals(this.currentTextPath, wire.path)) {
2370
2452
  this.accumulatedText += wire.delta.text;
2371
2453
  }
2372
2454
  return;