@kubitnodes/stellaris 0.4.0 → 0.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.
Files changed (59) hide show
  1. package/dist/cli/index.js +42 -19
  2. package/dist/server/index.js +159 -58
  3. package/package.json +1 -1
  4. package/web/dist/assets/BackLink-fxPmFUg8.js +1 -0
  5. package/web/dist/assets/Button-DxGzriWQ.js +1 -0
  6. package/web/dist/assets/ChannelForms-C3m4IPnZ.js +1 -0
  7. package/web/dist/assets/ChannelView-DvtoxLi5.js +2 -0
  8. package/web/dist/assets/CitizenView-Dq64VsZi.js +2 -0
  9. package/web/dist/assets/{CitizensView-DXD3qkq1.js → CitizensView-BOZHABbN.js} +1 -1
  10. package/web/dist/assets/{CliIcon-DL4ZwgZv.js → CliIcon-D3GddnP4.js} +1 -1
  11. package/web/dist/assets/CronsView-CI947lZS.js +1 -0
  12. package/web/dist/assets/{FileTree-BFa_KVI8.js → FileTree-DSFN4sd8.js} +1 -1
  13. package/web/dist/assets/{FileView-Cd9Ijarf.js → FileView-DyUYLXRC.js} +2 -2
  14. package/web/dist/assets/{KnowledgeView-D_a4pjGV.js → KnowledgeView-CMdXaTvS.js} +1 -1
  15. package/web/dist/assets/{Markdown-DPqvK--4.js → Markdown-BSeWHBBA.js} +5 -5
  16. package/web/dist/assets/{MessageItem-WBTzWwyj.js → MessageItem-qIxS96uJ.js} +1 -1
  17. package/web/dist/assets/{MetricsView-YUkYixqT.js → MetricsView-BhjdwkX1.js} +1 -1
  18. package/web/dist/assets/NeedsYouView-7uZW9dY8.js +1 -0
  19. package/web/dist/assets/Overview-D6kiK8sE.js +1 -0
  20. package/web/dist/assets/{Pane-C9TbEME4.js → Pane-pZUzNQIW.js} +1 -1
  21. package/web/dist/assets/{Picker-BK48MjLY.js → Picker-CDf6os_x.js} +1 -1
  22. package/web/dist/assets/{ProjectView-sL3aGAC6.js → ProjectView-B6OmxiT9.js} +1 -1
  23. package/web/dist/assets/ProposalView-B1X8Jlo0.js +1 -0
  24. package/web/dist/assets/{ProposalsView-DTGx8NDn.js → ProposalsView-DBGpcAPO.js} +1 -1
  25. package/web/dist/assets/{Quota-BkF7pa0u.js → Quota-Brd4ddB4.js} +1 -1
  26. package/web/dist/assets/RunnersView-45OXtalD.js +1 -0
  27. package/web/dist/assets/{SkillFileList-BUpKUkaG.js → SkillFileList-BUImALv9.js} +1 -1
  28. package/web/dist/assets/SkillView-B1knYZV8.js +1 -0
  29. package/web/dist/assets/SocietyView-F7bzrNv5.js +1 -0
  30. package/web/dist/assets/{StickyList-DafbJutb.js → StickyList-D_ED_-LW.js} +1 -1
  31. package/web/dist/assets/{SubjectThread-Jyl5YCFL.js → SubjectThread-B5zvoRSd.js} +1 -1
  32. package/web/dist/assets/{TaskView-Jke2pVGT.js → TaskView-B5_QTI3p.js} +2 -2
  33. package/web/dist/assets/{TasksView-zJTIaqEA.js → TasksView-DZLNqU2V.js} +1 -1
  34. package/web/dist/assets/{ThreadForms-DwUOktyP.js → ThreadForms-CbFClyd0.js} +1 -1
  35. package/web/dist/assets/{ThreadItem-Blx8gSvO.js → ThreadItem-kFn5M3Cx.js} +1 -1
  36. package/web/dist/assets/{ThreadView-CdQpBb7M.js → ThreadView-CHFs0zaS.js} +1 -1
  37. package/web/dist/assets/{citizen-BOn5BBRL.js → citizen-B8p56JRh.js} +1 -1
  38. package/web/dist/assets/{compose-D-X3BQgY.js → compose-CV4o6TMb.js} +1 -1
  39. package/web/dist/assets/{format-Dhd2NUB3.js → format-BYF63sQU.js} +1 -1
  40. package/web/dist/assets/{governance-CWzi7hzX.js → governance-BjIg0eok.js} +1 -1
  41. package/web/dist/assets/index-CyROMh25.js +24 -0
  42. package/web/dist/assets/{mentions-S-r32HKM.js → mentions-BH0RZXfc.js} +1 -1
  43. package/web/dist/assets/{tasks-D_Fy7dtN.js → tasks-D2Es55TQ.js} +1 -1
  44. package/web/dist/assets/useMatch-DeOpK57G.js +1 -0
  45. package/web/dist/index.html +8 -8
  46. package/web/dist/assets/BackLink-BLiSCuKv.js +0 -1
  47. package/web/dist/assets/Button-D4-iu6aX.js +0 -1
  48. package/web/dist/assets/ChannelForms-DjviQfVm.js +0 -1
  49. package/web/dist/assets/ChannelView-B-0H7Ps8.js +0 -2
  50. package/web/dist/assets/CitizenView-gE5GgCBa.js +0 -2
  51. package/web/dist/assets/CronsView-CX_G5D3h.js +0 -1
  52. package/web/dist/assets/NeedsYouView-CE_B9bZQ.js +0 -1
  53. package/web/dist/assets/Overview-BGUnf6K1.js +0 -1
  54. package/web/dist/assets/ProposalView-BhZR4MIR.js +0 -1
  55. package/web/dist/assets/RunnersView-Bl62pXNw.js +0 -1
  56. package/web/dist/assets/SkillView-BQJApSh7.js +0 -1
  57. package/web/dist/assets/SocietyView-JSdVE50m.js +0 -1
  58. package/web/dist/assets/index-iplooQqq.js +0 -24
  59. package/web/dist/assets/useMatch-DZXCHHaG.js +0 -1
package/dist/cli/index.js CHANGED
@@ -370,6 +370,18 @@ function mergeQuota(previous, next) {
370
370
  windows: [...windows.values()]
371
371
  };
372
372
  }
