@astralform/js 7.3.0 → 7.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
@@ -389,6 +389,69 @@ function isApiKeyConfig(config) {
389
389
  }
390
390
  var AstralformClient = class {
391
391
  constructor(config) {
392
+ // --- Code mode: the app user's projects ---
393
+ /**
394
+ * The projects (GitHub repositories) this app user works with, and what they
395
+ * may add.
396
+ *
397
+ * A project list is per app user within a code-mode agent: the developer
398
+ * connects the workspace's GitHub account, and each user curates their own
399
+ * list from what that connection covers. Every method 404s on a chat-mode
400
+ * agent, so the surface is invisible rather than empty there.
401
+ */
402
+ this.code = {
403
+ projects: {
404
+ /** This user's projects on the active agent, oldest first. */
405
+ list: async () => {
406
+ const raw = await this.get(
407
+ "/v1/code/projects"
408
+ );
409
+ return raw.map((p) => camelizeKeys(p));
410
+ },
411
+ /**
412
+ * What the workspace's GitHub installations cover, minus what this user
413
+ * has already added. Read `state` before the list: an empty `repositories`
414
+ * means something different in each of its three values.
415
+ */
416
+ available: async () => {
417
+ const raw = await this.get("/v1/code/projects/available");
418
+ return {
419
+ state: raw.state,
420
+ repositories: (raw.repositories ?? []).map((r) => ({
421
+ fullName: r.full_name,
422
+ private: r.private
423
+ })),
424
+ // Defaulted like its neighbours: the `unavailable` branch has nothing
425
+ // to count, and an absent field behind a `number` type prints
426
+ // "undefined" in a picker rather than a number.
427
+ totalCount: raw.total_count ?? 0,
428
+ partial: raw.partial ?? false
429
+ };
430
+ },
431
+ /**
432
+ * Add a repository. The server checks it against the workspace's own
433
+ * installations and answers a repository it cannot reach the same way it
434
+ * answers one owned by someone else — deliberately, so this call cannot be
435
+ * used to discover which organisations use Astralform.
436
+ */
437
+ add: async (repoFullName) => {
438
+ const raw = await this.post(
439
+ "/v1/code/projects",
440
+ { repo_full_name: repoFullName }
441
+ );
442
+ return camelizeKeys(raw);
443
+ },
444
+ /**
445
+ * Remove a project. Tasks already bound to that repository keep their
446
+ * binding — they simply stop grouping under it.
447
+ */
448
+ remove: async (owner, repo) => {
449
+ await this.del(
450
+ `/v1/code/projects/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`
451
+ );
452
+ }
453
+ }
454
+ };
392
455
  if (isApiKeyConfig(config)) {
393
456
  if (!config.apiKey || typeof config.apiKey !== "string") {
394
457
  throw new Error("apiKey is required and must be a non-empty string");
@@ -629,10 +692,18 @@ var AstralformClient = class {
629
692
  }
630
693
  };
631
694
  }
632
- async getConversations(limit = 50, offset = 0) {
695
+ /**
696
+ * A page of conversations, newest-updated first.
697
+ *
698
+ * `options.repository` narrows to one project's tasks (`owner/repo`) on a
699
+ * code-mode agent — the same paging applies within the filter, so a client
700
+ * showing tasks per project pages each project separately.
701
+ */
702
+ async getConversations(limit = 50, offset = 0, options) {
633
703
  const safeLimit = Math.max(1, Math.min(200, Math.floor(Number(limit))));
634
704
  const safeOffset = Math.max(0, Math.floor(Number(offset)));
635
- const raw = await this.get(`/v1/conversations?limit=${safeLimit}&offset=${safeOffset}`);
705
+ const filter = options?.repository ? `&repository=${encodeURIComponent(options.repository)}` : "";
706
+ const raw = await this.get(`/v1/conversations?limit=${safeLimit}&offset=${safeOffset}${filter}`);
636
707
  return raw.map((c) => camelizeKeys(c));
637
708
  }
638
709
  async getMessages(conversationId) {
@@ -1727,6 +1798,9 @@ var ChatSession = class {
1727
1798
  image_mode: options?.imageMode,
1728
1799
  video_mode: options?.videoMode,
1729
1800
  goal: options?.goal,
1801
+ // The project this task belongs to, on a code-mode agent. Write-once
1802
+ // server-side: sent on every turn, honoured on the first.
1803
+ repository: options?.repository,
1730
1804
  // Per-request model choice (client-side model selection).
1731
1805
  provider: options?.provider,
1732
1806
  model: options?.model,
@@ -2579,23 +2653,8 @@ var StreamManager = class {
2579
2653
  this.setState("streaming");
2580
2654
  try {
2581
2655
  await this.session.send(content, {
2582
- // Address the send explicitly. `ChatSession.send` otherwise falls back
2583
- // to `session.conversationId`, which LAGS this pointer: a restore
2584
- // assigns it synchronously but only reaches the next switch's own
2585
- // `loadConversation` an await later, so between the two the session
2586
- // still names the conversation the user left. The manager's pointer
2587
- // moved the moment the user clicked; it is the authority.
2588
- conversationId: target ?? void 0,
2589
- agentName: options?.agentName,
2590
- uploadIds: options?.uploadIds,
2591
- planMode: options?.planMode,
2592
- imageMode: options?.imageMode,
2593
- videoMode: options?.videoMode,
2594
- goal: options?.goal,
2595
- provider: options?.provider,
2596
- model: options?.model,
2597
- reasoningEffort: options?.reasoningEffort,
2598
- temperature: options?.temperature
2656
+ ...options,
2657
+ conversationId: target ?? void 0
2599
2658
  });
2600
2659
  } catch {
2601
2660
  }