373
+ var UNKNOWN_LIMIT_HOLD_MS = 30 * 6e4;
374
+ function stopsTurns(quota) {
375
+ return quota.status === "limited" && !(quota.source === "read" && quota.extraUsage?.enabled === true);
376
+ }
377
+ function limitHold(quota, now) {
378
+ if (!stopsTurns(quota)) {
379
+ return null;
380
+ }
381
+ const known = quota.limitedUntil !== null;
382
+ const until = quota.limitedUntil ?? new Date(Date.parse(quota.observedAt) + UNKNOWN_LIMIT_HOLD_MS).toISOString();
383
+ return Date.parse(until) > now.getTime() ? { until, known } : null;
384
+ }
373
385
  function readings(quota) {
374
386
  const windows = quota.windows.map((window) => `${window.key}=${window.usedPercent === null ? "?" : Math.round(window.usedPercent)}@${window.resetsAt === null ? "?" : Math.round(Date.parse(window.resetsAt) / 6e4)}`).toSorted();
375
387
  return windows.join(",");
@@ -699,12 +711,16 @@ var RunnerSchema = z5.object({
699
711
  function runnerPause(runner2, cli) {
700
712
  return runner2.paused ?? runner2.pausedClis?.[cli];
701
713
  }
702
- function heldOn(runners, runner2, cli) {
714
+ function runnerLimitHold(runner2, cli, now) {
715
+ const quota = runner2.quotas?.[cli];
716
+ return quota === void 0 ? null : limitHold(quota, now);
717
+ }
718
+ function heldOn(runners, runner2, cli, now) {
703
719
  if (runner2 === void 0 || cli === null) {
704
720
  return false;
705
721
  }
706
722
  const record = runners.find((each) => each.name === runner2);
707
- return record !== void 0 && runnerPause(record, cli) !== void 0;
723
+ return record !== void 0 && (runnerPause(record, cli) !== void 0 || runnerLimitHold(record, cli, now) !== null);
708
724
  }
709
725
  var ProposalKindSchema = z5.enum([
710
726
  "role",
@@ -2284,7 +2300,7 @@ var RunnerConfigSchema = z13.object({
2284
2300
  });
2285
2301
 
2286
2302
  // packages/shared/dist/version.js
2287
- var STELLARIS_VERSION = "0.4.0";
2303
+ var STELLARIS_VERSION = "0.4.1";
2288
2304
 
2289
2305
  // packages/shared/dist/mentions.js
2290
2306
  import { parse, postprocess, preprocess } from "micromark";
@@ -3900,8 +3916,10 @@ function plain(value) {
3900
3916
  return "";
3901
3917
  return JSON.stringify(value);
3902
3918
  }
3903
- function sentence(summary) {
3904
- return summary.trim().replace(/\.+$/, "");
3919
+ var SENTENCE_END = /[.…?!]["'”’»)\]}*_]*$/u;
3920
+ function asSentence(text) {
3921
+ const trimmed = text.trim();
3922
+ return SENTENCE_END.test(trimmed) ? trimmed : `${trimmed}.`;
3905
3923
  }
3906
3924
  function skillSummary(data, body) {
3907
3925
  const description = data["description"];
@@ -5485,7 +5503,7 @@ ${current.body}`);
5485
5503
  name: args.name,
5486
5504
  reason: args.reason
5487
5505
  });
5488
- await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${args.name} removed from the society by ${actor.name}: ${sentence(withoutMentions(args.reason))}. Every citizen's skills index stops listing it from its next turn.`);
5506
+ await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${args.name} removed from the society by ${actor.name}: ${asSentence(withoutMentions(args.reason))} Every citizen's skills index stops listing it from its next turn.`);
5489
5507
  return removed;
5490
5508
  });
5491
5509
  }
@@ -6555,7 +6573,7 @@ ${args.summary}`);
6555
6573
  });
6556
6574
  const thread2 = await this.openThreadUnlocked(actor.name, this.proposalThreadOpening(proposal2));
6557
6575
  const rationale = args.rationale.trim();
6558
- await this.appendThreadMessage(actor.name, thread2.id, `Proposal ${proposal2.id}: ${describeCharter(proposal2.kind, proposal2.charter)}.${rationale.length === 0 ? "" : `
6576
+ await this.appendThreadMessage(actor.name, thread2.id, `Proposal ${proposal2.id}: ${asSentence(describeCharter(proposal2.kind, proposal2.charter))}${rationale.length === 0 ? "" : `
6559
6577
 
6560
6578
  ${rationale}`}`);
6561
6579
  return proposal2;
@@ -7259,7 +7277,7 @@ ${rationale}`}`);
7259
7277
  const ts = this.now().toISOString();
7260
7278
  const landed = current.onDone !== "none";
7261
7279
  if (landed) {
7262
- await this.postTaskNote(actor.name, current, input.ok ? `${sentence(input.detail)}.` : `Landing failed: ${sentence(input.detail)}. The task waits at its last stage for its participants to reshape the plan.`, input.ok ? { action: "landed", stage: current.stage, to: null } : { action: "reopened", stage: current.stage, to: current.stage });
7280
+ await this.postTaskNote(actor.name, current, input.ok ? asSentence(input.detail) : `Landing failed: ${asSentence(input.detail)} The task waits at its last stage for its participants to reshape the plan.`, input.ok ? { action: "landed", stage: current.stage, to: null } : { action: "reopened", stage: current.stage, to: current.stage });
7263
7281
  }
7264
7282
  if (input.ok) {
7265
7283
  const task3 = await this.writeEndedTask(actor.name, location.project, {
@@ -7279,7 +7297,7 @@ ${rationale}`}`);
7279
7297
  await this.refreshMember(name);
7280
7298
  }
7281
7299
  if (landed) {
7282
- await this.appendMessage(actor.name, channelRef(location.project, task3.channel), `Task ${task3.id} "${task3.title}" is done: ${sentence(input.detail)}.`);
7300
+ await this.appendMessage(actor.name, channelRef(location.project, task3.channel), `Task ${task3.id} "${task3.title}" is done: ${asSentence(input.detail)}`);
7283
7301
  }
7284
7302
  return task3;
7285
7303
  }
@@ -7990,7 +8008,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
7990
8008
  files: taken.files.length,
7991
8009
  ...meta
7992
8010
  });
7993
- await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${skill2.name} ${replaced ? "updated in" : "promoted to"} the society: ${sentence(skill2.summary)}. Every citizen's skills index now lists it.`);
8011
+ await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${skill2.name} ${replaced ? "updated in" : "promoted to"} the society: ${asSentence(skill2.summary)} Every citizen's skills index now lists it.`);
7994
8012
  return replaced;
7995
8013
  }
7996
8014
  /**
@@ -8237,7 +8255,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
8237
8255
  });
8238
8256
  const purpose = withoutMentions(input.purpose);
8239
8257
  await this.appendMessage(SYSTEM_ACTOR.name, ref, `Channel ${ref} opened: ${purpose}`);
8240
- await this.appendMessage(SYSTEM_ACTOR.name, channelRef(input.project, "general"), `Channel ${ref} opened by ${by}: ${purpose}. Subscribe to it to follow its posts; its tasks and mentions reach whoever they concern either way.`);
8258
+ await this.appendMessage(SYSTEM_ACTOR.name, channelRef(input.project, "general"), `Channel ${ref} opened by ${by}: ${asSentence(purpose)} Subscribe to it to follow its posts; its tasks and mentions reach whoever they concern either way.`);
8241
8259
  return ref;
8242
8260
  }
8243
8261
  /**
@@ -8660,17 +8678,18 @@ ${charter.purpose}
8660
8678
  return kept > lease ? new Date(kept).toISOString() : null;
8661
8679
  }
8662
8680
  /**
8663
- * The lease a holder keeps while the user's pause holds its turns in the task's project
8664
- * (`heldOn`), when later than the one it has, else null. It is renewed for a full lease once less
8665
- * than half is left, as for a pending cron, so it never lapses while the holder cannot take a
8666
- * turn there, and after the resume the holder has at least half a lease to come back to it.
8681
+ * The lease a holder keeps while the user's pause, or its CLI's limit, holds its turns in the
8682
+ * task's project (`heldOn`), when later than the one it has, else null. It is renewed for a full
8683
+ * lease once less than half is left, as for a pending cron, so it never lapses while the holder
8684
+ * cannot take a turn there, and after the resume or the reset the holder has at least half a
8685
+ * lease to come back to it.
8667
8686
  */
8668
8687
  async pausedLease(task2, runners, runner2, now) {
8669
8688
  if (task2.claimedBy === void 0 || task2.leaseExpiresAt === void 0) {
8670
8689
  return null;
8671
8690
  }
8672
8691
  const holder = await this.readAgent(task2.claimedBy).catch(() => null);
8673
- if (holder === null || !heldOn(runners, runner2, holder.cli)) {
8692
+ if (holder === null || !heldOn(runners, runner2, holder.cli, now)) {
8674
8693
  return null;
8675
8694
  }
8676
8695
  const lease = Date.parse(task2.leaseExpiresAt);
@@ -8761,9 +8780,9 @@ ${charter.purpose}
8761
8780
  });
8762
8781
  }
8763
8782
  const verdict = outcome === "approved" ? "Approved" : "Rejected";
8764
- const why = reason === void 0 ? "" : ` Reason: ${reason}.`;
8783
+ const why = reason === void 0 ? "" : ` Reason: ${asSentence(reason)}`;
8765
8784
  const result = provision === void 0 ? "" : ` Provisioned: ${Object.entries(provision).map(([key, value]) => `${key} ${plain(value)}`).join(", ")}.`;
8766
- const post = `${verdict}: ${describeCharter(proposal2.kind, proposal2.charter)}.${why}${result}`;
8785
+ const post = `${verdict}: ${asSentence(describeCharter(proposal2.kind, proposal2.charter))}${why}${result}`;
8767
8786
  await this.appendThreadMessage(actor.name, thread2.id, actor.name === SYSTEM_ACTOR.name ? withoutMentions(post) : post, { step: { action: outcome } });
8768
8787
  await this.endThreadOf(proposalId, actor.name, outcome);
8769
8788
  return decision;
@@ -9040,7 +9059,11 @@ runner.command("add <name>").description("Register a runner and print its token
9040
9059
  runner.command("list").description("List runners and what they offer").action(async () => {
9041
9060
  const board = await open3();
9042
9061
  const runners = await board.listRunners();
9043
- print(runners, () => runners.length === 0 ? "No runners yet; add one with `runner add <name>`." : runners.map((each) => `${each.name} ${each.status}${each.paused === void 0 ? "" : " paused"} ${each.os}${each.version === void 0 ? "" : ` v${each.version}`} ${each.clis.join(",") || "no CLIs yet"}${each.capabilities.length === 0 ? "" : ` ${each.capabilities.join(",")}`}${Object.keys(each.pausedClis ?? {}).map((cli) => ` ${cli} paused`).join("")}`).join("\n"));
9062
+ const now = /* @__PURE__ */ new Date();
9063
+ print(runners, () => runners.length === 0 ? "No runners yet; add one with `runner add <name>`." : runners.map((each) => `${each.name} ${each.status}${each.paused === void 0 ? "" : " paused"} ${each.os}${each.version === void 0 ? "" : ` v${each.version}`} ${each.clis.join(",") || "no CLIs yet"}${each.capabilities.length === 0 ? "" : ` ${each.capabilities.join(",")}`}${Object.keys(each.pausedClis ?? {}).map((cli) => ` ${cli} paused`).join("")}${each.clis.map((cli) => {
9064
+ const hold = runnerLimitHold(each, cli, now);
9065
+ return hold === null ? "" : ` ${cli} held until ${hold.until}`;
9066
+ }).join("")}`).join("\n"));
9044
9067
  });
9045
9068
  for (const [verb, paused] of [
9046
9069
  ["pause", true],
@@ -398,6 +398,21 @@ function quotaLevel(quota, now) {
398
398
  const high = quota.windows.some((window) => windowCurrent(window, now) && window.usedPercent !== null && window.usedPercent >= QUOTA_WARNING_PERCENT);
399
399
  return quota.status === "warning" || high ? "warning" : "ok";
400
400
  }
401
+ var UNKNOWN_LIMIT_HOLD_MS = 30 * 6e4;
402
+ function stopsTurns(quota) {
403
+ return quota.status === "limited" && !(quota.source === "read" && quota.extraUsage?.enabled === true);
404
+ }
405
+ function limitHold(quota, now) {
406
+ if (!stopsTurns(quota)) {
407
+ return null;
408
+ }
409
+ const known = quota.limitedUntil !== null;
410
+ const until = quota.limitedUntil ?? new Date(Date.parse(quota.observedAt) + UNKNOWN_LIMIT_HOLD_MS).toISOString();
411
+ return Date.parse(until) > now.getTime() ? { until, known } : null;
412
+ }
413
+ function limitLapsed(quota, now) {
414
+ return stopsTurns(quota) && quota.limitedUntil === null && limitHold(quota, now) === null;
415
+ }
401
416
  var WINDOW_NAMES = {
402
417
  five_hour: "5h",
403
418
  seven_day: "7d",
@@ -751,12 +766,16 @@ var RunnerSchema = z5.object({
751
766
  function runnerPause(runner, cli) {
752
767
  return runner.paused ?? runner.pausedClis?.[cli];
753
768
  }
754
- function heldOn(runners2, runner, cli) {
769
+ function runnerLimitHold(runner, cli, now) {
770
+ const quota = runner.quotas?.[cli];
771
+ return quota === void 0 ? null : limitHold(quota, now);
772
+ }
773
+ function heldOn(runners2, runner, cli, now) {
755
774
  if (runner === void 0 || cli === null) {
756
775
  return false;
757
776
  }
758
777
  const record = runners2.find((each) => each.name === runner);
759
- return record !== void 0 && runnerPause(record, cli) !== void 0;
778
+ return record !== void 0 && (runnerPause(record, cli) !== void 0 || runnerLimitHold(record, cli, now) !== null);
760
779
  }
761
780
  var ProposalKindSchema = z5.enum([
762
781
  "role",
@@ -2467,7 +2486,7 @@ var RunnerConfigSchema = z13.object({
2467
2486
  });
2468
2487
 
2469
2488
  // packages/shared/dist/version.js
2470
- var STELLARIS_VERSION = "0.4.0";
2489
+ var STELLARIS_VERSION = "0.4.1";
2471
2490
 
2472
2491
  // packages/shared/dist/mentions.js
2473
2492
  import { parse, postprocess, preprocess } from "micromark";
@@ -4086,8 +4105,10 @@ function plain(value) {
4086
4105
  return "";
4087
4106
  return JSON.stringify(value);
4088
4107
  }
4089
- function sentence(summary) {
4090
- return summary.trim().replace(/\.+$/, "");
4108
+ var SENTENCE_END = /[.…?!]["'”’»)\]}*_]*$/u;
4109
+ function asSentence(text) {
4110
+ const trimmed = text.trim();
4111
+ return SENTENCE_END.test(trimmed) ? trimmed : `${trimmed}.`;
4091
4112
  }
4092
4113
  function skillSummary(data, body) {
4093
4114
  const description = data["description"];
@@ -5671,7 +5692,7 @@ ${current.body}`);
5671
5692
  name: args.name,
5672
5693
  reason: args.reason
5673
5694
  });
5674
- await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${args.name} removed from the society by ${actor.name}: ${sentence(withoutMentions(args.reason))}. Every citizen's skills index stops listing it from its next turn.`);
5695
+ await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${args.name} removed from the society by ${actor.name}: ${asSentence(withoutMentions(args.reason))} Every citizen's skills index stops listing it from its next turn.`);
5675
5696
  return removed;
5676
5697
  });
5677
5698
  }
@@ -6741,7 +6762,7 @@ ${args.summary}`);
6741
6762
  });
6742
6763
  const thread = await this.openThreadUnlocked(actor.name, this.proposalThreadOpening(proposal));
6743
6764
  const rationale = args.rationale.trim();
6744
- await this.appendThreadMessage(actor.name, thread.id, `Proposal ${proposal.id}: ${describeCharter(proposal.kind, proposal.charter)}.${rationale.length === 0 ? "" : `
6765
+ await this.appendThreadMessage(actor.name, thread.id, `Proposal ${proposal.id}: ${asSentence(describeCharter(proposal.kind, proposal.charter))}${rationale.length === 0 ? "" : `
6745
6766
 
6746
6767
  ${rationale}`}`);
6747
6768
  return proposal;
@@ -7445,7 +7466,7 @@ ${rationale}`}`);
7445
7466
  const ts = this.now().toISOString();
7446
7467
  const landed = current.onDone !== "none";
7447
7468
  if (landed) {
7448
- await this.postTaskNote(actor.name, current, input.ok ? `${sentence(input.detail)}.` : `Landing failed: ${sentence(input.detail)}. The task waits at its last stage for its participants to reshape the plan.`, input.ok ? { action: "landed", stage: current.stage, to: null } : { action: "reopened", stage: current.stage, to: current.stage });
7469
+ await this.postTaskNote(actor.name, current, input.ok ? asSentence(input.detail) : `Landing failed: ${asSentence(input.detail)} The task waits at its last stage for its participants to reshape the plan.`, input.ok ? { action: "landed", stage: current.stage, to: null } : { action: "reopened", stage: current.stage, to: current.stage });
7449
7470
  }
7450
7471
  if (input.ok) {
7451
7472
  const task2 = await this.writeEndedTask(actor.name, location.project, {
@@ -7465,7 +7486,7 @@ ${rationale}`}`);
7465
7486
  await this.refreshMember(name);
7466
7487
  }
7467
7488
  if (landed) {
7468
- await this.appendMessage(actor.name, channelRef(location.project, task2.channel), `Task ${task2.id} "${task2.title}" is done: ${sentence(input.detail)}.`);
7489
+ await this.appendMessage(actor.name, channelRef(location.project, task2.channel), `Task ${task2.id} "${task2.title}" is done: ${asSentence(input.detail)}`);
7469
7490
  }
7470
7491
  return task2;
7471
7492
  }
@@ -8176,7 +8197,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
8176
8197
  files: taken.files.length,
8177
8198
  ...meta
8178
8199
  });
8179
- await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${skill.name} ${replaced ? "updated in" : "promoted to"} the society: ${sentence(skill.summary)}. Every citizen's skills index now lists it.`);
8200
+ await this.appendMessage(SYSTEM_ACTOR.name, "general", `Skill ${skill.name} ${replaced ? "updated in" : "promoted to"} the society: ${asSentence(skill.summary)} Every citizen's skills index now lists it.`);
8180
8201
  return replaced;
8181
8202
  }
8182
8203
  /**
@@ -8423,7 +8444,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
8423
8444
  });
8424
8445
  const purpose = withoutMentions(input.purpose);
8425
8446
  await this.appendMessage(SYSTEM_ACTOR.name, ref, `Channel ${ref} opened: ${purpose}`);
8426
- await this.appendMessage(SYSTEM_ACTOR.name, channelRef(input.project, "general"), `Channel ${ref} opened by ${by}: ${purpose}. Subscribe to it to follow its posts; its tasks and mentions reach whoever they concern either way.`);
8447
+ await this.appendMessage(SYSTEM_ACTOR.name, channelRef(input.project, "general"), `Channel ${ref} opened by ${by}: ${asSentence(purpose)} Subscribe to it to follow its posts; its tasks and mentions reach whoever they concern either way.`);
8427
8448
  return ref;
8428
8449
  }
8429
8450
  /**
@@ -8846,17 +8867,18 @@ ${charter.purpose}
8846
8867
  return kept > lease ? new Date(kept).toISOString() : null;
8847
8868
  }
8848
8869
  /**
8849
- * The lease a holder keeps while the user's pause holds its turns in the task's project
8850
- * (`heldOn`), when later than the one it has, else null. It is renewed for a full lease once less
8851
- * than half is left, as for a pending cron, so it never lapses while the holder cannot take a
8852
- * turn there, and after the resume the holder has at least half a lease to come back to it.
8870
+ * The lease a holder keeps while the user's pause, or its CLI's limit, holds its turns in the
8871
+ * task's project (`heldOn`), when later than the one it has, else null. It is renewed for a full
8872
+ * lease once less than half is left, as for a pending cron, so it never lapses while the holder
8873
+ * cannot take a turn there, and after the resume or the reset the holder has at least half a
8874
+ * lease to come back to it.
8853
8875
  */
8854
8876
  async pausedLease(task, runners2, runner, now) {
8855
8877
  if (task.claimedBy === void 0 || task.leaseExpiresAt === void 0) {
8856
8878
  return null;
8857
8879
  }
8858
8880
  const holder = await this.readAgent(task.claimedBy).catch(() => null);
8859
- if (holder === null || !heldOn(runners2, runner, holder.cli)) {
8881
+ if (holder === null || !heldOn(runners2, runner, holder.cli, now)) {
8860
8882
  return null;
8861
8883
  }
8862
8884
  const lease = Date.parse(task.leaseExpiresAt);
@@ -8947,9 +8969,9 @@ ${charter.purpose}
8947
8969
  });
8948
8970
  }
8949
8971
  const verdict = outcome === "approved" ? "Approved" : "Rejected";
8950
- const why = reason === void 0 ? "" : ` Reason: ${reason}.`;
8972
+ const why = reason === void 0 ? "" : ` Reason: ${asSentence(reason)}`;
8951
8973
  const result = provision === void 0 ? "" : ` Provisioned: ${Object.entries(provision).map(([key, value]) => `${key} ${plain(value)}`).join(", ")}.`;
8952
- const post = `${verdict}: ${describeCharter(proposal.kind, proposal.charter)}.${why}${result}`;
8974
+ const post = `${verdict}: ${asSentence(describeCharter(proposal.kind, proposal.charter))}${why}${result}`;
8953
8975
  await this.appendThreadMessage(actor.name, thread.id, actor.name === SYSTEM_ACTOR.name ? withoutMentions(post) : post, { step: { action: outcome } });
8954
8976
  await this.endThreadOf(proposalId, actor.name, outcome);
8955
8977
  return decision;
@@ -10501,7 +10523,7 @@ var Scheduler = class {
10501
10523
  }
10502
10524
  members ??= await this.board.projectMembers(project.slug);
10503
10525
  const takers = members.filter((member) => member.cli !== null && mayHoldStage(member, task));
10504
- if (takers.length > 0 && takers.every((taker) => heldOn(runners2, project.runner, taker.cli))) {
10526
+ if (takers.length > 0 && takers.every((taker) => heldOn(runners2, project.runner, taker.cli, new Date(now)))) {
10505
10527
  this.state.waitingHeld[key] = iso(now);
10506
10528
  continue;
10507
10529
  }
@@ -10708,7 +10730,8 @@ var Scheduler = class {
10708
10730
  last = Math.max(last, Date.parse(ended));
10709
10731
  }
10710
10732
  }
10711
- const held = heldOn(runners2, agent.homeRunner, agent.cli) || projects.some((project) => agent.memberships.includes(project.slug) && heldOn(runners2, project.runner, agent.cli));
10733
+ const at = new Date(now);
10734
+ const held = heldOn(runners2, agent.homeRunner, agent.cli, at) || projects.some((project) => agent.memberships.includes(project.slug) && heldOn(runners2, project.runner, agent.cli, at));
10712
10735
  if (held) {
10713
10736
  this.state.idleHeld[agent.name] = iso(now);
10714
10737
  }
@@ -11227,7 +11250,11 @@ function runnerLine(runner, projects, now) {
11227
11250
  ].join(", ");
11228
11251
  const pauses = runner.clis.flatMap((cli) => {
11229
11252
  const pause = runner.pausedClis?.[cli];
11230
- return pause === void 0 ? [] : [`${cli} paused by the user since ${pause.at}`];
11253
+ const hold = runnerLimitHold(runner, cli, now);
11254
+ return [
11255
+ ...pause === void 0 ? [] : [`${cli} paused by the user since ${pause.at}`],
11256
+ ...hold === null ? [] : [`${cli} held by its limit until ${hold.until}`]
11257
+ ];
11231
11258
  });
11232
11259
  const quotas = runner.clis.flatMap((cli) => {
11233
11260
  const quota = runner.quotas?.[cli];
@@ -11400,7 +11427,7 @@ function buildTurnPrompt(input) {
11400
11427
  }
11401
11428
  const machines = input.runners;
11402
11429
  if (machines !== void 0 && machines !== null) {
11403
- lines.push("", "## Runners", "", "The machines turns run on. A project lives on the runner that takes its first turn, one that offers every capability the project and that turn's task require, and moves only when the user moves it. A task's required_capabilities must name capabilities exactly as a runner below offers them; a task needing one its project's runner lacks waits. A runner's quota is what each CLI's login there last reported of its plan, in percent of each window; a CLI limited until a time fails its turns there until then. A runner, or a CLI on it, paused by the user starts none of the turns it would run until the user resumes it: they wait, and the stages their citizens hold keep their leases.", "");
11430
+ lines.push("", "## Runners", "", "The machines turns run on. A project lives on the runner that takes its first turn, one that offers every capability the project and that turn's task require, and moves only when the user moves it. A task's required_capabilities must name capabilities exactly as a runner below offers them; a task needing one its project's runner lacks waits. A runner's quota is what each CLI's login there last reported of its plan, in percent of each window. A runner, or a CLI on it, paused by the user starts none of the turns it would run until the user resumes it, and a CLI held by its limit starts none of its turns there until the limit lifts, at the reset the CLI gave or, with none given, half an hour after the report: they wait, and the stages their citizens hold keep their leases.", "");
11404
11431
  if (machines.runners.length === 0) {
11405
11432
  lines.push("None registered yet.");
11406
11433
  }
@@ -12297,6 +12324,9 @@ var TurnHost = class {
12297
12324
 
12298
12325
  // packages/turn-host/dist/hub.js
12299
12326
  import { randomUUID as randomUUID2 } from "node:crypto";
12327
+ function receiptOverdue(seat) {
12328
+ return [...seat.unacked.values()].some((each) => each.overdue);
12329
+ }
12300
12330
  var RunnerProtocolError = class extends Error {
12301
12331
  };
12302
12332
  var RunnerAwayError = class extends Error {
@@ -12341,6 +12371,13 @@ var RunnerHub = class {
12341
12371
  starting = /* @__PURE__ */ new Map();
12342
12372
  /** Moves started since the hub did, so a placement that a move overtook gives its turn back. */
12343
12373
  moveStarts = 0;
12374
+ /** When each runner was last asked to read a CLI's quota for a limit with no reset, by `runner/cli`. */
12375
+ limitReads = /* @__PURE__ */ new Map();
12376
+ /**
12377
+ * The turn let through once a limit with no reset had held its half hour, by `runner/cli`: the
12378
+ * report it tries, which `observedAt` names, and whether the turn has ended.
12379
+ */
12380
+ limitTries = /* @__PURE__ */ new Map();
12344
12381
  constructor(options) {
12345
12382
  this.board = options.board;
12346
12383
  this.host = options.host;
@@ -12377,9 +12414,9 @@ var RunnerHub = class {
12377
12414
  if (hello.protocol !== RUNNER_PROTOCOL) {
12378
12415
  throw new RunnerProtocolError(`runner protocol ${hello.protocol} is not ${RUNNER_PROTOCOL}; upgrade the runner or the server`);
12379
12416
  }
12380
- const seat = this.seats.get(name);
12417
+ let seat = this.seats.get(name);
12381
12418
  if (seat === void 0) {
12382
- this.seats.set(name, {
12419
+ seat = {
12383
12420
  name,
12384
12421
  hello,
12385
12422
  send: null,
@@ -12387,19 +12424,18 @@ var RunnerHub = class {
12387
12424
  warm: /* @__PURE__ */ new Set(),
12388
12425
  disconnectedAt: null,
12389
12426
  grace: null,
12390
- unacked: /* @__PURE__ */ new Map(),
12391
- unconfirmed: false
12392
- });
12427
+ unacked: /* @__PURE__ */ new Map()
12428
+ };
12429
+ this.seats.set(name, seat);
12393
12430
  } else {
12394
12431
  seat.hello = hello;
12395
12432
  seat.warm = /* @__PURE__ */ new Set();
12396
12433
  }
12397
12434
  const still = new Set(hello.turns);
12398
- const unacked = this.seats.get(name)?.unacked ?? /* @__PURE__ */ new Map();
12399
12435
  for (const turnId of this.host.turnsOn(name)) {
12400
12436
  if (still.has(turnId)) {
12401
- this.settle(unacked, turnId);
12402
- } else if (!unacked.has(turnId)) {
12437
+ this.settle(seat, turnId);
12438
+ } else if (!seat.unacked.has(turnId)) {
12403
12439
  await this.host.abortTurn(turnId, `runner ${name} restarted during the turn`);
12404
12440
  }
12405
12441
  }
@@ -12422,7 +12458,6 @@ var RunnerHub = class {
12422
12458
  }
12423
12459
  seat.send = send;
12424
12460
  seat.disconnectedAt = null;
12425
- seat.unconfirmed = false;
12426
12461
  if (seat.grace !== null) {
12427
12462
  clearTimeout(seat.grace);
12428
12463
  seat.grace = null;
@@ -12463,24 +12498,23 @@ var RunnerHub = class {
12463
12498
  }
12464
12499
  await Promise.allSettled(this.detaching);
12465
12500
  }
12501
+ /** A turn's steps, which show its job arrived as its receipt would. */
12466
12502
  async turnEvents(runner, turnId, entries) {
12467
12503
  this.assertOwns(runner, turnId);
12504
+ this.received(runner, turnId);
12468
12505
  await this.host.addEvents(turnId, entries);
12469
12506
  }
12470
12507
  /** A runner's word that a turn's job arrived, which also shows its stream delivers again. */
12471
12508
  received(runner, turnId) {
12472
12509
  const seat = this.seats.get(runner);
12473
- if (seat === void 0 || !seat.unacked.has(turnId)) {
12474
- return;
12475
- }
12476
- this.settle(seat.unacked, turnId);
12477
- if (seat.unconfirmed) {
12478
- seat.unconfirmed = false;
12479
- this.log.info({ runner }, "runner is taking jobs again");
12510
+ if (seat !== void 0) {
12511
+ this.settle(seat, turnId);
12480
12512
  }
12481
12513
  }
12514
+ /** A turn's outcome, which shows its job arrived as its receipt would. */
12482
12515
  async turnOutcome(runner, turnId, outcome) {
12483
12516
  this.assertOwns(runner, turnId);
12517
+ this.received(runner, turnId);
12484
12518
  const ack = await this.host.closeTurn(turnId, outcome);
12485
12519
  return ack ?? { dropWorkspace: false };
12486
12520
  }
@@ -12625,8 +12659,10 @@ var RunnerHub = class {
12625
12659
  }
12626
12660
  const cli = agent.cli;
12627
12661
  const runners2 = await this.board.listRunners();
12628
- const free = (seat) => seat.send !== null && !seat.unconfirmed && seat.hello.clis.includes(cli) && (seat.hello.slots === null || seat.inUse < seat.hello.slots) && !heldOn(runners2, seat.name, cli);
12662
+ const now = this.now();
12663
+ const free = (seat) => seat.send !== null && !receiptOverdue(seat) && seat.hello.clis.includes(cli) && (seat.hello.slots === null || seat.inUse < seat.hello.slots) && !heldOn(runners2, seat.name, cli, now) && !this.tryRunning(runners2, seat.name, cli, now);
12629
12664
  let chosen;
12665
+ let waitsOn = agent.homeRunner;
12630
12666
  if (dispatch.project === SOCIETY_SCOPE) {
12631
12667
  chosen = await this.pinnedSeat(agent.name, agent.homeRunner, cli, free);
12632
12668
  } else {
@@ -12655,8 +12691,12 @@ var RunnerHub = class {
12655
12691
  }
12656
12692
  const seat = this.seats.get(home);
12657
12693
  chosen = seat !== void 0 && free(seat) ? seat : void 0;
12694
+ waitsOn = home;
12658
12695
  }
12659
12696
  if (chosen === void 0) {
12697
+ if (waitsOn !== void 0) {
12698
+ this.readAfterLimit(runners2, waitsOn, cli, now);
12699
+ }
12660
12700
  return null;
12661
12701
  }
12662
12702
  if (dispatch.project !== SOCIETY_SCOPE && (this.moving.has(dispatch.project) || this.moveStarts !== starts)) {
@@ -12666,7 +12706,17 @@ var RunnerHub = class {
12666
12706
  if (dispatch.project !== SOCIETY_SCOPE) {
12667
12707
  this.starting.set(dispatch.project, (this.starting.get(dispatch.project) ?? 0) + 1);
12668
12708
  }
12669
- return { runner: chosen.name };
12709
+ const runner = chosen.name;
12710
+ const lapsed = runners2.find((each) => each.name === runner)?.quotas?.[cli];
12711
+ const tried = this.limitTries.get(`${runner}/${cli}`);
12712
+ if (lapsed !== void 0 && limitLapsed(lapsed, now) && tried?.observedAt !== lapsed.observedAt) {
12713
+ this.limitTries.set(`${runner}/${cli}`, {
12714
+ observedAt: lapsed.observedAt,
12715
+ dispatch,
12716
+ ended: false
12717
+ });
12718
+ }
12719
+ return { runner };
12670
12720
  }
12671
12721
  async runTurn(dispatch, assignment) {
12672
12722
  if ("refused" in assignment) {
@@ -12703,10 +12753,15 @@ var RunnerHub = class {
12703
12753
  return await opened.ended;
12704
12754
  } finally {
12705
12755
  opening();
12756
+ for (const tried of this.limitTries.values()) {
12757
+ if (tried.dispatch === dispatch) {
12758
+ tried.ended = true;
12759
+ }
12760
+ }
12706
12761
  if (seat !== void 0) {
12707
12762
  seat.inUse = Math.max(0, seat.inUse - 1);
12708
12763
  if (turnId !== null) {
12709
- this.settle(seat.unacked, turnId);
12764
+ this.settle(seat, turnId);
12710
12765
  }
12711
12766
  }
12712
12767
  }
@@ -13096,6 +13151,40 @@ var RunnerHub = class {
13096
13151
  await this.board.recordQuota(runner, cli, quota);
13097
13152
  return quota;
13098
13153
  }
13154
+ /**
13155
+ * Whether a turn let through to try a limit with no reset, once its hold ran out, is still running
13156
+ * on the runner, so the CLI's other turns there wait for what it reports: a refusal records a newer
13157
+ * limit, which holds them another half hour, and a turn that ends without one lets them go.
13158
+ */
13159
+ tryRunning(runners2, name, cli, now) {
13160
+ const quota = runners2.find((each) => each.name === name)?.quotas?.[cli];
13161
+ const tried = this.limitTries.get(`${name}/${cli}`);
13162
+ return quota !== void 0 && tried !== void 0 && !tried.ended && tried.observedAt === quota.observedAt && limitLapsed(quota, now);
13163
+ }
13164
+ /**
13165
+ * Asks a runner to read a CLI's quota again while a limit it gave no reset for holds a turn
13166
+ * waiting for it there, at most once per `UNKNOWN_LIMIT_HOLD_MS` for each runner and CLI, so a
13167
+ * reset the read names becomes the hold and a clear read lifts it. Not while the user's pause
13168
+ * holds the CLI there too, nor on a runner from before quota reads; a read that fails changes
13169
+ * nothing, and the hold runs out as it would have.
13170
+ */
13171
+ readAfterLimit(runners2, name, cli, now) {
13172
+ const record = runners2.find((each) => each.name === name);
13173
+ const seat = this.seats.get(name);
13174
+ if (record === void 0 || seat === void 0 || seat.send === null || !seat.hello.quotaReads || !seat.hello.clis.includes(cli) || runnerPause(record, cli) !== void 0 || runnerLimitHold(record, cli, now)?.known !== false) {
13175
+ return;
13176
+ }
13177
+ const key = `${name}/${cli}`;
13178
+ const last = this.limitReads.get(key);
13179
+ if (last !== void 0 && now.getTime() - last < UNKNOWN_LIMIT_HOLD_MS) {
13180
+ return;
13181
+ }
13182
+ this.limitReads.set(key, now.getTime());
13183
+ this.log.info({ runner: name, cli }, "reading a quota limited until an unknown time");
13184
+ this.quota(name, cli).catch((error) => {
13185
+ this.log.warn({ runner: name, cli, error: String(error) }, "could not read a limited quota");
13186
+ });
13187
+ }
13099
13188
  /** Calls `listener` whenever a runner registers, connects, or goes away. */
13100
13189
  onRunnerChange(listener) {
13101
13190
  this.runnerListeners.add(listener);
@@ -13109,29 +13198,41 @@ var RunnerHub = class {
13109
13198
  /**
13110
13199
  * Waits for a runner to acknowledge a job. One that does not in time may be behind a stream that
13111
13200
  * looks open but delivers nothing, as when its machine's network stops passing traffic, so no
13112
- * more turns go to it until it acknowledges or connects again; the turns it has carry on, since
13113
- * they report by posts.
13201
+ * more turns go to it until the job is acknowledged, its turn ends, or the runner connects again;
13202
+ * the turns it has carry on, since they report by posts.
13114
13203
  */
13115
13204
  expectAck(seat, job) {
13116
13205
  const previous = seat.unacked.get(job.turnId);
13117
13206
  if (previous !== void 0) {
13118
13207
  clearTimeout(previous.timer);
13119
13208
  }
13120
- const timer = setTimeout(() => {
13121
- if (!seat.unacked.has(job.turnId) || seat.unconfirmed || seat.send === null) {
13122
- return;
13123
- }
13124
- seat.unconfirmed = true;
13125
- this.log.warn({ runner: seat.name, turnId: job.turnId, waitedMs: this.ackTimeoutMs }, "runner has not acknowledged a job; no more turns go to it until it does or reconnects");
13126
- }, this.ackTimeoutMs);
13127
- timer.unref?.();
13128
- seat.unacked.set(job.turnId, { job, timer });
13129
- }
13130
- settle(unacked, turnId) {
13131
- const entry = unacked.get(turnId);
13132
- if (entry !== void 0) {
13133
- clearTimeout(entry.timer);
13134
- unacked.delete(turnId);
13209
+ const entry = {
13210
+ job,
13211
+ timer: setTimeout(() => {
13212
+ if (seat.unacked.get(job.turnId) !== entry || seat.send === null) {
13213
+ return;
13214
+ }
13215
+ const wasHeld = receiptOverdue(seat);
13216
+ entry.overdue = true;
13217
+ if (!wasHeld) {
13218
+ this.log.warn({ runner: seat.name, turnId: job.turnId, waitedMs: this.ackTimeoutMs }, "runner has not acknowledged a job; no more turns go to it until it does, the turn ends, or it reconnects");
13219
+ }
13220
+ }, this.ackTimeoutMs),
13221
+ overdue: false
13222
+ };
13223
+ entry.timer.unref?.();
13224
+ seat.unacked.set(job.turnId, entry);
13225
+ }
13226
+ /** A job acknowledged, or its turn over: the hold it put on its runner, if any, goes with it. */
13227
+ settle(seat, turnId) {
13228
+ const entry = seat.unacked.get(turnId);
13229
+ if (entry === void 0) {
13230
+ return;
13231
+ }
13232
+ clearTimeout(entry.timer);
13233
+ seat.unacked.delete(turnId);
13234
+ if (entry.overdue && !receiptOverdue(seat)) {
13235
+ this.log.info({ runner: seat.name }, "runner is taking jobs again");
13135
13236
  }
13136
13237
  }
13137
13238
  async detach(seat) {
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "codex",
9
9
  "multi-agent"
10
10
  ],
11
- "version": "0.4.0",
11
+ "version": "0.4.1",
12
12
  "license": "MIT",
13
13
  "homepage": "https://github.com/kr4t0n/stellaris",
14
14
  "repository": {
@@ -0,0 +1 @@
1
+ import{Jn as e,S as t,tn as n}from"./format-BYF63sQU.js";import{O as r,n as i,r as a,t as o,v as s}from"./index-CyROMh25.js";var c=e(),l=`grid size-7 shrink-0 place-items-center rounded-lg text-fg-tertiary hover:bg-surface-2/70 hover:text-fg-primary`;function u({fallback:e}){let u=n(),d=r({select:e=>e.location.state.__TSR_index}),f=a(),p=s(),m=t(),h=i(f,d);if(h===null)return e;let g=o(h,{entity:e=>p.get(e)?.title,project:e=>m.data?.find(t=>t.slug===e)?.name});return(0,c.jsx)(`button`,{type:`button`,onClick:()=>u.history.back(),"aria-label":`Back to ${g}`,title:`Back to ${g}`,className:l,children:`←`})}export{u as t};
@@ -0,0 +1 @@
1
+ import{An as e,Bn as t,In as n,Jn as r,Mn as i,Pn as a,Qn as o,Un as s,Wn as c,Yn as l,kn as u,qn as d}from"./format-BYF63sQU.js";var f=class extends u{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||p(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??(this.state.status===`pending`?this.execute(this.state.variables):Promise.resolve())}async execute(t){let n=()=>{this.#i({type:`continue`})},r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey},i=this.#r=e({fn:()=>this.options.mutationFn?this.options.mutationFn(t,r):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:n,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)}),a=this.state.status===`pending`,o=!i.canStart();try{if(a)n();else{this.#i({type:`pending`,variables:t,isPaused:o}),this.#n.config.onMutate&&await this.#n.config.onMutate(t,this,r);let e=await this.options.onMutate?.(t,r);e!==this.state.context&&this.#i({type:`pending`,context:e,variables:t,isPaused:o})}let e=await i.start();return await this.#n.config.onSuccess?.(e,t,this.state.context,this,r),await this.options.onSuccess?.(e,t,this.state.context,r),await this.#n.config.onSettled?.(e,null,this.state.variables,this.state.context,this,r),await this.options.onSettled?.(e,null,t,this.state.context,r),this.#i({type:`success`,data:e}),e}catch(e){try{await this.#n.config.onError?.(e,t,this.state.context,this,r)}catch(e){Promise.reject(e)}try{await this.options.onError?.(e,t,this.state.context,r)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,e,this.state.variables,this.state.context,this,r)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,e,t,this.state.context,r)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:e}),e}finally{this.#r===i&&(this.#r=void 0),this.#n.runNext(this)}}#i(e){let t=t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}};this.state=t(this.state),i.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function p(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var m=class extends a{#e;#t=void 0;#n;#r;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#i()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),s(this.options,t)||this.#e.getMutationCache().notify({type:`observerOptionsUpdated`,mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&n(t.mutationKey)!==n(this.options.mutationKey)?this.reset():this.#n?.state.status===`pending`&&this.#n.setOptions(this.options)}onSubscribe(){this.listeners.size===1&&this.#n&&(this.#n.addObserver(this),this.#i())}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#i(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#i(),this.#a()}mutate(e,t){return this.#r=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#i(){let e=this.#n?.state??p();this.#t={...e,isPending:e.status===`pending`,isSuccess:e.status===`success`,isError:e.status===`error`,isIdle:e.status===`idle`,mutate:this.mutate,reset:this.reset}}#a(e){i.batch(()=>{if(this.#r&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type===`success`){try{this.#r.onSuccess?.(e.data,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(e.data,null,t,n,r)}catch(e){Promise.reject(e)}}else if(e?.type===`error`){try{this.#r.onError?.(e.error,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(void 0,e.error,t,n,r)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},h=o(l(),1);function g(e,n){let r=d(n),[a]=h.useState(()=>new m(r,e));h.useEffect(()=>{a.setOptions(e)},[a,e]);let o=h.useSyncExternalStore(h.useCallback(e=>a.subscribe(i.batchCalls(e)),[a]),()=>a.getCurrentResult(),()=>a.getCurrentResult()),s=h.useCallback((...e)=>{a.mutate(e[0],e[1]).catch(t)},[a]);if(o.error&&c(a.options.throwOnError,[o.error]))throw o.error;return{...o,mutate:s,mutateAsync:o.mutate}}var _=r(),v={primary:`bg-fg-primary font-medium text-surface-0 hover:bg-white`,ghost:`text-fg-tertiary hover:bg-surface-2/70 hover:text-fg-primary`};function y({variant:e=`ghost`,className:t=``,...n}){return(0,_.jsx)(`button`,{type:`button`,...n,className:`inline-flex h-7 shrink-0 items-center rounded-md px-3 text-xs transition-colors focus-visible:ring-2 focus-visible:ring-fg-primary/30 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-40 ${v[e]} ${t}`})}export{g as n,f as r,y as t};
@@ -0,0 +1 @@
1
+ import{$t as e,Jn as t,Qn as n,Yn as r,j as i,qn as a,t as o}from"./format-BYF63sQU.js";import{n as s,t as c}from"./Button-DxGzriWQ.js";import{l}from"./compose-CV4o6TMb.js";import{n as u,r as d}from"./ThreadForms-CbFClyd0.js";var f=n(r(),1),p=t();function m({project:e,onDone:t}){let{api:n}=i(),r=a(),o=l(),[m,h]=(0,f.useState)(``),[g,_]=(0,f.useState)(``),v=(0,f.useRef)(null);(0,f.useEffect)(()=>v.current?.focus(),[]);let y=s({mutationFn:()=>n.createChannel({project:e,name:m.trim(),purpose:g.trim()}),onSuccess:async e=>{await r.invalidateQueries({queryKey:[`channels`]}),t(),await o({to:`/c/$`,params:{_splat:e}})}});return(0,p.jsxs)(`form`,{"aria-label":`New channel`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),y.mutate()},children:[(0,p.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,p.jsx)(`div`,{className:`w-40 shrink-0`,children:(0,p.jsx)(`input`,{ref:v,value:m,maxLength:64,spellCheck:!1,"aria-label":`Channel name`,placeholder:`release-0-4`,onChange:e=>h(e.target.value),className:`${u} font-mono`})}),(0,p.jsx)(`input`,{value:g,"aria-label":`Purpose`,placeholder:`What the channel is for`,onChange:e=>_(e.target.value),className:`${u} min-w-0 flex-1`}),(0,p.jsx)(c,{onClick:t,children:`Cancel`}),(0,p.jsx)(c,{variant:`primary`,type:`submit`,disabled:y.isPending||m.trim()===``||g.trim()===``,children:y.isPending?`Opening…`:`Open`})]}),(0,p.jsx)(d,{error:y.error})]})}function h({channel:e,onDone:t}){let{api:n}=i(),r=a(),[o,l]=(0,f.useState)(``),m=s({mutationFn:()=>n.archiveChannel({channel:e,reason:o.trim()}),onSuccess:async()=>{await r.invalidateQueries({queryKey:[`channels`]}),t()}});return(0,p.jsxs)(`form`,{"aria-label":`Archive the channel`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),m.mutate()},children:[(0,p.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,p.jsx)(`input`,{value:o,"aria-label":`Reason`,placeholder:`Why its work is done`,onChange:e=>l(e.target.value),className:`${u} min-w-0 flex-1`}),(0,p.jsx)(c,{onClick:t,children:`Cancel`}),(0,p.jsx)(c,{variant:`primary`,type:`submit`,disabled:m.isPending||o.trim()===``,children:m.isPending?`Archiving…`:`Archive channel`})]}),(0,p.jsx)(d,{error:m.error})]})}function g({channels:t,now:n}){return t.length===0?(0,p.jsx)(`p`,{className:`text-meta`,children:`No channels.`}):(0,p.jsx)(`ul`,{className:`space-y-1`,children:t.map(t=>(0,p.jsxs)(`li`,{children:[(0,p.jsxs)(e,{to:`/c/$`,params:{_splat:t.ref},className:`text-sm hover:text-fg-primary ${t.archived===void 0?`text-fg-secondary`:`text-fg-tertiary`}`,children:[`# `,t.name]}),(0,p.jsxs)(`span`,{className:`text-meta`,children:[` `,`· `,t.messages,` messages`,t.archived===void 0?``:` · archived ${o(t.archived.at,n)}`]})]},t.ref))})}export{g as n,m as r,h as t};